From 18794c820f109e6de2e16af0f058b562b0492cbc Mon Sep 17 00:00:00 2001 From: Matt Mower Date: Wed, 11 Nov 2015 16:22:45 -0600 Subject: Update dosfstools * Version 3.0.28 * Update filenames in source * Remove unnecessary symlink to fsck * Commit "Recode short filenames from DOS codepage (default 437)." has been reverted since we do not have access to iconv * Commits cherry-picked on top of 3.0.28: - mkfs.fat: fix incorrect int type - Prevent out of bound array read in date_dos2unix() Change-Id: I50310235c62ec2e6bc90afcd10f2814d3afb5113 --- dosfstools/src/boot.c | 226 +++--- dosfstools/src/boot.h | 4 +- dosfstools/src/check.c | 184 +++-- dosfstools/src/check.h | 7 +- dosfstools/src/common.c | 9 +- dosfstools/src/common.h | 11 +- dosfstools/src/dosfsck.c | 224 ------ dosfstools/src/dosfsck.h | 218 ------ dosfstools/src/dosfslabel.c | 128 ---- dosfstools/src/fat.c | 107 +-- dosfstools/src/fat.h | 21 +- dosfstools/src/fatlabel.c | 144 ++++ dosfstools/src/file.c | 12 +- dosfstools/src/file.h | 5 +- dosfstools/src/fsck.fat.c | 215 ++++++ dosfstools/src/fsck.fat.h | 191 +++++ dosfstools/src/io.c | 16 +- dosfstools/src/io.h | 11 +- dosfstools/src/lfn.c | 62 +- dosfstools/src/lfn.h | 3 +- dosfstools/src/mkdosfs.c | 1733 ------------------------------------------ dosfstools/src/mkfs.fat.c | 1754 +++++++++++++++++++++++++++++++++++++++++++ dosfstools/src/msdos_fs.h | 61 ++ dosfstools/src/version.h | 7 +- 24 files changed, 2773 insertions(+), 2580 deletions(-) delete mode 100644 dosfstools/src/dosfsck.c delete mode 100644 dosfstools/src/dosfsck.h delete mode 100644 dosfstools/src/dosfslabel.c create mode 100644 dosfstools/src/fatlabel.c create mode 100644 dosfstools/src/fsck.fat.c create mode 100644 dosfstools/src/fsck.fat.h delete mode 100644 dosfstools/src/mkdosfs.c create mode 100644 dosfstools/src/mkfs.fat.c create mode 100644 dosfstools/src/msdos_fs.h (limited to 'dosfstools/src') diff --git a/dosfstools/src/boot.c b/dosfstools/src/boot.c index bbaee0471..0c0918f8b 100644 --- a/dosfstools/src/boot.c +++ b/dosfstools/src/boot.c @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -24,16 +25,17 @@ * by Roman Hodek */ #include +#include #include -#include #include #include #include "common.h" -#include "dosfsck.h" +#include "fsck.fat.h" #include "fat.h" #include "io.h" #include "boot.h" +#include "check.h" #define ROUND_TO_MULTIPLE(n,m) ((n) && (m) ? (n)+(m)-1-((n)-1)%(m) : 0) /* don't divide by zero */ @@ -43,8 +45,8 @@ #define FAT16_THRESHOLD 65525 static struct { - __u8 media; - char *descr; + uint8_t media; + const char *descr; } mediabytes[] = { { 0xf0, "5.25\" or 3.5\" HD floppy"}, { @@ -58,20 +60,11 @@ static struct { 0xfe, "5.25\" 160k floppy 1s/40tr/8sec"}, { 0xff, "5.25\" 320k floppy 2s/40tr/8sec"},}; -#if defined __alpha || defined __arm || defined __arm__ || defined __ia64__ || defined __x86_64__ \ - || defined __ppc64__ || defined __bfin__ || defined __MICROBLAZE__ -/* Unaligned fields must first be copied byte-wise */ +/* Unaligned fields must first be accessed byte-wise */ #define GET_UNALIGNED_W(f) \ - ({ \ - unsigned short __v; \ - memcpy( &__v, &f, sizeof(__v) ); \ - CF_LE_W( *(unsigned short *)&__v ); \ - }) -#else -#define GET_UNALIGNED_W(f) CF_LE_W( *(unsigned short *)&f ) -#endif + ( (uint16_t)f[0] | ((uint16_t)f[1]<<8) ) -static char *get_media_descr(unsigned char media) +static const char *get_media_descr(unsigned char media) { int i; @@ -96,14 +89,14 @@ static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss) /* On Atari, a 24 bit serial number is stored at offset 8 of the boot * sector */ printf("Serial number 0x%x\n", - b->system_id[5] | (b-> - system_id[6] << 8) | (b->system_id[7] << 16)); + b->system_id[5] | (b->system_id[6] << 8) | (b-> + system_id[7] << 16)); } printf("Media byte 0x%02x (%s)\n", b->media, get_media_descr(b->media)); printf("%10d bytes per logical sector\n", GET_UNALIGNED_W(b->sector_size)); printf("%10d bytes per cluster\n", fs->cluster_size); - printf("%10d reserved sector%s\n", CF_LE_W(b->reserved), - CF_LE_W(b->reserved) == 1 ? "" : "s"); + printf("%10d reserved sector%s\n", le16toh(b->reserved), + le16toh(b->reserved) == 1 ? "" : "s"); printf("First FAT starts at byte %llu (sector %llu)\n", (unsigned long long)fs->fat_start, (unsigned long long)fs->fat_start / lss); @@ -117,21 +110,21 @@ static void dump_boot(DOS_FS * fs, struct boot_sector *b, unsigned lss) printf("%10d root directory entries\n", fs->root_entries); } else { printf("Root directory start at cluster %lu (arbitrary size)\n", - fs->root_cluster); + (unsigned long)fs->root_cluster); } printf("Data area starts at byte %llu (sector %llu)\n", (unsigned long long)fs->data_start, (unsigned long long)fs->data_start / lss); - printf("%10lu data clusters (%llu bytes)\n", fs->clusters, + printf("%10lu data clusters (%llu bytes)\n", (unsigned long)fs->clusters, (unsigned long long)fs->clusters * fs->cluster_size); - printf("%u sectors/track, %u heads\n", CF_LE_W(b->secs_track), - CF_LE_W(b->heads)); + printf("%u sectors/track, %u heads\n", le16toh(b->secs_track), + le16toh(b->heads)); printf("%10u hidden sectors\n", atari_format ? /* On Atari, the hidden field is only 16 bit wide and unused */ (((unsigned char *)&b->hidden)[0] | - ((unsigned char *)&b->hidden)[1] << 8) : CF_LE_L(b->hidden)); + ((unsigned char *)&b->hidden)[1] << 8) : le32toh(b->hidden)); sectors = GET_UNALIGNED_W(b->sectors); - printf("%10u sectors total\n", sectors ? sectors : CF_LE_L(b->total_sect)); + printf("%10u sectors total\n", sectors ? sectors : le32toh(b->total_sect)); } static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) @@ -140,7 +133,7 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) if (!fs->backupboot_start) { printf("There is no backup boot sector.\n"); - if (CF_LE_W(b->reserved) < 3) { + if (le16toh(b->reserved) < 3) { printf("And there is no space for creating one!\n"); return; } @@ -152,15 +145,15 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) int bbs; /* The usual place for the backup boot sector is sector 6. Choose * that or the last reserved sector. */ - if (CF_LE_W(b->reserved) >= 7 && CF_LE_W(b->info_sector) != 6) + if (le16toh(b->reserved) >= 7 && le16toh(b->info_sector) != 6) bbs = 6; else { - bbs = CF_LE_W(b->reserved) - 1; - if (bbs == CF_LE_W(b->info_sector)) + bbs = le16toh(b->reserved) - 1; + if (bbs == le16toh(b->info_sector)) --bbs; /* this is never 0, as we checked reserved >= 3! */ } fs->backupboot_start = bbs * lss; - b->backup_boot = CT_LE_W(bbs); + b->backup_boot = htole16(bbs); fs_write(fs->backupboot_start, sizeof(*b), b); fs_write((loff_t) offsetof(struct boot_sector, backup_boot), sizeof(b->backup_boot), &b->backup_boot); @@ -173,18 +166,18 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) fs_read(fs->backupboot_start, sizeof(b2), &b2); if (memcmp(b, &b2, sizeof(b2)) != 0) { /* there are any differences */ - __u8 *p, *q; + uint8_t *p, *q; int i, pos, first = 1; char buf[20]; printf("There are differences between boot sector and its backup.\n"); - printf("Differences: (offset:original/backup)\n "); + printf("This is mostly harmless. Differences: (offset:original/backup)\n "); pos = 2; - for (p = (__u8 *) b, q = (__u8 *) & b2, i = 0; i < sizeof(b2); + for (p = (uint8_t *) b, q = (uint8_t *) & b2, i = 0; i < sizeof(b2); ++p, ++q, ++i) { if (*p != *q) { sprintf(buf, "%s%u:%02x/%02x", first ? "" : ", ", - (unsigned)(p - (__u8 *) b), *p, *q); + (unsigned)(p - (uint8_t *) b), *p, *q); if (pos + strlen(buf) > 78) printf("\n "), pos = 2; printf("%s", buf); @@ -214,11 +207,11 @@ static void check_backup_boot(DOS_FS * fs, struct boot_sector *b, int lss) static void init_fsinfo(struct info_sector *i) { - i->magic = CT_LE_L(0x41615252); - i->signature = CT_LE_L(0x61417272); - i->free_clusters = CT_LE_L(-1); - i->next_cluster = CT_LE_L(2); - i->boot_sign = CT_LE_W(0xaa55); + i->magic = htole32(0x41615252); + i->signature = htole32(0x61417272); + i->free_clusters = htole32(-1); + i->next_cluster = htole32(2); + i->boot_sign = htole16(0xaa55); } static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) @@ -234,14 +227,14 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) if (interactive && get_key("12", "?") == '1') { /* search for a free reserved sector (not boot sector and not * backup boot sector) */ - __u32 s; - for (s = 1; s < CF_LE_W(b->reserved); ++s) - if (s != CF_LE_W(b->backup_boot)) + uint32_t s; + for (s = 1; s < le16toh(b->reserved); ++s) + if (s != le16toh(b->backup_boot)) break; - if (s > 0 && s < CF_LE_W(b->reserved)) { + if (s > 0 && s < le16toh(b->reserved)) { init_fsinfo(&i); fs_write((loff_t) s * lss, sizeof(i), &i); - b->info_sector = CT_LE_W(s); + b->info_sector = htole16(s); fs_write((loff_t) offsetof(struct boot_sector, info_sector), sizeof(b->info_sector), &b->info_sector); if (fs->backupboot_start) @@ -257,24 +250,24 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) return; } - fs->fsinfo_start = CF_LE_W(b->info_sector) * lss; + fs->fsinfo_start = le16toh(b->info_sector) * lss; fs_read(fs->fsinfo_start, sizeof(i), &i); - if (i.magic != CT_LE_L(0x41615252) || - i.signature != CT_LE_L(0x61417272) || i.boot_sign != CT_LE_W(0xaa55)) { + if (i.magic != htole32(0x41615252) || + i.signature != htole32(0x61417272) || i.boot_sign != htole16(0xaa55)) { printf("FSINFO sector has bad magic number(s):\n"); - if (i.magic != CT_LE_L(0x41615252)) + if (i.magic != htole32(0x41615252)) printf(" Offset %llu: 0x%08x != expected 0x%08x\n", (unsigned long long)offsetof(struct info_sector, magic), - CF_LE_L(i.magic), 0x41615252); - if (i.signature != CT_LE_L(0x61417272)) + le32toh(i.magic), 0x41615252); + if (i.signature != htole32(0x61417272)) printf(" Offset %llu: 0x%08x != expected 0x%08x\n", (unsigned long long)offsetof(struct info_sector, signature), - CF_LE_L(i.signature), 0x61417272); - if (i.boot_sign != CT_LE_W(0xaa55)) + le32toh(i.signature), 0x61417272); + if (i.boot_sign != htole16(0xaa55)) printf(" Offset %llu: 0x%04x != expected 0x%04x\n", (unsigned long long)offsetof(struct info_sector, boot_sign), - CF_LE_W(i.boot_sign), 0xaa55); + le16toh(i.boot_sign), 0xaa55); if (interactive) printf("1) Correct\n2) Don't correct (FSINFO invalid then)\n"); else @@ -287,7 +280,45 @@ static void read_fsinfo(DOS_FS * fs, struct boot_sector *b, int lss) } if (fs->fsinfo_start) - fs->free_clusters = CF_LE_L(i.free_clusters); + 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) @@ -317,16 +348,16 @@ void read_boot(DOS_FS * fs) die("Currently, only 1 or 2 FATs are supported, not %d.\n", b.fats); fs->nfats = b.fats; sectors = GET_UNALIGNED_W(b.sectors); - total_sectors = sectors ? sectors : CF_LE_L(b.total_sect); + total_sectors = sectors ? sectors : le32toh(b.total_sect); 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((loff_t) ((total_sectors & ~1) - 1) * (loff_t) logical_sector_size, logical_sector_size); - fat_length = CF_LE_W(b.fat_length) ? - CF_LE_W(b.fat_length) : CF_LE_L(b.fat32_length); - fs->fat_start = (loff_t) CF_LE_W(b.reserved) * logical_sector_size; - fs->root_start = ((loff_t) CF_LE_W(b.reserved) + b.fats * fat_length) * + fat_length = le16toh(b.fat_length) ? + le16toh(b.fat_length) : le32toh(b.fat32_length); + fs->fat_start = (loff_t) le16toh(b.reserved) * logical_sector_size; + fs->root_start = ((loff_t) le16toh(b.reserved) + b.fats * fat_length) * logical_sector_size; fs->root_entries = GET_UNALIGNED_W(b.dir_entries); fs->data_start = fs->root_start + ROUND_TO_MULTIPLE(fs->root_entries << @@ -339,7 +370,7 @@ void read_boot(DOS_FS * fs) fs->free_clusters = -1; /* unknown */ if (!b.fat_length && b.fat32_length) { fs->fat_bits = 32; - fs->root_cluster = CF_LE_L(b.root_cluster); + fs->root_cluster = le32toh(b.root_cluster); if (!fs->root_cluster && fs->root_entries) /* M$ hasn't specified this, but it looks reasonable: If * root_cluster is 0 but there is a separate root dir @@ -360,9 +391,10 @@ void read_boot(DOS_FS * fs) " but has only %lu clusters, less than the required " "minimum of %d.\n" " This may lead to problems on some systems.\n", - fs->clusters, FAT16_THRESHOLD); + (unsigned long)fs->clusters, FAT16_THRESHOLD); - fs->backupboot_start = CF_LE_W(b.backup_boot) * logical_sector_size; + check_fat_state_bit(fs, &b); + fs->backupboot_start = le16toh(b.backup_boot) * logical_sector_size; check_backup_boot(fs, &b, logical_sector_size); read_fsinfo(fs, &b, logical_sector_size); @@ -372,6 +404,7 @@ void read_boot(DOS_FS * fs) fs->fat_bits = (fs->clusters >= FAT12_THRESHOLD) ? 16 : 12; if (fs->clusters >= FAT16_THRESHOLD) die("Too many clusters (%lu) for FAT16 filesystem.", fs->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. */ @@ -392,7 +425,7 @@ void read_boot(DOS_FS * fs) fs->eff_fat_bits = (fs->fat_bits == 32) ? 28 : fs->fat_bits; fs->fat_size = fat_length * logical_sector_size; - fs->label = calloc(12, sizeof(__u8)); + fs->label = calloc(12, sizeof(uint8_t)); if (fs->fat_bits == 12 || fs->fat_bits == 16) { struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b; if (b16->extended_sig == 0x29) @@ -407,8 +440,8 @@ void read_boot(DOS_FS * fs) } if (fs->clusters > - ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2) - die("File system has %d clusters but only space for %d FAT entries.", + ((uint64_t)fs->fat_size * 8 / fs->fat_bits) - 2) + die("Filesystem has %d clusters but only space for %d FAT entries.", fs->clusters, ((unsigned long long)fs->fat_size * 8 / fs->fat_bits) - 2); if (!fs->root_entries && !fs->root_cluster) @@ -430,34 +463,37 @@ void read_boot(DOS_FS * fs) static void write_boot_label(DOS_FS * fs, char *label) { - struct boot_sector b; - struct boot_sector_16 *b16 = (struct boot_sector_16 *)&b; - - fs_read(0, sizeof(b), &b); if (fs->fat_bits == 12 || fs->fat_bits == 16) { - if (b16->extended_sig != 0x29) { - b16->extended_sig = 0x29; - b16->serial = 0; - memmove(b16->fs_type, fs->fat_bits == 12 ? "FAT12 " : "FAT16 ", + struct boot_sector_16 b16; + + fs_read(0, sizeof(b16), &b16); + if (b16.extended_sig != 0x29) { + b16.extended_sig = 0x29; + b16.serial = 0; + memmove(b16.fs_type, fs->fat_bits == 12 ? "FAT12 " : "FAT16 ", 8); } - memmove(b16->label, label, 11); + memmove(b16.label, label, 11); + fs_write(0, sizeof(b16), &b16); } else if (fs->fat_bits == 32) { + struct boot_sector b; + + fs_read(0, sizeof(b), &b); if (b.extended_sig != 0x29) { b.extended_sig = 0x29; b.serial = 0; memmove(b.fs_type, "FAT32 ", 8); } memmove(b.label, label, 11); + fs_write(0, sizeof(b), &b); + if (fs->backupboot_start) + fs_write(fs->backupboot_start, sizeof(b), &b); } - fs_write(0, sizeof(b), &b); - if (fs->fat_bits == 32 && fs->backupboot_start) - fs_write(fs->backupboot_start, sizeof(b), &b); } -static loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de) +loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de) { - unsigned long cluster; + uint32_t cluster; loff_t offset; int i; @@ -468,7 +504,7 @@ static loff_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 & ATTR_VOLUME) + if (de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME) return offset; offset += sizeof(DIR_ENT); } @@ -477,7 +513,7 @@ static loff_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 & ATTR_VOLUME) + if (de->attr != VFAT_LN_ATTR && de->attr & ATTR_VOLUME) return offset; } } @@ -490,19 +526,33 @@ static void write_volume_label(DOS_FS * fs, char *label) time_t now = time(NULL); struct tm *mtime = localtime(&now); loff_t offset; + int created; DIR_ENT de; + created = 0; offset = find_volume_de(fs, &de); - if (offset == 0) - return; - + if (offset == 0) { + created = 1; + offset = alloc_rootdir_entry(fs, &de, label); + } memcpy(de.name, label, 11); - de.time = CT_LE_W((unsigned short)((mtime->tm_sec >> 1) + + de.time = htole16((unsigned short)((mtime->tm_sec >> 1) + (mtime->tm_min << 5) + (mtime->tm_hour << 11))); - de.date = CT_LE_W((unsigned short)(mtime->tm_mday + + de.date = htole16((unsigned short)(mtime->tm_mday + ((mtime->tm_mon + 1) << 5) + ((mtime->tm_year - 80) << 9))); + if (created) { + de.attr = ATTR_VOLUME; + de.ctime_ms = 0; + de.ctime = de.time; + de.cdate = de.date; + de.adate = de.date; + de.starthi = 0; + de.start = 0; + de.size = 0; + } + fs_write(offset, sizeof(DIR_ENT), &de); } diff --git a/dosfstools/src/boot.h b/dosfstools/src/boot.h index c9edfa337..d52e62476 100644 --- a/dosfstools/src/boot.h +++ b/dosfstools/src/boot.h @@ -1,6 +1,7 @@ /* boot.h - Read and analyze ia PC/MS-DOS boot sector Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -24,6 +25,7 @@ void read_boot(DOS_FS * fs); void write_label(DOS_FS * fs, char *label); +loff_t find_volume_de(DOS_FS * fs, DIR_ENT * de); /* Reads the boot sector from the currently open device and initializes *FS */ diff --git a/dosfstools/src/check.c b/dosfstools/src/check.c index 3f175b019..bbb97e4f3 100644 --- a/dosfstools/src/check.c +++ b/dosfstools/src/check.c @@ -1,7 +1,8 @@ -/* check.c - Check and repair a PC/MS-DOS file system +/* check.c - Check and repair a PC/MS-DOS filesystem Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -30,7 +31,7 @@ #include #include "common.h" -#include "dosfsck.h" +#include "fsck.fat.h" #include "io.h" #include "fat.h" #include "file.h" @@ -41,8 +42,8 @@ static DOS_FILE *root; /* get start field of a dir entry */ #define FSTART(p,fs) \ - ((unsigned long)CF_LE_W(p->dir_ent.start) | \ - (fs->fat_bits == 32 ? CF_LE_W(p->dir_ent.starthi) << 16 : 0)) + ((uint32_t)le16toh(p->dir_ent.start) | \ + (fs->fat_bits == 32 ? le16toh(p->dir_ent.starthi) << 16 : 0)) #define MODIFY(p,i,v) \ do { \ @@ -55,22 +56,22 @@ static DOS_FILE *root; #define MODIFY_START(p,v,fs) \ do { \ - unsigned long __v = (v); \ + uint32_t __v = (v); \ if (!p->offset) { \ /* writing to fake entry for FAT32 root dir */ \ if (!__v) die("Oops, deleting FAT32 root dir!"); \ fs->root_cluster = __v; \ - p->dir_ent.start = CT_LE_W(__v&0xffff); \ - p->dir_ent.starthi = CT_LE_W(__v>>16); \ - __v = CT_LE_L(__v); \ + p->dir_ent.start = htole16(__v&0xffff); \ + p->dir_ent.starthi = htole16(__v>>16); \ + __v = htole32(__v); \ fs_write((loff_t)offsetof(struct boot_sector,root_cluster), \ sizeof(((struct boot_sector *)0)->root_cluster), \ &__v); \ } \ else { \ - MODIFY(p,start,CT_LE_W((__v)&0xffff)); \ + MODIFY(p,start,htole16((__v)&0xffff)); \ if (fs->fat_bits == 32) \ - MODIFY(p,starthi,CT_LE_W((__v)>>16)); \ + MODIFY(p,starthi,htole16((__v)>>16)); \ } \ } while(0) @@ -82,7 +83,7 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) if (fs->root_cluster) { DIR_ENT d2; int i = 0, got = 0; - unsigned long clu_num, prev = 0; + uint32_t clu_num, prev = 0; loff_t offset2; clu_num = fs->root_cluster; @@ -131,8 +132,8 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) while (1) { char expanded[12]; sprintf(expanded, pattern, curr_num); - memcpy(de->name + 4, expanded, 4); - memcpy(de->ext, expanded + 4, 3); + memcpy(de->name, expanded, 8); + memcpy(de->ext, expanded + 8, 3); clu_num = fs->root_cluster; i = 0; offset2 = cluster_start(fs, clu_num); @@ -174,7 +175,10 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern) offset = fs->root_start + next_free * sizeof(DIR_ENT); memset(de, 0, sizeof(DIR_ENT)); while (1) { - sprintf((char *)de->name, pattern, curr_num); + char expanded[12]; + sprintf(expanded, pattern, curr_num); + memcpy(de->name, expanded, 8); + memcpy(de->ext, expanded + 8, 3); for (scan = 0; scan < fs->root_entries; scan++) if (scan != next_free && !strncmp((const char *)root[scan].name, @@ -220,18 +224,22 @@ static char *path_name(DOS_FILE * file) return path; } -static int day_n[] = - { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; - /* JanFebMarApr May Jun Jul Aug Sep Oct Nov Dec */ +static const int day_n[] = + { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 0, 0, 0, 0 }; +/* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */ /* Convert a MS-DOS time/date pair to a UNIX date (seconds since 1 1 70). */ -time_t date_dos2unix(unsigned short time, unsigned short date) +static time_t date_dos2unix(unsigned short time, unsigned short date) { int month, year; time_t secs; month = ((date >> 5) & 15) - 1; + if (month < 0) { + /* make sure that nothing bad happens if the month bits were zero */ + month = 0; + } year = date >> 9; secs = (time & 31) * 2 + 60 * ((time >> 5) & 63) + (time >> 11) * 3600 + @@ -249,18 +257,19 @@ static char *file_stat(DOS_FILE * file) time_t date; date = - date_dos2unix(CF_LE_W(file->dir_ent.time), CF_LE_W(file->dir_ent.date)); + date_dos2unix(le16toh(file->dir_ent.time), le16toh(file->dir_ent.date)); tm = localtime(&date); strftime(tmp, 99, "%H:%M:%S %b %d %Y", tm); - sprintf(temp, " Size %u bytes, date %s", CF_LE_L(file->dir_ent.size), tmp); + sprintf(temp, " Size %u bytes, date %s", le32toh(file->dir_ent.size), tmp); return temp; } static int bad_name(DOS_FILE * file) { int i, spc, suspicious = 0; - char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; - unsigned char *name = file->dir_ent.name; + const char *bad_chars = atari_format ? "*?\\/:" : "*?<>|\"\\/:"; + const unsigned char *name = file->dir_ent.name; + const unsigned char *ext = file->dir_ent.ext; /* Do not complain about (and auto-correct) the extended attribute files * of OS/2. */ @@ -268,6 +277,11 @@ static int bad_name(DOS_FILE * file) strncmp((const char *)name, "WP ROOT SF", 11) == 0) return 0; + /* check if we have neither a long filename nor a short name */ + if ((file->lfn == NULL) && (file->dir_ent.lcase & FAT_NO_83NAME)) { + return 1; + } + /* don't complain about the dummy 11 bytes used by patched Linux kernels */ if (file->dir_ent.lcase & FAT_NO_83NAME) @@ -282,12 +296,12 @@ static int bad_name(DOS_FILE * file) return 1; } - for (i = 8; i < 11; i++) { - if (name[i] < ' ' || name[i] == 0x7f) + for (i = 0; i < 3; i++) { + if (ext[i] < ' ' || ext[i] == 0x7f) return 1; - if (name[i] > 0x7f) + if (ext[i] > 0x7f) ++suspicious; - if (strchr(bad_chars, name[i])) + if (strchr(bad_chars, ext[i])) return 1; } @@ -302,11 +316,11 @@ static int bad_name(DOS_FILE * file) } spc = 0; - for (i = 8; i < 11; i++) { - if (name[i] == ' ') + for (i = 0; i < 3; i++) { + if (ext[i] == ' ') spc = 1; else if (spc) - /* non-space after a space not allowed, space terminates the name + /* non-space after a space not allowed, space terminates the ext * part */ return 1; } @@ -340,7 +354,7 @@ static void lfn_remove(loff_t from, loff_t to) static void drop_file(DOS_FS * fs, DOS_FILE * file) { - unsigned long cluster; + uint32_t cluster; MODIFY(file, name[0], DELETED_FLAG); if (file->lfn) @@ -351,13 +365,12 @@ static void drop_file(DOS_FS * fs, DOS_FILE * file) --n_files; } -static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters) +static void truncate_file(DOS_FS * fs, DOS_FILE * file, uint32_t clusters) { int deleting; - unsigned long walk, next, prev; + uint32_t walk, next; walk = FSTART(file, fs); - prev = 0; if ((deleting = !clusters)) MODIFY_START(file, 0, fs); while (walk > 0 && walk != -1) { @@ -366,7 +379,6 @@ static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters) set_fat(fs, walk, 0); else if ((deleting = !--clusters)) set_fat(fs, walk, -1); - prev = walk; walk = next; } } @@ -374,7 +386,7 @@ static void truncate_file(DOS_FS * fs, DOS_FILE * file, unsigned long clusters) static void auto_rename(DOS_FILE * file) { DOS_FILE *first, *walk; - unsigned long int number; + uint32_t number; if (!file->offset) return; /* cannot rename FAT32 root dir */ @@ -382,7 +394,7 @@ static void auto_rename(DOS_FILE * file) number = 0; while (1) { char num[8]; - sprintf(num, "%07lu", number); + sprintf(num, "%07lu", (unsigned long)number); memcpy(file->dir_ent.name, "FSCK", 4); memcpy(file->dir_ent.name + 4, num, 4); memcpy(file->dir_ent.ext, num + 4, 3); @@ -392,7 +404,16 @@ static void auto_rename(DOS_FILE * file) (const char *)file->dir_ent.name, MSDOS_NAME)) break; if (!walk) { - fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + if (file->dir_ent.lcase & FAT_NO_83NAME) { + /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not + present */ + file->dir_ent.lcase &= ~FAT_NO_83NAME; + /* reset the attributes, only keep DIR and VOLUME */ + file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); + fs_write(file->offset, MSDOS_NAME + 2, file->dir_ent.name); + } else { + fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + } if (file->lfn) lfn_fix_checksum(file->lfn_offset, file->offset, (const char *)file->dir_ent.name); @@ -426,7 +447,16 @@ static void rename_file(DOS_FILE * file) walk[1] = 0; for (walk = name; *walk == ' ' || *walk == '\t'; walk++) ; if (file_cvt(walk, file->dir_ent.name)) { - fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + if (file->dir_ent.lcase & FAT_NO_83NAME) { + /* as we only assign a new 8.3 filename, reset flag that 8.3 name is not + present */ + file->dir_ent.lcase &= ~FAT_NO_83NAME; + /* reset the attributes, only keep DIR and VOLUME */ + file->dir_ent.attr &= ~(ATTR_DIR | ATTR_VOLUME); + fs_write(file->offset, MSDOS_NAME + 2, file->dir_ent.name); + } else { + fs_write(file->offset, MSDOS_NAME, file->dir_ent.name); + } if (file->lfn) lfn_fix_checksum(file->lfn_offset, file->offset, (const char *)file->dir_ent.name); @@ -438,7 +468,7 @@ static void rename_file(DOS_FILE * file) static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots) { - char *name; + const char *name; name = strncmp((const char *)file->dir_ent.name, MSDOS_DOT, @@ -462,7 +492,7 @@ static int handle_dot(DOS_FS * fs, DOS_FILE * file, int dots) rename_file(file); return 0; case '4': - MODIFY(file, size, CT_LE_L(0)); + MODIFY(file, size, htole32(0)); MODIFY(file, attr, file->dir_ent.attr | ATTR_DIR); break; } @@ -479,21 +509,21 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) { DOS_FILE *owner; int restart; - unsigned long expect, curr, this, clusters, prev, walk, clusters2; + uint32_t expect, curr, this, clusters, prev, walk, clusters2; if (file->dir_ent.attr & ATTR_DIR) { - if (CF_LE_L(file->dir_ent.size)) { + if (le32toh(file->dir_ent.size)) { printf("%s\n Directory has non-zero size. Fixing it.\n", path_name(file)); - MODIFY(file, size, CT_LE_L(0)); + MODIFY(file, size, htole32(0)); } if (file->parent && !strncmp((const char *)file->dir_ent.name, MSDOS_DOT, MSDOS_NAME)) { expect = FSTART(file->parent, fs); if (FSTART(file, fs) != expect) { - printf("%s\n Start (%ld) does not point to parent (%ld)\n", - path_name(file), FSTART(file, fs), expect); + printf("%s\n Start (%lu) does not point to parent (%lu)\n", + path_name(file), (unsigned long)FSTART(file, fs), (long)expect); MODIFY_START(file, expect, fs); } return 0; @@ -507,7 +537,7 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) expect = 0; if (FSTART(file, fs) != expect) { printf("%s\n Start (%lu) does not point to .. (%lu)\n", - path_name(file), FSTART(file, fs), expect); + path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)expect); MODIFY_START(file, expect, fs); } return 0; @@ -519,12 +549,20 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) return 0; } } + if (FSTART(file, fs) == 1) { + printf("%s\n Bad start cluster 1. Truncating file.\n", + path_name(file)); + if (!file->offset) + die("Bad FAT32 root directory! (bad start cluster 1)\n"); + MODIFY_START(file, 0, fs); + } if (FSTART(file, fs) >= fs->clusters + 2) { printf ("%s\n Start cluster beyond limit (%lu > %lu). Truncating file.\n", - path_name(file), FSTART(file, fs), fs->clusters + 1); + path_name(file), (unsigned long)FSTART(file, fs), (unsigned long)(fs->clusters + 1)); if (!file->offset) - die("Bad FAT32 root directory! (bad start cluster)\n"); + die("Bad FAT32 root directory! (start cluster beyond limit: %lu > %lu)\n", + (unsigned long)FSTART(file, fs), (unsigned long)(fs->clusters + 1)); MODIFY_START(file, 0, fs); } clusters = prev = 0; @@ -535,7 +573,7 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) if (!curEntry.value || bad_cluster(fs, curr)) { printf("%s\n Contains a %s cluster (%lu). Assuming EOF.\n", - path_name(file), curEntry.value ? "bad" : "free", curr); + path_name(file), curEntry.value ? "bad" : "free", (unsigned long)curr); if (prev) set_fat(fs, prev, -1); else if (!file->offset) @@ -544,14 +582,14 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) MODIFY_START(file, 0, fs); break; } - if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) <= - (unsigned long long)clusters * fs->cluster_size) { + if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) <= + (uint64_t)clusters * fs->cluster_size) { printf - ("%s\n File size is %u bytes, cluster chain length is > %llu " + ("%s\n File size is %u bytes, cluster chain length is > %lu " "bytes.\n Truncating file to %u bytes.\n", path_name(file), - CF_LE_L(file->dir_ent.size), - (unsigned long long)clusters * fs->cluster_size, - CF_LE_L(file->dir_ent.size)); + le32toh(file->dir_ent.size), + (uint64_t)clusters * fs->cluster_size, + le32toh(file->dir_ent.size)); truncate_file(fs, file, clusters); break; } @@ -599,7 +637,7 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) else MODIFY_START(owner, 0, fs); MODIFY(owner, size, - CT_LE_L((unsigned long long)clusters * + htole32((uint64_t)clusters * fs->cluster_size)); if (restart) return 1; @@ -628,16 +666,16 @@ static int check_file(DOS_FS * fs, DOS_FILE * file) clusters++; prev = curr; } - if (!(file->dir_ent.attr & ATTR_DIR) && CF_LE_L(file->dir_ent.size) > - (unsigned long long)clusters * fs->cluster_size) { + if (!(file->dir_ent.attr & ATTR_DIR) && le32toh(file->dir_ent.size) > + (uint64_t)clusters * fs->cluster_size) { printf ("%s\n File size is %u bytes, cluster chain length is %llu bytes." "\n Truncating file to %llu bytes.\n", path_name(file), - CF_LE_L(file->dir_ent.size), + le32toh(file->dir_ent.size), (unsigned long long)clusters * fs->cluster_size, (unsigned long long)clusters * fs->cluster_size); MODIFY(file, size, - CT_LE_L((unsigned long long)clusters * fs->cluster_size)); + htole32((uint64_t)clusters * fs->cluster_size)); } return 0; } @@ -807,10 +845,10 @@ static int check_dir(DOS_FS * fs, DOS_FILE ** root, int dots) static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) { DOS_FILE *owner; - unsigned long walk, prev, clusters, next_clu; + uint32_t walk, prev, clusters, next_clu; prev = clusters = 0; - for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2; + for (walk = FSTART(file, fs); walk > 1 && walk < fs->clusters + 2; walk = next_clu) { next_clu = next_cluster(fs, walk); @@ -821,7 +859,7 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) if ((owner = get_owner(fs, walk))) { if (owner == file) { printf("%s\n Circular cluster chain. Truncating to %lu " - "cluster%s.\n", path_name(file), clusters, + "cluster%s.\n", path_name(file), (unsigned long)clusters, clusters == 1 ? "" : "s"); if (prev) set_fat(fs, prev, -1); @@ -840,7 +878,7 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) clusters++; } else { printf("%s\n Cluster %lu (%lu) is unreadable. Skipping it.\n", - path_name(file), clusters, walk); + path_name(file), (unsigned long)clusters, (unsigned long)walk); if (prev) set_fat(fs, prev, next_cluster(fs, walk)); else @@ -851,7 +889,7 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) set_owner(fs, walk, file); } /* Revert ownership (for now) */ - for (walk = FSTART(file, fs); walk > 0 && walk < fs->clusters + 2; + for (walk = FSTART(file, fs); walk > 1 && walk < fs->clusters + 2; walk = next_cluster(fs, walk)) if (bad_cluster(fs, walk)) break; @@ -863,9 +901,9 @@ static void test_file(DOS_FS * fs, DOS_FILE * file, int read_test) static void undelete(DOS_FS * fs, DOS_FILE * file) { - unsigned long clusters, left, prev, walk; + uint32_t clusters, left, prev, walk; - clusters = left = (CF_LE_L(file->dir_ent.size) + fs->cluster_size - 1) / + clusters = left = (le32toh(file->dir_ent.size) + fs->cluster_size - 1) / fs->cluster_size; prev = 0; @@ -891,7 +929,7 @@ static void undelete(DOS_FS * fs, DOS_FILE * file) MODIFY_START(file, 0, fs); if (left) printf("Warning: Did only undelete %lu of %lu cluster%s.\n", - clusters - left, clusters, clusters == 1 ? "" : "s"); + (unsigned long)clusters - left, (unsigned long)clusters, clusters == 1 ? "" : "s"); } @@ -924,11 +962,11 @@ static void add_file(DOS_FS * fs, DOS_FILE *** chain, DOS_FILE * parent, fs_read(offset, sizeof(DIR_ENT), &de); else { /* Construct a DIR_ENT for the root directory */ + memset(&de, 0, sizeof de); memcpy(de.name, " ", MSDOS_NAME); de.attr = ATTR_DIR; - de.size = de.time = de.date = 0; - de.start = CT_LE_W(fs->root_cluster & 0xffff); - de.starthi = CT_LE_W((fs->root_cluster >> 16) & 0xffff); + de.start = htole16(fs->root_cluster & 0xffff); + de.starthi = htole16((fs->root_cluster >> 16) & 0xffff); } if ((type = file_type(cp, (char *)de.name)) != fdt_none) { if (type == fdt_undelete && (de.attr & ATTR_DIR)) @@ -974,7 +1012,7 @@ static int scan_dir(DOS_FS * fs, DOS_FILE * this, FDSC ** cp) { DOS_FILE **chain; int i; - unsigned long clu_num; + uint32_t clu_num; chain = &this->first; i = 0; diff --git a/dosfstools/src/check.h b/dosfstools/src/check.h index 277c44b6f..fcb6bea39 100644 --- a/dosfstools/src/check.h +++ b/dosfstools/src/check.h @@ -1,6 +1,7 @@ -/* check.h - Check and repair a PC/MS-DOS file system +/* check.h - Check and repair a PC/MS-DOS filesystem Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -33,7 +34,7 @@ loff_t alloc_rootdir_entry(DOS_FS * fs, DIR_ENT * de, const char *pattern); int scan_root(DOS_FS * fs); /* Scans the root directory and recurses into all subdirectories. See check.c - for all the details. Returns a non-zero integer if the file system has to + for all the details. Returns a non-zero integer if the filesystem has to be checked again. */ #endif diff --git a/dosfstools/src/common.c b/dosfstools/src/common.c index 51605a2aa..9d1119322 100644 --- a/dosfstools/src/common.c +++ b/dosfstools/src/common.c @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -36,7 +37,7 @@ typedef struct _link { struct _link *next; } LINK; -void die(char *msg, ...) +void die(const char *msg, ...) { va_list args; @@ -47,7 +48,7 @@ void die(char *msg, ...) exit(1); } -void pdie(char *msg, ...) +void pdie(const char *msg, ...) { va_list args; @@ -95,7 +96,7 @@ int min(int a, int b) return a < b ? a : b; } -char get_key(char *valid, char *prompt) +char get_key(const char *valid, const char *prompt) { int ch, okay; diff --git a/dosfstools/src/common.h b/dosfstools/src/common.h index 395eabb59..c15efb538 100644 --- a/dosfstools/src/common.h +++ b/dosfstools/src/common.h @@ -1,6 +1,7 @@ /* common.h - Common functions Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,20 +16,18 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ -#include - #ifndef _COMMON_H #define _COMMON_H -void die(char *msg, ...) __attribute((noreturn)); +void die(const char *msg, ...) __attribute((noreturn)); /* Displays a prinf-style message and terminates the program. */ -void pdie(char *msg, ...) __attribute((noreturn)); +void pdie(const char *msg, ...) __attribute((noreturn)); /* Like die, but appends an error message according to the state of errno. */ @@ -49,7 +48,7 @@ int min(int a, int b); /* Returns the smaller integer value of a and b. */ -char get_key(char *valid, char *prompt); +char get_key(const char *valid, const char *prompt); /* Displays PROMPT and waits for user input. Only characters in VALID are accepted. Terminates the program on EOF. Returns the character. */ diff --git a/dosfstools/src/dosfsck.c b/dosfstools/src/dosfsck.c deleted file mode 100644 index a7a59e1a1..000000000 --- a/dosfstools/src/dosfsck.c +++ /dev/null @@ -1,224 +0,0 @@ -/* dosfsck.c - User interface - - Copyright (C) 1993 Werner Almesberger - Copyright (C) 1998 Roman Hodek - - 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - On Debian systems, the complete text of the GNU General Public License - can be found in /usr/share/common-licenses/GPL-3 file. -*/ - -/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 - * by Roman Hodek */ - -#include "version.h" - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "dosfsck.h" -#include "io.h" -#include "boot.h" -#include "fat.h" -#include "file.h" -#include "check.h" - -int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; -int atari_format = 0; -unsigned n_files = 0; -void *mem_queue = NULL; - -#ifdef USE_ANDROID_RETVALS -unsigned retandroid = 1; -#else -unsigned retandroid = 0; -#endif - -static void usage(char *name) -{ - fprintf(stderr, "usage: %s [-aAflrtvVwy] [-d path -d ...] " - "[-u path -u ...]\n%15sdevice\n", name, ""); - fprintf(stderr, " -a automatically repair the file system\n"); - fprintf(stderr, " -A toggle Atari file system format\n"); - fprintf(stderr, " -d path drop that file\n"); - fprintf(stderr, " -f ignored\n"); - fprintf(stderr, " -l list path names\n"); - fprintf(stderr, - " -n no-op, check non-interactively without changing\n"); - fprintf(stderr, " -p same as -a, for compat with other *fsck\n"); - fprintf(stderr, " -r interactively repair the file system\n"); - fprintf(stderr, " -t test for bad clusters\n"); - fprintf(stderr, " -u path try to undelete that (non-directory) file\n"); - fprintf(stderr, " -v verbose mode\n"); - fprintf(stderr, " -V perform a verification pass\n"); - fprintf(stderr, " -w write changes to disk immediately\n"); - fprintf(stderr, " -y same as -a, for compat with other *fsck\n"); - if (retandroid) { - exit(1); - } else { - exit(2); - } -} - -/* - * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant - * of MS-DOS filesystem by default. - */ -static void check_atari(void) -{ -#ifdef __mc68000__ - FILE *f; - char line[128], *p; - - if (!(f = fopen("/proc/hardware", "r"))) { - perror("/proc/hardware"); - return; - } - - while (fgets(line, sizeof(line), f)) { - if (strncmp(line, "Model:", 6) == 0) { - p = line + 6; - p += strspn(p, " \t"); - if (strncmp(p, "Atari ", 6) == 0) - atari_format = 1; - break; - } - } - fclose(f); -#endif -} - -int main(int argc, char **argv) -{ - DOS_FS fs; - int salvage_files, verify, c; - unsigned n_files_check = 0, n_files_verify = 0; - unsigned long free_clusters; - - memset(&fs, 0, sizeof(fs)); - rw = salvage_files = verify = 0; - interactive = 1; - check_atari(); - - while ((c = getopt(argc, argv, "Aad:flnprtu:vVwy")) != EOF) - switch (c) { - case 'A': /* toggle Atari format */ - atari_format = !atari_format; - break; - case 'a': - case 'p': - case 'y': - rw = 1; - interactive = 0; - salvage_files = 1; - break; - case 'd': - file_add(optarg, fdt_drop); - break; - case 'f': - salvage_files = 1; - break; - case 'l': - list = 1; - break; - case 'n': - rw = 0; - interactive = 0; - break; - case 'r': - rw = 1; - interactive = 1; - break; - case 't': - test = 1; - break; - case 'u': - file_add(optarg, fdt_undelete); - break; - case 'v': - verbose = 1; - printf("dosfsck " VERSION " (" VERSION_DATE ")\n"); - break; - case 'V': - verify = 1; - break; - case 'w': - write_immed = 1; - break; - default: - usage(argv[0]); - } - if ((test || write_immed) && !rw) { - fprintf(stderr, "-t and -w require -a or -r\n"); - if (retandroid) { - exit(1); - } else { - exit(2); - } - } - if (optind != argc - 1) - usage(argv[0]); - - printf("dosfsck " VERSION ", " VERSION_DATE ", FAT32, LFN\n"); - fs_open(argv[optind], rw); - read_boot(&fs); - if (verify) - printf("Starting check/repair pass.\n"); - while (read_fat(&fs), scan_root(&fs)) - qfree(&mem_queue); - if (test) - fix_bad(&fs); - if (salvage_files && 0) - reclaim_file(&fs); - else - reclaim_free(&fs); - free_clusters = update_free(&fs); - file_unused(); - qfree(&mem_queue); - n_files_check = n_files; - if (verify) { - n_files = 0; - printf("Starting verification pass.\n"); - read_fat(&fs); - scan_root(&fs); - reclaim_free(&fs); - qfree(&mem_queue); - n_files_verify = n_files; - } - - if (fs_changed()) { - if (rw) { - if (interactive) - rw = get_key("yn", "Perform changes ? (y/n)") == 'y'; - else - printf("Performing changes.\n"); - } else - printf("Leaving file system unchanged.\n"); - } - - printf("%s: %u files, %lu/%lu clusters\n", argv[optind], - n_files, fs.clusters - free_clusters, fs.clusters); - - if (retandroid) { - return fs_close(rw) ? 4 : 0; - } else { - return fs_close(rw) ? 1 : 0; - } -} diff --git a/dosfstools/src/dosfsck.h b/dosfstools/src/dosfsck.h deleted file mode 100644 index 6f53c72cf..000000000 --- a/dosfstools/src/dosfsck.h +++ /dev/null @@ -1,218 +0,0 @@ -/* dosfsck.h - Common data structures and global variables - - Copyright (C) 1993 Werner Almesberger - Copyright (C) 1998 Roman Hodek - - 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - On Debian systems, the complete text of the GNU General Public License - can be found in /usr/share/common-licenses/GPL-3 file. -*/ - -/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 - * by Roman Hodek */ - -#ifndef _DOSFSCK_H -#define _DOSFSCK_H - -#include -#define _LINUX_STAT_H /* hack to avoid inclusion of */ -#define _LINUX_STRING_H_ /* hack to avoid inclusion of */ -#define _LINUX_FS_H /* hack to avoid inclusion of */ - -#ifdef _USING_BIONIC_ -#include -#endif - -#include -#include - -#include - -#undef CF_LE_W -#undef CF_LE_L -#undef CT_LE_W -#undef CT_LE_L - -#if __BYTE_ORDER == __BIG_ENDIAN -#include -#define CF_LE_W(v) bswap_16(v) -#define CF_LE_L(v) bswap_32(v) -#define CT_LE_W(v) CF_LE_W(v) -#define CT_LE_L(v) CF_LE_L(v) -#else -#define CF_LE_W(v) (v) -#define CF_LE_L(v) (v) -#define CT_LE_W(v) (v) -#define CT_LE_L(v) (v) -#endif /* __BIG_ENDIAN */ - -#define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME) - -/* ++roman: Use own definition of boot sector structure -- the kernel headers' - * name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */ -struct boot_sector { - __u8 ignored[3]; /* Boot strap short or near jump */ - __u8 system_id[8]; /* Name - can be used to special case - partition manager volumes */ - __u8 sector_size[2]; /* bytes per logical sector */ - __u8 cluster_size; /* sectors/cluster */ - __u16 reserved; /* reserved sectors */ - __u8 fats; /* number of FATs */ - __u8 dir_entries[2]; /* root directory entries */ - __u8 sectors[2]; /* number of sectors */ - __u8 media; /* media code (unused) */ - __u16 fat_length; /* sectors/FAT */ - __u16 secs_track; /* sectors per track */ - __u16 heads; /* number of heads */ - __u32 hidden; /* hidden sectors (unused) */ - __u32 total_sect; /* number of sectors (if sectors == 0) */ - - /* The following fields are only used by FAT32 */ - __u32 fat32_length; /* sectors/FAT */ - __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ - __u8 version[2]; /* major, minor filesystem version */ - __u32 root_cluster; /* first cluster in root directory */ - __u16 info_sector; /* filesystem info sector */ - __u16 backup_boot; /* backup boot sector */ - __u8 reserved2[12]; /* Unused */ - - __u8 drive_number; /* Logical Drive Number */ - __u8 reserved3; /* Unused */ - - __u8 extended_sig; /* Extended Signature (0x29) */ - __u32 serial; /* Serial number */ - __u8 label[11]; /* FS label */ - __u8 fs_type[8]; /* FS Type */ - - /* fill up to 512 bytes */ - __u8 junk[422]; -} __attribute__ ((packed)); - -struct boot_sector_16 { - __u8 ignored[3]; /* Boot strap short or near jump */ - __u8 system_id[8]; /* Name - can be used to special case - partition manager volumes */ - __u8 sector_size[2]; /* bytes per logical sector */ - __u8 cluster_size; /* sectors/cluster */ - __u16 reserved; /* reserved sectors */ - __u8 fats; /* number of FATs */ - __u8 dir_entries[2]; /* root directory entries */ - __u8 sectors[2]; /* number of sectors */ - __u8 media; /* media code (unused) */ - __u16 fat_length; /* sectors/FAT */ - __u16 secs_track; /* sectors per track */ - __u16 heads; /* number of heads */ - __u32 hidden; /* hidden sectors (unused) */ - __u32 total_sect; /* number of sectors (if sectors == 0) */ - - __u8 drive_number; /* Logical Drive Number */ - __u8 reserved2; /* Unused */ - - __u8 extended_sig; /* Extended Signature (0x29) */ - __u32 serial; /* Serial number */ - __u8 label[11]; /* FS label */ - __u8 fs_type[8]; /* FS Type */ - - /* fill up to 512 bytes */ - __u8 junk[450]; -} __attribute__ ((packed)); - -struct info_sector { - __u32 magic; /* Magic for info sector ('RRaA') */ - __u8 junk[0x1dc]; - __u32 reserved1; /* Nothing as far as I can tell */ - __u32 signature; /* 0x61417272 ('rrAa') */ - __u32 free_clusters; /* Free cluster count. -1 if unknown */ - __u32 next_cluster; /* Most recently allocated cluster. */ - __u32 reserved2[3]; - __u16 reserved3; - __u16 boot_sign; -}; - -typedef struct { - __u8 name[8], ext[3]; /* name and extension */ - __u8 attr; /* attribute bits */ - __u8 lcase; /* Case for base and extension */ - __u8 ctime_ms; /* Creation time, milliseconds */ - __u16 ctime; /* Creation time */ - __u16 cdate; /* Creation date */ - __u16 adate; /* Last access date */ - __u16 starthi; /* High 16 bits of cluster in FAT32 */ - __u16 time, date, start; /* time, date and first cluster */ - __u32 size; /* file size (in bytes) */ -} __attribute__ ((packed)) DIR_ENT; - -typedef struct _dos_file { - DIR_ENT dir_ent; - char *lfn; - loff_t offset; - loff_t lfn_offset; - struct _dos_file *parent; /* parent directory */ - struct _dos_file *next; /* next entry */ - struct _dos_file *first; /* first entry (directory only) */ -} DOS_FILE; - -typedef struct { - unsigned long value; - unsigned long reserved; -} FAT_ENTRY; - -typedef struct { - int nfats; - loff_t fat_start; - unsigned int fat_size; /* unit is bytes */ - unsigned int fat_bits; /* size of a FAT entry */ - unsigned int eff_fat_bits; /* # of used bits in a FAT entry */ - unsigned long root_cluster; /* 0 for old-style root dir */ - loff_t root_start; - unsigned int root_entries; - loff_t data_start; - unsigned int cluster_size; - unsigned long clusters; - loff_t fsinfo_start; /* 0 if not present */ - long free_clusters; - loff_t backupboot_start; /* 0 if not present */ - unsigned char *fat; - DOS_FILE **cluster_owner; - char *label; -} DOS_FS; - -#ifndef offsetof -#define offsetof(t,e) ((int)&(((t *)0)->e)) -#endif - -extern int interactive, rw, list, verbose, test, write_immed; -extern int atari_format; -extern unsigned n_files; -extern void *mem_queue; -extern unsigned retandroid; - -/* value to use as end-of-file marker */ -#define FAT_EOF(fs) ((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs)) -#define FAT_IS_EOF(fs,v) ((unsigned long)(v) >= (0xff8|FAT_EXTD(fs))) -/* value to mark bad clusters */ -#define FAT_BAD(fs) (0xff7 | FAT_EXTD(fs)) -/* range of values used for bad clusters */ -#define FAT_MIN_BAD(fs) ((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs)) -#define FAT_MAX_BAD(fs) ((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs)) -#define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs)) - -/* return -16 as a number with fs->fat_bits bits */ -#define FAT_EXTD(fs) (((1 << fs->eff_fat_bits)-1) & ~0xf) - -/* marker for files with no 8.3 name */ -#define FAT_NO_83NAME 32 - -#endif diff --git a/dosfstools/src/dosfslabel.c b/dosfstools/src/dosfslabel.c deleted file mode 100644 index 5e2d2824f..000000000 --- a/dosfstools/src/dosfslabel.c +++ /dev/null @@ -1,128 +0,0 @@ -/* dosfslabel.c - User interface - - Copyright (C) 1993 Werner Almesberger - Copyright (C) 1998 Roman Hodek - Copyright (C) 2007 Red Hat, Inc. - - 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - On Debian systems, the complete text of the GNU General Public License - can be found in /usr/share/common-licenses/GPL-3 file. -*/ - -#include "version.h" - -#include -#include -#include -#include -#include -#include - -#ifdef _USING_BIONIC_ -#include -#endif - -#include "common.h" -#include "dosfsck.h" -#include "io.h" -#include "boot.h" -#include "fat.h" -#include "file.h" -#include "check.h" - -int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; -int atari_format = 0; -unsigned n_files = 0; -void *mem_queue = NULL; - -static void usage(int error) -{ - FILE *f = error ? stderr : stdout; - int status = error ? 1 : 0; - - fprintf(f, "usage: dosfslabel device [label]\n"); - exit(status); -} - -/* - * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant - * of MS-DOS filesystem by default. - */ -static void check_atari(void) -{ -#ifdef __mc68000__ - FILE *f; - char line[128], *p; - - if (!(f = fopen("/proc/hardware", "r"))) { - perror("/proc/hardware"); - return; - } - - while (fgets(line, sizeof(line), f)) { - if (strncmp(line, "Model:", 6) == 0) { - p = line + 6; - p += strspn(p, " \t"); - if (strncmp(p, "Atari ", 6) == 0) - atari_format = 1; - break; - } - } - fclose(f); -#endif -} - -int main(int argc, char *argv[]) -{ - DOS_FS fs; - rw = 0; - - char *device = NULL; - char *label = NULL; - - check_atari(); - - if (argc < 2 || argc > 3) - usage(1); - - if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) - usage(0); - else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { - printf("dosfslabel " VERSION ", " VERSION_DATE ", FAT32, LFN\n"); - exit(0); - } - - device = argv[1]; - if (argc == 3) { - label = argv[2]; - if (strlen(label) > 11) { - fprintf(stderr, - "dosfslabel: labels can be no longer than 11 characters\n"); - exit(1); - } - rw = 1; - } - - fs_open(device, rw); - read_boot(&fs); - if (!rw) { - fprintf(stdout, "%s\n", fs.label); - exit(0); - } - - write_label(&fs, label); - fs_close(rw); - return 0; -} diff --git a/dosfstools/src/fat.c b/dosfstools/src/fat.c index 5a0dfb0f2..5a92f5684 100644 --- a/dosfstools/src/fat.c +++ b/dosfstools/src/fat.c @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -29,7 +30,7 @@ #include #include "common.h" -#include "dosfsck.h" +#include "fsck.fat.h" #include "io.h" #include "check.h" #include "fat.h" @@ -42,7 +43,7 @@ * @param[in] cluster Cluster of interest * @param[in] fs Information from the FAT boot sectors (bits per FAT entry) */ -void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs) +void get_fat(FAT_ENTRY * entry, void *fat, uint32_t cluster, DOS_FS * fs) { unsigned char *ptr; @@ -53,13 +54,13 @@ void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs) (ptr[0] | ptr[1] << 8)); break; case 16: - entry->value = CF_LE_W(((unsigned short *)fat)[cluster]); + entry->value = le16toh(((unsigned short *)fat)[cluster]); break; case 32: /* According to M$, the high 4 bits of a FAT32 entry are reserved and * are not part of the cluster number. So we cut them off. */ { - unsigned long e = CF_LE_L(((unsigned int *)fat)[cluster]); + uint32_t e = le32toh(((unsigned int *)fat)[cluster]); entry->value = e & 0xfffffff; entry->reserved = e >> 28; } @@ -79,24 +80,34 @@ void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs) */ void read_fat(DOS_FS * fs) { - int eff_size; - unsigned long i; + int eff_size, alloc_size; + uint32_t i; void *first, *second = NULL; int first_ok, second_ok; - unsigned long total_num_clusters; + uint32_t total_num_clusters; /* Clean up from previous pass */ - free(fs->fat); - free(fs->cluster_owner); + if (fs->fat) + free(fs->fat); + if (fs->cluster_owner) + free(fs->cluster_owner); fs->fat = NULL; fs->cluster_owner = NULL; total_num_clusters = fs->clusters + 2UL; eff_size = (total_num_clusters * fs->fat_bits + 7) / 8ULL; - first = alloc(eff_size); + + if (fs->fat_bits != 12) + alloc_size = eff_size; + else + /* round up to an even number of FAT entries to avoid special + * casing the last entry in get_fat() */ + alloc_size = (total_num_clusters * 12 + 23) / 24 * 3; + + first = alloc(alloc_size); fs_read(fs->fat_start, eff_size, first); if (fs->nfats > 1) { - second = alloc(eff_size); + second = alloc(alloc_size); fs_read(fs->fat_start + fs->fat_size, eff_size, second); } if (second && memcmp(first, second, eff_size) != 0) { @@ -148,13 +159,13 @@ void read_fat(DOS_FS * fs) FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, i, fs); if (curEntry.value == 1) { - printf("Cluster %ld out of range (1). Setting to EOF.\n", i - 2); + printf("Cluster %ld out of range (1). Setting to EOF.\n", (long)(i - 2)); set_fat(fs, i, -1); } if (curEntry.value >= fs->clusters + 2 && (curEntry.value < FAT_MIN_BAD(fs))) { printf("Cluster %ld out of range (%ld > %ld). Setting to EOF.\n", - i - 2, curEntry.value, fs->clusters + 2 - 1); + (long)(i - 2), (long)curEntry.value, (long)(fs->clusters + 2 - 1)); set_fat(fs, i, -1); } } @@ -173,13 +184,13 @@ void read_fat(DOS_FS * fs) * -1 == end-of-chain * -2 == bad cluster */ -void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new) +void set_fat(DOS_FS * fs, uint32_t cluster, int32_t new) { unsigned char *data = NULL; int size; loff_t offs; - if ((long)new == -1) + if (new == -1) new = FAT_EOF(fs); else if ((long)new == -2) new = FAT_BAD(fs); @@ -204,7 +215,7 @@ void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new) case 16: data = fs->fat + cluster * 2; offs = fs->fat_start + cluster * 2; - *(unsigned short *)data = CT_LE_W(new); + *(unsigned short *)data = htole16(new); size = 2; break; case 32: @@ -216,7 +227,7 @@ void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new) offs = fs->fat_start + cluster * 4; /* According to M$, the high 4 bits of a FAT32 entry are reserved and * are not part of the cluster number. So we never touch them. */ - *(unsigned long *)data = CT_LE_L((new & 0xfffffff) | + *(uint32_t *)data = htole32((new & 0xfffffff) | (curEntry.reserved << 28)); size = 4; } @@ -230,7 +241,7 @@ void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new) } } -int bad_cluster(DOS_FS * fs, unsigned long cluster) +int bad_cluster(DOS_FS * fs, uint32_t cluster) { FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, cluster, fs); @@ -248,9 +259,9 @@ int bad_cluster(DOS_FS * fs, unsigned long cluster) * @return -1 'cluster' is at the end of the chain * @return Other values Next cluster in this chain */ -unsigned long next_cluster(DOS_FS * fs, unsigned long cluster) +uint32_t next_cluster(DOS_FS * fs, uint32_t cluster) { - unsigned long value; + uint32_t value; FAT_ENTRY curEntry; get_fat(&curEntry, fs->fat, cluster, fs); @@ -261,10 +272,10 @@ unsigned long next_cluster(DOS_FS * fs, unsigned long cluster) return FAT_IS_EOF(fs, value) ? -1 : value; } -loff_t cluster_start(DOS_FS * fs, unsigned long cluster) +loff_t cluster_start(DOS_FS * fs, uint32_t cluster) { return fs->data_start + ((loff_t) cluster - - 2) * (unsigned long long)fs->cluster_size; + 2) * (uint64_t)fs->cluster_size; } /** @@ -276,7 +287,7 @@ loff_t cluster_start(DOS_FS * fs, unsigned long cluster) * @param[in] owner Information on dentry that owns this cluster * (may be NULL) */ -void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner) +void set_owner(DOS_FS * fs, uint32_t cluster, DOS_FILE * owner) { if (fs->cluster_owner == NULL) die("Internal error: attempt to set owner in non-existent table"); @@ -287,7 +298,7 @@ void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner) fs->cluster_owner[cluster] = owner; } -DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster) +DOS_FILE *get_owner(DOS_FS * fs, uint32_t cluster) { if (fs->cluster_owner == NULL) return NULL; @@ -297,7 +308,7 @@ DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster) void fix_bad(DOS_FS * fs) { - unsigned long i; + uint32_t i; if (verbose) printf("Checking for bad clusters.\n"); @@ -307,7 +318,7 @@ void fix_bad(DOS_FS * fs) if (!get_owner(fs, i) && !FAT_IS_BAD(fs, curEntry.value)) if (!fs_test(cluster_start(fs, i), fs->cluster_size)) { - printf("Cluster %lu is unreadable.\n", i); + printf("Cluster %lu is unreadable.\n", (unsigned long)i); set_fat(fs, i, -2); } } @@ -316,7 +327,7 @@ void fix_bad(DOS_FS * fs) void reclaim_free(DOS_FS * fs) { int reclaimed; - unsigned long i; + uint32_t i; if (verbose) printf("Checking for unused clusters.\n"); @@ -332,7 +343,7 @@ void reclaim_free(DOS_FS * fs) } } if (reclaimed) - printf("Reclaimed %d unused cluster%s (%llu bytes).\n", reclaimed, + printf("Reclaimed %d unused cluster%s (%llu bytes).\n", (int)reclaimed, reclaimed == 1 ? "" : "s", (unsigned long long)reclaimed * fs->cluster_size); } @@ -347,11 +358,11 @@ void reclaim_free(DOS_FS * fs) * clusters link to it. * @param[in] start_cluster Where to start scanning for orphans */ -static void tag_free(DOS_FS * fs, DOS_FILE * owner, unsigned long *num_refs, - unsigned long start_cluster) +static void tag_free(DOS_FS * fs, DOS_FILE * owner, uint32_t *num_refs, + uint32_t start_cluster) { int prev; - unsigned long i, walk; + uint32_t i, walk; if (start_cluster == 0) start_cluster = 2; @@ -400,16 +411,16 @@ void reclaim_file(DOS_FS * fs) DOS_FILE orphan; int reclaimed, files; int changed = 0; - unsigned long i, next, walk; - unsigned long *num_refs = NULL; /* Only for orphaned clusters */ - unsigned long total_num_clusters; + uint32_t i, next, walk; + uint32_t *num_refs = NULL; /* Only for orphaned clusters */ + uint32_t total_num_clusters; if (verbose) printf("Reclaiming unconnected clusters.\n"); total_num_clusters = fs->clusters + 2UL; - num_refs = alloc(total_num_clusters * sizeof(unsigned long)); - memset(num_refs, 0, (total_num_clusters * sizeof(unsigned long))); + num_refs = alloc(total_num_clusters * sizeof(uint32_t)); + memset(num_refs, 0, (total_num_clusters * sizeof(uint32_t))); /* Guarantee that all orphan chains (except cycles) end cleanly * with an end-of-chain mark. @@ -454,7 +465,7 @@ void reclaim_file(DOS_FS * fs) die("Internal error: num_refs going below zero"); set_fat(fs, i, -1); changed = curEntry.value; - printf("Broke cycle at cluster %lu in free chain.\n", i); + printf("Broke cycle at cluster %lu in free chain.\n", (unsigned long)i); /* If we've created a new chain head, * tag_free() can claim it @@ -474,13 +485,13 @@ void reclaim_file(DOS_FS * fs) DIR_ENT de; loff_t offset; files++; - offset = alloc_rootdir_entry(fs, &de, "FSCK%04d"); - de.start = CT_LE_W(i & 0xffff); + offset = alloc_rootdir_entry(fs, &de, "FSCK%04dREC"); + de.start = htole16(i & 0xffff); if (fs->fat_bits == 32) - de.starthi = CT_LE_W(i >> 16); + de.starthi = htole16(i >> 16); for (walk = i; walk > 0 && walk != -1; walk = next_cluster(fs, walk)) { - de.size = CT_LE_L(CF_LE_L(de.size) + fs->cluster_size); + de.size = htole32(le32toh(de.size) + fs->cluster_size); reclaimed++; } fs_write(offset, sizeof(DIR_ENT), &de); @@ -494,10 +505,10 @@ void reclaim_file(DOS_FS * fs) free(num_refs); } -unsigned long update_free(DOS_FS * fs) +uint32_t update_free(DOS_FS * fs) { - unsigned long i; - unsigned long free = 0; + uint32_t i; + uint32_t free = 0; int do_set = 0; for (i = 2; i < fs->clusters + 2; i++) { @@ -516,7 +527,7 @@ unsigned long update_free(DOS_FS * fs) if (fs->free_clusters != 0xFFFFFFFF) { if (free != fs->free_clusters) { printf("Free cluster summary wrong (%ld vs. really %ld)\n", - fs->free_clusters, free); + (long)fs->free_clusters, (long)free); if (interactive) printf("1) Correct\n2) Don't correct\n"); else @@ -525,7 +536,7 @@ unsigned long update_free(DOS_FS * fs) do_set = 1; } } else { - printf("Free cluster summary uninitialized (should be %ld)\n", free); + printf("Free cluster summary uninitialized (should be %ld)\n", (long)free); if (rw) { if (interactive) printf("1) Set it\n2) Leave it uninitialized\n"); @@ -537,7 +548,7 @@ unsigned long update_free(DOS_FS * fs) } if (do_set) { - unsigned long le_free = CT_LE_L(free); + uint32_t le_free = htole32(free); fs->free_clusters = free; fs_write(fs->fsinfo_start + offsetof(struct info_sector, free_clusters), sizeof(le_free), &le_free); diff --git a/dosfstools/src/fat.h b/dosfstools/src/fat.h index 13ac1b345..b50ed4a96 100644 --- a/dosfstools/src/fat.h +++ b/dosfstools/src/fat.h @@ -1,6 +1,7 @@ /* fat.h - Read/write access to the FAT Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + THe complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -24,41 +25,41 @@ void read_fat(DOS_FS * fs); -/* Loads the FAT of the file system described by FS. Initializes the FAT, +/* Loads the FAT of the filesystem described by FS. Initializes the FAT, replaces broken FATs and rejects invalid cluster entries. */ -void get_fat(FAT_ENTRY * entry, void *fat, unsigned long cluster, DOS_FS * fs); +void get_fat(FAT_ENTRY * entry, void *fat, uint32_t cluster, DOS_FS * fs); /* Retrieve the FAT entry (next chained cluster) for CLUSTER. */ -void set_fat(DOS_FS * fs, unsigned long cluster, unsigned long new); +void set_fat(DOS_FS * fs, uint32_t cluster, int32_t new); /* Changes the value of the CLUSTERth cluster of the FAT of FS to NEW. Special values of NEW are -1 (EOF, 0xff8 or 0xfff8) and -2 (bad sector, 0xff7 or 0xfff7) */ -int bad_cluster(DOS_FS * fs, unsigned long cluster); +int bad_cluster(DOS_FS * fs, uint32_t cluster); /* Returns a non-zero integer if the CLUSTERth cluster is marked as bad or zero otherwise. */ -unsigned long next_cluster(DOS_FS * fs, unsigned long cluster); +uint32_t next_cluster(DOS_FS * fs, uint32_t cluster); /* Returns the number of the cluster following CLUSTER, or -1 if this is the last cluster of the respective cluster chain. CLUSTER must not be a bad cluster. */ -loff_t cluster_start(DOS_FS * fs, unsigned long cluster); +loff_t cluster_start(DOS_FS * fs, uint32_t cluster); /* Returns the byte offset of CLUSTER, relative to the respective device. */ -void set_owner(DOS_FS * fs, unsigned long cluster, DOS_FILE * owner); +void set_owner(DOS_FS * fs, uint32_t cluster, DOS_FILE * owner); /* Sets the owner pointer of the respective cluster to OWNER. If OWNER was NULL before, it can be set to NULL or any non-NULL value. Otherwise, only NULL is accepted as the new value. */ -DOS_FILE *get_owner(DOS_FS * fs, unsigned long cluster); +DOS_FILE *get_owner(DOS_FS * fs, uint32_t cluster); /* Returns the owner of the repective cluster or NULL if the cluster has no owner. */ @@ -77,7 +78,7 @@ void reclaim_file(DOS_FS * fs); for them in the root directory. Also tries to fix all inconsistencies (e.g. loops, shared clusters, etc.) in the process. */ -unsigned long update_free(DOS_FS * fs); +uint32_t update_free(DOS_FS * fs); /* Updates free cluster count in FSINFO sector. */ diff --git a/dosfstools/src/fatlabel.c b/dosfstools/src/fatlabel.c new file mode 100644 index 000000000..1484ba527 --- /dev/null +++ b/dosfstools/src/fatlabel.c @@ -0,0 +1,144 @@ +/* fatlabel.c - User interface + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2007 Red Hat, Inc. + Copyright (C) 2008-2014 Daniel Baumann + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "fsck.fat.h" +#include "io.h" +#include "boot.h" +#include "fat.h" +#include "file.h" +#include "check.h" + +int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; +int atari_format = 0; +unsigned n_files = 0; +void *mem_queue = NULL; + +static void usage(int error) +{ + FILE *f = error ? stderr : stdout; + int status = error ? 1 : 0; + + fprintf(f, "usage: fatlabel device [label]\n"); + exit(status); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +int main(int argc, char *argv[]) +{ + DOS_FS fs = { 0 }; + rw = 0; + + int i; + + char *device = NULL; + char label[12] = { 0 }; + + loff_t offset; + DIR_ENT de; + + check_atari(); + + if (argc < 2 || argc > 3) + usage(1); + + if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) + usage(0); + else if (!strcmp(argv[1], "-V") || !strcmp(argv[1], "--version")) { + printf("fatlabel " VERSION " (" VERSION_DATE ")\n"); + exit(0); + } + + device = argv[1]; + if (argc == 3) { + strncpy(label, argv[2], 11); + if (strlen(argv[2]) > 11) { + fprintf(stderr, + "fatlabel: labels can be no longer than 11 characters\n"); + exit(1); + } + for (i = 0; label[i] && i < 11; i++) + /* don't know if here should be more strict !uppercase(label[i]) */ + if (islower(label[i])) { + fprintf(stderr, + "fatlabel: warning - lowercase labels might not work properly with DOS or Windows\n"); + break; + } + rw = 1; + } + + fs_open(device, rw); + read_boot(&fs); + if (fs.fat_bits == 32) + read_fat(&fs); + if (!rw) { + offset = find_volume_de(&fs, &de); + if (offset == 0) + fprintf(stdout, "%s\n", fs.label); + else + fprintf(stdout, "%.8s%.3s\n", de.name, de.ext); + exit(0); + } + + write_label(&fs, label); + fs_close(rw); + return 0; +} diff --git a/dosfstools/src/file.c b/dosfstools/src/file.c index a73b73f5e..dffcec1cd 100644 --- a/dosfstools/src/file.c +++ b/dosfstools/src/file.c @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -29,16 +30,9 @@ #include #include -#define _LINUX_STAT_H /* hack to avoid inclusion of */ -#define _LINUX_STRING_H_ /* hack to avoid inclusion of */ -#define _LINUX_FS_H /* hack to avoid inclusion of */ - -#include - -#include - #include "common.h" #include "file.h" +#include "msdos_fs.h" FDSC *fp_root = NULL; diff --git a/dosfstools/src/file.h b/dosfstools/src/file.h index 40bd58a92..eaaf356be 100644 --- a/dosfstools/src/file.h +++ b/dosfstools/src/file.h @@ -1,6 +1,7 @@ /* file.h - Additional file attributes Copyright (C) 1993 Werner Almesberger + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,13 +16,15 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ #ifndef _FILE_H #define _FILE_H +#include "msdos_fs.h" + typedef enum { fdt_none, fdt_drop, fdt_undelete } FD_TYPE; typedef struct _fptr { diff --git a/dosfstools/src/fsck.fat.c b/dosfstools/src/fsck.fat.c new file mode 100644 index 000000000..2bc3dc216 --- /dev/null +++ b/dosfstools/src/fsck.fat.c @@ -0,0 +1,215 @@ +/* fsck.fat.c - User interface + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#include "version.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "fsck.fat.h" +#include "io.h" +#include "boot.h" +#include "fat.h" +#include "file.h" +#include "check.h" + +int interactive = 0, rw = 0, list = 0, test = 0, verbose = 0, write_immed = 0; +int atari_format = 0, boot_only = 0; +unsigned n_files = 0; +void *mem_queue = NULL; + +static void usage(char *name) +{ + fprintf(stderr, "usage: %s [-aAbflrtvVwy] [-d path -d ...] " + "[-u path -u ...]\n%15sdevice\n", name, ""); + fprintf(stderr, " -a automatically repair the filesystem\n"); + fprintf(stderr, " -A toggle Atari filesystem format\n"); + fprintf(stderr, " -b make read-only boot sector check\n"); + fprintf(stderr, " -d path drop that file\n"); + fprintf(stderr, " -f salvage unused chains to files\n"); + fprintf(stderr, " -l list path names\n"); + fprintf(stderr, + " -n no-op, check non-interactively without changing\n"); + fprintf(stderr, " -p same as -a, for compat with other *fsck\n"); + fprintf(stderr, " -r interactively repair the filesystem (default)\n"); + fprintf(stderr, " -t test for bad clusters\n"); + fprintf(stderr, " -u path try to undelete that (non-directory) file\n"); + fprintf(stderr, " -v verbose mode\n"); + fprintf(stderr, " -V perform a verification pass\n"); + fprintf(stderr, " -w write changes to disk immediately\n"); + fprintf(stderr, " -y same as -a, for compat with other *fsck\n"); + exit(2); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +int main(int argc, char **argv) +{ + DOS_FS fs; + int salvage_files, verify, c; + uint32_t free_clusters = 0; + + memset(&fs, 0, sizeof(fs)); + salvage_files = verify = 0; + rw = interactive = 1; + check_atari(); + + while ((c = getopt(argc, argv, "Aad:bflnprtu:vVwy")) != -1) + switch (c) { + case 'A': /* toggle Atari format */ + atari_format = !atari_format; + break; + case 'a': + case 'p': + case 'y': + rw = 1; + interactive = 0; + salvage_files = 1; + break; + case 'b': + rw = 0; + interactive = 0; + boot_only = 1; + break; + case 'd': + file_add(optarg, fdt_drop); + break; + case 'f': + salvage_files = 1; + break; + case 'l': + list = 1; + break; + case 'n': + rw = 0; + interactive = 0; + break; + case 'r': + rw = 1; + interactive = 1; + break; + case 't': + test = 1; + break; + case 'u': + file_add(optarg, fdt_undelete); + break; + case 'v': + verbose = 1; + break; + case 'V': + verify = 1; + break; + case 'w': + write_immed = 1; + break; + default: + usage(argv[0]); + } + if ((test || write_immed) && !rw) { + fprintf(stderr, "-t and -w can not be used in read only mode\n"); + exit(2); + } + if (optind != argc - 1) + usage(argv[0]); + + printf("fsck.fat " VERSION " (" VERSION_DATE ")\n"); + fs_open(argv[optind], rw); + + read_boot(&fs); + if (boot_only) + goto exit; + + if (verify) + printf("Starting check/repair pass.\n"); + while (read_fat(&fs), scan_root(&fs)) + qfree(&mem_queue); + if (test) + fix_bad(&fs); + if (salvage_files) + reclaim_file(&fs); + else + reclaim_free(&fs); + free_clusters = update_free(&fs); + file_unused(); + qfree(&mem_queue); + if (verify) { + n_files = 0; + printf("Starting verification pass.\n"); + read_fat(&fs); + scan_root(&fs); + reclaim_free(&fs); + qfree(&mem_queue); + } + +exit: + if (fs_changed()) { + if (rw) { + if (interactive) + rw = get_key("yn", "Perform changes ? (y/n)") == 'y'; + else + printf("Performing changes.\n"); + } else + printf("Leaving filesystem unchanged.\n"); + } + + if (!boot_only) + printf("%s: %u files, %lu/%lu clusters\n", argv[optind], + n_files, (unsigned long)fs.clusters - free_clusters, (unsigned long)fs.clusters); + + return fs_close(rw) ? 1 : 0; +} diff --git a/dosfstools/src/fsck.fat.h b/dosfstools/src/fsck.fat.h new file mode 100644 index 000000000..e5f617871 --- /dev/null +++ b/dosfstools/src/fsck.fat.h @@ -0,0 +1,191 @@ +/* fsck.fat.h - Common data structures and global variables + + Copyright (C) 1993 Werner Almesberger + Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* FAT32, VFAT, Atari format support, and various fixes additions May 1998 + * by Roman Hodek */ + +#ifndef _DOSFSCK_H +#define _DOSFSCK_H + +#include +#include +#include +#include + +#include "msdos_fs.h" + +#define VFAT_LN_ATTR (ATTR_RO | ATTR_HIDDEN | ATTR_SYS | ATTR_VOLUME) + +#define FAT_STATE_DIRTY 0x01 + +/* ++roman: Use own definition of boot sector structure -- the kernel headers' + * name for it is msdos_boot_sector in 2.0 and fat_boot_sector in 2.1 ... */ +struct boot_sector { + uint8_t ignored[3]; /* Boot strap short or near jump */ + uint8_t system_id[8]; /* Name - can be used to special case + partition manager volumes */ + uint8_t sector_size[2]; /* bytes per logical sector */ + uint8_t cluster_size; /* sectors/cluster */ + uint16_t reserved; /* reserved sectors */ + uint8_t fats; /* number of FATs */ + uint8_t dir_entries[2]; /* root directory entries */ + uint8_t sectors[2]; /* number of sectors */ + uint8_t media; /* media code (unused) */ + uint16_t fat_length; /* sectors/FAT */ + uint16_t secs_track; /* sectors per track */ + uint16_t heads; /* number of heads */ + uint32_t hidden; /* hidden sectors (unused) */ + uint32_t total_sect; /* number of sectors (if sectors == 0) */ + + /* The following fields are only used by FAT32 */ + uint32_t fat32_length; /* sectors/FAT */ + uint16_t flags; /* bit 8: fat mirroring, low 4: active fat */ + uint8_t version[2]; /* major, minor filesystem version */ + uint32_t root_cluster; /* first cluster in root directory */ + uint16_t info_sector; /* filesystem info sector */ + uint16_t backup_boot; /* backup boot sector */ + uint8_t reserved2[12]; /* Unused */ + + uint8_t drive_number; /* Logical Drive Number */ + uint8_t reserved3; /* Unused */ + + uint8_t extended_sig; /* Extended Signature (0x29) */ + uint32_t serial; /* Serial number */ + uint8_t label[11]; /* FS label */ + uint8_t fs_type[8]; /* FS Type */ + + /* fill up to 512 bytes */ + uint8_t junk[422]; +} __attribute__ ((packed)); + +struct boot_sector_16 { + uint8_t ignored[3]; /* Boot strap short or near jump */ + uint8_t system_id[8]; /* Name - can be used to special case + partition manager volumes */ + uint8_t sector_size[2]; /* bytes per logical sector */ + uint8_t cluster_size; /* sectors/cluster */ + uint16_t reserved; /* reserved sectors */ + uint8_t fats; /* number of FATs */ + uint8_t dir_entries[2]; /* root directory entries */ + uint8_t sectors[2]; /* number of sectors */ + uint8_t media; /* media code (unused) */ + uint16_t fat_length; /* sectors/FAT */ + uint16_t secs_track; /* sectors per track */ + uint16_t heads; /* number of heads */ + uint32_t hidden; /* hidden sectors (unused) */ + uint32_t total_sect; /* number of sectors (if sectors == 0) */ + + uint8_t drive_number; /* Logical Drive Number */ + uint8_t reserved2; /* Unused */ + + uint8_t extended_sig; /* Extended Signature (0x29) */ + uint32_t serial; /* Serial number */ + uint8_t label[11]; /* FS label */ + uint8_t fs_type[8]; /* FS Type */ + + /* fill up to 512 bytes */ + uint8_t junk[450]; +} __attribute__ ((packed)); + +struct info_sector { + uint32_t magic; /* Magic for info sector ('RRaA') */ + uint8_t junk[0x1dc]; + uint32_t reserved1; /* Nothing as far as I can tell */ + uint32_t signature; /* 0x61417272 ('rrAa') */ + uint32_t free_clusters; /* Free cluster count. -1 if unknown */ + uint32_t next_cluster; /* Most recently allocated cluster. */ + uint32_t reserved2[3]; + uint16_t reserved3; + uint16_t boot_sign; +}; + +typedef struct { + uint8_t name[8], ext[3]; /* name and extension */ + uint8_t attr; /* attribute bits */ + uint8_t lcase; /* Case for base and extension */ + uint8_t ctime_ms; /* Creation time, milliseconds */ + uint16_t ctime; /* Creation time */ + uint16_t cdate; /* Creation date */ + uint16_t adate; /* Last access date */ + uint16_t starthi; /* High 16 bits of cluster in FAT32 */ + uint16_t time, date, start; /* time, date and first cluster */ + uint32_t size; /* file size (in bytes) */ +} __attribute__ ((packed)) DIR_ENT; + +typedef struct _dos_file { + DIR_ENT dir_ent; + char *lfn; + loff_t offset; + loff_t lfn_offset; + struct _dos_file *parent; /* parent directory */ + struct _dos_file *next; /* next entry */ + struct _dos_file *first; /* first entry (directory only) */ +} DOS_FILE; + +typedef struct { + uint32_t value; + uint32_t reserved; +} FAT_ENTRY; + +typedef struct { + int nfats; + loff_t fat_start; + unsigned int fat_size; /* unit is bytes */ + unsigned int fat_bits; /* size of a FAT entry */ + unsigned int eff_fat_bits; /* # of used bits in a FAT entry */ + uint32_t root_cluster; /* 0 for old-style root dir */ + loff_t root_start; + unsigned int root_entries; + loff_t data_start; + unsigned int cluster_size; + uint32_t clusters; + loff_t fsinfo_start; /* 0 if not present */ + long free_clusters; + loff_t backupboot_start; /* 0 if not present */ + unsigned char *fat; + DOS_FILE **cluster_owner; + char *label; +} DOS_FS; + +extern int interactive, rw, list, verbose, test, write_immed; +extern int atari_format; +extern unsigned n_files; +extern void *mem_queue; + +/* value to use as end-of-file marker */ +#define FAT_EOF(fs) ((atari_format ? 0xfff : 0xff8) | FAT_EXTD(fs)) +#define FAT_IS_EOF(fs,v) ((uint32_t)(v) >= (0xff8|FAT_EXTD(fs))) +/* value to mark bad clusters */ +#define FAT_BAD(fs) (0xff7 | FAT_EXTD(fs)) +/* range of values used for bad clusters */ +#define FAT_MIN_BAD(fs) ((atari_format ? 0xff0 : 0xff7) | FAT_EXTD(fs)) +#define FAT_MAX_BAD(fs) ((atari_format ? 0xff7 : 0xff7) | FAT_EXTD(fs)) +#define FAT_IS_BAD(fs,v) ((v) >= FAT_MIN_BAD(fs) && (v) <= FAT_MAX_BAD(fs)) + +/* return -16 as a number with fs->fat_bits bits */ +#define FAT_EXTD(fs) (((1 << fs->eff_fat_bits)-1) & ~0xf) + +/* marker for files with no 8.3 name */ +#define FAT_NO_83NAME 32 + +#endif diff --git a/dosfstools/src/io.c b/dosfstools/src/io.c index a703c2db1..450432c8e 100644 --- a/dosfstools/src/io.c +++ b/dosfstools/src/io.c @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -30,7 +31,6 @@ * by Roman Hodek */ #define _LARGEFILE64_SOURCE -#include #include #include #include @@ -41,7 +41,7 @@ #include #include -#include "dosfsck.h" +#include "fsck.fat.h" #include "common.h" #include "io.h" @@ -132,9 +132,11 @@ void fs_read(loff_t pos, int size, void *data) if (walk->pos < pos + size && walk->pos + walk->size > pos) { if (walk->pos < pos) memcpy(data, (char *)walk->data + pos - walk->pos, min(size, - walk->size - - pos + - walk->pos)); + walk-> + size - + pos + + walk-> + pos)); else memcpy((char *)data + walk->pos - pos, walk->data, min(walk->size, size + pos - walk->pos)); @@ -220,7 +222,7 @@ int fs_close(int write) changes = next; } if (close(fd) < 0) - pdie("closing file system"); + pdie("closing filesystem"); return changed || did_change; } diff --git a/dosfstools/src/io.h b/dosfstools/src/io.h index 2db4ea7ac..d23d07ee3 100644 --- a/dosfstools/src/io.h +++ b/dosfstools/src/io.h @@ -2,6 +2,7 @@ Copyright (C) 1993 Werner Almesberger Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -16,7 +17,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ @@ -26,7 +27,7 @@ #ifndef _IO_H #define _IO_H -#include /* for loff_t */ +#include /* for loff_t */ loff_t llseek(int fd, loff_t offset, int whence); @@ -34,7 +35,7 @@ loff_t llseek(int fd, loff_t offset, int whence); void fs_open(char *path, int rw); -/* Opens the file system PATH. If RW is zero, the file system is opened +/* Opens the filesystem PATH. If RW is zero, the filesystem is opened read-only, otherwise, it is opened read-write. */ void fs_read(loff_t pos, int size, void *data); @@ -55,13 +56,13 @@ void fs_write(loff_t pos, int size, void *data); int fs_close(int write); -/* Closes the file system, performs all pending changes if WRITE is non-zero +/* Closes the filesystem, performs all pending changes if WRITE is non-zero and removes the list of changes. Returns a non-zero integer if the file system has been changed since the last fs_open, zero otherwise. */ int fs_changed(void); -/* Determines whether the file system has changed. See fs_close. */ +/* Determines whether the filesystem has changed. See fs_close. */ extern unsigned device_no; diff --git a/dosfstools/src/lfn.c b/dosfstools/src/lfn.c index 736491cb0..2601172ee 100644 --- a/dosfstools/src/lfn.c +++ b/dosfstools/src/lfn.c @@ -1,6 +1,7 @@ /* lfn.c - Functions for handling VFAT long filenames Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,11 +16,12 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ #include +#include #include #include #include @@ -27,19 +29,19 @@ #include "common.h" #include "io.h" -#include "dosfsck.h" +#include "fsck.fat.h" #include "lfn.h" #include "file.h" typedef struct { - __u8 id; /* sequence number for slot */ - __u8 name0_4[10]; /* first 5 characters in name */ - __u8 attr; /* attribute byte */ - __u8 reserved; /* always 0 */ - __u8 alias_checksum; /* checksum for 8.3 alias */ - __u8 name5_10[12]; /* 6 more characters in name */ - __u16 start; /* starting cluster number, 0 in long slots */ - __u8 name11_12[4]; /* last 2 characters in name */ + uint8_t id; /* sequence number for slot */ + uint8_t name0_4[10]; /* first 5 characters in name */ + uint8_t attr; /* attribute byte */ + uint8_t reserved; /* always 0 */ + uint8_t alias_checksum; /* checksum for 8.3 alias */ + uint8_t name5_10[12]; /* 6 more characters in name */ + uint16_t start; /* starting cluster number, 0 in long slots */ + uint8_t name11_12[4]; /* last 2 characters in name */ } LFN_ENT; #define LFN_ID_START 0x40 @@ -85,6 +87,22 @@ static unsigned char fat_uni2esc[64] = { (cnv_unicode( lfn_unicode+(lfn_slot*CHARS_PER_LFN*2), \ lfn_parts*CHARS_PER_LFN, 0 )) +#define BYTES_TO_WCHAR(cl,ch) ((wchar_t)((unsigned)(cl) + ((unsigned)(ch) << 8))) +static size_t mbslen(wchar_t x) +{ + wchar_t wstr[] = { x, 0 }; + return wcstombs(NULL, wstr, 0); +} + +static size_t wctombs(char *dest, wchar_t x) +{ + wchar_t wstr[] = { x, 0 }; + size_t size = wcstombs(NULL, wstr, 0); + if (size != (size_t) - 1) + size = wcstombs(dest, wstr, size + 1); + return size; +} + /* This function converts an unicode string to a normal ASCII string, assuming * ISO-8859-1 charset. Characters not in 8859-1 are converted to the same * escape notation as used by the kernel, i.e. the uuencode-like ":xxx" */ @@ -93,10 +111,13 @@ static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q) const unsigned char *up; unsigned char *out, *cp; int len, val; + size_t x; for (len = 0, up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); up += 2) { - if (UNICODE_CONVERTABLE(up[0], up[1])) + if ((x = mbslen(BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1) + len += x; + else if (UNICODE_CONVERTABLE(up[0], up[1])) ++len; else len += 4; @@ -104,7 +125,10 @@ static char *cnv_unicode(const unsigned char *uni, int maxlen, int use_q) cp = out = use_q ? qalloc(&mem_queue, len + 1) : alloc(len + 1); for (up = uni; (up - uni) / 2 < maxlen && (up[0] || up[1]); up += 2) { - if (UNICODE_CONVERTABLE(up[0], up[1])) + if ((x = + wctombs((char *)cp, BYTES_TO_WCHAR(up[0], up[1]))) != (size_t) - 1) + cp += x; + else if (UNICODE_CONVERTABLE(up[0], up[1])) *cp++ = up[0]; else { /* here the same escape notation is used as in the Linux kernel */ @@ -150,7 +174,7 @@ static void clear_lfn_slots(int start, int end) void lfn_fix_checksum(loff_t from, loff_t to, const char *short_name) { int i; - __u8 sum; + uint8_t sum; for (sum = 0, i = 0; i < 11; i++) sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + short_name[i]; @@ -366,7 +390,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) sizeof(lfn->reserved), &lfn->reserved); } } - if (lfn->start != CT_LE_W(0)) { + if (lfn->start != htole16(0)) { printf("Start cluster field in VFAT long filename slot is not 0 " "(but 0x%04x).\n", lfn->start); if (interactive) @@ -374,7 +398,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) else printf("Auto-setting to 0.\n"); if (!interactive || get_key("12", "?") == '1') { - lfn->start = CT_LE_W(0); + lfn->start = htole16(0); fs_write(dir_offset + offsetof(LFN_ENT, start), sizeof(lfn->start), &lfn->start); } @@ -386,7 +410,7 @@ void lfn_add_slot(DIR_ENT * de, loff_t dir_offset) char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) { char *lfn; - __u8 sum; + uint8_t sum; int i; *lfn_offset = 0; @@ -430,7 +454,7 @@ char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) return NULL; case '3': for (i = 0; i < lfn_parts; ++i) { - __u8 id = (lfn_parts - i) | (i == 0 ? LFN_ID_START : 0); + uint8_t id = (lfn_parts - i) | (i == 0 ? LFN_ID_START : 0); fs_write(lfn_offsets[i] + offsetof(LFN_ENT, id), sizeof(id), &id); } @@ -440,8 +464,10 @@ char *lfn_get(DIR_ENT * de, loff_t * lfn_offset) } } - for (sum = 0, i = 0; i < 11; i++) + for (sum = 0, i = 0; i < 8; i++) sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->name[i]; + for (i = 0; i < 3; i++) + sum = (((sum & 1) << 7) | ((sum & 0xfe) >> 1)) + de->ext[i]; if (sum != lfn_checksum) { /* checksum doesn't match, long name doesn't apply to this alias */ /* Causes: 1) alias renamed */ diff --git a/dosfstools/src/lfn.h b/dosfstools/src/lfn.h index 2ea44a725..e5c3991ff 100644 --- a/dosfstools/src/lfn.h +++ b/dosfstools/src/lfn.h @@ -1,6 +1,7 @@ /* lfn.h - Functions for handling VFAT long filenames Copyright (C) 1998 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,7 +16,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ diff --git a/dosfstools/src/mkdosfs.c b/dosfstools/src/mkdosfs.c deleted file mode 100644 index 9873bef12..000000000 --- a/dosfstools/src/mkdosfs.c +++ /dev/null @@ -1,1733 +0,0 @@ -/* mkdosfs.c - utility to create FAT/MS-DOS filesystems - - Copyright (C) 1991 Linus Torvalds - Copyright (C) 1992-1993 Remy Card - Copyright (C) 1993-1994 David Hudson - Copyright (C) 1998 H. Peter Anvin - Copyright (C) 1998-2005 Roman Hodek - - 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 - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - On Debian systems, the complete text of the GNU General Public License - can be found in /usr/share/common-licenses/GPL-3 file. -*/ - -/* Description: Utility to allow an MS-DOS filesystem to be created - under Linux. A lot of the basic structure of this program has been - borrowed from Remy Card's "mke2fs" code. - - As far as possible the aim here is to make the "mkdosfs" command - look almost identical to the other Linux filesystem make utilties, - eg bad blocks are still specified as blocks, not sectors, but when - it comes down to it, DOS is tied to the idea of a sector (512 bytes - as a rule), and not the block. For example the boot block does not - occupy a full cluster. - - Fixes/additions May 1998 by Roman Hodek - : - - Atari format support - - New options -A, -S, -C - - Support for filesystems > 2GB - - FAT32 support */ - -/* Include the header files */ - -#include "version.h" - -#include -#include -#if defined(_USING_BIONIC_) -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#if __BYTE_ORDER == __BIG_ENDIAN - -#include -#ifdef __le16_to_cpu -/* ++roman: 2.1 kernel headers define these function, they're probably more - * efficient then coding the swaps machine-independently. */ -#define CF_LE_W __le16_to_cpu -#define CF_LE_L __le32_to_cpu -#define CT_LE_W __cpu_to_le16 -#define CT_LE_L __cpu_to_le32 -#else -#define CF_LE_W(v) ((((v) & 0xff) << 8) | (((v) >> 8) & 0xff)) -#define CF_LE_L(v) (((unsigned)(v)>>24) | (((unsigned)(v)>>8)&0xff00) | \ - (((unsigned)(v)<<8)&0xff0000) | ((unsigned)(v)<<24)) -#define CT_LE_W(v) CF_LE_W(v) -#define CT_LE_L(v) CF_LE_L(v) -#endif /* defined(__le16_to_cpu) */ - -#else - -#define CF_LE_W(v) (v) -#define CF_LE_L(v) (v) -#define CT_LE_W(v) (v) -#define CT_LE_L(v) (v) - -#endif /* __BIG_ENDIAN */ - -/* In earlier versions, an own llseek() was used, but glibc lseek() is - * sufficient (or even better :) for 64 bit offsets in the meantime */ -#define llseek lseek64 - -/* Constant definitions */ - -#define TRUE 1 /* Boolean constants */ -#define FALSE 0 - -#define TEST_BUFFER_BLOCKS 16 -#define HARD_SECTOR_SIZE 512 -#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE ) - -/* Macro definitions */ - -/* Report a failure message and return a failure error code */ - -#define die( str ) fatal_error( "%s: " str "\n" ) - -/* Mark a cluster in the FAT as bad */ - -#define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD ) - -/* Compute ceil(a/b) */ - -inline int cdiv(int a, int b) -{ - return (a + b - 1) / b; -} - -/* MS-DOS filesystem structures -- I included them here instead of - including linux/msdos_fs.h since that doesn't include some fields we - need */ - -#define ATTR_RO 1 /* read-only */ -#define ATTR_HIDDEN 2 /* hidden */ -#define ATTR_SYS 4 /* system */ -#define ATTR_VOLUME 8 /* volume label */ -#define ATTR_DIR 16 /* directory */ -#define ATTR_ARCH 32 /* archived */ - -#define ATTR_NONE 0 /* no attribute bits */ -#define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN) - /* attribute bits that are copied "as is" */ - -/* FAT values */ -#define FAT_EOF (atari_format ? 0x0fffffff : 0x0ffffff8) -#define FAT_BAD 0x0ffffff7 - -#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */ -#define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */ -#define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */ -#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */ - -#define BOOT_SIGN 0xAA55 /* Boot sector magic number */ - -#define MAX_CLUST_12 ((1 << 12) - 16) -#define MAX_CLUST_16 ((1 << 16) - 16) -#define MIN_CLUST_32 65529 -/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong - * to the cluster number. So the max. cluster# is based on 2^28 */ -#define MAX_CLUST_32 ((1 << 28) - 16) - -#define FAT12_THRESHOLD 4085 - -#define OLDGEMDOS_MAX_SECTORS 32765 -#define GEMDOS_MAX_SECTORS 65531 -#define GEMDOS_MAX_SECTOR_SIZE (16*1024) - -#define BOOTCODE_SIZE 448 -#define BOOTCODE_FAT32_SIZE 420 - -/* __attribute__ ((packed)) is used on all structures to make gcc ignore any - * alignments */ - -struct msdos_volume_info { - __u8 drive_number; /* BIOS drive number */ - __u8 RESERVED; /* Unused */ - __u8 ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */ - __u8 volume_id[4]; /* Volume ID number */ - __u8 volume_label[11]; /* Volume label */ - __u8 fs_type[8]; /* Typically FAT12 or FAT16 */ -} __attribute__ ((packed)); - -struct msdos_boot_sector { - __u8 boot_jump[3]; /* Boot strap short or near jump */ - __u8 system_id[8]; /* Name - can be used to special case - partition manager volumes */ - __u8 sector_size[2]; /* bytes per logical sector */ - __u8 cluster_size; /* sectors/cluster */ - __u16 reserved; /* reserved sectors */ - __u8 fats; /* number of FATs */ - __u8 dir_entries[2]; /* root directory entries */ - __u8 sectors[2]; /* number of sectors */ - __u8 media; /* media code (unused) */ - __u16 fat_length; /* sectors/FAT */ - __u16 secs_track; /* sectors per track */ - __u16 heads; /* number of heads */ - __u32 hidden; /* hidden sectors (unused) */ - __u32 total_sect; /* number of sectors (if sectors == 0) */ - union { - struct { - struct msdos_volume_info vi; - __u8 boot_code[BOOTCODE_SIZE]; - } __attribute__ ((packed)) _oldfat; - struct { - __u32 fat32_length; /* sectors/FAT */ - __u16 flags; /* bit 8: fat mirroring, low 4: active fat */ - __u8 version[2]; /* major, minor filesystem version */ - __u32 root_cluster; /* first cluster in root directory */ - __u16 info_sector; /* filesystem info sector */ - __u16 backup_boot; /* backup boot sector */ - __u16 reserved2[6]; /* Unused */ - struct msdos_volume_info vi; - __u8 boot_code[BOOTCODE_FAT32_SIZE]; - } __attribute__ ((packed)) _fat32; - } __attribute__ ((packed)) fstype; - __u16 boot_sign; -} __attribute__ ((packed)); -#define fat32 fstype._fat32 -#define oldfat fstype._oldfat - -struct fat32_fsinfo { - __u32 reserved1; /* Nothing as far as I can tell */ - __u32 signature; /* 0x61417272L */ - __u32 free_clusters; /* Free cluster count. -1 if unknown */ - __u32 next_cluster; /* Most recently allocated cluster. - * Unused under Linux. */ - __u32 reserved2[4]; -}; - -struct msdos_dir_entry { - char name[8], ext[3]; /* name and extension */ - __u8 attr; /* attribute bits */ - __u8 lcase; /* Case for base and extension */ - __u8 ctime_ms; /* Creation time, milliseconds */ - __u16 ctime; /* Creation time */ - __u16 cdate; /* Creation date */ - __u16 adate; /* Last access date */ - __u16 starthi; /* high 16 bits of first cl. (FAT32) */ - __u16 time, date, start; /* time, date and first cluster */ - __u32 size; /* file size (in bytes) */ -} __attribute__ ((packed)); - -/* The "boot code" we put into the filesystem... it writes a message and - tells the user to try again */ - -char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 }; - -char dummy_boot_jump_m68k[2] = { 0x60, 0x1c }; - -#define MSG_OFFSET_OFFSET 3 -char dummy_boot_code[BOOTCODE_SIZE] = "\x0e" /* push cs */ - "\x1f" /* pop ds */ - "\xbe\x5b\x7c" /* mov si, offset message_txt */ - /* write_msg: */ - "\xac" /* lodsb */ - "\x22\xc0" /* and al, al */ - "\x74\x0b" /* jz key_press */ - "\x56" /* push si */ - "\xb4\x0e" /* mov ah, 0eh */ - "\xbb\x07\x00" /* mov bx, 0007h */ - "\xcd\x10" /* int 10h */ - "\x5e" /* pop si */ - "\xeb\xf0" /* jmp write_msg */ - /* key_press: */ - "\x32\xe4" /* xor ah, ah */ - "\xcd\x16" /* int 16h */ - "\xcd\x19" /* int 19h */ - "\xeb\xfe" /* foo: jmp foo */ - /* message_txt: */ - "This is not a bootable disk. Please insert a bootable floppy and\r\n" - "press any key to try again ... \r\n"; - -#define MESSAGE_OFFSET 29 /* Offset of message in above code */ - -/* Global variables - the root of all evil :-) - see these and weep! */ - -static char *program_name = "mkdosfs"; /* Name of the program */ -static char *device_name = NULL; /* Name of the device on which to create the filesystem */ -static int atari_format = 0; /* Use Atari variation of MS-DOS FS format */ -static int check = FALSE; /* Default to no readablity checking */ -static int verbose = 0; /* Default to verbose mode off */ -static long volume_id; /* Volume ID number */ -static time_t create_time; /* Creation time */ -static struct timeval create_timeval; /* Creation time */ -static char volume_name[] = " "; /* Volume name */ -static unsigned long long blocks; /* Number of blocks in filesystem */ -static int sector_size = 512; /* Size of a logical sector */ -static int sector_size_set = 0; /* User selected sector size */ -static int backup_boot = 0; /* Sector# of backup boot sector */ -static int reserved_sectors = 0; /* Number of reserved sectors */ -static int badblocks = 0; /* Number of bad blocks in the filesystem */ -static int nr_fats = 2; /* Default number of FATs to produce */ -static int size_fat = 0; /* Size in bits of FAT entries */ -static int size_fat_by_user = 0; /* 1 if FAT size user selected */ -static int dev = -1; /* FS block device file handle */ -static int ignore_full_disk = 0; /* Ignore warning about 'full' disk devices */ -static off_t currently_testing = 0; /* Block currently being tested (if autodetect bad blocks) */ -static struct msdos_boot_sector bs; /* Boot sector data */ -static int start_data_sector; /* Sector number for the start of the data area */ -static int start_data_block; /* Block number for the start of the data area */ -static unsigned char *fat; /* File allocation table */ -static unsigned alloced_fat_length; /* # of FAT sectors we can keep in memory */ -static unsigned char *info_sector; /* FAT32 info sector */ -static struct msdos_dir_entry *root_dir; /* Root directory */ -static int size_root_dir; /* Size of the root directory in bytes */ -static int sectors_per_cluster = 0; /* Number of sectors per disk cluster */ -static int root_dir_entries = 0; /* Number of root directory entries */ -static char *blank_sector; /* Blank sector - all zeros */ -static int hidden_sectors = 0; /* Number of hidden sectors */ -static int malloc_entire_fat = FALSE; /* Whether we should malloc() the entire FAT or not */ -static int align_structures = TRUE; /* Whether to enforce alignment */ -static int orphaned_sectors = 0; /* Sectors that exist in the last block of filesystem */ - -/* Function prototype definitions */ - -static void fatal_error(const char *fmt_string) __attribute__ ((noreturn)); -static void mark_FAT_cluster(int cluster, unsigned int value); -static void mark_FAT_sector(int sector, unsigned int value); -static long do_check(char *buffer, int try, off_t current_block); -static void alarm_intr(int alnum); -static void check_blocks(void); -static void get_list_blocks(char *filename); -static int valid_offset(int fd, loff_t offset); -static unsigned long long count_blocks(char *filename, int *remainder); -static void check_mount(char *device_name); -static void establish_params(int device_num, int size); -static void setup_tables(void); -static void write_tables(void); - -/* The function implementations */ - -/* Handle the reporting of fatal errors. Volatile to let gcc know that this doesn't return */ - -static void fatal_error(const char *fmt_string) -{ - fprintf(stderr, fmt_string, program_name, device_name); - exit(1); /* The error exit code is 1! */ -} - -/* Mark the specified cluster as having a particular value */ - -static void mark_FAT_cluster(int cluster, unsigned int value) -{ - switch (size_fat) { - case 12: - value &= 0x0fff; - if (((cluster * 3) & 0x1) == 0) { - fat[3 * cluster / 2] = (unsigned char)(value & 0x00ff); - fat[(3 * cluster / 2) + 1] = - (unsigned char)((fat[(3 * cluster / 2) + 1] & 0x00f0) - | ((value & 0x0f00) >> 8)); - } else { - fat[3 * cluster / 2] = - (unsigned char)((fat[3 * cluster / 2] & 0x000f) | - ((value & 0x000f) << 4)); - fat[(3 * cluster / 2) + 1] = (unsigned char)((value & 0x0ff0) >> 4); - } - break; - - case 16: - value &= 0xffff; - fat[2 * cluster] = (unsigned char)(value & 0x00ff); - fat[(2 * cluster) + 1] = (unsigned char)(value >> 8); - break; - - case 32: - value &= 0xfffffff; - fat[4 * cluster] = (unsigned char)(value & 0x000000ff); - fat[(4 * cluster) + 1] = (unsigned char)((value & 0x0000ff00) >> 8); - fat[(4 * cluster) + 2] = (unsigned char)((value & 0x00ff0000) >> 16); - fat[(4 * cluster) + 3] = (unsigned char)((value & 0xff000000) >> 24); - break; - - default: - die("Bad FAT size (not 12, 16, or 32)"); - } -} - -/* Mark a specified sector as having a particular value in it's FAT entry */ - -static void mark_FAT_sector(int sector, unsigned int value) -{ - int cluster; - - cluster = (sector - start_data_sector) / (int)(bs.cluster_size) / - (sector_size / HARD_SECTOR_SIZE); - if (cluster < 0) - die("Invalid cluster number in mark_FAT_sector: probably bug!"); - - mark_FAT_cluster(cluster, value); -} - -/* Perform a test on a block. Return the number of blocks that could be read successfully */ - -static long do_check(char *buffer, int try, off_t current_block) -{ - long got; - - if (llseek(dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */ - !=current_block * BLOCK_SIZE) - die("seek failed during testing for blocks"); - - got = read(dev, buffer, try * BLOCK_SIZE); /* Try reading! */ - if (got < 0) - got = 0; - - if (got & (BLOCK_SIZE - 1)) - printf("Unexpected values in do_check: probably bugs\n"); - got /= BLOCK_SIZE; - - return got; -} - -/* Alarm clock handler - display the status of the quest for bad blocks! Then retrigger the alarm for five senconds - later (so we can come here again) */ - -static void alarm_intr(int alnum) -{ - if (currently_testing >= blocks) - return; - - signal(SIGALRM, alarm_intr); - alarm(5); - if (!currently_testing) - return; - - printf("%lld... ", (unsigned long long)currently_testing); - fflush(stdout); -} - -static void check_blocks(void) -{ - int try, got; - int i; - static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; - - if (verbose) { - printf("Searching for bad blocks "); - fflush(stdout); - } - currently_testing = 0; - if (verbose) { - signal(SIGALRM, alarm_intr); - alarm(5); - } - try = TEST_BUFFER_BLOCKS; - while (currently_testing < blocks) { - if (currently_testing + try > blocks) - try = blocks - currently_testing; - got = do_check(blkbuf, try, currently_testing); - currently_testing += got; - if (got == try) { - try = TEST_BUFFER_BLOCKS; - continue; - } else - try = 1; - if (currently_testing < start_data_block) - die("bad blocks before data-area: cannot make fs"); - - for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ - mark_sector_bad(currently_testing * SECTORS_PER_BLOCK + i); - badblocks++; - currently_testing++; - } - - if (verbose) - printf("\n"); - - if (badblocks) - printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); -} - -static void get_list_blocks(char *filename) -{ - int i; - FILE *listfile; - unsigned long blockno; - - listfile = fopen(filename, "r"); - if (listfile == (FILE *) NULL) - die("Can't open file of bad blocks"); - - while (!feof(listfile)) { - fscanf(listfile, "%ld\n", &blockno); - for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ - mark_sector_bad(blockno * SECTORS_PER_BLOCK + i); - badblocks++; - } - fclose(listfile); - - if (badblocks) - printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); -} - -/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it - isn't valid or TRUE if it is */ - -static int valid_offset(int fd, loff_t offset) -{ - char ch; - - if (llseek(fd, offset, SEEK_SET) < 0) - return FALSE; - if (read(fd, &ch, 1) < 1) - return FALSE; - return TRUE; -} - -/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */ - -static unsigned long long count_blocks(char *filename, int *remainder) - -{ - loff_t high, low; - int fd; - - if ((fd = open(filename, O_RDONLY)) < 0) { - perror(filename); - exit(1); - } - - /* first try SEEK_END, which should work on most devices nowadays */ - if ((low = llseek(fd, 0, SEEK_END)) <= 0) { - low = 0; - for (high = 1; valid_offset(fd, high); high *= 2) - low = high; - while (low < high - 1) { - const loff_t mid = (low + high) / 2; - if (valid_offset(fd, mid)) - low = mid; - else - high = mid; - } - ++low; - } - - close(fd); - *remainder = (low%BLOCK_SIZE)/sector_size; - return(low / BLOCK_SIZE); -} - -/* Check to see if the specified device is currently mounted - abort if it is */ - -static void check_mount(char *device_name) -{ -#if ! defined(_USING_BIONIC_) - FILE *f; - struct mntent *mnt; - - if ((f = setmntent(MOUNTED, "r")) == NULL) - return; - while ((mnt = getmntent(f)) != NULL) - if (strcmp(device_name, mnt->mnt_fsname) == 0) - die("%s contains a mounted file system."); - endmntent(f); -#endif -} - -/* Establish the geometry and media parameters for the device */ - -static void establish_params(int device_num, int size) -{ - long loop_size; - struct hd_geometry geometry; - struct floppy_struct param; - int def_root_dir_entries = 512; - - if ((0 == device_num) || ((device_num & 0xff00) == 0x0200)) - /* file image or floppy disk */ - { - if (0 == device_num) { - param.size = size / 512; - switch (param.size) { - case 720: - param.sect = 9; - param.head = 2; - break; - case 1440: - param.sect = 9; - param.head = 2; - break; - case 2400: - param.sect = 15; - param.head = 2; - break; - case 2880: - param.sect = 18; - param.head = 2; - break; - case 5760: - param.sect = 36; - param.head = 2; - break; - default: - /* fake values */ - param.sect = 32; - param.head = 64; - break; - } - - } else { /* is a floppy diskette */ - - if (ioctl(dev, FDGETPRM, ¶m)) /* Can we get the diskette geometry? */ - die("unable to get diskette geometry for '%s'"); - } - bs.secs_track = CT_LE_W(param.sect); /* Set up the geometry information */ - bs.heads = CT_LE_W(param.head); - switch (param.size) { /* Set up the media descriptor byte */ - case 720: /* 5.25", 2, 9, 40 - 360K */ - bs.media = (char)0xfd; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 1440: /* 3.5", 2, 9, 80 - 720K */ - bs.media = (char)0xf9; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 2400: /* 5.25", 2, 15, 80 - 1200K */ - bs.media = (char)0xf9; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - case 5760: /* 3.5", 2, 36, 80 - 2880K */ - bs.media = (char)0xf0; - bs.cluster_size = (char)2; - def_root_dir_entries = 224; - break; - - case 2880: /* 3.5", 2, 18, 80 - 1440K */ -floppy_default: - bs.media = (char)0xf0; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - default: /* Anything else */ - if (0 == device_num) - goto def_hd_params; - else - goto floppy_default; - } - } else if ((device_num & 0xff00) == 0x0700) { /* This is a loop device */ - if (ioctl(dev, BLKGETSIZE, &loop_size)) - die("unable to get loop device size"); - - switch (loop_size) { /* Assuming the loop device -> floppy later */ - case 720: /* 5.25", 2, 9, 40 - 360K */ - bs.secs_track = CF_LE_W(9); - bs.heads = CF_LE_W(2); - bs.media = (char)0xfd; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 1440: /* 3.5", 2, 9, 80 - 720K */ - bs.secs_track = CF_LE_W(9); - bs.heads = CF_LE_W(2); - bs.media = (char)0xf9; - bs.cluster_size = (char)2; - def_root_dir_entries = 112; - break; - - case 2400: /* 5.25", 2, 15, 80 - 1200K */ - bs.secs_track = CF_LE_W(15); - bs.heads = CF_LE_W(2); - bs.media = (char)0xf9; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - case 5760: /* 3.5", 2, 36, 80 - 2880K */ - bs.secs_track = CF_LE_W(36); - bs.heads = CF_LE_W(2); - bs.media = (char)0xf0; - bs.cluster_size = (char)2; - bs.dir_entries[0] = (char)224; - bs.dir_entries[1] = (char)0; - break; - - case 2880: /* 3.5", 2, 18, 80 - 1440K */ - bs.secs_track = CF_LE_W(18); - bs.heads = CF_LE_W(2); - bs.media = (char)0xf0; - bs.cluster_size = (char)(atari_format ? 2 : 1); - def_root_dir_entries = 224; - break; - - default: /* Anything else: default hd setup */ - printf("Loop device does not match a floppy size, using " - "default hd params\n"); - bs.secs_track = CT_LE_W(32); /* these are fake values... */ - bs.heads = CT_LE_W(64); - goto def_hd_params; - } - } else - /* Must be a hard disk then! */ - { - /* Can we get the drive geometry? (Note I'm not too sure about */ - /* whether to use HDIO_GETGEO or HDIO_REQ) */ - if (ioctl(dev, HDIO_GETGEO, &geometry) || geometry.sectors == 0 - || geometry.heads == 0) { - printf("unable to get drive geometry, using default 255/63\n"); - bs.secs_track = CT_LE_W(63); - bs.heads = CT_LE_W(255); - } else { - bs.secs_track = CT_LE_W(geometry.sectors); /* Set up the geometry information */ - bs.heads = CT_LE_W(geometry.heads); - } -def_hd_params: - bs.media = (char)0xf8; /* Set up the media descriptor for a hard drive */ - if (!size_fat && blocks * SECTORS_PER_BLOCK > 1064960) { - if (verbose) - printf("Auto-selecting FAT32 for large filesystem\n"); - size_fat = 32; - } - if (size_fat == 32) { - /* For FAT32, try to do the same as M$'s format command - * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): - * fs size <= 260M: 0.5k clusters - * fs size <= 8G: 4k clusters - * fs size <= 16G: 8k clusters - * fs size > 16G: 16k clusters - */ - unsigned long sz_mb = - (blocks + (1 << (20 - BLOCK_SIZE_BITS)) - 1) >> (20 - - BLOCK_SIZE_BITS); - bs.cluster_size = - sz_mb > 16 * 1024 ? 32 : sz_mb > 8 * 1024 ? 16 : sz_mb > - 260 ? 8 : 1; - } else { - /* FAT12 and FAT16: start at 4 sectors per cluster */ - bs.cluster_size = (char)4; - } - } - - if (!root_dir_entries) - root_dir_entries = def_root_dir_entries; -} - -/* - * If alignment is enabled, round the first argument up to the second; the - * latter must be a power of two. - */ -static unsigned int align_object(unsigned int sectors, unsigned int clustsize) -{ - if (align_structures) - return (sectors + clustsize - 1) & ~(clustsize - 1); - else - return sectors; -} - -/* Create the filesystem data tables */ - -static void setup_tables(void) -{ - unsigned num_sectors; - unsigned cluster_count = 0, fat_length; - struct tm *ctime; - struct msdos_volume_info *vi = - (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi); - - if (atari_format) - /* On Atari, the first few bytes of the boot sector are assigned - * differently: The jump code is only 2 bytes (and m68k machine code - * :-), then 6 bytes filler (ignored), then 3 byte serial number. */ - memcpy(bs.system_id - 1, "mkdosf", 6); - else - strcpy((char *)bs.system_id, "mkdosfs"); - if (sectors_per_cluster) - bs.cluster_size = (char)sectors_per_cluster; - if (size_fat == 32) { - /* Under FAT32, the root dir is in a cluster chain, and this is - * signalled by bs.dir_entries being 0. */ - root_dir_entries = 0; - } - - if (atari_format) { - bs.system_id[5] = (unsigned char)(volume_id & 0x000000ff); - bs.system_id[6] = (unsigned char)((volume_id & 0x0000ff00) >> 8); - bs.system_id[7] = (unsigned char)((volume_id & 0x00ff0000) >> 16); - } else { - vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff); - vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8); - vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16); - vi->volume_id[3] = (unsigned char)(volume_id >> 24); - } - - if (!atari_format) { - memcpy(vi->volume_label, volume_name, 11); - - memcpy(bs.boot_jump, dummy_boot_jump, 3); - /* Patch in the correct offset to the boot code */ - bs.boot_jump[1] = ((size_fat == 32 ? - (char *)&bs.fat32.boot_code : - (char *)&bs.oldfat.boot_code) - (char *)&bs) - 2; - - if (size_fat == 32) { - int offset = (char *)&bs.fat32.boot_code - - (char *)&bs + MESSAGE_OFFSET + 0x7c00; - if (dummy_boot_code[BOOTCODE_FAT32_SIZE - 1]) - printf("Warning: message too long; truncated\n"); - dummy_boot_code[BOOTCODE_FAT32_SIZE - 1] = 0; - memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE); - bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff; - bs.fat32.boot_code[MSG_OFFSET_OFFSET + 1] = offset >> 8; - } else { - memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE); - } - bs.boot_sign = CT_LE_W(BOOT_SIGN); - } else { - memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2); - } - if (verbose >= 2) - printf("Boot jump code is %02x %02x\n", - bs.boot_jump[0], bs.boot_jump[1]); - - if (!reserved_sectors) - reserved_sectors = (size_fat == 32) ? 32 : 1; - else { - if (size_fat == 32 && reserved_sectors < 2) - die("On FAT32 at least 2 reserved sectors are needed."); - } - bs.reserved = CT_LE_W(reserved_sectors); - if (verbose >= 2) - printf("Using %d reserved sectors\n", reserved_sectors); - bs.fats = (char)nr_fats; - if (!atari_format || size_fat == 32) - bs.hidden = CT_LE_L(hidden_sectors); - else { - /* In Atari format, hidden is a 16 bit field */ - __u16 hidden = CT_LE_W(hidden_sectors); - if (hidden_sectors & ~0xffff) - die("#hidden doesn't fit in 16bit field of Atari format\n"); - memcpy(&bs.hidden, &hidden, 2); - } - - num_sectors = (long long)(blocks *BLOCK_SIZE / sector_size)+orphaned_sectors; - - if (!atari_format) { - unsigned fatdata1216; /* Sectors for FATs + data area (FAT12/16) */ - unsigned fatdata32; /* Sectors for FATs + data area (FAT32) */ - unsigned fatlength12, fatlength16, fatlength32; - unsigned maxclust12, maxclust16, maxclust32; - unsigned clust12, clust16, clust32; - int maxclustsize; - unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size); - - /* - * If the filesystem is 8192 sectors or less (4 MB with 512-byte - * sectors, i.e. floppy size), don't align the data structures. - */ - if (num_sectors <= 8192) { - if (align_structures && verbose >= 2) - printf("Disabling alignment due to tiny filesystem\n"); - - align_structures = FALSE; - } - - if (sectors_per_cluster) - bs.cluster_size = maxclustsize = sectors_per_cluster; - else - /* An initial guess for bs.cluster_size should already be set */ - maxclustsize = 128; - - do { - fatdata32 = num_sectors - - align_object(reserved_sectors, bs.cluster_size); - fatdata1216 = fatdata32 - - align_object(root_dir_sectors, bs.cluster_size); - - if (verbose >= 2) - printf("Trying with %d sectors/cluster:\n", bs.cluster_size); - - /* The factor 2 below avoids cut-off errors for nr_fats == 1. - * The "nr_fats*3" is for the reserved first two FAT entries */ - clust12 = 2 * ((long long)fatdata1216 * sector_size + nr_fats * 3) / - (2 * (int)bs.cluster_size * sector_size + nr_fats * 3); - fatlength12 = cdiv(((clust12 + 2) * 3 + 1) >> 1, sector_size); - fatlength12 = align_object(fatlength12, bs.cluster_size); - /* Need to recalculate number of clusters, since the unused parts of the - * FATS and data area together could make up space for an additional, - * not really present cluster. */ - clust12 = (fatdata1216 - nr_fats * fatlength12) / bs.cluster_size; - maxclust12 = (fatlength12 * 2 * sector_size) / 3; - if (maxclust12 > MAX_CLUST_12) - maxclust12 = MAX_CLUST_12; - if (verbose >= 2) - printf("FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", - clust12, fatlength12, maxclust12, MAX_CLUST_12); - if (clust12 > maxclust12 - 2) { - clust12 = 0; - if (verbose >= 2) - printf("FAT12: too much clusters\n"); - } - - clust16 = ((long long)fatdata1216 * sector_size + nr_fats * 4) / - ((int)bs.cluster_size * sector_size + nr_fats * 2); - fatlength16 = cdiv((clust16 + 2) * 2, sector_size); - fatlength16 = align_object(fatlength16, bs.cluster_size); - /* Need to recalculate number of clusters, since the unused parts of the - * FATS and data area together could make up space for an additional, - * not really present cluster. */ - clust16 = (fatdata1216 - nr_fats * fatlength16) / bs.cluster_size; - maxclust16 = (fatlength16 * sector_size) / 2; - if (maxclust16 > MAX_CLUST_16) - maxclust16 = MAX_CLUST_16; - if (verbose >= 2) - printf("FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", - clust16, fatlength16, maxclust16, MAX_CLUST_16); - if (clust16 > maxclust16 - 2) { - if (verbose >= 2) - printf("FAT16: too much clusters\n"); - clust16 = 0; - } - /* The < 4078 avoids that the filesystem will be misdetected as having a - * 12 bit FAT. */ - if (clust16 < FAT12_THRESHOLD - && !(size_fat_by_user && size_fat == 16)) { - if (verbose >= 2) - printf(clust16 < FAT12_THRESHOLD ? - "FAT16: would be misdetected as FAT12\n" : - "FAT16: too much clusters\n"); - clust16 = 0; - } - - clust32 = ((long long)fatdata32 * sector_size + nr_fats * 8) / - ((int)bs.cluster_size * sector_size + nr_fats * 4); - fatlength32 = cdiv((clust32 + 2) * 4, sector_size); - fatlength32 = align_object(fatlength32, bs.cluster_size); - /* Need to recalculate number of clusters, since the unused parts of the - * FATS and data area together could make up space for an additional, - * not really present cluster. */ - clust32 = (fatdata32 - nr_fats * fatlength32) / bs.cluster_size; - maxclust32 = (fatlength32 * sector_size) / 4; - if (maxclust32 > MAX_CLUST_32) - maxclust32 = MAX_CLUST_32; - if (clust32 && clust32 < MIN_CLUST_32 - && !(size_fat_by_user && size_fat == 32)) { - clust32 = 0; - if (verbose >= 2) - printf("FAT32: not enough clusters (%d)\n", MIN_CLUST_32); - } - if (verbose >= 2) - printf("FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", - clust32, fatlength32, maxclust32, MAX_CLUST_32); - if (clust32 > maxclust32) { - clust32 = 0; - if (verbose >= 2) - printf("FAT32: too much clusters\n"); - } - - if ((clust12 && (size_fat == 0 || size_fat == 12)) || - (clust16 && (size_fat == 0 || size_fat == 16)) || - (clust32 && size_fat == 32)) - break; - - bs.cluster_size <<= 1; - } while (bs.cluster_size && bs.cluster_size <= maxclustsize); - - /* Use the optimal FAT size if not specified; - * FAT32 is (not yet) choosen automatically */ - if (!size_fat) { - size_fat = (clust16 > clust12) ? 16 : 12; - if (verbose >= 2) - printf("Choosing %d bits for FAT\n", size_fat); - } - - switch (size_fat) { - case 12: - cluster_count = clust12; - fat_length = fatlength12; - bs.fat_length = CT_LE_W(fatlength12); - memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8); - break; - - case 16: - if (clust16 < FAT12_THRESHOLD) { - if (size_fat_by_user) { - fprintf(stderr, "WARNING: Not enough clusters for a " - "16 bit FAT! The filesystem will be\n" - "misinterpreted as having a 12 bit FAT without " - "mount option \"fat=16\".\n"); - } else { - fprintf(stderr, "This filesystem has an unfortunate size. " - "A 12 bit FAT cannot provide\n" - "enough clusters, but a 16 bit FAT takes up a little " - "bit more space so that\n" - "the total number of clusters becomes less than the " - "threshold value for\n" - "distinction between 12 and 16 bit FATs.\n"); - die("Make the file system a bit smaller manually."); - } - } - cluster_count = clust16; - fat_length = fatlength16; - bs.fat_length = CT_LE_W(fatlength16); - memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8); - break; - - case 32: - if (clust32 < MIN_CLUST_32) - fprintf(stderr, - "WARNING: Not enough clusters for a 32 bit FAT!\n"); - cluster_count = clust32; - fat_length = fatlength32; - bs.fat_length = CT_LE_W(0); - bs.fat32.fat32_length = CT_LE_L(fatlength32); - memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8); - root_dir_entries = 0; - break; - - default: - die("FAT not 12, 16 or 32 bits"); - } - - /* Adjust the reserved number of sectors for alignment */ - reserved_sectors = align_object(reserved_sectors, bs.cluster_size); - bs.reserved = CT_LE_W(reserved_sectors); - - /* Adjust the number of root directory entries to help enforce alignment */ - if (align_structures) { - root_dir_entries = align_object(root_dir_sectors, bs.cluster_size) - * (sector_size >> 5); - } - } else { - unsigned clusters, maxclust, fatdata; - - /* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on - * hard disks. So use 12 bit if the size of the file system suggests that - * this fs is for a floppy disk, if the user hasn't explicitly requested a - * size. - */ - if (!size_fat) - size_fat = (num_sectors == 1440 || num_sectors == 2400 || - num_sectors == 2880 || num_sectors == 5760) ? 12 : 16; - if (verbose >= 2) - printf("Choosing %d bits for FAT\n", size_fat); - - /* Atari format: cluster size should be 2, except explicitly requested by - * the user, since GEMDOS doesn't like other cluster sizes very much. - * Instead, tune the sector size for the FS to fit. - */ - bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2; - if (!sector_size_set) { - while (num_sectors > GEMDOS_MAX_SECTORS) { - num_sectors >>= 1; - sector_size <<= 1; - } - } - if (verbose >= 2) - printf("Sector size must be %d to have less than %d log. sectors\n", - sector_size, GEMDOS_MAX_SECTORS); - - /* Check if there are enough FAT indices for how much clusters we have */ - do { - fatdata = num_sectors - cdiv(root_dir_entries * 32, sector_size) - - reserved_sectors; - /* The factor 2 below avoids cut-off errors for nr_fats == 1 and - * size_fat == 12 - * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries - */ - clusters = - (2 * - ((long long)fatdata * sector_size - - 2 * nr_fats * size_fat / 8)) / (2 * ((int)bs.cluster_size * - sector_size + - nr_fats * size_fat / 8)); - fat_length = cdiv((clusters + 2) * size_fat / 8, sector_size); - /* Need to recalculate number of clusters, since the unused parts of the - * FATS and data area together could make up space for an additional, - * not really present cluster. */ - clusters = (fatdata - nr_fats * fat_length) / bs.cluster_size; - maxclust = (fat_length * sector_size * 8) / size_fat; - if (verbose >= 2) - printf("ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n", - sector_size, clusters, fat_length, maxclust); - - /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd); - * first two numbers are reserved */ - if (maxclust <= - (size_fat == 32 ? MAX_CLUST_32 : (1 << size_fat) - 0x10) - && clusters <= maxclust - 2) - break; - if (verbose >= 2) - printf(clusters > maxclust - 2 ? - "Too many clusters\n" : "FAT too big\n"); - - /* need to increment sector_size once more to */ - if (sector_size_set) - die("With this sector size, the maximum number of FAT entries " - "would be exceeded."); - num_sectors >>= 1; - sector_size <<= 1; - } while (sector_size <= GEMDOS_MAX_SECTOR_SIZE); - - if (sector_size > GEMDOS_MAX_SECTOR_SIZE) - die("Would need a sector size > 16k, which GEMDOS can't work with"); - - cluster_count = clusters; - if (size_fat != 32) - bs.fat_length = CT_LE_W(fat_length); - else { - bs.fat_length = 0; - bs.fat32.fat32_length = CT_LE_L(fat_length); - } - } - - bs.sector_size[0] = (char)(sector_size & 0x00ff); - bs.sector_size[1] = (char)((sector_size & 0xff00) >> 8); - - bs.dir_entries[0] = (char)(root_dir_entries & 0x00ff); - bs.dir_entries[1] = (char)((root_dir_entries & 0xff00) >> 8); - - if (size_fat == 32) { - /* set up additional FAT32 fields */ - bs.fat32.flags = CT_LE_W(0); - bs.fat32.version[0] = 0; - bs.fat32.version[1] = 0; - bs.fat32.root_cluster = CT_LE_L(2); - bs.fat32.info_sector = CT_LE_W(1); - if (!backup_boot) - backup_boot = (reserved_sectors >= 7) ? 6 : - (reserved_sectors >= 2) ? reserved_sectors - 1 : 0; - else { - if (backup_boot == 1) - die("Backup boot sector must be after sector 1"); - else if (backup_boot >= reserved_sectors) - die("Backup boot sector must be a reserved sector"); - } - if (verbose >= 2) - printf("Using sector %d as backup boot sector (0 = none)\n", - backup_boot); - bs.fat32.backup_boot = CT_LE_W(backup_boot); - memset(&bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2)); - } - - if (atari_format) { - /* Just some consistency checks */ - if (num_sectors >= GEMDOS_MAX_SECTORS) - die("GEMDOS can't handle more than 65531 sectors"); - else if (num_sectors >= OLDGEMDOS_MAX_SECTORS) - printf("Warning: More than 32765 sector need TOS 1.04 " - "or higher.\n"); - } - if (num_sectors >= 65536) { - bs.sectors[0] = (char)0; - bs.sectors[1] = (char)0; - bs.total_sect = CT_LE_L(num_sectors); - } else { - bs.sectors[0] = (char)(num_sectors & 0x00ff); - bs.sectors[1] = (char)((num_sectors & 0xff00) >> 8); - if (!atari_format) - bs.total_sect = CT_LE_L(0); - } - - if (!atari_format) - vi->ext_boot_sign = MSDOS_EXT_SIGN; - - if (!cluster_count) { - if (sectors_per_cluster) /* If yes, die if we'd spec'd sectors per cluster */ - die("Too many clusters for file system - try more sectors per cluster"); - else - die("Attempting to create a too large file system"); - } - - /* The two following vars are in hard sectors, i.e. 512 byte sectors! */ - start_data_sector = (reserved_sectors + nr_fats * fat_length) * - (sector_size / HARD_SECTOR_SIZE); - start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / - SECTORS_PER_BLOCK; - - if (blocks < start_data_block + 32) /* Arbitrary undersize file system! */ - die("Too few blocks for viable file system"); - - if (verbose) { - printf("%s has %d head%s and %d sector%s per track,\n", - device_name, CF_LE_W(bs.heads), - (CF_LE_W(bs.heads) != 1) ? "s" : "", CF_LE_W(bs.secs_track), - (CF_LE_W(bs.secs_track) != 1) ? "s" : ""); - printf("logical sector size is %d,\n", sector_size); - printf("using 0x%02x media descriptor, with %d sectors;\n", - (int)(bs.media), num_sectors); - printf("file system has %d %d-bit FAT%s and %d sector%s per cluster.\n", - (int)(bs.fats), size_fat, (bs.fats != 1) ? "s" : "", - (int)(bs.cluster_size), (bs.cluster_size != 1) ? "s" : ""); - printf("FAT size is %d sector%s, and provides %d cluster%s.\n", - fat_length, (fat_length != 1) ? "s" : "", - cluster_count, (cluster_count != 1) ? "s" : ""); - printf("There %s %u reserved sector%s.\n", - (reserved_sectors != 1) ? "are" : "is", - reserved_sectors, (reserved_sectors != 1) ? "s" : ""); - - if (size_fat != 32) { - unsigned root_dir_entries = - bs.dir_entries[0] + ((bs.dir_entries[1]) * 256); - unsigned root_dir_sectors = - cdiv(root_dir_entries * 32, sector_size); - printf("Root directory contains %u slots and uses %u sectors.\n", - root_dir_entries, root_dir_sectors); - } - printf("Volume ID is %08lx, ", volume_id & - (atari_format ? 0x00ffffff : 0xffffffff)); - if (strcmp(volume_name, " ")) - printf("volume label %s.\n", volume_name); - else - printf("no volume label.\n"); - } - - /* Make the file allocation tables! */ - - if (malloc_entire_fat) - alloced_fat_length = fat_length; - else - alloced_fat_length = 1; - - if ((fat = - (unsigned char *)malloc(alloced_fat_length * sector_size)) == NULL) - die("unable to allocate space for FAT image in memory"); - - memset(fat, 0, alloced_fat_length * sector_size); - - mark_FAT_cluster(0, 0xffffffff); /* Initial fat entries */ - mark_FAT_cluster(1, 0xffffffff); - fat[0] = (unsigned char)bs.media; /* Put media type in first byte! */ - if (size_fat == 32) { - /* Mark cluster 2 as EOF (used for root dir) */ - mark_FAT_cluster(2, FAT_EOF); - } - - /* Make the root directory entries */ - - size_root_dir = (size_fat == 32) ? - bs.cluster_size * sector_size : - (((int)bs.dir_entries[1] * 256 + (int)bs.dir_entries[0]) * - sizeof(struct msdos_dir_entry)); - if ((root_dir = (struct msdos_dir_entry *)malloc(size_root_dir)) == NULL) { - free(fat); /* Tidy up before we die! */ - die("unable to allocate space for root directory in memory"); - } - - memset(root_dir, 0, size_root_dir); - if (memcmp(volume_name, " ", 11)) { - struct msdos_dir_entry *de = &root_dir[0]; - memcpy(de->name, volume_name, 8); - memcpy(de->ext, volume_name + 8, 3); - de->attr = ATTR_VOLUME; - ctime = localtime(&create_time); - de->time = CT_LE_W((unsigned short)((ctime->tm_sec >> 1) + - (ctime->tm_min << 5) + - (ctime->tm_hour << 11))); - de->date = - CT_LE_W((unsigned short)(ctime->tm_mday + - ((ctime->tm_mon + 1) << 5) + - ((ctime->tm_year - 80) << 9))); - de->ctime_ms = 0; - de->ctime = de->time; - de->cdate = de->date; - de->adate = de->date; - de->starthi = CT_LE_W(0); - de->start = CT_LE_W(0); - de->size = CT_LE_L(0); - } - - if (size_fat == 32) { - /* For FAT32, create an info sector */ - struct fat32_fsinfo *info; - - if (!(info_sector = malloc(sector_size))) - die("Out of memory"); - memset(info_sector, 0, sector_size); - /* fsinfo structure is at offset 0x1e0 in info sector by observation */ - info = (struct fat32_fsinfo *)(info_sector + 0x1e0); - - /* Info sector magic */ - info_sector[0] = 'R'; - info_sector[1] = 'R'; - info_sector[2] = 'a'; - info_sector[3] = 'A'; - - /* Magic for fsinfo structure */ - info->signature = CT_LE_L(0x61417272); - /* We've allocated cluster 2 for the root dir. */ - info->free_clusters = CT_LE_L(cluster_count - 1); - info->next_cluster = CT_LE_L(2); - - /* Info sector also must have boot sign */ - *(__u16 *) (info_sector + 0x1fe) = CT_LE_W(BOOT_SIGN); - } - - if (!(blank_sector = malloc(sector_size))) - die("Out of memory"); - memset(blank_sector, 0, sector_size); -} - -/* Write the new filesystem's data tables to wherever they're going to end up! */ - -#define error(str) \ - do { \ - free (fat); \ - if (info_sector) free (info_sector); \ - free (root_dir); \ - die (str); \ - } while(0) - -#define seekto(pos,errstr) \ - do { \ - loff_t __pos = (pos); \ - if (llseek (dev, __pos, SEEK_SET) != __pos) \ - error ("seek to " errstr " failed whilst writing tables"); \ - } while(0) - -#define writebuf(buf,size,errstr) \ - do { \ - int __size = (size); \ - if (write (dev, buf, __size) != __size) \ - error ("failed whilst writing " errstr); \ - } while(0) - -static void write_tables(void) -{ - int x; - int fat_length; - - fat_length = (size_fat == 32) ? - CF_LE_L(bs.fat32.fat32_length) : CF_LE_W(bs.fat_length); - - seekto(0, "start of device"); - /* clear all reserved sectors */ - for (x = 0; x < reserved_sectors; ++x) - writebuf(blank_sector, sector_size, "reserved sector"); - /* seek back to sector 0 and write the boot sector */ - seekto(0, "boot sector"); - writebuf((char *)&bs, sizeof(struct msdos_boot_sector), "boot sector"); - /* on FAT32, write the info sector and backup boot sector */ - if (size_fat == 32) { - seekto(CF_LE_W(bs.fat32.info_sector) * sector_size, "info sector"); - writebuf(info_sector, 512, "info sector"); - if (backup_boot != 0) { - seekto(backup_boot * sector_size, "backup boot sector"); - writebuf((char *)&bs, sizeof(struct msdos_boot_sector), - "backup boot sector"); - } - } - /* seek to start of FATS and write them all */ - seekto(reserved_sectors * sector_size, "first FAT"); - for (x = 1; x <= nr_fats; x++) { - int y; - int blank_fat_length = fat_length - alloced_fat_length; - writebuf(fat, alloced_fat_length * sector_size, "FAT"); - for (y = 0; y < blank_fat_length; y++) - writebuf(blank_sector, sector_size, "FAT"); - } - /* Write the root directory directly after the last FAT. This is the root - * dir area on FAT12/16, and the first cluster on FAT32. */ - writebuf((char *)root_dir, size_root_dir, "root directory"); - - if (blank_sector) - free(blank_sector); - if (info_sector) - free(info_sector); - free(root_dir); /* Free up the root directory space from setup_tables */ - free(fat); /* Free up the fat table space reserved during setup_tables */ -} - -/* Report the command usage and return a failure error code */ - -void usage(void) -{ - fatal_error("\ -Usage: mkdosfs [-a][-A][-c][-C][-v][-I][-l bad-block-file][-b backup-boot-sector]\n\ - [-m boot-msg-file][-n volume-name][-i volume-id]\n\ - [-s sectors-per-cluster][-S logical-sector-size][-f number-of-FATs]\n\ - [-h hidden-sectors][-F fat-size][-r root-dir-entries][-R reserved-sectors]\n\ - /dev/name [blocks]\n"); -} - -/* - * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant - * of MS-DOS filesystem by default. - */ -static void check_atari(void) -{ -#ifdef __mc68000__ - FILE *f; - char line[128], *p; - - if (!(f = fopen("/proc/hardware", "r"))) { - perror("/proc/hardware"); - return; - } - - while (fgets(line, sizeof(line), f)) { - if (strncmp(line, "Model:", 6) == 0) { - p = line + 6; - p += strspn(p, " \t"); - if (strncmp(p, "Atari ", 6) == 0) - atari_format = 1; - break; - } - } - fclose(f); -#endif -} - -/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible - way. In the event that some/all of the options are invalid we need to tell the user so that something can be done! */ - -int main(int argc, char **argv) -{ - int c; - char *tmp; - char *listfile = NULL; - FILE *msgfile; - struct stat statbuf; - int i = 0, pos, ch; - int create = 0; - unsigned long long cblocks = 0; - int min_sector_size; - - if (argc && *argv) { /* What's the program name? */ - char *p; - program_name = *argv; - if ((p = strrchr(program_name, '/'))) - program_name = p + 1; - } - - gettimeofday(&create_timeval, NULL); - create_time = create_timeval.tv_sec; - volume_id = (u_int32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ - check_atari(); - - printf("%s " VERSION " (" VERSION_DATE ")\n", program_name); - - while ((c = getopt(argc, argv, "aAb:cCf:F:Ii:l:m:n:r:R:s:S:h:v")) != EOF) - /* Scan the command line for options */ - switch (c) { - case 'A': /* toggle Atari format */ - atari_format = !atari_format; - break; - - case 'a': /* a : skip alignment */ - align_structures = FALSE; - break; - - case 'b': /* b : location of backup boot sector */ - backup_boot = (int)strtol(optarg, &tmp, 0); - if (*tmp || backup_boot < 2 || backup_boot > 0xffff) { - printf("Bad location for backup boot sector : %s\n", optarg); - usage(); - } - break; - - case 'c': /* c : Check FS as we build it */ - check = TRUE; - malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ - break; - - case 'C': /* C : Create a new file */ - create = TRUE; - break; - - case 'f': /* f : Choose number of FATs */ - nr_fats = (int)strtol(optarg, &tmp, 0); - if (*tmp || nr_fats < 1 || nr_fats > 4) { - printf("Bad number of FATs : %s\n", optarg); - usage(); - } - break; - - case 'F': /* F : Choose FAT size */ - size_fat = (int)strtol(optarg, &tmp, 0); - if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32)) { - printf("Bad FAT type : %s\n", optarg); - usage(); - } - size_fat_by_user = 1; - break; - - case 'h': /* h : number of hidden sectors */ - hidden_sectors = (int)strtol(optarg, &tmp, 0); - if (*tmp || hidden_sectors < 0) { - printf("Bad number of hidden sectors : %s\n", optarg); - usage(); - } - break; - - case 'I': - ignore_full_disk = 1; - break; - - case 'i': /* i : specify volume ID */ - volume_id = strtoul(optarg, &tmp, 16); - if (*tmp) { - printf("Volume ID must be a hexadecimal number\n"); - usage(); - } - break; - - case 'l': /* l : Bad block filename */ - listfile = optarg; - malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ - break; - - case 'm': /* m : Set boot message */ - if (strcmp(optarg, "-")) { - msgfile = fopen(optarg, "r"); - if (!msgfile) - perror(optarg); - } else - msgfile = stdin; - - if (msgfile) { - /* The boot code ends at offset 448 and needs a null terminator */ - i = MESSAGE_OFFSET; - pos = 0; /* We are at beginning of line */ - do { - ch = getc(msgfile); - switch (ch) { - case '\r': /* Ignore CRs */ - case '\0': /* and nulls */ - break; - - case '\n': /* LF -> CR+LF if necessary */ - if (pos) { /* If not at beginning of line */ - dummy_boot_code[i++] = '\r'; - pos = 0; - } - dummy_boot_code[i++] = '\n'; - break; - - case '\t': /* Expand tabs */ - do { - dummy_boot_code[i++] = ' '; - pos++; - } - while (pos % 8 && i < BOOTCODE_SIZE - 1); - break; - - case EOF: - dummy_boot_code[i++] = '\0'; /* Null terminator */ - break; - - default: - dummy_boot_code[i++] = ch; /* Store character */ - pos++; /* Advance position */ - break; - } - } - while (ch != EOF && i < BOOTCODE_SIZE - 1); - - /* Fill up with zeros */ - while (i < BOOTCODE_SIZE - 1) - dummy_boot_code[i++] = '\0'; - dummy_boot_code[BOOTCODE_SIZE - 1] = '\0'; /* Just in case */ - - if (ch != EOF) - printf("Warning: message too long; truncated\n"); - - if (msgfile != stdin) - fclose(msgfile); - } - break; - - case 'n': /* n : Volume name */ - sprintf(volume_name, "%-11.11s", optarg); - break; - - case 'r': /* r : Root directory entries */ - root_dir_entries = (int)strtol(optarg, &tmp, 0); - if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768) { - printf("Bad number of root directory entries : %s\n", optarg); - usage(); - } - break; - - case 'R': /* R : number of reserved sectors */ - reserved_sectors = (int)strtol(optarg, &tmp, 0); - if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff) { - printf("Bad number of reserved sectors : %s\n", optarg); - usage(); - } - break; - - case 's': /* s : Sectors per cluster */ - sectors_per_cluster = (int)strtol(optarg, &tmp, 0); - if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2 - && sectors_per_cluster != 4 && sectors_per_cluster != 8 - && sectors_per_cluster != 16 - && sectors_per_cluster != 32 - && sectors_per_cluster != 64 - && sectors_per_cluster != 128)) { - printf("Bad number of sectors per cluster : %s\n", optarg); - usage(); - } - break; - - case 'S': /* S : Sector size */ - sector_size = (int)strtol(optarg, &tmp, 0); - if (*tmp || (sector_size != 512 && sector_size != 1024 && - sector_size != 2048 && sector_size != 4096 && - sector_size != 8192 && sector_size != 16384 && - sector_size != 32768)) { - printf("Bad logical sector size : %s\n", optarg); - usage(); - } - sector_size_set = 1; - break; - - case 'v': /* v : Verbose execution */ - ++verbose; - break; - - default: - printf("Unknown option: %c\n", c); - usage(); - } - if (optind < argc) { - device_name = argv[optind]; /* Determine the number of blocks in the FS */ - - if (!device_name) { - printf("No device specified.\n"); - usage(); - } - - if (!create) - cblocks = count_blocks(device_name, &orphaned_sectors); /* Have a look and see! */ - } - if (optind == argc - 2) { /* Either check the user specified number */ - blocks = strtoull(argv[optind + 1], &tmp, 0); - if (!create && blocks != cblocks) { - fprintf(stderr, "Warning: block count mismatch: "); - fprintf(stderr, "found %llu but assuming %llu.\n", cblocks, blocks); - } - } else if (optind == argc - 1) { /* Or use value found */ - if (create) - die("Need intended size with -C."); - blocks = cblocks; - tmp = ""; - } else { - fprintf(stderr, "No device specified!\n"); - usage(); - } - if (*tmp) { - printf("Bad block count : %s\n", argv[optind + 1]); - usage(); - } - - if (check && listfile) /* Auto and specified bad block handling are mutually */ - die("-c and -l are incompatible"); /* exclusive of each other! */ - - if (!create) { - check_mount(device_name); /* Is the device already mounted? */ - dev = open(device_name, O_EXCL | O_RDWR); /* Is it a suitable device to build the FS on? */ - if (dev < 0) { - fprintf(stderr, "%s: unable to open %s: %s\n", program_name, - device_name, strerror(errno)); - exit(1); /* The error exit code is 1! */ - } - } else { - loff_t offset = blocks * BLOCK_SIZE - 1; - char null = 0; - /* create the file */ - dev = open(device_name, O_EXCL | O_RDWR | O_CREAT | O_TRUNC, 0666); - if (dev < 0) - die("unable to create %s"); - /* seek to the intended end-1, and write one byte. this creates a - * sparse-as-possible file of appropriate size. */ - if (llseek(dev, offset, SEEK_SET) != offset) - die("seek failed"); - if (write(dev, &null, 1) < 0) - die("write failed"); - if (llseek(dev, 0, SEEK_SET) != 0) - die("seek failed"); - } - - if (fstat(dev, &statbuf) < 0) - die("unable to stat %s"); - if (!S_ISBLK(statbuf.st_mode)) { - statbuf.st_rdev = 0; - check = 0; - } else - /* - * Ignore any 'full' fixed disk devices, if -I is not given. - * On a MO-disk one doesn't need partitions. The filesytem can go - * directly to the whole disk. Under other OSes this is known as - * the 'superfloppy' format. As I don't know how to find out if - * this is a MO disk I introduce a -I (ignore) switch. -Joey - */ - if (!ignore_full_disk && ((statbuf.st_rdev & 0xff3f) == 0x0300 || /* hda, hdb */ - (statbuf.st_rdev & 0xff0f) == 0x0800 || /* sd */ - (statbuf.st_rdev & 0xff3f) == 0x0d00 || /* xd */ - (statbuf.st_rdev & 0xff3f) == 0x1600) /* hdc, hdd */ - ) - die("Device partition expected, not making filesystem on entire device '%s' (use -I to override)"); - - if (sector_size_set) { - if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) - if (sector_size < min_sector_size) { - sector_size = min_sector_size; - fprintf(stderr, - "Warning: sector size was set to %d (minimal for this device)\n", - sector_size); - } - } else { - if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) { - sector_size = min_sector_size; - sector_size_set = 1; - } - } - - if (sector_size > 4096) - fprintf(stderr, - "Warning: sector size is set to %d > 4096, such filesystem will not propably mount\n", - sector_size); - - establish_params(statbuf.st_rdev, statbuf.st_size); - /* Establish the media parameters */ - - setup_tables(); /* Establish the file system tables */ - - if (check) /* Determine any bad block locations and mark them */ - check_blocks(); - else if (listfile) - get_list_blocks(listfile); - - write_tables(); /* Write the file system tables away! */ - - exit(0); /* Terminate with no errors! */ -} diff --git a/dosfstools/src/mkfs.fat.c b/dosfstools/src/mkfs.fat.c new file mode 100644 index 000000000..dddbe2472 --- /dev/null +++ b/dosfstools/src/mkfs.fat.c @@ -0,0 +1,1754 @@ +/* mkfs.fat.c - utility to create FAT/MS-DOS filesystems + + Copyright (C) 1991 Linus Torvalds + Copyright (C) 1992-1993 Remy Card + Copyright (C) 1993-1994 David Hudson + Copyright (C) 1998 H. Peter Anvin + Copyright (C) 1998-2005 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +/* Description: Utility to allow an MS-DOS filesystem to be created + under Linux. A lot of the basic structure of this program has been + borrowed from Remy Card's "mke2fs" code. + + As far as possible the aim here is to make the "mkfs.fat" command + look almost identical to the other Linux filesystem make utilties, + eg bad blocks are still specified as blocks, not sectors, but when + it comes down to it, DOS is tied to the idea of a sector (512 bytes + as a rule), and not the block. For example the boot block does not + occupy a full cluster. + + Fixes/additions May 1998 by Roman Hodek + : + - Atari format support + - New options -A, -S, -C + - Support for filesystems > 2GB + - FAT32 support */ + +/* Include the header files */ + +#include "version.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "msdos_fs.h" + +/* In earlier versions, an own llseek() was used, but glibc lseek() is + * sufficient (or even better :) for 64 bit offsets in the meantime */ +#define llseek lseek + +/* Constant definitions */ + +#define TRUE 1 /* Boolean constants */ +#define FALSE 0 + +#define TEST_BUFFER_BLOCKS 16 +#define HARD_SECTOR_SIZE 512 +#define SECTORS_PER_BLOCK ( BLOCK_SIZE / HARD_SECTOR_SIZE ) + +#define NO_NAME "NO NAME " + +/* Macro definitions */ + +/* Report a failure message and return a failure error code */ + +#define die( str ) fatal_error( "%s: " str "\n" ) + +/* Mark a cluster in the FAT as bad */ + +#define mark_sector_bad( sector ) mark_FAT_sector( sector, FAT_BAD ) + +/* Compute ceil(a/b) */ + +static inline int cdiv(int a, int b) +{ + return (a + b - 1) / b; +} + +/* FAT values */ +#define FAT_EOF (atari_format ? 0x0fffffff : 0x0ffffff8) +#define FAT_BAD 0x0ffffff7 + +#define MSDOS_EXT_SIGN 0x29 /* extended boot sector signature */ +#define MSDOS_FAT12_SIGN "FAT12 " /* FAT12 filesystem signature */ +#define MSDOS_FAT16_SIGN "FAT16 " /* FAT16 filesystem signature */ +#define MSDOS_FAT32_SIGN "FAT32 " /* FAT32 filesystem signature */ + +#define BOOT_SIGN 0xAA55 /* Boot sector magic number */ + +#define MAX_CLUST_12 ((1 << 12) - 16) +#define MAX_CLUST_16 ((1 << 16) - 16) +#define MIN_CLUST_32 65529 +/* M$ says the high 4 bits of a FAT32 FAT entry are reserved and don't belong + * to the cluster number. So the max. cluster# is based on 2^28 */ +#define MAX_CLUST_32 ((1 << 28) - 16) + +#define FAT12_THRESHOLD 4085 + +#define OLDGEMDOS_MAX_SECTORS 32765 +#define GEMDOS_MAX_SECTORS 65531 +#define GEMDOS_MAX_SECTOR_SIZE (16*1024) + +#define BOOTCODE_SIZE 448 +#define BOOTCODE_FAT32_SIZE 420 + +/* __attribute__ ((packed)) is used on all structures to make gcc ignore any + * alignments */ + +struct msdos_volume_info { + uint8_t drive_number; /* BIOS drive number */ + uint8_t RESERVED; /* Unused */ + uint8_t ext_boot_sign; /* 0x29 if fields below exist (DOS 3.3+) */ + uint8_t volume_id[4]; /* Volume ID number */ + uint8_t volume_label[11]; /* Volume label */ + uint8_t fs_type[8]; /* Typically FAT12 or FAT16 */ +} __attribute__ ((packed)); + +struct msdos_boot_sector { + uint8_t boot_jump[3]; /* Boot strap short or near jump */ + uint8_t system_id[8]; /* Name - can be used to special case + partition manager volumes */ + uint8_t sector_size[2]; /* bytes per logical sector */ + uint8_t cluster_size; /* sectors/cluster */ + uint16_t reserved; /* reserved sectors */ + uint8_t fats; /* number of FATs */ + uint8_t dir_entries[2]; /* root directory entries */ + uint8_t sectors[2]; /* number of sectors */ + uint8_t media; /* media code (unused) */ + uint16_t fat_length; /* sectors/FAT */ + uint16_t secs_track; /* sectors per track */ + uint16_t heads; /* number of heads */ + uint32_t hidden; /* hidden sectors (unused) */ + uint32_t total_sect; /* number of sectors (if sectors == 0) */ + union { + struct { + struct msdos_volume_info vi; + uint8_t boot_code[BOOTCODE_SIZE]; + } __attribute__ ((packed)) _oldfat; + struct { + uint32_t fat32_length; /* sectors/FAT */ + uint16_t flags; /* bit 8: fat mirroring, low 4: active fat */ + uint8_t version[2]; /* major, minor filesystem version */ + uint32_t root_cluster; /* first cluster in root directory */ + uint16_t info_sector; /* filesystem info sector */ + uint16_t backup_boot; /* backup boot sector */ + uint16_t reserved2[6]; /* Unused */ + struct msdos_volume_info vi; + uint8_t boot_code[BOOTCODE_FAT32_SIZE]; + } __attribute__ ((packed)) _fat32; + } __attribute__ ((packed)) fstype; + uint16_t boot_sign; +} __attribute__ ((packed)); +#define fat32 fstype._fat32 +#define oldfat fstype._oldfat + +struct fat32_fsinfo { + uint32_t reserved1; /* Nothing as far as I can tell */ + uint32_t signature; /* 0x61417272L */ + uint32_t free_clusters; /* Free cluster count. -1 if unknown */ + uint32_t next_cluster; /* Most recently allocated cluster. + * Unused under Linux. */ + uint32_t reserved2[4]; +}; + +/* The "boot code" we put into the filesystem... it writes a message and + tells the user to try again */ + +unsigned char dummy_boot_jump[3] = { 0xeb, 0x3c, 0x90 }; + +unsigned char dummy_boot_jump_m68k[2] = { 0x60, 0x1c }; + +#define MSG_OFFSET_OFFSET 3 +char dummy_boot_code[BOOTCODE_SIZE] = "\x0e" /* push cs */ + "\x1f" /* pop ds */ + "\xbe\x5b\x7c" /* mov si, offset message_txt */ + /* write_msg: */ + "\xac" /* lodsb */ + "\x22\xc0" /* and al, al */ + "\x74\x0b" /* jz key_press */ + "\x56" /* push si */ + "\xb4\x0e" /* mov ah, 0eh */ + "\xbb\x07\x00" /* mov bx, 0007h */ + "\xcd\x10" /* int 10h */ + "\x5e" /* pop si */ + "\xeb\xf0" /* jmp write_msg */ + /* key_press: */ + "\x32\xe4" /* xor ah, ah */ + "\xcd\x16" /* int 16h */ + "\xcd\x19" /* int 19h */ + "\xeb\xfe" /* foo: jmp foo */ + /* message_txt: */ + "This is not a bootable disk. Please insert a bootable floppy and\r\n" + "press any key to try again ... \r\n"; + +#define MESSAGE_OFFSET 29 /* Offset of message in above code */ + +/* Global variables - the root of all evil :-) - see these and weep! */ + +static const char *program_name = "mkfs.fat"; /* Name of the program */ +static char *device_name = NULL; /* Name of the device on which to create the filesystem */ +static int atari_format = 0; /* Use Atari variation of MS-DOS FS format */ +static int check = FALSE; /* Default to no readablity checking */ +static int verbose = 0; /* Default to verbose mode off */ +static long volume_id; /* Volume ID number */ +static time_t create_time; /* Creation time */ +static char volume_name[] = NO_NAME; /* Volume name */ +static uint64_t blocks; /* Number of blocks in filesystem */ +static int sector_size = 512; /* Size of a logical sector */ +static int sector_size_set = 0; /* User selected sector size */ +static int backup_boot = 0; /* Sector# of backup boot sector */ +static int reserved_sectors = 0; /* Number of reserved sectors */ +static int badblocks = 0; /* Number of bad blocks in the filesystem */ +static int nr_fats = 2; /* Default number of FATs to produce */ +static int size_fat = 0; /* Size in bits of FAT entries */ +static int size_fat_by_user = 0; /* 1 if FAT size user selected */ +static int dev = -1; /* FS block device file handle */ +static int ignore_full_disk = 0; /* Ignore warning about 'full' disk devices */ +static off_t currently_testing = 0; /* Block currently being tested (if autodetect bad blocks) */ +static struct msdos_boot_sector bs; /* Boot sector data */ +static int start_data_sector; /* Sector number for the start of the data area */ +static int start_data_block; /* Block number for the start of the data area */ +static unsigned char *fat; /* File allocation table */ +static unsigned alloced_fat_length; /* # of FAT sectors we can keep in memory */ +static unsigned char *info_sector; /* FAT32 info sector */ +static struct msdos_dir_entry *root_dir; /* Root directory */ +static int size_root_dir; /* Size of the root directory in bytes */ +static int sectors_per_cluster = 0; /* Number of sectors per disk cluster */ +static int root_dir_entries = 0; /* Number of root directory entries */ +static char *blank_sector; /* Blank sector - all zeros */ +static int hidden_sectors = 0; /* Number of hidden sectors */ +static int hidden_sectors_by_user = 0; /* -h option invoked */ +static int drive_number_option = 0; /* drive number */ +static int drive_number_by_user = 0; /* drive number option invoked */ +static int fat_media_byte = 0; /* media byte in header and starting FAT */ +static int malloc_entire_fat = FALSE; /* Whether we should malloc() the entire FAT or not */ +static int align_structures = TRUE; /* Whether to enforce alignment */ +static int orphaned_sectors = 0; /* Sectors that exist in the last block of filesystem */ +static int invariant = 0; /* Whether to set normally randomized or + current time based values to + constants */ + +/* Function prototype definitions */ + +static void fatal_error(const char *fmt_string) __attribute__ ((noreturn)); +static void mark_FAT_cluster(int cluster, unsigned int value); +static void mark_FAT_sector(int sector, unsigned int value); +static long do_check(char *buffer, int try, off_t current_block); +static void alarm_intr(int alnum); +static void check_blocks(void); +static void get_list_blocks(char *filename); +static int valid_offset(int fd, loff_t offset); +static uint64_t count_blocks(char *filename, int *remainder); +static void check_mount(char *device_name); +static void establish_params(int device_num, int size); +static void setup_tables(void); +static void write_tables(void); + +/* The function implementations */ + +/* Handle the reporting of fatal errors. Volatile to let gcc know that this doesn't return */ + +static void fatal_error(const char *fmt_string) +{ + fprintf(stderr, fmt_string, program_name, device_name); + exit(1); /* The error exit code is 1! */ +} + +/* Mark the specified cluster as having a particular value */ + +static void mark_FAT_cluster(int cluster, unsigned int value) +{ + switch (size_fat) { + case 12: + value &= 0x0fff; + if (((cluster * 3) & 0x1) == 0) { + fat[3 * cluster / 2] = (unsigned char)(value & 0x00ff); + fat[(3 * cluster / 2) + 1] = + (unsigned char)((fat[(3 * cluster / 2) + 1] & 0x00f0) + | ((value & 0x0f00) >> 8)); + } else { + fat[3 * cluster / 2] = + (unsigned char)((fat[3 * cluster / 2] & 0x000f) | + ((value & 0x000f) << 4)); + fat[(3 * cluster / 2) + 1] = (unsigned char)((value & 0x0ff0) >> 4); + } + break; + + case 16: + value &= 0xffff; + fat[2 * cluster] = (unsigned char)(value & 0x00ff); + fat[(2 * cluster) + 1] = (unsigned char)(value >> 8); + break; + + case 32: + value &= 0xfffffff; + fat[4 * cluster] = (unsigned char)(value & 0x000000ff); + fat[(4 * cluster) + 1] = (unsigned char)((value & 0x0000ff00) >> 8); + fat[(4 * cluster) + 2] = (unsigned char)((value & 0x00ff0000) >> 16); + fat[(4 * cluster) + 3] = (unsigned char)((value & 0xff000000) >> 24); + break; + + default: + die("Bad FAT size (not 12, 16, or 32)"); + } +} + +/* Mark a specified sector as having a particular value in it's FAT entry */ + +static void mark_FAT_sector(int sector, unsigned int value) +{ + int cluster; + + cluster = (sector - start_data_sector) / (int)(bs.cluster_size) / + (sector_size / HARD_SECTOR_SIZE); + if (cluster < 0) + die("Invalid cluster number in mark_FAT_sector: probably bug!"); + + mark_FAT_cluster(cluster, value); +} + +/* Perform a test on a block. Return the number of blocks that could be read successfully */ + +static long do_check(char *buffer, int try, off_t current_block) +{ + long got; + + if (llseek(dev, current_block * BLOCK_SIZE, SEEK_SET) /* Seek to the correct location */ + !=current_block * BLOCK_SIZE) + die("seek failed during testing for blocks"); + + got = read(dev, buffer, try * BLOCK_SIZE); /* Try reading! */ + if (got < 0) + got = 0; + + if (got & (BLOCK_SIZE - 1)) + printf("Unexpected values in do_check: probably bugs\n"); + got /= BLOCK_SIZE; + + return got; +} + +/* Alarm clock handler - display the status of the quest for bad blocks! Then retrigger the alarm for five senconds + later (so we can come here again) */ + +static void alarm_intr(int alnum) +{ + (void)alnum; + + if (currently_testing >= blocks) + return; + + signal(SIGALRM, alarm_intr); + alarm(5); + if (!currently_testing) + return; + + printf("%lld... ", (unsigned long long)currently_testing); + fflush(stdout); +} + +static void check_blocks(void) +{ + int try, got; + int i; + static char blkbuf[BLOCK_SIZE * TEST_BUFFER_BLOCKS]; + + if (verbose) { + printf("Searching for bad blocks "); + fflush(stdout); + } + currently_testing = 0; + if (verbose) { + signal(SIGALRM, alarm_intr); + alarm(5); + } + try = TEST_BUFFER_BLOCKS; + while (currently_testing < blocks) { + if (currently_testing + try > blocks) + try = blocks - currently_testing; + got = do_check(blkbuf, try, currently_testing); + currently_testing += got; + if (got == try) { + try = TEST_BUFFER_BLOCKS; + continue; + } else + try = 1; + if (currently_testing < start_data_block) + die("bad blocks before data-area: cannot make fs"); + + for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ + mark_sector_bad(currently_testing * SECTORS_PER_BLOCK + i); + badblocks++; + currently_testing++; + } + + if (verbose) + printf("\n"); + + if (badblocks) + printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); +} + +static void get_list_blocks(char *filename) +{ + int i; + FILE *listfile; + long blockno; + + listfile = fopen(filename, "r"); + if (listfile == (FILE *) NULL) + die("Can't open file of bad blocks"); + + while (!feof(listfile)) { + fscanf(listfile, "%ld\n", &blockno); + for (i = 0; i < SECTORS_PER_BLOCK; i++) /* Mark all of the sectors in the block as bad */ + mark_sector_bad(blockno * SECTORS_PER_BLOCK + i); + badblocks++; + } + fclose(listfile); + + if (badblocks) + printf("%d bad block%s\n", badblocks, (badblocks > 1) ? "s" : ""); +} + +/* Given a file descriptor and an offset, check whether the offset is a valid offset for the file - return FALSE if it + isn't valid or TRUE if it is */ + +static int valid_offset(int fd, loff_t offset) +{ + char ch; + + if (llseek(fd, offset, SEEK_SET) < 0) + return FALSE; + if (read(fd, &ch, 1) < 1) + return FALSE; + return TRUE; +} + +/* Given a filename, look to see how many blocks of BLOCK_SIZE are present, returning the answer */ + +static uint64_t count_blocks(char *filename, int *remainder) +{ + loff_t high, low; + int fd; + + if ((fd = open(filename, O_RDONLY)) < 0) { + perror(filename); + exit(1); + } + + /* first try SEEK_END, which should work on most devices nowadays */ + if ((low = llseek(fd, 0, SEEK_END)) <= 0) { + low = 0; + for (high = 1; valid_offset(fd, high); high *= 2) + low = high; + while (low < high - 1) { + const loff_t mid = (low + high) / 2; + if (valid_offset(fd, mid)) + low = mid; + else + high = mid; + } + ++low; + } + + close(fd); + *remainder = (low % BLOCK_SIZE) / sector_size; + return (low / BLOCK_SIZE); +} + +/* Check to see if the specified device is currently mounted - abort if it is */ + +static void check_mount(char *device_name) +{ + FILE *f; + struct mntent *mnt; + + if ((f = setmntent(MOUNTED, "r")) == NULL) + return; + while ((mnt = getmntent(f)) != NULL) + if (strcmp(device_name, mnt->mnt_fsname) == 0) + die("%s contains a mounted filesystem."); + endmntent(f); +} + +/* Establish the geometry and media parameters for the device */ + +static void establish_params(int device_num, int size) +{ + long loop_size; + struct hd_geometry geometry; + struct floppy_struct param; + int def_root_dir_entries = 512; + + if ((0 == device_num) || ((device_num & 0xff00) == 0x0200)) + /* file image or floppy disk */ + { + if (0 == device_num) { + param.size = size / 512; + switch (param.size) { + case 720: + param.sect = 9; + param.head = 2; + break; + case 1440: + param.sect = 9; + param.head = 2; + break; + case 2400: + param.sect = 15; + param.head = 2; + break; + case 2880: + param.sect = 18; + param.head = 2; + break; + case 5760: + param.sect = 36; + param.head = 2; + break; + default: + /* fake values */ + param.sect = 32; + param.head = 64; + break; + } + + } else { /* is a floppy diskette */ + + if (ioctl(dev, FDGETPRM, ¶m)) /* Can we get the diskette geometry? */ + die("unable to get diskette geometry for '%s'"); + } + bs.secs_track = htole16(param.sect); /* Set up the geometry information */ + bs.heads = htole16(param.head); + switch (param.size) { /* Set up the media descriptor byte */ + case 720: /* 5.25", 2, 9, 40 - 360K */ + bs.media = (char)0xfd; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 1440: /* 3.5", 2, 9, 80 - 720K */ + bs.media = (char)0xf9; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 2400: /* 5.25", 2, 15, 80 - 1200K */ + bs.media = (char)0xf9; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + case 5760: /* 3.5", 2, 36, 80 - 2880K */ + bs.media = (char)0xf0; + bs.cluster_size = (char)2; + def_root_dir_entries = 224; + break; + + case 2880: /* 3.5", 2, 18, 80 - 1440K */ +floppy_default: + bs.media = (char)0xf0; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + default: /* Anything else */ + if (0 == device_num) + goto def_hd_params; + else + goto floppy_default; + } + } else if ((device_num & 0xff00) == 0x0700) { /* This is a loop device */ + if (ioctl(dev, BLKGETSIZE, &loop_size)) + die("unable to get loop device size"); + + switch (loop_size) { /* Assuming the loop device -> floppy later */ + case 720: /* 5.25", 2, 9, 40 - 360K */ + bs.secs_track = le16toh(9); + bs.heads = le16toh(2); + bs.media = (char)0xfd; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 1440: /* 3.5", 2, 9, 80 - 720K */ + bs.secs_track = le16toh(9); + bs.heads = le16toh(2); + bs.media = (char)0xf9; + bs.cluster_size = (char)2; + def_root_dir_entries = 112; + break; + + case 2400: /* 5.25", 2, 15, 80 - 1200K */ + bs.secs_track = le16toh(15); + bs.heads = le16toh(2); + bs.media = (char)0xf9; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + case 5760: /* 3.5", 2, 36, 80 - 2880K */ + bs.secs_track = le16toh(36); + bs.heads = le16toh(2); + bs.media = (char)0xf0; + bs.cluster_size = (char)2; + bs.dir_entries[0] = (char)224; + bs.dir_entries[1] = (char)0; + break; + + case 2880: /* 3.5", 2, 18, 80 - 1440K */ + bs.secs_track = le16toh(18); + bs.heads = le16toh(2); + bs.media = (char)0xf0; + bs.cluster_size = (char)(atari_format ? 2 : 1); + def_root_dir_entries = 224; + break; + + default: /* Anything else: default hd setup */ + printf("Loop device does not match a floppy size, using " + "default hd params\n"); + bs.secs_track = htole16(32); /* these are fake values... */ + bs.heads = htole16(64); + goto def_hd_params; + } + } else + /* Must be a hard disk then! */ + { + /* Can we get the drive geometry? (Note I'm not too sure about */ + /* whether to use HDIO_GETGEO or HDIO_REQ) */ + if (ioctl(dev, HDIO_GETGEO, &geometry) || geometry.sectors == 0 + || geometry.heads == 0) { + printf("unable to get drive geometry, using default 255/63\n"); + bs.secs_track = htole16(63); + bs.heads = htole16(255); + } else { + bs.secs_track = htole16(geometry.sectors); /* Set up the geometry information */ + bs.heads = htole16(geometry.heads); + if (!hidden_sectors_by_user) + hidden_sectors = htole32(geometry.start); + } +def_hd_params: + bs.media = (char)0xf8; /* Set up the media descriptor for a hard drive */ + if (!size_fat && blocks * SECTORS_PER_BLOCK > 1064960) { + if (verbose) + printf("Auto-selecting FAT32 for large filesystem\n"); + size_fat = 32; + } + if (size_fat == 32) { + /* For FAT32, try to do the same as M$'s format command + * (see http://www.win.tue.nl/~aeb/linux/fs/fat/fatgen103.pdf p. 20): + * fs size <= 260M: 0.5k clusters + * fs size <= 8G: 4k clusters + * fs size <= 16G: 8k clusters + * fs size <= 32G: 16k clusters + * fs size > 32G: 32k clusters + */ + uint32_t sz_mb = + (blocks + (1 << (20 - BLOCK_SIZE_BITS)) - 1) >> (20 - + BLOCK_SIZE_BITS); + bs.cluster_size = + sz_mb > 32 * 1024 ? 64 : sz_mb > 16 * 1024 ? 32 : sz_mb > + 8 * 1024 ? 16 : sz_mb > 260 ? 8 : 1; + } else { + /* FAT12 and FAT16: start at 4 sectors per cluster */ + bs.cluster_size = (char)4; + } + } + + if (!root_dir_entries) + root_dir_entries = def_root_dir_entries; +} + +/* + * If alignment is enabled, round the first argument up to the second; the + * latter must be a power of two. + */ +static unsigned int align_object(unsigned int sectors, unsigned int clustsize) +{ + if (align_structures) + return (sectors + clustsize - 1) & ~(clustsize - 1); + else + return sectors; +} + +/* Create the filesystem data tables */ + +static void setup_tables(void) +{ + unsigned num_sectors; + unsigned cluster_count = 0, fat_length; + struct tm *ctime; + struct msdos_volume_info *vi = + (size_fat == 32 ? &bs.fat32.vi : &bs.oldfat.vi); + + if (atari_format) { + /* On Atari, the first few bytes of the boot sector are assigned + * differently: The jump code is only 2 bytes (and m68k machine code + * :-), then 6 bytes filler (ignored), then 3 byte serial number. */ + bs.boot_jump[2] = 'm'; + memcpy((char *)bs.system_id, "kdosf", strlen("kdosf")); + } else + memcpy((char *)bs.system_id, "mkfs.fat", strlen("mkfs.fat")); + if (sectors_per_cluster) + bs.cluster_size = (char)sectors_per_cluster; + + if (fat_media_byte) + bs.media = (char) fat_media_byte; + + if (bs.media == 0xf8) + vi->drive_number=0x80; + else + vi->drive_number=0x00; + + if (drive_number_by_user) + vi->drive_number= (char) drive_number_option; + + if (size_fat == 32) { + /* Under FAT32, the root dir is in a cluster chain, and this is + * signalled by bs.dir_entries being 0. */ + root_dir_entries = 0; + } + + if (atari_format) { + bs.system_id[5] = (unsigned char)(volume_id & 0x000000ff); + bs.system_id[6] = (unsigned char)((volume_id & 0x0000ff00) >> 8); + bs.system_id[7] = (unsigned char)((volume_id & 0x00ff0000) >> 16); + } else { + vi->volume_id[0] = (unsigned char)(volume_id & 0x000000ff); + vi->volume_id[1] = (unsigned char)((volume_id & 0x0000ff00) >> 8); + vi->volume_id[2] = (unsigned char)((volume_id & 0x00ff0000) >> 16); + vi->volume_id[3] = (unsigned char)(volume_id >> 24); + } + + if (!atari_format) { + memcpy(vi->volume_label, volume_name, 11); + + memcpy(bs.boot_jump, dummy_boot_jump, 3); + /* Patch in the correct offset to the boot code */ + bs.boot_jump[1] = ((size_fat == 32 ? + (char *)&bs.fat32.boot_code : + (char *)&bs.oldfat.boot_code) - (char *)&bs) - 2; + + if (size_fat == 32) { + int offset = (char *)&bs.fat32.boot_code - + (char *)&bs + MESSAGE_OFFSET + 0x7c00; + if (dummy_boot_code[BOOTCODE_FAT32_SIZE - 1]) + printf("Warning: message too long; truncated\n"); + dummy_boot_code[BOOTCODE_FAT32_SIZE - 1] = 0; + memcpy(bs.fat32.boot_code, dummy_boot_code, BOOTCODE_FAT32_SIZE); + bs.fat32.boot_code[MSG_OFFSET_OFFSET] = offset & 0xff; + bs.fat32.boot_code[MSG_OFFSET_OFFSET + 1] = offset >> 8; + } else { + memcpy(bs.oldfat.boot_code, dummy_boot_code, BOOTCODE_SIZE); + } + bs.boot_sign = htole16(BOOT_SIGN); + } else { + memcpy(bs.boot_jump, dummy_boot_jump_m68k, 2); + } + if (verbose >= 2) + printf("Boot jump code is %02x %02x\n", + bs.boot_jump[0], bs.boot_jump[1]); + + if (!reserved_sectors) + reserved_sectors = (size_fat == 32) ? 32 : 1; + else { + if (size_fat == 32 && reserved_sectors < 2) + die("On FAT32 at least 2 reserved sectors are needed."); + } + bs.reserved = htole16(reserved_sectors); + if (verbose >= 2) + printf("Using %d reserved sectors\n", reserved_sectors); + bs.fats = (char)nr_fats; + if (!atari_format || size_fat == 32) + bs.hidden = htole32(hidden_sectors); + else { + /* In Atari format, hidden is a 16 bit field */ + uint16_t hidden = htole16(hidden_sectors); + if (hidden_sectors & ~0xffff) + die("#hidden doesn't fit in 16bit field of Atari format\n"); + memcpy(&bs.hidden, &hidden, 2); + } + + num_sectors = + (long long)(blocks * BLOCK_SIZE / sector_size) + orphaned_sectors; + + if (!atari_format) { + unsigned fatdata1216; /* Sectors for FATs + data area (FAT12/16) */ + unsigned fatdata32; /* Sectors for FATs + data area (FAT32) */ + unsigned fatlength12, fatlength16, fatlength32; + unsigned maxclust12, maxclust16, maxclust32; + unsigned clust12, clust16, clust32; + int maxclustsize; + unsigned root_dir_sectors = cdiv(root_dir_entries * 32, sector_size); + + /* + * If the filesystem is 8192 sectors or less (4 MB with 512-byte + * sectors, i.e. floppy size), don't align the data structures. + */ + if (num_sectors <= 8192) { + if (align_structures && verbose >= 2) + printf("Disabling alignment due to tiny filesystem\n"); + + align_structures = FALSE; + } + + if (sectors_per_cluster) + bs.cluster_size = maxclustsize = sectors_per_cluster; + else + /* An initial guess for bs.cluster_size should already be set */ + maxclustsize = 128; + + do { + fatdata32 = num_sectors - reserved_sectors; + fatdata1216 = fatdata32 + - align_object(root_dir_sectors, bs.cluster_size); + + if (verbose >= 2) + printf("Trying with %d sectors/cluster:\n", bs.cluster_size); + + /* The factor 2 below avoids cut-off errors for nr_fats == 1. + * The "nr_fats*3" is for the reserved first two FAT entries */ + clust12 = 2 * ((long long)fatdata1216 * sector_size + nr_fats * 3) / + (2 * (int)bs.cluster_size * sector_size + nr_fats * 3); + fatlength12 = cdiv(((clust12 + 2) * 3 + 1) >> 1, sector_size); + fatlength12 = align_object(fatlength12, bs.cluster_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust12 = (fatdata1216 - nr_fats * fatlength12) / bs.cluster_size; + maxclust12 = (fatlength12 * 2 * sector_size) / 3; + if (maxclust12 > MAX_CLUST_12) + maxclust12 = MAX_CLUST_12; + if (verbose >= 2) + printf("FAT12: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust12, fatlength12, maxclust12, MAX_CLUST_12); + if (clust12 > maxclust12 - 2) { + clust12 = 0; + if (verbose >= 2) + printf("FAT12: too much clusters\n"); + } + + clust16 = ((long long)fatdata1216 * sector_size + nr_fats * 4) / + ((int)bs.cluster_size * sector_size + nr_fats * 2); + fatlength16 = cdiv((clust16 + 2) * 2, sector_size); + fatlength16 = align_object(fatlength16, bs.cluster_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust16 = (fatdata1216 - nr_fats * fatlength16) / bs.cluster_size; + maxclust16 = (fatlength16 * sector_size) / 2; + if (maxclust16 > MAX_CLUST_16) + maxclust16 = MAX_CLUST_16; + if (verbose >= 2) + printf("FAT16: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust16, fatlength16, maxclust16, MAX_CLUST_16); + if (clust16 > maxclust16 - 2) { + if (verbose >= 2) + printf("FAT16: too much clusters\n"); + clust16 = 0; + } + /* The < 4078 avoids that the filesystem will be misdetected as having a + * 12 bit FAT. */ + if (clust16 < FAT12_THRESHOLD + && !(size_fat_by_user && size_fat == 16)) { + if (verbose >= 2) + printf(clust16 < FAT12_THRESHOLD ? + "FAT16: would be misdetected as FAT12\n" : + "FAT16: too much clusters\n"); + clust16 = 0; + } + + clust32 = ((long long)fatdata32 * sector_size + nr_fats * 8) / + ((int)bs.cluster_size * sector_size + nr_fats * 4); + fatlength32 = cdiv((clust32 + 2) * 4, sector_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clust32 = (fatdata32 - nr_fats * fatlength32) / bs.cluster_size; + maxclust32 = (fatlength32 * sector_size) / 4; + if (maxclust32 > MAX_CLUST_32) + maxclust32 = MAX_CLUST_32; + if (clust32 && clust32 < MIN_CLUST_32 + && !(size_fat_by_user && size_fat == 32)) { + clust32 = 0; + if (verbose >= 2) + printf("FAT32: not enough clusters (%d)\n", MIN_CLUST_32); + } + if (verbose >= 2) + printf("FAT32: #clu=%u, fatlen=%u, maxclu=%u, limit=%u\n", + clust32, fatlength32, maxclust32, MAX_CLUST_32); + if (clust32 > maxclust32) { + clust32 = 0; + if (verbose >= 2) + printf("FAT32: too much clusters\n"); + } + + if ((clust12 && (size_fat == 0 || size_fat == 12)) || + (clust16 && (size_fat == 0 || size_fat == 16)) || + (clust32 && size_fat == 32)) + break; + + bs.cluster_size <<= 1; + } while (bs.cluster_size && bs.cluster_size <= maxclustsize); + + /* Use the optimal FAT size if not specified; + * FAT32 is (not yet) choosen automatically */ + if (!size_fat) { + size_fat = (clust16 > clust12) ? 16 : 12; + if (verbose >= 2) + printf("Choosing %d bits for FAT\n", size_fat); + } + + switch (size_fat) { + case 12: + cluster_count = clust12; + fat_length = fatlength12; + bs.fat_length = htole16(fatlength12); + memcpy(vi->fs_type, MSDOS_FAT12_SIGN, 8); + break; + + case 16: + if (clust16 < FAT12_THRESHOLD) { + if (size_fat_by_user) { + fprintf(stderr, "WARNING: Not enough clusters for a " + "16 bit FAT! The filesystem will be\n" + "misinterpreted as having a 12 bit FAT without " + "mount option \"fat=16\".\n"); + } else { + fprintf(stderr, "This filesystem has an unfortunate size. " + "A 12 bit FAT cannot provide\n" + "enough clusters, but a 16 bit FAT takes up a little " + "bit more space so that\n" + "the total number of clusters becomes less than the " + "threshold value for\n" + "distinction between 12 and 16 bit FATs.\n"); + die("Make the filesystem a bit smaller manually."); + } + } + cluster_count = clust16; + fat_length = fatlength16; + bs.fat_length = htole16(fatlength16); + memcpy(vi->fs_type, MSDOS_FAT16_SIGN, 8); + break; + + case 32: + if (clust32 < MIN_CLUST_32) + fprintf(stderr, + "WARNING: Not enough clusters for a 32 bit FAT!\n"); + cluster_count = clust32; + fat_length = fatlength32; + bs.fat_length = htole16(0); + bs.fat32.fat32_length = htole32(fatlength32); + memcpy(vi->fs_type, MSDOS_FAT32_SIGN, 8); + root_dir_entries = 0; + break; + + default: + die("FAT not 12, 16 or 32 bits"); + } + + /* Adjust the number of root directory entries to help enforce alignment */ + if (align_structures) { + root_dir_entries = align_object(root_dir_sectors, bs.cluster_size) + * (sector_size >> 5); + } + } else { + unsigned clusters, maxclust, fatdata; + + /* GEMDOS always uses a 12 bit FAT on floppies, and always a 16 bit FAT on + * hard disks. So use 12 bit if the size of the filesystem suggests that + * this fs is for a floppy disk, if the user hasn't explicitly requested a + * size. + */ + if (!size_fat) + size_fat = (num_sectors == 1440 || num_sectors == 2400 || + num_sectors == 2880 || num_sectors == 5760) ? 12 : 16; + if (verbose >= 2) + printf("Choosing %d bits for FAT\n", size_fat); + + /* Atari format: cluster size should be 2, except explicitly requested by + * the user, since GEMDOS doesn't like other cluster sizes very much. + * Instead, tune the sector size for the FS to fit. + */ + bs.cluster_size = sectors_per_cluster ? sectors_per_cluster : 2; + if (!sector_size_set) { + while (num_sectors > GEMDOS_MAX_SECTORS) { + num_sectors >>= 1; + sector_size <<= 1; + } + } + if (verbose >= 2) + printf("Sector size must be %d to have less than %d log. sectors\n", + sector_size, GEMDOS_MAX_SECTORS); + + /* Check if there are enough FAT indices for how much clusters we have */ + do { + fatdata = num_sectors - cdiv(root_dir_entries * 32, sector_size) - + reserved_sectors; + /* The factor 2 below avoids cut-off errors for nr_fats == 1 and + * size_fat == 12 + * The "2*nr_fats*size_fat/8" is for the reserved first two FAT entries + */ + clusters = + (2 * + ((long long)fatdata * sector_size - + 2 * nr_fats * size_fat / 8)) / (2 * ((int)bs.cluster_size * + sector_size + + nr_fats * size_fat / 8)); + fat_length = cdiv((clusters + 2) * size_fat / 8, sector_size); + /* Need to recalculate number of clusters, since the unused parts of the + * FATS and data area together could make up space for an additional, + * not really present cluster. */ + clusters = (fatdata - nr_fats * fat_length) / bs.cluster_size; + maxclust = (fat_length * sector_size * 8) / size_fat; + if (verbose >= 2) + printf("ss=%d: #clu=%d, fat_len=%d, maxclu=%d\n", + sector_size, clusters, fat_length, maxclust); + + /* last 10 cluster numbers are special (except FAT32: 4 high bits rsvd); + * first two numbers are reserved */ + if (maxclust <= + (size_fat == 32 ? MAX_CLUST_32 : (1 << size_fat) - 0x10) + && clusters <= maxclust - 2) + break; + if (verbose >= 2) + printf(clusters > maxclust - 2 ? + "Too many clusters\n" : "FAT too big\n"); + + /* need to increment sector_size once more to */ + if (sector_size_set) + die("With this sector size, the maximum number of FAT entries " + "would be exceeded."); + num_sectors >>= 1; + sector_size <<= 1; + } while (sector_size <= GEMDOS_MAX_SECTOR_SIZE); + + if (sector_size > GEMDOS_MAX_SECTOR_SIZE) + die("Would need a sector size > 16k, which GEMDOS can't work with"); + + cluster_count = clusters; + if (size_fat != 32) + bs.fat_length = htole16(fat_length); + else { + bs.fat_length = 0; + bs.fat32.fat32_length = htole32(fat_length); + } + } + + bs.sector_size[0] = (char)(sector_size & 0x00ff); + bs.sector_size[1] = (char)((sector_size & 0xff00) >> 8); + + bs.dir_entries[0] = (char)(root_dir_entries & 0x00ff); + bs.dir_entries[1] = (char)((root_dir_entries & 0xff00) >> 8); + + if (size_fat == 32) { + /* set up additional FAT32 fields */ + bs.fat32.flags = htole16(0); + bs.fat32.version[0] = 0; + bs.fat32.version[1] = 0; + bs.fat32.root_cluster = htole32(2); + bs.fat32.info_sector = htole16(1); + if (!backup_boot) + backup_boot = (reserved_sectors >= 7) ? 6 : + (reserved_sectors >= 2) ? reserved_sectors - 1 : 0; + else { + if (backup_boot == 1) + die("Backup boot sector must be after sector 1"); + else if (backup_boot >= reserved_sectors) + die("Backup boot sector must be a reserved sector"); + } + if (verbose >= 2) + printf("Using sector %d as backup boot sector (0 = none)\n", + backup_boot); + bs.fat32.backup_boot = htole16(backup_boot); + memset(&bs.fat32.reserved2, 0, sizeof(bs.fat32.reserved2)); + } + + if (atari_format) { + /* Just some consistency checks */ + if (num_sectors >= GEMDOS_MAX_SECTORS) + die("GEMDOS can't handle more than 65531 sectors"); + else if (num_sectors >= OLDGEMDOS_MAX_SECTORS) + printf("Warning: More than 32765 sector need TOS 1.04 " + "or higher.\n"); + } + if (num_sectors >= 65536) { + bs.sectors[0] = (char)0; + bs.sectors[1] = (char)0; + bs.total_sect = htole32(num_sectors); + } else { + bs.sectors[0] = (char)(num_sectors & 0x00ff); + bs.sectors[1] = (char)((num_sectors & 0xff00) >> 8); + if (!atari_format) + bs.total_sect = htole32(0); + } + + if (!atari_format) + vi->ext_boot_sign = MSDOS_EXT_SIGN; + + if (!cluster_count) { + if (sectors_per_cluster) /* If yes, die if we'd spec'd sectors per cluster */ + die("Too many clusters for filesystem - try more sectors per cluster"); + else + die("Attempting to create a too large filesystem"); + } + + /* The two following vars are in hard sectors, i.e. 512 byte sectors! */ + start_data_sector = (reserved_sectors + nr_fats * fat_length) * + (sector_size / HARD_SECTOR_SIZE); + start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / + SECTORS_PER_BLOCK; + + if (blocks < start_data_block + 32) /* Arbitrary undersize filesystem! */ + die("Too few blocks for viable filesystem"); + + if (verbose) { + printf("%s has %d head%s and %d sector%s per track,\n", + device_name, le16toh(bs.heads), + (le16toh(bs.heads) != 1) ? "s" : "", le16toh(bs.secs_track), + (le16toh(bs.secs_track) != 1) ? "s" : ""); + printf("hidden sectors 0x%04x;\n", hidden_sectors); + printf("logical sector size is %d,\n", sector_size); + printf("using 0x%02x media descriptor, with %d sectors;\n", + (int)(bs.media), num_sectors); + printf("drive number 0x%02x;\n", (int) (vi->drive_number)); + printf("filesystem has %d %d-bit FAT%s and %d sector%s per cluster.\n", + (int)(bs.fats), size_fat, (bs.fats != 1) ? "s" : "", + (int)(bs.cluster_size), (bs.cluster_size != 1) ? "s" : ""); + printf("FAT size is %d sector%s, and provides %d cluster%s.\n", + fat_length, (fat_length != 1) ? "s" : "", + cluster_count, (cluster_count != 1) ? "s" : ""); + printf("There %s %u reserved sector%s.\n", + (reserved_sectors != 1) ? "are" : "is", + reserved_sectors, (reserved_sectors != 1) ? "s" : ""); + + if (size_fat != 32) { + unsigned root_dir_entries = + bs.dir_entries[0] + ((bs.dir_entries[1]) * 256); + unsigned root_dir_sectors = + cdiv(root_dir_entries * 32, sector_size); + printf("Root directory contains %u slots and uses %u sectors.\n", + root_dir_entries, root_dir_sectors); + } + printf("Volume ID is %08lx, ", volume_id & + (atari_format ? 0x00ffffff : 0xffffffff)); + if (strcmp(volume_name, NO_NAME)) + printf("volume label %s.\n", volume_name); + else + printf("no volume label.\n"); + } + + /* Make the file allocation tables! */ + + if (malloc_entire_fat) + alloced_fat_length = fat_length; + else + alloced_fat_length = 1; + + if ((fat = + (unsigned char *)malloc(alloced_fat_length * sector_size)) == NULL) + die("unable to allocate space for FAT image in memory"); + + memset(fat, 0, alloced_fat_length * sector_size); + + mark_FAT_cluster(0, 0xffffffff); /* Initial fat entries */ + mark_FAT_cluster(1, 0xffffffff); + fat[0] = (unsigned char)bs.media; /* Put media type in first byte! */ + if (size_fat == 32) { + /* Mark cluster 2 as EOF (used for root dir) */ + mark_FAT_cluster(2, FAT_EOF); + } + + /* Make the root directory entries */ + + size_root_dir = (size_fat == 32) ? + bs.cluster_size * sector_size : + (((int)bs.dir_entries[1] * 256 + (int)bs.dir_entries[0]) * + sizeof(struct msdos_dir_entry)); + if ((root_dir = (struct msdos_dir_entry *)malloc(size_root_dir)) == NULL) { + free(fat); /* Tidy up before we die! */ + die("unable to allocate space for root directory in memory"); + } + + memset(root_dir, 0, size_root_dir); + if (memcmp(volume_name, NO_NAME, 11)) { + struct msdos_dir_entry *de = &root_dir[0]; + memcpy(de->name, volume_name, 8); + memcpy(de->ext, volume_name + 8, 3); + de->attr = ATTR_VOLUME; + if (!invariant) + ctime = localtime(&create_time); + else + ctime = gmtime(&create_time); + de->time = htole16((unsigned short)((ctime->tm_sec >> 1) + + (ctime->tm_min << 5) + + (ctime->tm_hour << 11))); + de->date = + htole16((unsigned short)(ctime->tm_mday + + ((ctime->tm_mon + 1) << 5) + + ((ctime->tm_year - 80) << 9))); + de->ctime_cs = 0; + de->ctime = de->time; + de->cdate = de->date; + de->adate = de->date; + de->starthi = htole16(0); + de->start = htole16(0); + de->size = htole32(0); + } + + if (size_fat == 32) { + /* For FAT32, create an info sector */ + struct fat32_fsinfo *info; + + if (!(info_sector = malloc(sector_size))) + die("Out of memory"); + memset(info_sector, 0, sector_size); + /* fsinfo structure is at offset 0x1e0 in info sector by observation */ + info = (struct fat32_fsinfo *)(info_sector + 0x1e0); + + /* Info sector magic */ + info_sector[0] = 'R'; + info_sector[1] = 'R'; + info_sector[2] = 'a'; + info_sector[3] = 'A'; + + /* Magic for fsinfo structure */ + info->signature = htole32(0x61417272); + /* We've allocated cluster 2 for the root dir. */ + info->free_clusters = htole32(cluster_count - 1); + info->next_cluster = htole32(2); + + /* Info sector also must have boot sign */ + *(uint16_t *) (info_sector + 0x1fe) = htole16(BOOT_SIGN); + } + + if (!(blank_sector = malloc(sector_size))) + die("Out of memory"); + memset(blank_sector, 0, sector_size); +} + +/* Write the new filesystem's data tables to wherever they're going to end up! */ + +#define error(str) \ + do { \ + free (fat); \ + if (info_sector) free (info_sector); \ + free (root_dir); \ + die (str); \ + } while(0) + +#define seekto(pos,errstr) \ + do { \ + loff_t __pos = (pos); \ + if (llseek (dev, __pos, SEEK_SET) != __pos) \ + error ("seek to " errstr " failed whilst writing tables"); \ + } while(0) + +#define writebuf(buf,size,errstr) \ + do { \ + int __size = (size); \ + if (write (dev, buf, __size) != __size) \ + error ("failed whilst writing " errstr); \ + } while(0) + +static void write_tables(void) +{ + int x; + int fat_length; + + fat_length = (size_fat == 32) ? + le32toh(bs.fat32.fat32_length) : le16toh(bs.fat_length); + + seekto(0, "start of device"); + /* clear all reserved sectors */ + for (x = 0; x < reserved_sectors; ++x) + writebuf(blank_sector, sector_size, "reserved sector"); + /* seek back to sector 0 and write the boot sector */ + seekto(0, "boot sector"); + writebuf((char *)&bs, sizeof(struct msdos_boot_sector), "boot sector"); + /* on FAT32, write the info sector and backup boot sector */ + if (size_fat == 32) { + seekto(le16toh(bs.fat32.info_sector) * sector_size, "info sector"); + writebuf(info_sector, 512, "info sector"); + if (backup_boot != 0) { + seekto(backup_boot * sector_size, "backup boot sector"); + writebuf((char *)&bs, sizeof(struct msdos_boot_sector), + "backup boot sector"); + } + } + /* seek to start of FATS and write them all */ + seekto(reserved_sectors * sector_size, "first FAT"); + for (x = 1; x <= nr_fats; x++) { + int y; + int blank_fat_length = fat_length - alloced_fat_length; + writebuf(fat, alloced_fat_length * sector_size, "FAT"); + for (y = 0; y < blank_fat_length; y++) + writebuf(blank_sector, sector_size, "FAT"); + } + /* Write the root directory directly after the last FAT. This is the root + * dir area on FAT12/16, and the first cluster on FAT32. */ + writebuf((char *)root_dir, size_root_dir, "root directory"); + + if (blank_sector) + free(blank_sector); + if (info_sector) + free(info_sector); + free(root_dir); /* Free up the root directory space from setup_tables */ + free(fat); /* Free up the fat table space reserved during setup_tables */ +} + +/* Report the command usage and exit with the given error code */ + +static void usage(int exitval) +{ + fprintf(stderr, "\ +Usage: mkfs.fat [-a][-A][-c][-C][-v][-I][-l bad-block-file][-b backup-boot-sector]\n\ + [-m boot-msg-file][-n volume-name][-i volume-id]\n\ + [-s sectors-per-cluster][-S logical-sector-size][-f number-of-FATs]\n\ + [-h hidden-sectors][-F fat-size][-r root-dir-entries][-R reserved-sectors]\n\ + [-M FAT-media-byte][-D drive_number]\n\ + [--invariant]\n\ + [--help]\n\ + /dev/name [blocks]\n"); + exit(exitval); +} + +/* + * ++roman: On m68k, check if this is an Atari; if yes, turn on Atari variant + * of MS-DOS filesystem by default. + */ +static void check_atari(void) +{ +#ifdef __mc68000__ + FILE *f; + char line[128], *p; + + if (!(f = fopen("/proc/hardware", "r"))) { + perror("/proc/hardware"); + return; + } + + while (fgets(line, sizeof(line), f)) { + if (strncmp(line, "Model:", 6) == 0) { + p = line + 6; + p += strspn(p, " \t"); + if (strncmp(p, "Atari ", 6) == 0) + atari_format = 1; + break; + } + } + fclose(f); +#endif +} + +/* The "main" entry point into the utility - we pick up the options and attempt to process them in some sort of sensible + way. In the event that some/all of the options are invalid we need to tell the user so that something can be done! */ + +int main(int argc, char **argv) +{ + int c; + char *tmp; + char *listfile = NULL; + FILE *msgfile; + struct stat statbuf; + int i = 0, pos, ch; + int create = 0; + uint64_t cblocks = 0; + int min_sector_size; + int bad_block_count = 0; + struct timeval create_timeval; + + enum {OPT_HELP=1000, OPT_INVARIANT,}; + const struct option long_options[] = { + {"help", no_argument, NULL, OPT_HELP}, + {"invariant", no_argument, NULL, OPT_INVARIANT}, + {0,} + }; + + if (argc && *argv) { /* What's the program name? */ + char *p; + program_name = *argv; + if ((p = strrchr(program_name, '/'))) + program_name = p + 1; + } + + gettimeofday(&create_timeval, NULL); + create_time = create_timeval.tv_sec; + volume_id = (uint32_t) ((create_timeval.tv_sec << 20) | create_timeval.tv_usec); /* Default volume ID = creation time, fudged for more uniqueness */ + check_atari(); + + printf("mkfs.fat " VERSION " (" VERSION_DATE ")\n"); + + while ((c = getopt_long(argc, argv, "aAb:cCf:D:F:Ii:l:m:M:n:r:R:s:S:h:v", + long_options, NULL)) != -1) + /* Scan the command line for options */ + switch (c) { + case 'A': /* toggle Atari format */ + atari_format = !atari_format; + break; + + case 'a': /* a : skip alignment */ + align_structures = FALSE; + break; + + case 'b': /* b : location of backup boot sector */ + backup_boot = (int)strtol(optarg, &tmp, 0); + if (*tmp || backup_boot < 2 || backup_boot > 0xffff) { + printf("Bad location for backup boot sector : %s\n", optarg); + usage(1); + } + break; + + case 'c': /* c : Check FS as we build it */ + check = TRUE; + malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ + break; + + case 'C': /* C : Create a new file */ + create = TRUE; + break; + + case 'D': /* D : Choose Drive Number */ + drive_number_option = (int) strtol (optarg, &tmp, 0); + if (*tmp || (drive_number_option != 0 && drive_number_option != 0x80)) { + printf ("Drive number must be 0 or 0x80: %s\n", optarg); + usage(1); + } + drive_number_by_user=1; + break; + + case 'f': /* f : Choose number of FATs */ + nr_fats = (int)strtol(optarg, &tmp, 0); + if (*tmp || nr_fats < 1 || nr_fats > 4) { + printf("Bad number of FATs : %s\n", optarg); + usage(1); + } + break; + + case 'F': /* F : Choose FAT size */ + size_fat = (int)strtol(optarg, &tmp, 0); + if (*tmp || (size_fat != 12 && size_fat != 16 && size_fat != 32)) { + printf("Bad FAT type : %s\n", optarg); + usage(1); + } + size_fat_by_user = 1; + break; + + case 'h': /* h : number of hidden sectors */ + hidden_sectors = (int)strtol(optarg, &tmp, 0); + if (*tmp || hidden_sectors < 0) { + printf("Bad number of hidden sectors : %s\n", optarg); + usage(1); + } + hidden_sectors_by_user = 1; + break; + + case 'I': + ignore_full_disk = 1; + break; + + case 'i': /* i : specify volume ID */ + volume_id = strtoul(optarg, &tmp, 16); + if (*tmp) { + printf("Volume ID must be a hexadecimal number\n"); + usage(1); + } + break; + + case 'l': /* l : Bad block filename */ + listfile = optarg; + malloc_entire_fat = TRUE; /* Need to be able to mark clusters bad */ + break; + + case 'm': /* m : Set boot message */ + if (strcmp(optarg, "-")) { + msgfile = fopen(optarg, "r"); + if (!msgfile) + perror(optarg); + } else + msgfile = stdin; + + if (msgfile) { + /* The boot code ends at offset 448 and needs a null terminator */ + i = MESSAGE_OFFSET; + pos = 0; /* We are at beginning of line */ + do { + ch = getc(msgfile); + switch (ch) { + case '\r': /* Ignore CRs */ + case '\0': /* and nulls */ + break; + + case '\n': /* LF -> CR+LF if necessary */ + if (pos) { /* If not at beginning of line */ + dummy_boot_code[i++] = '\r'; + pos = 0; + } + dummy_boot_code[i++] = '\n'; + break; + + case '\t': /* Expand tabs */ + do { + dummy_boot_code[i++] = ' '; + pos++; + } + while (pos % 8 && i < BOOTCODE_SIZE - 1); + break; + + case EOF: + dummy_boot_code[i++] = '\0'; /* Null terminator */ + break; + + default: + dummy_boot_code[i++] = ch; /* Store character */ + pos++; /* Advance position */ + break; + } + } + while (ch != EOF && i < BOOTCODE_SIZE - 1); + + /* Fill up with zeros */ + while (i < BOOTCODE_SIZE - 1) + dummy_boot_code[i++] = '\0'; + dummy_boot_code[BOOTCODE_SIZE - 1] = '\0'; /* Just in case */ + + if (ch != EOF) + printf("Warning: message too long; truncated\n"); + + if (msgfile != stdin) + fclose(msgfile); + } + break; + + case 'M': /* M : FAT Media byte */ + fat_media_byte = (int)strtol(optarg, &tmp, 0); + if (*tmp) { + printf("Bad number for media descriptor : %s\n", optarg); + usage(1); + } + if (fat_media_byte != 0xf0 && (fat_media_byte < 0xf8 || fat_media_byte > 0xff)) { + printf("FAT Media byte must either be between 0xF8 and 0xFF or be 0xF0 : %s\n", optarg); + usage(1); + } + break; + + case 'n': /* n : Volume name */ + sprintf(volume_name, "%-11.11s", optarg); + for (i = 0; volume_name[i] && i < 11; i++) + /* don't know if here should be more strict !uppercase(label[i]) */ + if (islower(volume_name[i])) { + fprintf(stderr, + "mkfs.fat: warning - lowercase labels might not work properly with DOS or Windows\n"); + break; + } + + break; + + case 'r': /* r : Root directory entries */ + root_dir_entries = (int)strtol(optarg, &tmp, 0); + if (*tmp || root_dir_entries < 16 || root_dir_entries > 32768) { + printf("Bad number of root directory entries : %s\n", optarg); + usage(1); + } + break; + + case 'R': /* R : number of reserved sectors */ + reserved_sectors = (int)strtol(optarg, &tmp, 0); + if (*tmp || reserved_sectors < 1 || reserved_sectors > 0xffff) { + printf("Bad number of reserved sectors : %s\n", optarg); + usage(1); + } + break; + + case 's': /* s : Sectors per cluster */ + sectors_per_cluster = (int)strtol(optarg, &tmp, 0); + if (*tmp || (sectors_per_cluster != 1 && sectors_per_cluster != 2 + && sectors_per_cluster != 4 && sectors_per_cluster != 8 + && sectors_per_cluster != 16 + && sectors_per_cluster != 32 + && sectors_per_cluster != 64 + && sectors_per_cluster != 128)) { + printf("Bad number of sectors per cluster : %s\n", optarg); + usage(1); + } + break; + + case 'S': /* S : Sector size */ + sector_size = (int)strtol(optarg, &tmp, 0); + if (*tmp || (sector_size != 512 && sector_size != 1024 && + sector_size != 2048 && sector_size != 4096 && + sector_size != 8192 && sector_size != 16384 && + sector_size != 32768)) { + printf("Bad logical sector size : %s\n", optarg); + usage(1); + } + sector_size_set = 1; + break; + + case 'v': /* v : Verbose execution */ + ++verbose; + break; + + case OPT_HELP: + usage(0); + break; + + case OPT_INVARIANT: + invariant = 1; + volume_id = 0x1234abcd; + create_time = 1426325213; + break; + + default: + printf("Unknown option: %c\n", c); + usage(1); + } + if (optind < argc) { + device_name = argv[optind]; /* Determine the number of blocks in the FS */ + + if (!device_name) { + printf("No device specified.\n"); + usage(1); + } + + if (!create) + cblocks = count_blocks(device_name, &orphaned_sectors); /* Have a look and see! */ + } + if (optind == argc - 2) { /* Either check the user specified number */ + blocks = strtoull(argv[optind + 1], &tmp, 0); + if (!create && blocks != cblocks) { + fprintf(stderr, "Warning: block count mismatch: "); + fprintf(stderr, "found %llu but assuming %llu.\n", (unsigned long long)cblocks, (unsigned long long)blocks); + } + if (*tmp) + bad_block_count = 1; + } else if (optind == argc - 1) { /* Or use value found */ + if (create) + die("Need intended size with -C."); + blocks = cblocks; + } else { + fprintf(stderr, "No device specified!\n"); + usage(1); + } + if (bad_block_count) { + printf("Bad block count : %s\n", argv[optind + 1]); + usage(1); + } + + if (check && listfile) /* Auto and specified bad block handling are mutually */ + die("-c and -l are incompatible"); /* exclusive of each other! */ + + if (!create) { + check_mount(device_name); /* Is the device already mounted? */ + dev = open(device_name, O_EXCL | O_RDWR); /* Is it a suitable device to build the FS on? */ + if (dev < 0) { + fprintf(stderr, "%s: unable to open %s: %s\n", program_name, + device_name, strerror(errno)); + exit(1); /* The error exit code is 1! */ + } + } else { + /* create the file */ + dev = open(device_name, O_EXCL | O_RDWR | O_CREAT, 0666); + if (dev < 0) { + if (errno == EEXIST) + die("file %s already exists"); + else + die("unable to create %s"); + } + /* expand to desired size */ + if (ftruncate(dev, blocks * BLOCK_SIZE)) + die("unable to resize %s"); + } + + if (fstat(dev, &statbuf) < 0) + die("unable to stat %s"); + if (!S_ISBLK(statbuf.st_mode)) { + statbuf.st_rdev = 0; + check = 0; + } else + /* + * Ignore any 'full' fixed disk devices, if -I is not given. + * On a MO-disk one doesn't need partitions. The filesytem can go + * directly to the whole disk. Under other OSes this is known as + * the 'superfloppy' format. As I don't know how to find out if + * this is a MO disk I introduce a -I (ignore) switch. -Joey + */ + if (!ignore_full_disk && ((statbuf.st_rdev & 0xffffff3f) == 0x0300 || /* hda, hdb */ + (statbuf.st_rdev & 0xffffff0f) == 0x0800 || /* sd */ + (statbuf.st_rdev & 0xffffff3f) == 0x0d00 || /* xd */ + (statbuf.st_rdev & 0xffffff3f) == 0x1600) /* hdc, hdd */ + ) + die("Device partition expected, not making filesystem on entire device '%s' (use -I to override)"); + + if (sector_size_set) { + if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) + if (sector_size < min_sector_size) { + sector_size = min_sector_size; + fprintf(stderr, + "Warning: sector size was set to %d (minimal for this device)\n", + sector_size); + } + } else { + if (ioctl(dev, BLKSSZGET, &min_sector_size) >= 0) { + sector_size = min_sector_size; + sector_size_set = 1; + } + } + + if (sector_size > 4096) + fprintf(stderr, + "Warning: sector size is set to %d > 4096, such filesystem will not propably mount\n", + sector_size); + + establish_params(statbuf.st_rdev, statbuf.st_size); + /* Establish the media parameters */ + + setup_tables(); /* Establish the filesystem tables */ + + if (check) /* Determine any bad block locations and mark them */ + check_blocks(); + else if (listfile) + get_list_blocks(listfile); + + write_tables(); /* Write the filesystem tables away! */ + + exit(0); /* Terminate with no errors! */ +} diff --git a/dosfstools/src/msdos_fs.h b/dosfstools/src/msdos_fs.h new file mode 100644 index 000000000..54b2a3446 --- /dev/null +++ b/dosfstools/src/msdos_fs.h @@ -0,0 +1,61 @@ +/* msdos_fs.h - MS-DOS filesystem constants/structures + + 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + The complete text of the GNU General Public License + can be found in /usr/share/common-licenses/GPL-3 file. +*/ + +#ifndef _MSDOS_FS_H +#define _MSDOS_FS_H + +#include + +#define SECTOR_SIZE 512 /* sector size (bytes) */ +#define MSDOS_DPS (SECTOR_SIZE / sizeof(struct msdos_dir_entry)) +#define MSDOS_DPS_BITS 4 /* log2(MSDOS_DPS) */ +#define MSDOS_DIR_BITS 5 /* log2(sizeof(struct msdos_dir_entry)) */ + +#define ATTR_NONE 0 /* no attribute bits */ +#define ATTR_RO 1 /* read-only */ +#define ATTR_HIDDEN 2 /* hidden */ +#define ATTR_SYS 4 /* system */ +#define ATTR_VOLUME 8 /* volume label */ +#define ATTR_DIR 16 /* directory */ +#define ATTR_ARCH 32 /* archived */ + +/* attribute bits that are copied "as is" */ +#define ATTR_UNUSED (ATTR_VOLUME | ATTR_ARCH | ATTR_SYS | ATTR_HIDDEN) + +#define DELETED_FLAG 0xe5 /* marks file as deleted when in name[0] */ +#define IS_FREE(n) (!*(n) || *(n) == DELETED_FLAG) + +#define MSDOS_NAME 11 /* maximum name length */ +#define MSDOS_DOT ". " /* ".", padded to MSDOS_NAME chars */ +#define MSDOS_DOTDOT ".. " /* "..", padded to MSDOS_NAME chars */ + +struct msdos_dir_entry { + uint8_t name[8], ext[3]; /* name and extension */ + uint8_t attr; /* attribute bits */ + uint8_t lcase; /* Case for base and extension */ + uint8_t ctime_cs; /* Creation time, centiseconds (0-199) */ + uint16_t ctime; /* Creation time */ + uint16_t cdate; /* Creation date */ + uint16_t adate; /* Last access date */ + uint16_t starthi; /* High 16 bits of cluster in FAT32 */ + uint16_t time, date, start; /* time, date and first cluster */ + uint32_t size; /* file size (in bytes) */ +} __attribute__ ((packed)); + +#endif /* _MSDOS_FS_H */ diff --git a/dosfstools/src/version.h b/dosfstools/src/version.h index 6379103b6..f0716d346 100644 --- a/dosfstools/src/version.h +++ b/dosfstools/src/version.h @@ -1,6 +1,7 @@ /* version.h Copyright (C) 1998-2005 Roman Hodek + Copyright (C) 2008-2014 Daniel Baumann 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 @@ -15,14 +16,14 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . - On Debian systems, the complete text of the GNU General Public License + The complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-3 file. */ #ifndef _version_h #define _version_h -#define VERSION "3.0.12" -#define VERSION_DATE "29 Oct 2011" +#define VERSION "3.0.28" +#define VERSION_DATE "2015-05-16" #endif -- cgit v1.2.3