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)
110 block_size = sysconf(_SC_PAGESIZE);
114 erase_size = ((uint64_t)
115 le32_to_cpu(sb.fat_sector_start) +
116 le32_to_cpu(sb.fat_sector_count)) * SECTOR_SIZE(sb);
117 erase_size = ROUND_UP(erase_size, cbm_alignment());
118 erase_size += cbm_size();
119 erase_size = ROUND_UP(erase_size, uct_alignment());
120 erase_size += uct_size();
121 erase_size = ROUND_UP(erase_size, rootdir_alignment());
122 erase_size += rootdir_size();
124 erase_blocks = DIV_ROUND_UP(erase_size, block_size);
126 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
128 exfat_error("seek failed");
132 block = malloc(block_size);
135 exfat_error("failed to allocate erase block");
138 memset(block, 0, block_size);
140 for (i = 0; i < erase_blocks; i++)
142 if (write(fd, block, block_size) == -1)
145 exfat_error("failed to erase block %"PRIu64, i);
148 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
150 printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
160 * - Volume Boot Record (VBR)
161 * - Main Boot Sector (MBR)
162 * - Main Extended Boot Sectors (MEBS)
166 * - Volume Boot Record copy
167 * - File Allocation Table (FAT)
173 #define FS_OBJECT(order, name) \
174 {#name, order, name##_alignment, name##_size, name##_write}
175 static struct exfat_structure structures[] =
182 FS_OBJECT(1, rootdir)
186 static off_t write_structure(int fd, struct exfat_structure* structure,
189 off_t alignment = structure->get_alignment();
190 off_t base = ROUND_UP(current, alignment);
192 if (lseek(fd, base, SEEK_SET) == (off_t) -1)
194 exfat_error("seek to %"PRIu64" failed", base);
197 if (structure->order > 0)
199 int rc = structure->write_data(base, fd);
202 exfat_error("%s creation failed: %s", structure->name,
208 return base + structure->get_size();
211 static int write_structures(int fd)
221 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
223 current = write_structure(fd, &structures[i], current);
224 if (current == (off_t) -1)
226 remainder += structures[i].order;
229 while (remainder > 0);
233 static int get_spc_bits(int user_defined, off_t volume_size)
235 if (user_defined != -1)
238 if (volume_size < 256ull * 1024 * 1024)
240 else if (volume_size < 32ull * 1024 * 1024 * 1024)
241 return 6; /* 32 KB */
243 return 8; /* 128 KB */
246 static int set_volume_label(int fd, const char* volume_label)
248 le16_t tmp[EXFAT_ENAME_MAX + 1];
250 if (volume_label == NULL)
253 memset(tmp, 0, sizeof(tmp));
254 if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
255 strlen(volume_label)) != 0)
260 memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
261 label_entry.length = utf16_length(tmp);
262 label_entry.type |= EXFAT_ENTRY_VALID;
266 static uint32_t get_volume_serial(uint32_t user_defined)
270 if (user_defined != 0)
273 if (gettimeofday(&now, NULL) != 0)
275 return (now.tv_sec << 20) | now.tv_usec;
278 static int mkfs(const char* spec, int sector_bits, int spc_bits,
279 const char* volume_label, uint32_t volume_serial, int first_sector)
284 fd = exfat_open(spec, 0);
288 volume_size = lseek(fd, 0, SEEK_END);
289 if (volume_size == (off_t) -1)
292 exfat_error("seek failed");
295 spc_bits = get_spc_bits(spc_bits, volume_size);
297 if (set_volume_label(fd, volume_label) != 0)
303 volume_serial = get_volume_serial(volume_serial);
304 if (volume_serial == 0)
307 exfat_error("failed to get current time to form volume id");
311 if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
318 printf("Creating... %2u%%", 0);
320 if (erase_device(fd) != 0)
325 if (write_structures(fd) != 0)
330 puts("\b\b\b\bdone.");
332 printf("Flushing... ");
337 exfat_error("fsync failed");
343 exfat_error("close failed");
346 printf("File system created successfully.\n");
350 static int logarithm2(int n)
354 for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
360 static void usage(const char* prog)
362 fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
363 "[-p partition-first-sector] "
364 "[-s sectors-per-cluster] [-v] <device>\n", prog);
368 int main(int argc, char* argv[])
370 const char* spec = NULL;
373 const char* volume_label = NULL;
374 uint32_t volume_serial = 0;
375 int first_sector = 0;
377 printf("mkexfatfs %u.%u.%u\n",
378 EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
380 for (pp = argv + 1; *pp; pp++)
382 if (strcmp(*pp, "-s") == 0)
387 spc_bits = logarithm2(atoi(*pp));
390 exfat_error("invalid option value: `%s'", *pp);
394 else if (strcmp(*pp, "-n") == 0)
400 /* TODO check length */
402 else if (strcmp(*pp, "-i") == 0)
407 volume_serial = strtol(*pp, NULL, 16);
409 else if (strcmp(*pp, "-p") == 0)
414 first_sector = atoi(*pp);
416 else if (strcmp(*pp, "-v") == 0)
418 puts("Copyright (C) 2010 Andrew Nayenko");
421 else if (spec == NULL)
429 return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);