]> git.sven.stormbind.net Git - sven/exfat-utils.git/blobdiff - mkfs/main.c
Imported Upstream version 1.1.0
[sven/exfat-utils.git] / mkfs / main.c
index 885dd71263a5dd5b9ec69064d2ebe9d66e7b89fe..61d9a6dab29d4d51862266450e11a69caa18a3cd 100644 (file)
@@ -2,11 +2,12 @@
        main.c (15.08.10)
        Creates exFAT file system.
 
        main.c (15.08.10)
        Creates exFAT file system.
 
-       Copyright (C) 2010  Andrew Nayenko
+       Free exFAT implementation.
+       Copyright (C) 2011-2014  Andrew Nayenko
 
 
-       This program is free software: you can redistribute it and/or modify
+       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
        it under the terms of the GNU General Public License as published by
-       the Free Software Foundation, either version 3 of the License, or
+       the Free Software Foundation, either version 2 of the License, or
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
        (at your option) any later version.
 
        This program is distributed in the hope that it will be useful,
@@ -14,8 +15,9 @@
        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
        GNU General Public License for more details.
 
        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 <http://www.gnu.org/licenses/>.
+       You should have received a copy of the GNU General Public License along
+       with this program; if not, write to the Free Software Foundation, Inc.,
+       51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
 
 #include <sys/types.h>
 */
 
 #include <sys/types.h>
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
 #include <stdio.h>
 #include <string.h>
 #include <limits.h>
-#include <errno.h>
 #include <exfat.h>
 #include <exfat.h>
+#include "mkexfat.h"
 #include "vbr.h"
 #include "fat.h"
 #include "cbm.h"
 #include "uct.h"
 #include "rootdir.h"
 
 #include "vbr.h"
 #include "fat.h"
 #include "cbm.h"
 #include "uct.h"
 #include "rootdir.h"
 
-#define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
-
-struct exfat_super_block sb;
-struct exfat_entry_label label_entry = {EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID};
-struct exfat_entry_bitmap bitmap_entry = {EXFAT_ENTRY_BITMAP};
-struct exfat_entry_upcase upcase_entry = {EXFAT_ENTRY_UPCASE};
-
-struct exfat_structure
+const struct fs_object* objects[] =
 {
 {
-       const char* name;
-       int order;
-       off_t (*get_alignment)(void);
-       off_t (*get_size)(void);
-       int (*write_data)(struct exfat_dev*, off_t);
+       &vbr,
+       &vbr,
+       &fat,
+       /* clusters heap */
+       &cbm,
+       &uct,
+       &rootdir,
+       NULL,
 };
 
 };
 
-static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
-               uint32_t volume_serial, int first_sector)
+static struct
 {
 {
-       uint32_t clusters_max = (volume_size >> sector_bits >> spc_bits);
-       uint32_t fat_sectors = DIV_ROUND_UP(clusters_max * 4, 1 << sector_bits);
-       uint32_t allocated_clusters;
-
-       memset(&sb, 0, sizeof(struct exfat_super_block));
-       sb.jump[0] = 0xeb;
-       sb.jump[1] = 0x76;
-       sb.jump[2] = 0x90;
-       memcpy(sb.oem_name, "EXFAT   ", sizeof(sb.oem_name));
-       sb.sector_start = cpu_to_le64(first_sector);
-       sb.sector_count = cpu_to_le64(volume_size >> sector_bits);
-       sb.fat_sector_start = cpu_to_le32(128); /* FIXME */
-       sb.fat_sector_count = cpu_to_le32(ROUND_UP(
-                       le32_to_cpu(sb.fat_sector_start) + fat_sectors, 1 << spc_bits) -
-                       le32_to_cpu(sb.fat_sector_start));
-       /* cluster_sector_start will be set later */
-       sb.cluster_count = cpu_to_le32(clusters_max -
-                       ((le32_to_cpu(sb.fat_sector_start) +
-                         le32_to_cpu(sb.fat_sector_count)) >> spc_bits));
-       /* rootdir_cluster will be set later */
-       sb.volume_serial = cpu_to_le32(volume_serial);
-       sb.version.major = 1;
-       sb.version.minor = 0;
-       sb.volume_state = cpu_to_le16(0);
-       sb.sector_bits = sector_bits;
-       sb.spc_bits = spc_bits;
-       sb.fat_count = 1;
-       sb.drive_no = 0x80;
-       sb.allocated_percent = 0;
-       sb.boot_signature = cpu_to_le16(0xaa55);
-
-       allocated_clusters =
-                       DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
-                       DIV_ROUND_UP(uct_size(), CLUSTER_SIZE(sb)) +
-                       DIV_ROUND_UP(rootdir_size(), CLUSTER_SIZE(sb));
-       if (clusters_max < ((le32_to_cpu(sb.fat_sector_start) +
-                       le32_to_cpu(sb.fat_sector_count)) >> spc_bits) +
-                       allocated_clusters)
-       {
-               exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
-               return 1;
-       }
-       exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
-                       allocated_clusters);
-       return 0;
+       int sector_bits;
+       int spc_bits;
+       off_t volume_size;
+       le16_t volume_label[EXFAT_ENAME_MAX + 1];
+       uint32_t volume_serial;
+       uint64_t first_sector;
 }
 }
+param;
 
 
-static int erase_device(struct exfat_dev* dev)
+int get_sector_bits(void)
 {
 {
-       off_t erase_size;
-       off_t erase_blocks;
-       long block_size;
-       void* block;
-       off_t i;
-
-       block_size = sysconf(_SC_PAGESIZE);
-       if (block_size < 1)
-               block_size = 0x1000;
-
-       erase_size = ((uint64_t)
-                       le32_to_cpu(sb.fat_sector_start) +
-                       le32_to_cpu(sb.fat_sector_count)) * SECTOR_SIZE(sb);
-       erase_size = ROUND_UP(erase_size, cbm_alignment());
-       erase_size += cbm_size();
-       erase_size = ROUND_UP(erase_size, uct_alignment());
-       erase_size += uct_size();
-       erase_size = ROUND_UP(erase_size, rootdir_alignment());
-       erase_size += rootdir_size();
-
-       erase_blocks = DIV_ROUND_UP(erase_size, block_size);
-
-       if (exfat_seek(dev, 0, SEEK_SET) == (off_t) -1)
-       {
-               exfat_error("seek failed");
-               return 1;
-       }
+       return param.sector_bits;
+}
 
 
-       block = malloc(block_size);
-       if (block == NULL)
-       {
-               exfat_error("failed to allocate erase block");
-               return 1;
-       }
-       memset(block, 0, block_size);
+int get_spc_bits(void)
+{
+       return param.spc_bits;
+}
 
 
-       for (i = 0; i < erase_blocks; i++)
-       {
-               if (exfat_write(dev, block, block_size) < 0)
-               {
-                       free(block);
-                       exfat_error("failed to erase block %"PRIu64, i);
-                       return 1;
-               }
-               if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
-               {
-                       printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
-                       fflush(stdout);
-               }
-       }
-       free(block);
-       return 0;
+off_t get_volume_size(void)
+{
+       return param.volume_size;
 }
 
 }
 
-/*
- * exFAT layout:
- * - Volume Boot Record (VBR)
- *   - Main Boot Sector (MBR)
- *   - Main Extended Boot Sectors (MEBS)
- *   - OEM Parameters
- *   - Reserved sector
- *   - Checksum sector
- * - Volume Boot Record copy
- * - File Allocation Table (FAT)
- * - Clusters heap
- *   - Clusters bitmap
- *   - Upper case table
- *   - Root directory
- */
-#define FS_OBJECT(order, name) \
-       {#name, order, name##_alignment, name##_size, name##_write}
-static struct exfat_structure structures[] =
+const le16_t* get_volume_label(void)
 {
 {
-       FS_OBJECT(3, vbr),
-       FS_OBJECT(3, vbr),
-       FS_OBJECT(2, fat),
-       FS_OBJECT(1, cbm),
-       FS_OBJECT(1, uct),
-       FS_OBJECT(1, rootdir)
-};
-#undef FS_OBJECT
+       return param.volume_label;
+}
 
 
-static off_t write_structure(struct exfat_dev* dev,
-               struct exfat_structure* structure, off_t current)
+uint32_t get_volume_serial(void)
 {
 {
-       off_t alignment = structure->get_alignment();
-       off_t base = ROUND_UP(current, alignment);
+       return param.volume_serial;
+}
 
 
-       if (exfat_seek(dev, base, SEEK_SET) == (off_t) -1)
-       {
-               exfat_error("seek to %"PRIu64" failed", base);
-               return -1;
-       }
-       if (structure->order > 0)
-       {
-               int rc = structure->write_data(dev, base);
-               if (rc != 0)
-               {
-                       exfat_error("%s creation failed: %s", structure->name,
-                                       strerror(rc));
-                       return -1;
-               }
-               structure->order--;
-       }
-       return base + structure->get_size();
+uint64_t get_first_sector(void)
+{
+       return param.first_sector;
 }
 
 }
 
-static int write_structures(struct exfat_dev* dev)
+int get_sector_size(void)
 {
 {
-       off_t current;
-       size_t i;
-       int remainder;
+       return 1 << get_sector_bits();
+}
 
 
-       do
-       {
-               current = 0;
-               remainder = 0;
-               for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
-               {
-                       current = write_structure(dev, &structures[i], current);
-                       if (current == (off_t) -1)
-                               return 1;
-                       remainder += structures[i].order;
-               }
-       }
-       while (remainder > 0);
-       return 0;
+int get_cluster_size(void)
+{
+       return get_sector_size() << get_spc_bits();
 }
 
 }
 
-static int get_spc_bits(int user_defined, off_t volume_size)
+static int setup_spc_bits(int sector_bits, int user_defined, off_t volume_size)
 {
 {
+       int i;
+
        if (user_defined != -1)
        if (user_defined != -1)
+       {
+               off_t cluster_size = 1 << sector_bits << user_defined;
+               if (volume_size / cluster_size > EXFAT_LAST_DATA_CLUSTER)
+               {
+                       struct exfat_human_bytes chb, vhb;
+
+                       exfat_humanize_bytes(cluster_size, &chb);
+                       exfat_humanize_bytes(volume_size, &vhb);
+                       exfat_error("cluster size %"PRIu64" %s is too small for "
+                                       "%"PRIu64" %s volume, try -s %d",
+                                       chb.value, chb.unit,
+                                       vhb.value, vhb.unit,
+                                       1 << setup_spc_bits(sector_bits, -1, volume_size));
+                       return -1;
+               }
                return user_defined;
                return user_defined;
+       }
 
        if (volume_size < 256ull * 1024 * 1024)
 
        if (volume_size < 256ull * 1024 * 1024)
-               return 3;       /* 4 KB */
-       else if (volume_size < 32ull * 1024 * 1024 * 1024)
-               return 6;       /* 32 KB */
-       else
-               return 8;       /* 128 KB */
+               return MAX(0, 12 - sector_bits);        /* 4 KB */
+       if (volume_size < 32ull * 1024 * 1024 * 1024)
+               return MAX(0, 15 - sector_bits);        /* 32 KB */
+
+       for (i = 17; ; i++)                                             /* 128 KB or more */
+               if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
+                       return MAX(0, i - sector_bits);
 }
 
 }
 
-static int set_volume_label(const char* volume_label)
+static int setup_volume_label(le16_t label[EXFAT_ENAME_MAX + 1], const char* s)
 {
 {
-       le16_t tmp[EXFAT_ENAME_MAX + 1];
-
-       if (volume_label == NULL)
+       memset(label, 0, (EXFAT_ENAME_MAX + 1) * sizeof(le16_t));
+       if (s == NULL)
                return 0;
                return 0;
-
-       memset(tmp, 0, sizeof(tmp));
-       if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
-                               strlen(volume_label)) != 0)
-               return 1;
-       memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
-       label_entry.length = utf16_length(tmp);
-       label_entry.type |= EXFAT_ENTRY_VALID;
-       return 0;
+       return utf8_to_utf16(label, s, EXFAT_ENAME_MAX, strlen(s));
 }
 
 }
 
-static uint32_t get_volume_serial(uint32_t user_defined)
+static uint32_t setup_volume_serial(uint32_t user_defined)
 {
        struct timeval now;
 
 {
        struct timeval now;
 
@@ -268,76 +147,33 @@ static uint32_t get_volume_serial(uint32_t user_defined)
                return user_defined;
 
        if (gettimeofday(&now, NULL) != 0)
                return user_defined;
 
        if (gettimeofday(&now, NULL) != 0)
+       {
+               exfat_error("failed to form volume id");
                return 0;
                return 0;
+       }
        return (now.tv_sec << 20) | now.tv_usec;
 }
 
        return (now.tv_sec << 20) | now.tv_usec;
 }
 
-static int mkfs(const char* spec, int sector_bits, int spc_bits,
-               const char* volume_label, uint32_t volume_serial, int first_sector)
+static int setup(struct exfat_dev* dev, int sector_bits, int spc_bits,
+               const char* volume_label, uint32_t volume_serial,
+               uint64_t first_sector)
 {
 {
-       struct exfat_dev* dev;
-       off_t volume_size;
+       param.sector_bits = sector_bits;
+       param.first_sector = first_sector;
+       param.volume_size = exfat_get_size(dev);
 
 
-       dev = exfat_open(spec, 0);
-       if (dev == NULL)
+       param.spc_bits = setup_spc_bits(sector_bits, spc_bits, param.volume_size);
+       if (param.spc_bits == -1)
                return 1;
 
                return 1;
 
-       volume_size = exfat_seek(dev, 0, SEEK_END);
-       if (volume_size == (off_t) -1)
-       {
-               exfat_close(dev);
-               exfat_error("seek failed");
+       if (setup_volume_label(param.volume_label, volume_label) != 0)
                return 1;
                return 1;
-       }
-       spc_bits = get_spc_bits(spc_bits, volume_size);
 
 
-       if (set_volume_label(volume_label) != 0)
-       {
-               exfat_close(dev);
+       param.volume_serial = setup_volume_serial(volume_serial);
+       if (param.volume_serial == 0)
                return 1;
                return 1;
-       }
 
 
-       volume_serial = get_volume_serial(volume_serial);
-       if (volume_serial == 0)
-       {
-               exfat_close(dev);
-               exfat_error("failed to get current time to form volume id");
-               return 1;
-       }
-
-       if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
-                               first_sector) != 0)
-       {
-               exfat_close(dev);
-               return 1;
-       }
-
-       printf("Creating... %2u%%", 0);
-       fflush(stdout);
-       if (erase_device(dev) != 0)
-       {
-               exfat_close(dev);
-               return 1;
-       }
-       if (write_structures(dev) != 0)
-       {
-               exfat_close(dev);
-               return 1;
-       }
-       puts("\b\b\b\bdone.");
-
-       printf("Flushing... ");
-       fflush(stdout);
-       if (exfat_fsync(dev) != 0)
-       {
-               exfat_close(dev);
-               return 1;
-       }
-       puts("done.");
-       if (exfat_close(dev) != 0)
-               return 1;
-       printf("File system created successfully.\n");
-       return 0;
+       return mkfs(dev, param.volume_size);
 }
 
 static int logarithm2(int n)
 }
 
 static int logarithm2(int n)
@@ -354,70 +190,67 @@ static void usage(const char* prog)
 {
        fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
                        "[-p partition-first-sector] "
 {
        fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
                        "[-p partition-first-sector] "
-                       "[-s sectors-per-cluster] [-v] <device>\n", prog);
+                       "[-s sectors-per-cluster] [-V] <device>\n", prog);
        exit(1);
 }
 
 int main(int argc, char* argv[])
 {
        const char* spec = NULL;
        exit(1);
 }
 
 int main(int argc, char* argv[])
 {
        const char* spec = NULL;
-       char** pp;
+       int opt;
        int spc_bits = -1;
        const char* volume_label = NULL;
        uint32_t volume_serial = 0;
        int spc_bits = -1;
        const char* volume_label = NULL;
        uint32_t volume_serial = 0;
-       int first_sector = 0;
+       uint64_t first_sector = 0;
+       struct exfat_dev* dev;
 
        printf("mkexfatfs %u.%u.%u\n",
                        EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
 
 
        printf("mkexfatfs %u.%u.%u\n",
                        EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
 
-       for (pp = argv + 1; *pp; pp++)
+       while ((opt = getopt(argc, argv, "i:n:p:s:V")) != -1)
        {
        {
-               if (strcmp(*pp, "-s") == 0)
+               switch (opt)
                {
                {
-                       pp++;
-                       if (*pp == NULL)
-                               usage(argv[0]);
-                       spc_bits = logarithm2(atoi(*pp));
+               case 'i':
+                       volume_serial = strtol(optarg, NULL, 16);
+                       break;
+               case 'n':
+                       volume_label = optarg;
+                       break;
+               case 'p':
+                       first_sector = strtoll(optarg, NULL, 10);
+                       break;
+               case 's':
+                       spc_bits = logarithm2(atoi(optarg));
                        if (spc_bits < 0)
                        {
                        if (spc_bits < 0)
                        {
-                               exfat_error("invalid option value: `%s'", *pp);
+                               exfat_error("invalid option value: '%s'", optarg);
                                return 1;
                        }
                                return 1;
                        }
-               }
-               else if (strcmp(*pp, "-n") == 0)
-               {
-                       pp++;
-                       if (*pp == NULL)
-                               usage(argv[0]);
-                       volume_label = *pp;
-                       /* TODO check length */
-               }
-               else if (strcmp(*pp, "-i") == 0)
-               {
-                       pp++;
-                       if (*pp == NULL)
-                               usage(argv[0]);
-                       volume_serial = strtol(*pp, NULL, 16);
-               }
-               else if (strcmp(*pp, "-p") == 0)
-               {
-                       pp++;
-                       if (*pp == NULL)
-                               usage(argv[0]);
-                       first_sector = atoi(*pp);
-               }
-               else if (strcmp(*pp, "-v") == 0)
-               {
-                       puts("Copyright (C) 2010  Andrew Nayenko");
+                       break;
+               case 'V':
+                       puts("Copyright (C) 2011-2014  Andrew Nayenko");
                        return 0;
                        return 0;
-               }
-               else if (spec == NULL)
-                       spec = *pp;
-               else
+               default:
                        usage(argv[0]);
                        usage(argv[0]);
+                       break;
+               }
        }
        }
-       if (spec == NULL)
+       if (argc - optind != 1)
                usage(argv[0]);
                usage(argv[0]);
+       spec = argv[optind];
 
 
-       return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);
+       dev = exfat_open(spec, EXFAT_MODE_RW);
+       if (dev == NULL)
+               return 1;
+       if (setup(dev, 9, spc_bits, volume_label, volume_serial,
+                               first_sector) != 0)
+       {
+               exfat_close(dev);
+               return 1;
+       }
+       if (exfat_close(dev) != 0)
+               return 1;
+       printf("File system created successfully.\n");
+       return 0;
 }
 }