3 Creates exFAT file system.
5 Copyright (C) 2009, 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 block_bits, int bpc_bits,
55 uint32_t volume_serial)
57 uint32_t clusters_max = (volume_size >> block_bits >> bpc_bits);
58 uint32_t fat_blocks = DIV_ROUND_UP(clusters_max * 4, 1 << block_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.block_start = cpu_to_le64(0); /* FIXME */
67 sb.block_count = cpu_to_le64(volume_size >> block_bits);
68 sb.fat_block_start = cpu_to_le32(128); /* FIXME */
69 sb.fat_block_count = cpu_to_le32(ROUND_UP(
70 le32_to_cpu(sb.fat_block_start) + fat_blocks, 1 << bpc_bits) -
71 le32_to_cpu(sb.fat_block_start));
72 /* cluster_block_start will be set later */
73 sb.cluster_count = cpu_to_le32(clusters_max -
74 ((le32_to_cpu(sb.fat_block_start) +
75 le32_to_cpu(sb.fat_block_count)) >> bpc_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.block_bits = block_bits;
82 sb.bpc_bits = bpc_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 exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
97 static int erase_device(int fd)
99 uint64_t erase_blocks = (uint64_t)
100 le32_to_cpu(sb.fat_block_start) +
101 le32_to_cpu(sb.fat_block_count) +
102 DIV_ROUND_UP(cbm_size(), 1 << sb.block_bits) +
103 DIV_ROUND_UP(uct_size(), 1 << sb.block_bits) +
104 DIV_ROUND_UP(rootdir_size(), 1 << sb.block_bits);
108 if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
110 exfat_error("seek failed");
114 block = malloc(BLOCK_SIZE(sb));
117 exfat_error("failed to allocate erase block");
120 memset(block, 0, BLOCK_SIZE(sb));
122 for (i = 0; i < erase_blocks; i++)
124 if (write(fd, block, BLOCK_SIZE(sb)) == -1)
127 exfat_error("failed to erase block %"PRIu64, i);
130 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
132 printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
142 * - Volume Boot Record (VBR)
143 * - Main Boot Sector (MBR)
144 * - Main Extended Boot Sectors (MEBS)
148 * - Volume Boot Record copy
149 * - File Allocation Table (FAT)
155 #define FS_OBJECT(order, name) \
156 {#name, order, name##_alignment, name##_size, name##_write}
157 static struct exfat_structure structures[] =
164 FS_OBJECT(1, rootdir)
168 static off_t write_structure(int fd, struct exfat_structure* structure,
171 off_t alignment = structure->get_alignment();
172 off_t base = ROUND_UP(current, alignment);
174 if (lseek(fd, base, SEEK_SET) == (off_t) -1)
176 exfat_error("seek to %"PRIu64" failed", base);
179 if (structure->order > 0)
181 int rc = structure->write_data(base, fd);
184 exfat_error("%s creation failed: %s", structure->name,
190 return base + structure->get_size();
193 static int write_structures(int fd)
203 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
205 current = write_structure(fd, &structures[i], current);
206 if (current == (off_t) -1)
208 remainder += structures[i].order;
211 while (remainder > 0);
215 static int get_bpc_bits(int user_defined, off_t volume_size)
217 if (user_defined != -1)
220 if (volume_size < 256ull * 1024 * 1024)
222 else if (volume_size < 32ull * 1024 * 1024 * 1024)
223 return 6; /* 32 KB */
225 return 8; /* 128 KB */
228 static int set_volume_label(int fd, const char* volume_label)
230 le16_t tmp[EXFAT_ENAME_MAX + 1];
232 if (volume_label == NULL)
235 memset(tmp, 0, sizeof(tmp));
236 if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
237 strlen(volume_label)) != 0)
242 memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
243 label_entry.length = utf16_length(tmp);
244 label_entry.type |= EXFAT_ENTRY_VALID;
248 static uint32_t get_volume_serial(uint32_t user_defined)
252 if (user_defined != 0)
255 if (gettimeofday(&now, NULL) != 0)
257 return (now.tv_sec << 20) | now.tv_usec;
260 static int mkfs(const char* spec, int block_bits, int bpc_bits,
261 const char* volume_label, uint32_t volume_serial)
265 char spec_abs[PATH_MAX];
267 if (realpath(spec, spec_abs) == NULL)
269 exfat_error("failed to get absolute path for `%s'", spec);
273 fd = open(spec_abs, O_RDWR);
276 exfat_error("failed to open special file `%s'", spec_abs);
280 volume_size = lseek(fd, 0, SEEK_END);
281 if (volume_size == (off_t) -1)
284 exfat_error("seek failed");
287 bpc_bits = get_bpc_bits(bpc_bits, volume_size);
289 if (set_volume_label(fd, volume_label) != 0)
295 volume_serial = get_volume_serial(volume_serial);
296 if (volume_serial == 0)
299 exfat_error("failed to get current time to form volume id");
303 init_sb(volume_size, block_bits, bpc_bits, volume_serial);
305 printf("Creating... %2u%%", 0);
307 if (erase_device(fd) != 0)
312 if (write_structures(fd) != 0)
317 puts("\b\b\b\bdone.");
319 printf("Flushing... ");
324 exfat_error("fsync failed for `%s'", spec_abs);
330 exfat_error("close failed for `%s'", spec_abs);
333 printf("File system created successfully.\n");
337 static int logarithm2(int n)
341 for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
347 static void usage(const char* prog)
349 fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
350 "[-s sectors-per-cluster] <device>\n", prog);
354 int main(int argc, char* argv[])
356 const char* spec = NULL;
359 const char* volume_label = NULL;
360 uint32_t volume_serial = 0;
362 for (pp = argv + 1; *pp; pp++)
364 if (strcmp(*pp, "-s") == 0)
369 bpc_bits = logarithm2(atoi(*pp));
372 exfat_error("invalid option value: `%s'", *pp);
376 else if (strcmp(*pp, "-n") == 0)
382 /* TODO check length */
384 else if (strcmp(*pp, "-i") == 0)
389 volume_serial = strtol(*pp, NULL, 16);
391 else if (**pp == '-')
393 exfat_error("unrecognized option `%s'", *pp);
402 printf("mkexfatfs %u.%u.%u\n",
403 EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
405 return mkfs(spec, 9, bpc_bits, volume_label, volume_serial);