use PACKED macro insted of open-coding GCC-ism
[oweals/busybox.git] / util-linux / mkfs_vfat.c
index 95ebcbfba0df683b142d544ab379581e71413671..10de2af5b729a23c9d590d49cee6de8a5eb84224 100644 (file)
@@ -8,11 +8,15 @@
  * Licensed under GPLv2, see file LICENSE in this tarball for details.
  */
 #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>
+#include "volume_id/volume_id_internal.h"
 
 #define SECTOR_SIZE             512
 
@@ -75,7 +79,7 @@ struct msdos_dir_entry {
        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...&.|
@@ -92,7 +96,7 @@ struct msdos_volume_info { /* (offsets are relative to start of boot sector) */
        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 */
@@ -120,7 +124,7 @@ struct msdos_boot_sector {
        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
@@ -133,7 +137,7 @@ struct fat32_fsinfo {
        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];
@@ -163,8 +167,7 @@ static const char boot_code[] ALIGN1 =
        "This is not a bootable disk\r\n";
 
 
-// mark specified cluster as having a particular value
-#define        mark_cluster(cluster, value) \
+#define MARK_CLUSTER(cluster, value) \
        ((uint32_t *)fat)[cluster] = cpu_to_le32(value)
 
 void BUG_unsupported_field_size(void);
@@ -199,7 +202,6 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
        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;
@@ -275,10 +277,10 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                        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*)
@@ -294,7 +296,6 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                        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_sect = volume_size_bytes / bytes_per_sect;
 
        //
@@ -326,10 +327,22 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                         * 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, &param);
                        if (not_floppy == 0) {
                                // floppy disk
@@ -370,12 +383,14 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
        // 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;
+                       int spf_adj;
                        off_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,
@@ -385,8 +400,13 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                        if (tcl > 0x7fffffff)
                                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) {
+                       spf_adj = ((total_clust + 2) * 4 + bytes_per_sect - 1) / bytes_per_sect - 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)
@@ -395,7 +415,8 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                                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)
@@ -412,7 +433,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                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",
@@ -437,7 +458,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                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);
 
@@ -492,10 +513,10 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
 
                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++)
@@ -532,6 +553,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
 
 #if 0
        if (opts & OPT_c) {
+               uoff_t volume_size_blocks;
                unsigned start_data_sector;
                unsigned start_data_block;
                unsigned badblocks = 0;
@@ -539,11 +561,12 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                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) {
@@ -558,7 +581,7 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                        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) {
@@ -573,8 +596,8 @@ int mkfs_vfat_main(int argc UNUSED_PARAM, char **argv)
                        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++;