3 Creates exFAT file system.
5 Copyright (C) 2011, 2012 Andrew Nayenko
7 This program is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/types.h>
36 #define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
38 struct exfat_super_block sb;
39 struct exfat_entry_label label_entry = {EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID};
40 struct exfat_entry_bitmap bitmap_entry = {EXFAT_ENTRY_BITMAP};
41 struct exfat_entry_upcase upcase_entry = {EXFAT_ENTRY_UPCASE};
43 struct exfat_structure
47 off_t (*get_alignment)(void);
48 off_t (*get_size)(void);
49 int (*write_data)(struct exfat_dev*, off_t);
52 static int get_spc_bits(int sector_bits, int user_defined, off_t volume_size)
56 if (user_defined != -1)
59 if (volume_size < 256ull * 1024 * 1024)
60 return MAX(0, 12 - sector_bits); /* 4 KB */
61 if (volume_size < 32ull * 1024 * 1024 * 1024)
62 return MAX(0, 15 - sector_bits); /* 32 KB */
64 for (i = 17; ; i++) /* 128 KB or more */
65 if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
66 return MAX(0, i - sector_bits);
69 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
70 uint32_t volume_serial, int first_sector)
72 uint32_t clusters_max;
74 uint32_t allocated_clusters;
76 if ((volume_size >> sector_bits >> spc_bits) > EXFAT_LAST_DATA_CLUSTER)
78 struct exfat_human_bytes chb, vhb;
80 exfat_humanize_bytes(1 << sector_bits << spc_bits, &chb);
81 exfat_humanize_bytes(volume_size, &vhb);
82 exfat_error("cluster size %"PRIu64" %s is too small for "
83 "%"PRIu64" %s volume, try -s %d",
86 1 << get_spc_bits(sector_bits, -1, volume_size));
90 clusters_max = (volume_size >> sector_bits >> spc_bits);
91 fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t),
94 memset(&sb, 0, sizeof(struct exfat_super_block));
98 memcpy(sb.oem_name, "EXFAT ", sizeof(sb.oem_name));
99 sb.sector_start = cpu_to_le64(first_sector);
100 sb.sector_count = cpu_to_le64(volume_size >> sector_bits);
101 sb.fat_sector_start = cpu_to_le32(128); /* FIXME */
102 sb.fat_sector_count = cpu_to_le32(ROUND_UP(
103 le32_to_cpu(sb.fat_sector_start) + fat_sectors, 1 << spc_bits) -
104 le32_to_cpu(sb.fat_sector_start));
105 /* cluster_sector_start will be set later */
106 sb.cluster_count = cpu_to_le32(clusters_max -
107 ((le32_to_cpu(sb.fat_sector_start) +
108 le32_to_cpu(sb.fat_sector_count)) >> spc_bits));
109 /* rootdir_cluster will be set later */
110 sb.volume_serial = cpu_to_le32(volume_serial);
111 sb.version.major = 1;
112 sb.version.minor = 0;
113 sb.volume_state = cpu_to_le16(0);
114 sb.sector_bits = sector_bits;
115 sb.spc_bits = spc_bits;
118 sb.allocated_percent = 0;
119 sb.boot_signature = cpu_to_le16(0xaa55);
122 DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
123 DIV_ROUND_UP(uct_size(), CLUSTER_SIZE(sb)) +
124 DIV_ROUND_UP(rootdir_size(), CLUSTER_SIZE(sb));
125 if (clusters_max < ((le32_to_cpu(sb.fat_sector_start) +
126 le32_to_cpu(sb.fat_sector_count)) >> spc_bits) +
129 exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
132 exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
137 static int erase_device(struct exfat_dev* dev)
145 block_size = sysconf(_SC_PAGESIZE);
149 erase_size = ((uint64_t)
150 le32_to_cpu(sb.fat_sector_start) +
151 le32_to_cpu(sb.fat_sector_count)) * SECTOR_SIZE(sb);
152 erase_size = ROUND_UP(erase_size, cbm_alignment());
153 erase_size += cbm_size();
154 erase_size = ROUND_UP(erase_size, uct_alignment());
155 erase_size += uct_size();
156 erase_size = ROUND_UP(erase_size, rootdir_alignment());
157 erase_size += rootdir_size();
159 erase_blocks = DIV_ROUND_UP(erase_size, block_size);
161 if (exfat_seek(dev, 0, SEEK_SET) == (off_t) -1)
163 exfat_error("seek failed");
167 block = malloc(block_size);
170 exfat_error("failed to allocate erase block");
173 memset(block, 0, block_size);
175 for (i = 0; i < erase_blocks; i++)
177 if (exfat_write(dev, block, block_size) < 0)
180 exfat_error("failed to erase block %"PRIu64, i);
183 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
185 printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
195 * - Volume Boot Record (VBR)
196 * - Main Boot Sector (MBR)
197 * - Main Extended Boot Sectors (MEBS)
201 * - Volume Boot Record copy
202 * - File Allocation Table (FAT)
208 #define FS_OBJECT(order, name) \
209 {#name, order, name##_alignment, name##_size, name##_write}
210 static struct exfat_structure structures[] =
217 FS_OBJECT(1, rootdir)
221 static off_t write_structure(struct exfat_dev* dev,
222 struct exfat_structure* structure, off_t current)
224 off_t alignment = structure->get_alignment();
225 off_t base = ROUND_UP(current, alignment);
227 if (exfat_seek(dev, base, SEEK_SET) == (off_t) -1)
229 exfat_error("seek to %"PRIu64" failed", base);
232 if (structure->order > 0)
234 int rc = structure->write_data(dev, base);
237 exfat_error("%s creation failed: %s", structure->name,
243 return base + structure->get_size();
246 static int write_structures(struct exfat_dev* dev)
256 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
258 current = write_structure(dev, &structures[i], current);
259 if (current == (off_t) -1)
261 remainder += structures[i].order;
264 while (remainder > 0);
268 static int set_volume_label(const char* volume_label)
270 le16_t tmp[EXFAT_ENAME_MAX + 1];
272 if (volume_label == NULL)
275 memset(tmp, 0, sizeof(tmp));
276 if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
277 strlen(volume_label)) != 0)
279 memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
280 label_entry.length = utf16_length(tmp);
281 label_entry.type |= EXFAT_ENTRY_VALID;
285 static uint32_t get_volume_serial(uint32_t user_defined)
289 if (user_defined != 0)
292 if (gettimeofday(&now, NULL) != 0)
294 return (now.tv_sec << 20) | now.tv_usec;
297 static int mkfs(const char* spec, int sector_bits, int spc_bits,
298 const char* volume_label, uint32_t volume_serial, int first_sector)
300 struct exfat_dev* dev;
303 dev = exfat_open(spec, 0);
307 volume_size = exfat_seek(dev, 0, SEEK_END);
308 if (volume_size == (off_t) -1)
311 exfat_error("seek failed");
314 spc_bits = get_spc_bits(sector_bits, spc_bits, volume_size);
316 if (set_volume_label(volume_label) != 0)
322 volume_serial = get_volume_serial(volume_serial);
323 if (volume_serial == 0)
326 exfat_error("failed to get current time to form volume id");
330 if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
337 printf("Creating... %2u%%", 0);
339 if (erase_device(dev) != 0)
344 if (write_structures(dev) != 0)
349 puts("\b\b\b\bdone.");
351 printf("Flushing... ");
353 if (exfat_fsync(dev) != 0)
359 if (exfat_close(dev) != 0)
361 printf("File system created successfully.\n");
365 static int logarithm2(int n)
369 for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
375 static void usage(const char* prog)
377 fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
378 "[-p partition-first-sector] "
379 "[-s sectors-per-cluster] [-v] <device>\n", prog);
383 int main(int argc, char* argv[])
385 const char* spec = NULL;
388 const char* volume_label = NULL;
389 uint32_t volume_serial = 0;
390 int first_sector = 0;
392 printf("mkexfatfs %u.%u.%u\n",
393 EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
395 for (pp = argv + 1; *pp; pp++)
397 if (strcmp(*pp, "-s") == 0)
402 spc_bits = logarithm2(atoi(*pp));
405 exfat_error("invalid option value: `%s'", *pp);
409 else if (strcmp(*pp, "-n") == 0)
415 /* TODO check length */
417 else if (strcmp(*pp, "-i") == 0)
422 volume_serial = strtol(*pp, NULL, 16);
424 else if (strcmp(*pp, "-p") == 0)
429 first_sector = atoi(*pp);
431 else if (strcmp(*pp, "-v") == 0)
433 puts("Copyright (C) 2011, 2012 Andrew Nayenko");
436 else if (spec == NULL)
444 return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);