3 Creates exFAT file system.
5 Copyright (C) 2010 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)(off_t, int);
52 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
53 uint32_t volume_serial, int first_sector)
55 uint32_t clusters_max = (volume_size >> sector_bits >> spc_bits);
56 uint32_t fat_sectors = DIV_ROUND_UP(clusters_max * 4, 1 << sector_bits);
57 uint32_t allocated_clusters;
59 memset(&sb, 0, sizeof(struct exfat_super_block));
63 memcpy(sb.oem_name, "EXFAT ", sizeof(sb.oem_name));
64 sb.sector_start = cpu_to_le64(first_sector);
65 sb.sector_count = cpu_to_le64(volume_size >> sector_bits);
66 sb.fat_sector_start = cpu_to_le32(128); /* FIXME */
67 sb.fat_sector_count = cpu_to_le32(ROUND_UP(
68 le32_to_cpu(sb.fat_sector_start) + fat_sectors, 1 << spc_bits) -
69 le32_to_cpu(sb.fat_sector_start));
70 /* cluster_sector_start will be set later */
71 sb.cluster_count = cpu_to_le32(clusters_max -
72 ((le32_to_cpu(sb.fat_sector_start) +
73 le32_to_cpu(sb.fat_sector_count)) >> spc_bits));
74 /* rootdir_cluster will be set later */
75 sb.volume_serial = cpu_to_le32(volume_serial);
78 sb.volume_state = cpu_to_le16(0);
79 sb.sector_bits = sector_bits;
80 sb.spc_bits = spc_bits;
83 sb.allocated_percent = 0;
84 sb.boot_signature = cpu_to_le16(0xaa55);
87 DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
88 DIV_ROUND_UP(uct_size(), CLUSTER_SIZE(sb)) +
89 DIV_ROUND_UP(rootdir_size(), CLUSTER_SIZE(sb));
90 if (clusters_max < ((le32_to_cpu(sb.fat_sector_start) +
91 le32_to_cpu(sb.fat_sector_count)) >> spc_bits) +
94 exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
97 exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
102 static int erase_device(int fd)
109 erase_size = ((uint64_t)
110 le32_to_cpu(sb.fat_sector_start) +
111 le32_to_cpu(sb.fat_sector_count)) * SECTOR_SIZE(sb);
112 erase_size = ROUND_UP(erase_size, cbm_alignment());
113 erase_size += cbm_size();
114 erase_size = ROUND_UP(erase_size, uct_alignment());
115 erase_size += uct_size();
116 erase_size = ROUND_UP(erase_size, rootdir_alignment());
117 erase_size += rootdir_size();
119 erase_sectors = erase_size / SECTOR_SIZE(sb);
121 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
123 exfat_error("seek failed");
127 sector = malloc(SECTOR_SIZE(sb));
130 exfat_error("failed to allocate erase sector");
133 memset(sector, 0, SECTOR_SIZE(sb));
135 for (i = 0; i < erase_sectors; i++)
137 if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
140 exfat_error("failed to erase sector %"PRIu64, i);
143 if (i * 100 / erase_sectors != (i + 1) * 100 / erase_sectors)
145 printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_sectors);
155 * - Volume Boot Record (VBR)
156 * - Main Boot Sector (MBR)
157 * - Main Extended Boot Sectors (MEBS)
161 * - Volume Boot Record copy
162 * - File Allocation Table (FAT)
168 #define FS_OBJECT(order, name) \
169 {#name, order, name##_alignment, name##_size, name##_write}
170 static struct exfat_structure structures[] =
177 FS_OBJECT(1, rootdir)
181 static off_t write_structure(int fd, struct exfat_structure* structure,
184 off_t alignment = structure->get_alignment();
185 off_t base = ROUND_UP(current, alignment);
187 if (lseek(fd, base, SEEK_SET) == (off_t) -1)
189 exfat_error("seek to %"PRIu64" failed", base);
192 if (structure->order > 0)
194 int rc = structure->write_data(base, fd);
197 exfat_error("%s creation failed: %s", structure->name,
203 return base + structure->get_size();
206 static int write_structures(int fd)
216 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
218 current = write_structure(fd, &structures[i], current);
219 if (current == (off_t) -1)
221 remainder += structures[i].order;
224 while (remainder > 0);
228 static int get_spc_bits(int user_defined, off_t volume_size)
230 if (user_defined != -1)
233 if (volume_size < 256ull * 1024 * 1024)
235 else if (volume_size < 32ull * 1024 * 1024 * 1024)
236 return 6; /* 32 KB */
238 return 8; /* 128 KB */
241 static int set_volume_label(int fd, const char* volume_label)
243 le16_t tmp[EXFAT_ENAME_MAX + 1];
245 if (volume_label == NULL)
248 memset(tmp, 0, sizeof(tmp));
249 if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
250 strlen(volume_label)) != 0)
255 memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
256 label_entry.length = utf16_length(tmp);
257 label_entry.type |= EXFAT_ENTRY_VALID;
261 static uint32_t get_volume_serial(uint32_t user_defined)
265 if (user_defined != 0)
268 if (gettimeofday(&now, NULL) != 0)
270 return (now.tv_sec << 20) | now.tv_usec;
273 static int mkfs(const char* spec, int sector_bits, int spc_bits,
274 const char* volume_label, uint32_t volume_serial, int first_sector)
278 char spec_abs[PATH_MAX];
280 if (realpath(spec, spec_abs) == NULL)
282 exfat_error("failed to get absolute path for `%s'", spec);
286 fd = exfat_open(spec_abs, 0);
290 volume_size = lseek(fd, 0, SEEK_END);
291 if (volume_size == (off_t) -1)
294 exfat_error("seek failed");
297 spc_bits = get_spc_bits(spc_bits, volume_size);
299 if (set_volume_label(fd, volume_label) != 0)
305 volume_serial = get_volume_serial(volume_serial);
306 if (volume_serial == 0)
309 exfat_error("failed to get current time to form volume id");
313 if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
320 printf("Creating... %2u%%", 0);
322 if (erase_device(fd) != 0)
327 if (write_structures(fd) != 0)
332 puts("\b\b\b\bdone.");
334 printf("Flushing... ");
339 exfat_error("fsync failed for `%s'", spec_abs);
345 exfat_error("close failed for `%s'", spec_abs);
348 printf("File system created successfully.\n");
352 static int logarithm2(int n)
356 for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
362 static void usage(const char* prog)
364 fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
365 "[-p partition-first-sector] "
366 "[-s sectors-per-cluster] [-v] <device>\n", prog);
370 int main(int argc, char* argv[])
372 const char* spec = NULL;
375 const char* volume_label = NULL;
376 uint32_t volume_serial = 0;
377 int first_sector = 0;
379 printf("mkexfatfs %u.%u.%u\n",
380 EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
382 for (pp = argv + 1; *pp; pp++)
384 if (strcmp(*pp, "-s") == 0)
389 spc_bits = logarithm2(atoi(*pp));
392 exfat_error("invalid option value: `%s'", *pp);
396 else if (strcmp(*pp, "-n") == 0)
402 /* TODO check length */
404 else if (strcmp(*pp, "-i") == 0)
409 volume_serial = strtol(*pp, NULL, 16);
411 else if (strcmp(*pp, "-p") == 0)
416 first_sector = atoi(*pp);
418 else if (strcmp(*pp, "-v") == 0)
420 puts("Copyright (C) 2010 Andrew Nayenko");
423 else if (spec == NULL)
431 return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);