summaryrefslogtreecommitdiff
path: root/src/boot.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/boot.c')
-rw-r--r--src/boot.c404
1 files changed, 297 insertions, 107 deletions
diff --git a/src/boot.c b/src/boot.c
index 58b4286..4de450d 100644
--- a/src/boot.c
+++ b/src/boot.c
@@ -3,7 +3,8 @@
Copyright (C) 1993 Werner Almesberger <werner.almesberger@lrc.di.epfl.ch>
Copyright (C) 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
Copyright (C) 2008-2014 Daniel Baumann <mail@daniel-baumann.ch>
- Copyright (C) 2015 Andreas Bombe <aeb@debian.org>
+ Copyright (C) 2015-2017 Andreas Bombe <aeb@debian.org>
+ Copyright (C) 2018-2021 Pali Rohár <pali.rohar@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -25,6 +26,7 @@
/* FAT32, VFAT, Atari format support, and various fixes additions May 1998
* by Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de> */
+#include <limits.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
@@ -38,6 +40,7 @@
#include "io.h"
#include "boot.h"
#include "check.h"
+#include "charconv.h"
#define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0)
/* don't divide by zero */
@@ -46,6 +49,132 @@
#define FAT12_THRESHOLD 4085
#define FAT16_THRESHOLD 65525
+off_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern, int gen_name)
+{
+ static int curr_num = 0;
+ off_t offset;
+
+ if (fs->root_cluster) {
+ DIR_ENT d2;
+ int i = 0, got = 0;
+ uint32_t clu_num, prev = 0;
+ off_t offset2;
+
+ clu_num = fs->root_cluster;
+ offset = cluster_start(fs, clu_num);
+ while (clu_num > 0 && clu_num != -1) {
+ fs_read(offset, sizeof(DIR_ENT), &d2);
+ if (IS_FREE(d2.name) && d2.attr != VFAT_LN_ATTR) {
+ got = 1;
+ break;
+ }
+ i += sizeof(DIR_ENT);
+ offset += sizeof(DIR_ENT);
+ if ((i % fs->cluster_size) == 0) {
+ prev = clu_num;
+ if ((clu_num = next_cluster(fs, clu_num)) == 0 || clu_num == -1)
+ break;
+ offset = cluster_start(fs, clu_num);
+ }
+ }
+ if (!got) {
+ /* no free slot, need to extend root dir: alloc next free cluster
+ * after previous one */
+ if (!prev)
+ die("Root directory has no cluster allocated!");
+ for (clu_num = prev + 1; clu_num != prev; clu_num++) {
+ FAT_ENTRY entry;
+
+ if (clu_num >= fs->data_clusters + 2)
+ clu_num = 2;
+ get_fat(&entry, fs->fat, clu_num, fs);
+ if (!entry.value)
+ break;
+ }
+ if (clu_num == prev)
+ die("Root directory full and no free cluster");
+ set_fat(fs, prev, clu_num);
+ set_fat(fs, clu_num, -1);
+ set_owner(fs, clu_num, get_owner(fs, fs->root_cluster));
+ /* clear new cluster */
+ memset(&d2, 0, sizeof(d2));
+ offset = cluster_start(fs, clu_num);
+ for (i = 0; i < fs->cluster_size; i += sizeof(DIR_ENT))
+ fs_write(offset + i, sizeof(d2), &d2);
+ }
+ memset(de, 0, sizeof(DIR_ENT));
+ if (gen_name) {
+ while (1) {
+ char expanded[12];
+ sprintf(expanded, pattern, curr_num);
+ memcpy(de->name, expanded, MSDOS_NAME);
+ clu_num = fs->root_cluster;
+ i = 0;
+ offset2 = cluster_start(fs, clu_num);
+ while (clu_num > 0 && clu_num != -1) {
+ fs_read(offset2, sizeof(DIR_ENT), &d2);
+ if (offset2 != offset &&
+ !strncmp((const char *)d2.name, (const char *)de->name,
+ MSDOS_NAME))
+ break;
+ i += sizeof(DIR_ENT);
+ offset2 += sizeof(DIR_ENT);
+ if ((i % fs->cluster_size) == 0) {
+ if ((clu_num = next_cluster(fs, clu_num)) == 0 ||
+ clu_num == -1)
+ break;
+ offset2 = cluster_start(fs, clu_num);
+ }
+ }
+ if (clu_num == 0 || clu_num == -1)
+ break;
+ if (++curr_num >= 10000)
+ die("Unable to create unique name");
+ }
+ } else {
+ memcpy(de->name, pattern, MSDOS_NAME);
+ }
+ } else {
+ DIR_ENT *root;
+ int next_free = 0, scan;
+
+ root = alloc(fs->root_entries * sizeof(DIR_ENT));
+ fs_read(fs->root_start, fs->root_entries * sizeof(DIR_ENT), root);
+
+ while (next_free < fs->root_entries)
+ if (IS_FREE(root[next_free].name) &&
+ root[next_free].attr != VFAT_LN_ATTR)
+ break;
+ else
+ next_free++;
+ if (next_free == fs->root_entries)
+ die("Root directory is full.");
+ offset = fs->root_start + next_free * sizeof(DIR_ENT);
+ memset(de, 0, sizeof(DIR_ENT));
+ if (gen_name) {
+ while (1) {
+ char expanded[12];
+ sprintf(expanded, pattern, curr_num);
+ memcpy(de->name, expanded, MSDOS_NAME);
+ for (scan = 0; scan < fs->root_entries; scan++)
+ if (scan != next_free &&
+ !strncmp((const char *)root[scan].name,
+ (const char *)de->name, MSDOS_NAME))
+ break;
+ if (scan == fs->root_entries)
+ break;
+ if (++curr_num >= 10000)
+ die("Unable to create unique name");
+ }
+ } else {
+ memcpy(de->name, pattern, MSDOS_NAME);
+ }
+ free(root);
+ }
+ ++n_files;
+ return offset;
+}
+
static struct {
uint8_t media;
const char *descr;
@@ -103,8 +232,8 @@ static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss)
(unsigned long long)fs->fat_start,
(unsigned long long)fs->fat_start / lss);
printf("%10d FATs, %d bit entries\n", b->fats, fs->fat_bits);
- printf("%10lld bytes per FAT (= %llu sectors)\n", (long long)fs->fat_size,
- (long long)fs->fat_size / lss);
+ printf("%10u bytes per FAT (= %u sectors)\n", fs->fat_size,
+ fs->fat_size / lss);
if (!fs->root_cluster) {
printf("Root directory starts at byte %llu (sector %llu)\n",
(unsigned long long)fs->root_start,
@@ -140,11 +269,10 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, unsigned int l
printf("And there is no space for creating one!\n");
return;
}
- if (interactive)
- printf("1) Create one\n2) Do without a backup\n");
- else
- printf(" Auto-creating backup boot block.\n");
- if (!interactive || get_key("12", "?") == '1') {
+ if (get_choice(1, " Auto-creating backup boot block.",
+ 2,
+ 1, "Create one",
+ 2, "Do without a backup") == 1) {
unsigned int bbs;
/* The usual place for the backup boot sector is sector 6. Choose
* that or the last reserved sector. */
@@ -171,7 +299,7 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, unsigned int l
/* there are any differences */
uint8_t *p, *q;
int i, pos, first = 1;
- char buf[20];
+ char buf[32];
printf("There are differences between boot sector and its backup.\n");
printf("This is mostly harmless. Differences: (offset:original/backup)\n ");
@@ -190,16 +318,15 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, unsigned int l
}
printf("\n");
- if (interactive)
- printf("1) Copy original to backup\n"
- "2) Copy backup to original\n" "3) No action\n");
- else
- printf(" Not automatically fixing this.\n");
- switch (interactive ? get_key("123", "?") : '3') {
- case '1':
+ switch (get_choice(3, " Not automatically fixing this.",
+ 3,
+ 1, "Copy original to backup",
+ 2, "Copy backup to original",
+ 3, "No action")) {
+ case 1:
fs_write(fs->backupboot_start, sizeof(*b), b);
break;
- case '2':
+ case 2:
fs_write(0, sizeof(b2), &b2);
break;
default:
@@ -208,9 +335,8 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, unsigned int l
}
}
-static void init_fsinfo(struct info_sector *i)
+static void init_fsinfo_except_reserved(struct info_sector *i)
{
- memset(i, 0, sizeof (struct info_sector));
i->magic = htole32(0x41615252);
i->signature = htole32(0x61417272);
i->free_clusters = htole32(-1);
@@ -224,11 +350,10 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, unsigned int lss)
if (!b->info_sector) {
printf("No FSINFO sector\n");
- if (interactive)
- printf("1) Create one\n2) Do without FSINFO\n");
- else
- printf(" Not automatically creating it.\n");
- if (interactive && get_key("12", "?") == '1') {
+ if (get_choice(2, " Not automatically creating it.",
+ 2,
+ 1, "Create one",
+ 2, "Do without FSINFO") == 1) {
/* search for a free reserved sector (not boot sector and not
* backup boot sector) */
uint32_t s;
@@ -236,7 +361,8 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, unsigned int lss)
if (s != le16toh(b->backup_boot))
break;
if (s > 0 && s < le16toh(b->reserved)) {
- init_fsinfo(&i);
+ memset(&i, 0, sizeof (struct info_sector));
+ init_fsinfo_except_reserved(&i);
fs_write((off_t)s * lss, sizeof(i), &i);
b->info_sector = htole16(s);
fs_write(offsetof(struct boot_sector, info_sector),
@@ -272,12 +398,11 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, unsigned int lss)
printf(" Offset %llu: 0x%08x != expected 0x%08x\n",
(unsigned long long)offsetof(struct info_sector, boot_sign),
le32toh(i.boot_sign), 0xaa550000);
- if (interactive)
- printf("1) Correct\n2) Don't correct (FSINFO invalid then)\n");
- else
- printf(" Auto-correcting it.\n");
- if (!interactive || get_key("12", "?") == '1') {
- init_fsinfo(&i);
+ if (get_choice(1, " Auto-correcting it.",
+ 2,
+ 1, "Correct",
+ 2, "Don't correct (FSINFO invalid then)") == 1) {
+ init_fsinfo_except_reserved(&i);
fs_write(fs->fsinfo_start, sizeof(i), &i);
} else
fs->fsinfo_start = 0;
@@ -287,52 +412,15 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, unsigned int lss)
fs->free_clusters = le32toh(i.free_clusters);
}
-static char print_fat_dirty_state(void)
-{
- printf("Dirty bit is set. Fs was not properly unmounted and"
- " some data may be corrupt.\n");
-
- if (interactive) {
- printf("1) Remove dirty bit\n" "2) No action\n");
- return get_key("12", "?");
- } else
- printf(" Automatically removing dirty bit.\n");
- return '1';
-}
-
-static void check_fat_state_bit(DOS_FS * fs, void *b)
-{
- if (fs->fat_bits == 32) {
- struct boot_sector *b32 = b;
-
- if (b32->reserved3 & FAT_STATE_DIRTY) {
- printf("0x41: ");
- if (print_fat_dirty_state() == '1') {
- b32->reserved3 &= ~FAT_STATE_DIRTY;
- fs_write(0, sizeof(*b32), b32);
- }
- }
- } else {
- struct boot_sector_16 *b16 = b;
-
- if (b16->reserved2 & FAT_STATE_DIRTY) {
- printf("0x25: ");
- if (print_fat_dirty_state() == '1') {
- b16->reserved2 &= ~FAT_STATE_DIRTY;
- fs_write(0, sizeof(*b16), b16);
- }
- }
- }
-}
-
void read_boot(DOS_FS * fs)
{
struct boot_sector b;
unsigned total_sectors;
unsigned int logical_sector_size, sectors;
- off_t fat_length;
+ long long fat_length;
unsigned total_fat_entries;
off_t data_size;
+ long long position;
fs_read(0, sizeof(b), &b);
logical_sector_size = GET_UNALIGNED_W(b.sector_size);
@@ -343,7 +431,7 @@ void read_boot(DOS_FS * fs)
/* if the platform needs special handling of unaligned multibyte accesses */
/* but such handling isn't being provided. See GET_UNALIGNED_W() above. */
if (logical_sector_size & (SECTOR_SIZE - 1))
- die("Logical sector size (%d bytes) is not a multiple of the physical "
+ die("Logical sector size (%u bytes) is not a multiple of the physical "
"sector size.", logical_sector_size);
fs->cluster_size = b.cluster_size * logical_sector_size;
@@ -357,8 +445,11 @@ void read_boot(DOS_FS * fs)
if (verbose)
printf("Checking we can access the last sector of the filesystem\n");
/* Can't access last odd sector anyway, so round down */
- fs_test((off_t)((total_sectors & ~1) - 1) * logical_sector_size,
- logical_sector_size);
+ position = (long long)((total_sectors & ~1) - 1) * logical_sector_size;
+ if (position > OFF_MAX)
+ die("Filesystem is too large.");
+ if (!fs_test(position, logical_sector_size))
+ die("Failed to read sector %u.", (total_sectors & ~1) - 1);
fat_length = le16toh(b.fat_length) ?
le16toh(b.fat_length) : le32toh(b.fat32_length);
@@ -366,14 +457,22 @@ void read_boot(DOS_FS * fs)
die("FAT size is zero.");
fs->fat_start = (off_t)le16toh(b.reserved) * logical_sector_size;
- fs->root_start = ((off_t)le16toh(b.reserved) + b.fats * fat_length) *
+ position = (le16toh(b.reserved) + b.fats * fat_length) *
logical_sector_size;
+ if (position > OFF_MAX)
+ die("Filesystem is too large.");
+ fs->root_start = position;
fs->root_entries = GET_UNALIGNED_W(b.dir_entries);
- fs->data_start = fs->root_start + ROUND_TO_MULTIPLE(fs->root_entries <<
- MSDOS_DIR_BITS,
- logical_sector_size);
-
- data_size = (off_t)total_sectors * logical_sector_size - fs->data_start;
+ position = (long long)fs->root_start +
+ ROUND_TO_MULTIPLE(fs->root_entries << MSDOS_DIR_BITS,
+ logical_sector_size);
+ if (position > OFF_MAX)
+ die("Filesystem is too large.");
+ fs->data_start = position;
+ position = (long long)total_sectors * logical_sector_size - fs->data_start;
+ if (position > OFF_MAX)
+ die("Filesystem is too large.");
+ data_size = position;
if (data_size < fs->cluster_size)
die("Filesystem has no space for any data clusters");
@@ -390,23 +489,22 @@ void read_boot(DOS_FS * fs)
* (root_entries != 0), we handle the root dir the old way. Give a
* warning, but convertig to a root dir in a cluster chain seems
* to complex for now... */
- printf("Warning: FAT32 root dir not in cluster chain! "
+ fprintf(stderr, "Warning: FAT32 root dir not in cluster chain! "
"Compatibility mode...\n");
else if (!fs->root_cluster && !fs->root_entries)
die("No root directory!");
else if (fs->root_cluster && fs->root_entries)
- printf("Warning: FAT32 root dir is in a cluster chain, but "
+ fprintf(stderr, "Warning: FAT32 root dir is in a cluster chain, but "
"a separate root dir\n"
" area is defined. Cannot fix this easily.\n");
if (fs->data_clusters < FAT16_THRESHOLD)
- printf("Warning: Filesystem is FAT32 according to fat_length "
+ fprintf(stderr, "Warning: Filesystem is FAT32 according to fat_length "
"and fat32_length fields,\n"
" but has only %lu clusters, less than the required "
"minimum of %d.\n"
" This may lead to problems on some systems.\n",
(unsigned long)fs->data_clusters, FAT16_THRESHOLD);
- check_fat_state_bit(fs, &b);
fs->backupboot_start = le16toh(b.backup_boot) * logical_sector_size;
check_backup_boot(fs, &b, logical_sector_size);
@@ -418,7 +516,6 @@ void read_boot(DOS_FS * fs)
if (fs->data_clusters >= FAT16_THRESHOLD)
die("Too many clusters (%lu) for FAT16 filesystem.",
(unsigned long)fs->data_clusters);
- check_fat_state_bit(fs, &b);
} else {
/* On Atari, things are more difficult: GEMDOS always uses 12bit FATs
* on floppies, and always 16 bit on harddisks. */
@@ -433,23 +530,29 @@ void read_boot(DOS_FS * fs)
}
/* On FAT32, the high 4 bits of a FAT entry are reserved */
fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits;
- fs->fat_size = fat_length * logical_sector_size;
+ position = fat_length * logical_sector_size;
+ if (position > OFF_MAX)
+ die("Filesystem is too large.");
+ fs->fat_size = position;
- fs->label = calloc(12, sizeof(uint8_t));
+ fs->label[0] = 0;
if (fs->fat_bits == 12 || fs->fat_bits == 16) {
struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b;
- if (b16->extended_sig == 0x29)
+ if (b16->extended_sig == 0x29) {
memmove(fs->label, b16->label, 11);
- else
- fs->label = NULL;
+ fs->serial = b16->serial;
+ }
} else if (fs->fat_bits == 32) {
- if (b.extended_sig == 0x29)
+ if (b.extended_sig == 0x29) {
memmove(fs->label, &b.label, 11);
- else
- fs->label = NULL;
+ fs->serial = b.serial;
+ }
}
- total_fat_entries = (uint64_t)fs->fat_size * 8 / fs->fat_bits;
+ position = (long long)fs->fat_size * 8 / fs->fat_bits;
+ if (position > UINT_MAX)
+ die("FAT has space for too many entries (%lld).", (long long)position);
+ total_fat_entries = position;
if (fs->data_clusters > total_fat_entries - 2)
die("Filesystem has %u clusters but only space for %u FAT entries.",
fs->data_clusters, total_fat_entries - 2);
@@ -470,7 +573,8 @@ void read_boot(DOS_FS * fs)
dump_boot(fs, &b, logical_sector_size);
}
-static void write_boot_label(DOS_FS * fs, char *label)
+static void write_boot_label_or_serial(int label_mode, DOS_FS * fs,
+ const char *label, uint32_t serial)
{
if (fs->fat_bits == 12 || fs->fat_bits == 16) {
struct boot_sector_16 b16;
@@ -479,10 +583,16 @@ static void write_boot_label(DOS_FS * fs, char *label)
if (b16.extended_sig != 0x29) {
b16.extended_sig = 0x29;
b16.serial = 0;
+ memmove(b16.label, "NO NAME ", 11);
memmove(b16.fs_type, fs->fat_bits == 12 ? "FAT12 " : "FAT16 ",
8);
}
- memmove(b16.label, label, 11);
+
+ if (label_mode)
+ memmove(b16.label, label, 11);
+ else
+ b16.serial = serial;
+
fs_write(0, sizeof(b16), &b16);
} else if (fs->fat_bits == 32) {
struct boot_sector b;
@@ -491,15 +601,31 @@ static void write_boot_label(DOS_FS * fs, char *label)
if (b.extended_sig != 0x29) {
b.extended_sig = 0x29;
b.serial = 0;
+ memmove(b.label, "NO NAME ", 11);
memmove(b.fs_type, "FAT32 ", 8);
}
- memmove(b.label, label, 11);
+
+ if (label_mode)
+ memmove(b.label, label, 11);
+ else
+ b.serial = serial;
+
fs_write(0, sizeof(b), &b);
if (fs->backupboot_start)
fs_write(fs->backupboot_start, sizeof(b), &b);
}
}
+void write_boot_label(DOS_FS * fs, const char *label)
+{
+ write_boot_label_or_serial(1, fs, label, 0);
+}
+
+void write_serial(DOS_FS * fs, uint32_t serial)
+{
+ write_boot_label_or_serial(0, fs, NULL, serial);
+}
+
off_t find_volume_de(DOS_FS * fs, DIR_ENT * de)
{
uint32_t cluster;
@@ -513,7 +639,13 @@ off_t find_volume_de(DOS_FS * fs, DIR_ENT * de)
offset = cluster_start(fs, cluster);
for (i = 0; i * sizeof(DIR_ENT) < fs->cluster_size; i++) {
fs_read(offset, sizeof(DIR_ENT), de);
- if (de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME)
+
+ /* no point in scanning after end of directory marker */
+ if (!de->name[0])
+ return 0;
+
+ if (!IS_FREE(de->name) &&
+ de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME)
return offset;
offset += sizeof(DIR_ENT);
}
@@ -522,7 +654,13 @@ off_t find_volume_de(DOS_FS * fs, DIR_ENT * de)
for (i = 0; i < fs->root_entries; i++) {
offset = fs->root_start + i * sizeof(DIR_ENT);
fs_read(offset, sizeof(DIR_ENT), de);
- if (de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME)
+
+ /* no point in scanning after end of directory marker */
+ if (!de->name[0])
+ return 0;
+
+ if (!IS_FREE(de->name) &&
+ de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME)
return offset;
}
}
@@ -530,10 +668,10 @@ off_t find_volume_de(DOS_FS * fs, DIR_ENT * de)
return 0;
}
-static void write_volume_label(DOS_FS * fs, char *label)
+void write_volume_label(DOS_FS * fs, char *label)
{
- time_t now = time(NULL);
- struct tm *mtime = localtime(&now);
+ time_t now;
+ struct tm *mtime;
off_t offset;
int created;
DIR_ENT de;
@@ -544,13 +682,25 @@ static void write_volume_label(DOS_FS * fs, char *label)
created = 1;
offset = alloc_rootdir_entry(fs, &de, label, 0);
}
+
memcpy(de.name, label, 11);
- de.time = htole16((unsigned short)((mtime->tm_sec >> 1) +
- (mtime->tm_min << 5) +
- (mtime->tm_hour << 11)));
- de.date = htole16((unsigned short)(mtime->tm_mday +
- ((mtime->tm_mon + 1) << 5) +
- ((mtime->tm_year - 80) << 9)));
+ if (de.name[0] == 0xe5)
+ de.name[0] = 0x05;
+
+ now = time(NULL);
+ mtime = (now != (time_t)-1) ? localtime(&now) : NULL;
+ if (mtime && mtime->tm_year >= 80 && mtime->tm_year <= 207) {
+ de.time = htole16((unsigned short)((mtime->tm_sec >> 1) +
+ (mtime->tm_min << 5) +
+ (mtime->tm_hour << 11)));
+ de.date = htole16((unsigned short)(mtime->tm_mday +
+ ((mtime->tm_mon + 1) << 5) +
+ ((mtime->tm_year - 80) << 9)));
+ } else {
+ /* fallback to 1.1.1980 00:00:00 */
+ de.time = htole16(0);
+ de.date = htole16(1 + (1 << 5));
+ }
if (created) {
de.attr = ATTR_VOLUME;
de.ctime_ms = 0;
@@ -575,3 +725,43 @@ void write_label(DOS_FS * fs, char *label)
write_boot_label(fs, label);
write_volume_label(fs, label);
}
+
+void remove_label(DOS_FS *fs)
+{
+ off_t offset;
+ DIR_ENT de;
+
+ write_boot_label(fs, "NO NAME ");
+
+ offset = find_volume_de(fs, &de);
+ if (offset) {
+ /* mark entry as deleted */
+ de.name[0] = 0xe5;
+ /* remove ATTR_VOLUME for compatibility with older fatlabel version
+ * which ignores above deletion mark for entries with ATTR_VOLUME */
+ de.attr = 0;
+ fs_write(offset, sizeof(DIR_ENT), &de);
+ }
+}
+
+const char *pretty_label(const char *label)
+{
+ static char buffer[256];
+ char *p;
+ int i;
+ int last;
+
+ for (last = 10; last >= 0; last--) {
+ if (label[last] != ' ')
+ break;
+ }
+
+ p = buffer;
+ for (i = 0; i <= last && label[i] && p < buffer + sizeof(buffer) - 1; ++i) {
+ if (!dos_char_to_printable(&p, label[i], buffer + sizeof(buffer) - 1 - p))
+ *p++ = '_';
+ }
+ *p = 0;
+
+ return buffer;
+}