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>
38 #define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
40 struct exfat_super_block sb;
41 struct exfat_entry_label label_entry = {EXFAT_ENTRY_LABEL ^ EXFAT_ENTRY_VALID};
42 struct exfat_entry_bitmap bitmap_entry = {EXFAT_ENTRY_BITMAP};
43 struct exfat_entry_upcase upcase_entry = {EXFAT_ENTRY_UPCASE};
45 struct exfat_structure
49 off_t (*get_alignment)(void);
50 off_t (*get_size)(void);
51 int (*write_data)(off_t, int);
54 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
55 uint32_t volume_serial, int first_sector)
57 uint32_t clusters_max = (volume_size >> sector_bits >> spc_bits);
58 uint32_t fat_sectors = DIV_ROUND_UP(clusters_max * 4, 1 << sector_bits);
59 uint32_t allocated_clusters;
61 memset(&sb, 0, sizeof(struct exfat_super_block));
65 memcpy(sb.oem_name, "EXFAT ", sizeof(sb.oem_name));
66 sb.sector_start = cpu_to_le64(first_sector);
67 sb.sector_count = cpu_to_le64(volume_size >> sector_bits);
68 sb.fat_sector_start = cpu_to_le32(128); /* FIXME */
69 sb.fat_sector_count = cpu_to_le32(ROUND_UP(
70 le32_to_cpu(sb.fat_sector_start) + fat_sectors, 1 << spc_bits) -
71 le32_to_cpu(sb.fat_sector_start));
72 /* cluster_sector_start will be set later */
73 sb.cluster_count = cpu_to_le32(clusters_max -
74 ((le32_to_cpu(sb.fat_sector_start) +
75 le32_to_cpu(sb.fat_sector_count)) >> spc_bits));
76 /* rootdir_cluster will be set later */
77 sb.volume_serial = cpu_to_le32(volume_serial);
80 sb.volume_state = cpu_to_le16(0);
81 sb.sector_bits = sector_bits;
82 sb.spc_bits = spc_bits;
85 sb.allocated_percent = 0;
86 sb.boot_signature = cpu_to_le16(0xaa55);
89 DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
90 DIV_ROUND_UP(uct_size(), CLUSTER_SIZE(sb)) +
91 DIV_ROUND_UP(rootdir_size(), CLUSTER_SIZE(sb));
92 if (clusters_max < ((le32_to_cpu(sb.fat_sector_start) +
93 le32_to_cpu(sb.fat_sector_count)) >> spc_bits) +
96 exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
99 exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
104 static int erase_device(int fd)
106 uint64_t erase_sectors = (uint64_t)
107 le32_to_cpu(sb.fat_sector_start) +
108 le32_to_cpu(sb.fat_sector_count) +
109 DIV_ROUND_UP(cbm_size(), 1 << sb.sector_bits) +
110 DIV_ROUND_UP(uct_size(), 1 << sb.sector_bits) +
111 DIV_ROUND_UP(rootdir_size(), 1 << sb.sector_bits);
115 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
117 exfat_error("seek failed");
121 sector = malloc(SECTOR_SIZE(sb));
124 exfat_error("failed to allocate erase sector");
127 memset(sector, 0, SECTOR_SIZE(sb));
129 for (i = 0; i < erase_sectors; i++)
131 if (write(fd, sector, SECTOR_SIZE(sb)) == -1)
134 exfat_error("failed to erase sector %"PRIu64, i);
137 if (i * 100 / erase_sectors != (i + 1) * 100 / erase_sectors)
139 printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_sectors);
149 * - Volume Boot Record (VBR)
150 * - Main Boot Sector (MBR)
151 * - Main Extended Boot Sectors (MEBS)
155 * - Volume Boot Record copy
156 * - File Allocation Table (FAT)
162 #define FS_OBJECT(order, name) \
163 {#name, order, name##_alignment, name##_size, name##_write}
164 static struct exfat_structure structures[] =
171 FS_OBJECT(1, rootdir)
175 static off_t write_structure(int fd, struct exfat_structure* structure,
178 off_t alignment = structure->get_alignment();
179 off_t base = ROUND_UP(current, alignment);
181 if (lseek(fd, base, SEEK_SET) == (off_t) -1)
183 exfat_error("seek to %"PRIu64" failed", base);
186 if (structure->order > 0)
188 int rc = structure->write_data(base, fd);
191 exfat_error("%s creation failed: %s", structure->name,
197 return base + structure->get_size();
200 static int write_structures(int fd)
210 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
212 current = write_structure(fd, &structures[i], current);
213 if (current == (off_t) -1)
215 remainder += structures[i].order;
218 while (remainder > 0);
222 static int get_spc_bits(int user_defined, off_t volume_size)
224 if (user_defined != -1)
227 if (volume_size < 256ull * 1024 * 1024)
229 else if (volume_size < 32ull * 1024 * 1024 * 1024)
230 return 6; /* 32 KB */
232 return 8; /* 128 KB */
235 static int set_volume_label(int fd, const char* volume_label)
237 le16_t tmp[EXFAT_ENAME_MAX + 1];
239 if (volume_label == NULL)
242 memset(tmp, 0, sizeof(tmp));
243 if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
244 strlen(volume_label)) != 0)
249 memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
250 label_entry.length = utf16_length(tmp);
251 label_entry.type |= EXFAT_ENTRY_VALID;
255 static uint32_t get_volume_serial(uint32_t user_defined)
259 if (user_defined != 0)
262 if (gettimeofday(&now, NULL) != 0)
264 return (now.tv_sec << 20) | now.tv_usec;
267 static int mkfs(const char* spec, int sector_bits, int spc_bits,
268 const char* volume_label, uint32_t volume_serial, int first_sector)
272 char spec_abs[PATH_MAX];
274 if (realpath(spec, spec_abs) == NULL)
276 exfat_error("failed to get absolute path for `%s'", spec);
280 fd = open(spec_abs, O_RDWR);
283 exfat_error("failed to open special file `%s'", spec_abs);
287 volume_size = lseek(fd, 0, SEEK_END);
288 if (volume_size == (off_t) -1)
291 exfat_error("seek failed");
294 spc_bits = get_spc_bits(spc_bits, volume_size);
296 if (set_volume_label(fd, volume_label) != 0)
302 volume_serial = get_volume_serial(volume_serial);
303 if (volume_serial == 0)
306 exfat_error("failed to get current time to form volume id");
310 if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
317 printf("Creating... %2u%%", 0);
319 if (erase_device(fd) != 0)
324 if (write_structures(fd) != 0)
329 puts("\b\b\b\bdone.");
331 printf("Flushing... ");
336 exfat_error("fsync failed for `%s'", spec_abs);
342 exfat_error("close failed for `%s'", spec_abs);
345 printf("File system created successfully.\n");
349 static int logarithm2(int n)
353 for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
359 static void usage(const char* prog)
361 fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
362 "[-p partition-first-sector] "
363 "[-s sectors-per-cluster] [-v] <device>\n", prog);
367 int main(int argc, char* argv[])
369 const char* spec = NULL;
372 const char* volume_label = NULL;
373 uint32_t volume_serial = 0;
374 int first_sector = 0;
376 printf("mkexfatfs %u.%u.%u\n",
377 EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
379 for (pp = argv + 1; *pp; pp++)
381 if (strcmp(*pp, "-s") == 0)
386 spc_bits = logarithm2(atoi(*pp));
389 exfat_error("invalid option value: `%s'", *pp);
393 else if (strcmp(*pp, "-n") == 0)
399 /* TODO check length */
401 else if (strcmp(*pp, "-i") == 0)
406 volume_serial = strtol(*pp, NULL, 16);
408 else if (strcmp(*pp, "-p") == 0)
413 first_sector = atoi(*pp);
415 else if (strcmp(*pp, "-v") == 0)
417 puts("Copyright (C) 2010 Andrew Nayenko");
420 else if (spec == NULL)
428 return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);