*
* Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
*
- * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ * Licensed under GPLv2, see file LICENSE in this source tree.
*/
+
+//usage:#define mkfs_vfat_trivial_usage
+//usage: "[-v] [-n LABEL] BLOCKDEV [KBYTES]"
+/* Accepted but ignored:
+ "[-c] [-C] [-I] [-l bad-block-file] [-b backup-boot-sector] "
+ "[-m boot-msg-file] [-i volume-id] "
+ "[-s sectors-per-cluster] [-S logical-sector-size] [-f number-of-FATs] "
+ "[-h hidden-sectors] [-F fat-size] [-r root-dir-entries] [-R reserved-sectors] "
+*/
+//usage:#define mkfs_vfat_full_usage "\n\n"
+//usage: "Make a FAT32 filesystem\n"
+/* //usage: "\n -c Check device for bad blocks" */
+//usage: "\n -v Verbose"
+/* //usage: "\n -I Allow to use entire disk device (e.g. /dev/hda)" */
+//usage: "\n -n LBL Volume label"
+
#include "libbb.h"
-#include "volume_id/volume_id_internal.h"
#include <linux/hdreg.h> /* HDIO_GETGEO */
#include <linux/fd.h> /* FDGETPRM */
+#include <sys/mount.h> /* BLKSSZGET */
+#if !defined(BLKSSZGET)
+# define BLKSSZGET _IO(0x12, 104)
+#endif
//#include <linux/msdos_fs.h>
#define SECTOR_SIZE 512
#define ATTR_VOLUME 8
-#define NUM_FATS 2
+#define NUM_FATS 2
/* FAT32 filesystem looks like this:
* sector -nn...-1: "hidden" sectors, all sectors before this partition
uint16_t date; /* 018 date */
uint16_t start; /* 01a first cluster */
uint32_t size; /* 01c file size in bytes */
-} __attribute__ ((packed));
+} PACKED;
/* Example of boot sector's beginning:
0000 eb 58 90 4d 53 57 49 4e 34 2e 31 00 02 08 26 00 |...MSWIN4.1...&.|
uint32_t volume_id32; /* 043 volume ID number */
char volume_label[11];/* 047 volume label */
char fs_type[8]; /* 052 typically "FATnn" */
-} __attribute__ ((packed)); /* 05a end. Total size 26 (0x1a) bytes */
+} PACKED; /* 05a end. Total size 26 (0x1a) bytes */
struct msdos_boot_sector {
- char boot_jump[3]; /* 000 short or near jump instruction */
- char system_id[8]; /* 003 name - can be used to special case partition manager volumes */
+ /* We use strcpy to fill both, and gcc-4.4.x complains if they are separate */
+ char boot_jump_and_sys_id[3+8]; /* 000 short or near jump instruction */
+ /*char system_id[8];*/ /* 003 name - can be used to special case partition manager volumes */
uint16_t bytes_per_sect; /* 00b bytes per logical sector */
uint8_t sect_per_clust; /* 00d sectors/cluster */
uint16_t reserved_sect; /* 00e reserved sectors (sector offset of 1st FAT relative to volume start) */
char boot_code[0x200 - 0x5a - 2]; /* 05a */
#define BOOT_SIGN 0xAA55
uint16_t boot_sign; /* 1fe */
-} __attribute__ ((packed));
+} PACKED;
#define FAT_FSINFO_SIG1 0x41615252
#define FAT_FSINFO_SIG2 0x61417272
uint32_t reserved2[3];
uint16_t reserved3; /* 1fc */
uint16_t boot_sign; /* 1fe */
-} __attribute__ ((packed));
+} PACKED;
struct bug_check {
char BUG1[sizeof(struct msdos_dir_entry ) == 0x20 ? 1 : -1];
"This is not a bootable disk\r\n";
-// mark specified cluster as having a particular value
-#define mark_cluster(cluster, value) \
- ((uint32_t *)fat)[cluster] = cpu_to_le32(value)
+#define MARK_CLUSTER(cluster, value) \
+ ((uint32_t *)fat)[cluster] = SWAP_LE32(value)
void BUG_unsupported_field_size(void);
#define STORE_LE(field, value) \
do { \
if (sizeof(field) == 4) \
- field = cpu_to_le32(value); \
+ field = SWAP_LE32(value); \
else if (sizeof(field) == 2) \
- field = cpu_to_le16(value); \
+ field = SWAP_LE16(value); \
else if (sizeof(field) == 1) \
field = (value); \
else \
char *buf;
char *device_name;
uoff_t volume_size_bytes;
- uoff_t volume_size_blocks;
uoff_t volume_size_sect;
uint32_t total_clust;
uint32_t volume_id;
int dev;
unsigned bytes_per_sect;
unsigned sect_per_fat;
- unsigned opts;
+ unsigned opts;
uint16_t sect_per_track;
uint8_t media_byte;
uint8_t sect_per_clust;
// default volume ID = creation time
volume_id = time(NULL);
- dev = xopen(device_name, O_EXCL | O_RDWR);
- if (fstat(dev, &st) < 0)
- bb_simple_perror_msg_and_die(device_name);
+ dev = xopen(device_name, O_RDWR);
+ xfstat(dev, &st, device_name);
//
// Get image size and sector size
//
bytes_per_sect = SECTOR_SIZE;
- volume_size_bytes = st.st_size;
if (!S_ISBLK(st.st_mode)) {
if (!S_ISREG(st.st_mode)) {
if (!argv[1])
opts &= ~OPT_c;
} else {
int min_bytes_per_sect;
-
- // more portable than BLKGETSIZE[64]
- volume_size_bytes = xlseek(dev, 0, SEEK_END);
- xlseek(dev, 0, SEEK_SET);
#if 0
unsigned device_num;
// for true block devices we do check sanity
device_num == 0x0d00 || // xd
device_num == 0x1600 ) // hdc, hdd
)
- bb_error_msg_and_die("Will not try to make filesystem on full-disk device (use -I if wanted)");
+ bb_error_msg_and_die("will not try to make filesystem on full-disk device (use -I if wanted)");
// can't work on mounted filesystems
- if (find_mount_point(device_name, NULL))
- bb_error_msg_and_die("Can't format mounted filesystem");
+ if (find_mount_point(device_name, 0))
+ bb_error_msg_and_die("can't format mounted filesystem");
#endif
// get true sector size
// (parameter must be int*, not long* or size_t*)
bb_error_msg("for this device sector size is %u", min_bytes_per_sect);
}
}
- if (argv[1]) {
- volume_size_bytes = XATOOFF(argv[1]);
- if (volume_size_bytes >= MAXINT(off_t) / 1024)
- bb_error_msg_and_die("image size is too big");
- volume_size_bytes *= 1024;
- }
- volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS);
+ volume_size_bytes = get_volume_size_in_bytes(dev, argv[1], 1024, /*extend:*/ 1);
volume_size_sect = volume_size_bytes / bytes_per_sect;
//
* fs size <= 16G: 8k clusters
* fs size > 16G: 16k clusters
*/
- sect_per_clust = volume_size_bytes > ((off_t)16)*1024*1024*1024 ? 32 :
- volume_size_bytes > ((off_t)8)*1024*1024*1024 ? 16 :
- volume_size_bytes > 260*1024*1024 ? 8 : 1;
+ sect_per_clust = 1;
+ if (volume_size_bytes >= 260*1024*1024) {
+ sect_per_clust = 8;
+ /* fight gcc: */
+ /* "error: integer overflow in expression" */
+ /* "error: right shift count >= width of type" */
+ if (sizeof(off_t) > 4) {
+ unsigned t = (volume_size_bytes >> 31 >> 1);
+ if (t >= 8/4)
+ sect_per_clust = 16;
+ if (t >= 16/4)
+ sect_per_clust = 32;
+ }
+ }
} else {
+ // floppy, loop, or regular file
int not_floppy = ioctl(dev, FDGETPRM, ¶m);
if (not_floppy == 0) {
// floppy disk
// Calculate number of clusters, sectors/cluster, sectors/FAT
// (an initial guess for sect_per_clust should already be set)
//
- if ((off_t)(volume_size_sect - reserved_sect) < 16) // arbitrary limit
+ // "mkdosfs -v -F 32 image5k 5" is the minimum:
+ // 2 sectors for FATs and 2 data sectors
+ if ((off_t)(volume_size_sect - reserved_sect) < 4)
bb_error_msg_and_die("the image is too small for FAT32");
sect_per_fat = 1;
while (1) {
while (1) {
- unsigned spf;
- off_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
+ int spf_adj;
+ uoff_t tcl = (volume_size_sect - reserved_sect - NUM_FATS * sect_per_fat) / sect_per_clust;
// tcl may be > MAX_CLUST_32 here, but it may be
// because sect_per_fat is underestimated,
// and with increased sect_per_fat it still may become
// <= MAX_CLUST_32. Therefore, we do not check
// against MAX_CLUST_32, but against a bigger const:
- if (tcl > 0x7fffffff)
+ if (tcl > 0x80ffffff)
goto next;
total_clust = tcl; // fits in uint32_t
- spf = ((total_clust + 2) * 4 + bytes_per_sect - 1) / bytes_per_sect;
- if (spf <= sect_per_fat) {
+ // Every cluster needs 4 bytes in FAT. +2 entries since
+ // FAT has space for non-existent clusters 0 and 1.
+ // Let's see how many sectors that needs.
+ //May overflow at "*4":
+ //spf_adj = ((total_clust+2) * 4 + bytes_per_sect-1) / bytes_per_sect - sect_per_fat;
+ //Same in the more obscure, non-overflowing form:
+ spf_adj = ((total_clust+2) + (bytes_per_sect/4)-1) / (bytes_per_sect/4) - sect_per_fat;
+#if 0
+ bb_error_msg("sect_per_clust:%u sect_per_fat:%u total_clust:%u",
+ sect_per_clust, sect_per_fat, (int)tcl);
+ bb_error_msg("adjust to sect_per_fat:%d", spf_adj);
+#endif
+ if (spf_adj <= 0) {
// do not need to adjust sect_per_fat.
// so, was total_clust too big after all?
if (total_clust <= MAX_CLUST_32)
goto next;
}
// adjust sect_per_fat, go back and recalc total_clust
- sect_per_fat = spf;
+ // (note: just "sect_per_fat += spf_adj" isn't ok)
+ sect_per_fat += ((unsigned)spf_adj / 2) | 1;
}
next:
if (sect_per_clust == 128)
fprintf(stderr,
"Device '%s':\n"
"heads:%u, sectors/track:%u, bytes/sector:%u\n"
- "media descriptor:0x%02x\n"
+ "media descriptor:%02x\n"
"total sectors:%"OFF_FMT"u, clusters:%u, sectors/cluster:%u\n"
"FATs:2, sectors/FAT:%u\n"
"volumeID:%08x, label:'%s'\n",
// Write filesystem image sequentially (no seeking)
//
{
+ // (a | b) is poor man's max(a, b)
unsigned bufsize = reserved_sect;
//bufsize |= sect_per_fat; // can be quite large
bufsize |= 2; // use this instead
buf = xzalloc(bufsize * bytes_per_sect);
}
- { // boot, fsinfo sectors and their copies
+ { // boot and fsinfo sectors, and their copies
struct msdos_boot_sector *boot_blk = (void*)buf;
struct fat32_fsinfo *info = (void*)(buf + bytes_per_sect);
- strcpy(boot_blk->boot_jump, "\xeb\x58\x90" "mkdosfs"); // system_id[8] included :)
+ strcpy(boot_blk->boot_jump_and_sys_id, "\xeb\x58\x90" "mkdosfs");
STORE_LE(boot_blk->bytes_per_sect, bytes_per_sect);
STORE_LE(boot_blk->sect_per_clust, sect_per_clust);
- STORE_LE(boot_blk->reserved_sect, reserved_sect);
+ // cast in needed on big endian to suppress a warning
+ STORE_LE(boot_blk->reserved_sect, (uint16_t)reserved_sect);
STORE_LE(boot_blk->fats, 2);
//STORE_LE(boot_blk->dir_entries, 0); // for FAT32, stays 0
if (volume_size_sect <= 0xffff)
STORE_LE(boot_blk->volume_size_sect, volume_size_sect);
STORE_LE(boot_blk->media_byte, media_byte);
- // Bad: this would make Linux to think that it's fat12/16
+ // wrong: this would make Linux think that it's fat12/16:
//if (sect_per_fat <= 0xffff)
// STORE_LE(boot_blk->sect_per_fat, sect_per_fat);
+ // works:
//STORE_LE(boot_blk->sect_per_fat, 0);
STORE_LE(boot_blk->sect_per_track, sect_per_track);
STORE_LE(boot_blk->heads, heads);
memset(buf, 0, bytes_per_sect * 2);
// initial FAT entries
- mark_cluster(0, 0x0fffff00 | media_byte);
- mark_cluster(1, 0xffffffff);
+ MARK_CLUSTER(0, 0x0fffff00 | media_byte);
+ MARK_CLUSTER(1, 0xffffffff);
// mark cluster 2 as EOF (used for root dir)
- mark_cluster(2, EOF_FAT32);
+ MARK_CLUSTER(2, EOF_FAT32);
for (i = 0; i < NUM_FATS; i++) {
xwrite(dev, buf, bytes_per_sect);
for (j = 1; j < sect_per_fat; j++)
// create dir entry for volume_label
struct msdos_dir_entry *de;
#if 0
- struct tm tm;
+ struct tm tm_time;
uint16_t t, d;
#endif
de = (void*)buf;
strncpy(de->name, volume_label, sizeof(de->name));
STORE_LE(de->attr, ATTR_VOLUME);
#if 0
- localtime_r(&create_time, &tm);
- t = (tm.tm_sec >> 1) + (tm.tm_min << 5) + (tm.tm_hour << 11);
- d = tm.tm_mday + ((tm.tm_mon+1) << 5) + ((tm.tm_year-80) << 9);
+ localtime_r(&create_time, &tm_time);
+ t = (tm_time.tm_sec >> 1) + (tm_time.tm_min << 5) + (tm_time.tm_hour << 11);
+ d = tm_time.tm_mday + ((tm_time.tm_mon+1) << 5) + ((tm_time.tm_year-80) << 9);
STORE_LE(de->time, t);
STORE_LE(de->date, d);
//STORE_LE(de->ctime_cs, 0);
#if 0
if (opts & OPT_c) {
+ uoff_t volume_size_blocks;
unsigned start_data_sector;
unsigned start_data_block;
unsigned badblocks = 0;
off_t currently_testing;
char *blkbuf = xmalloc(BLOCK_SIZE * TEST_BUFFER_BLOCKS);
+ volume_size_blocks = (volume_size_bytes >> BLOCK_SIZE_BITS);
// N.B. the two following vars are in hard sectors, i.e. SECTOR_SIZE byte sectors!
start_data_sector = (reserved_sect + NUM_FATS * sect_per_fat) * (bytes_per_sect / SECTOR_SIZE);
start_data_block = (start_data_sector + SECTORS_PER_BLOCK - 1) / SECTORS_PER_BLOCK;
- bb_info_msg("Searching for bad blocks ");
+ bb_info_msg("searching for bad blocks ");
currently_testing = 0;
try = TEST_BUFFER_BLOCKS;
while (currently_testing < volume_size_blocks) {
if (got < 0)
got = 0;
if (got & (BLOCK_SIZE - 1))
- bb_error_msg("Unexpected values in do_check: probably bugs");
+ bb_error_msg("unexpected values in do_check: probably bugs");
got /= BLOCK_SIZE;
currently_testing += got;
if (got == try) {
for (i = 0; i < SECTORS_PER_BLOCK; i++) {
int cluster = (currently_testing * SECTORS_PER_BLOCK + i - start_data_sector) / (int) (sect_per_clust) / (bytes_per_sect / SECTOR_SIZE);
if (cluster < 0)
- bb_error_msg_and_die("Invalid cluster number in mark_sector: probably bug!");
- mark_cluster(cluster, BAD_FAT32);
+ bb_error_msg_and_die("invalid cluster number in mark_sector: probably bug!");
+ MARK_CLUSTER(cluster, BAD_FAT32);
}
badblocks++;
currently_testing++;