]> git.sven.stormbind.net Git - sven/exfat-utils.git/blob - mkfs/main.c
Merge tag 'upstream/0.9.7'
[sven/exfat-utils.git] / mkfs / main.c
1 /*
2         main.c (15.08.10)
3         Creates exFAT file system.
4
5         Copyright (C) 2010  Andrew Nayenko
6
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.
11
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.
16
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/>.
19 */
20
21 #include <sys/types.h>
22 #include <sys/time.h>
23 #include <unistd.h>
24 #include <inttypes.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <limits.h>
28 #include <errno.h>
29 #include <exfat.h>
30 #include "vbr.h"
31 #include "fat.h"
32 #include "cbm.h"
33 #include "uct.h"
34 #include "rootdir.h"
35
36 #define ROUND_UP(x, d) (DIV_ROUND_UP(x, d) * (d))
37
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};
42
43 struct exfat_structure
44 {
45         const char* name;
46         int order;
47         off_t (*get_alignment)(void);
48         off_t (*get_size)(void);
49         int (*write_data)(struct exfat_dev*, off_t);
50 };
51
52 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
53                 uint32_t volume_serial, int first_sector)
54 {
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;
58
59         memset(&sb, 0, sizeof(struct exfat_super_block));
60         sb.jump[0] = 0xeb;
61         sb.jump[1] = 0x76;
62         sb.jump[2] = 0x90;
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);
76         sb.version.major = 1;
77         sb.version.minor = 0;
78         sb.volume_state = cpu_to_le16(0);
79         sb.sector_bits = sector_bits;
80         sb.spc_bits = spc_bits;
81         sb.fat_count = 1;
82         sb.drive_no = 0x80;
83         sb.allocated_percent = 0;
84         sb.boot_signature = cpu_to_le16(0xaa55);
85
86         allocated_clusters =
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) +
92                         allocated_clusters)
93         {
94                 exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
95                 return 1;
96         }
97         exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
98                         allocated_clusters);
99         return 0;
100 }
101
102 static int erase_device(struct exfat_dev* dev)
103 {
104         off_t erase_size;
105         off_t erase_blocks;
106         long block_size;
107         void* block;
108         off_t i;
109
110         block_size = sysconf(_SC_PAGESIZE);
111         if (block_size < 1)
112                 block_size = 0x1000;
113
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();
123
124         erase_blocks = DIV_ROUND_UP(erase_size, block_size);
125
126         if (exfat_seek(dev, 0, SEEK_SET) == (off_t) -1)
127         {
128                 exfat_error("seek failed");
129                 return 1;
130         }
131
132         block = malloc(block_size);
133         if (block == NULL)
134         {
135                 exfat_error("failed to allocate erase block");
136                 return 1;
137         }
138         memset(block, 0, block_size);
139
140         for (i = 0; i < erase_blocks; i++)
141         {
142                 if (exfat_write(dev, block, block_size) < 0)
143                 {
144                         free(block);
145                         exfat_error("failed to erase block %"PRIu64, i);
146                         return 1;
147                 }
148                 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
149                 {
150                         printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
151                         fflush(stdout);
152                 }
153         }
154         free(block);
155         return 0;
156 }
157
158 /*
159  * exFAT layout:
160  * - Volume Boot Record (VBR)
161  *   - Main Boot Sector (MBR)
162  *   - Main Extended Boot Sectors (MEBS)
163  *   - OEM Parameters
164  *   - Reserved sector
165  *   - Checksum sector
166  * - Volume Boot Record copy
167  * - File Allocation Table (FAT)
168  * - Clusters heap
169  *   - Clusters bitmap
170  *   - Upper case table
171  *   - Root directory
172  */
173 #define FS_OBJECT(order, name) \
174         {#name, order, name##_alignment, name##_size, name##_write}
175 static struct exfat_structure structures[] =
176 {
177         FS_OBJECT(3, vbr),
178         FS_OBJECT(3, vbr),
179         FS_OBJECT(2, fat),
180         FS_OBJECT(1, cbm),
181         FS_OBJECT(1, uct),
182         FS_OBJECT(1, rootdir)
183 };
184 #undef FS_OBJECT
185
186 static off_t write_structure(struct exfat_dev* dev,
187                 struct exfat_structure* structure, off_t current)
188 {
189         off_t alignment = structure->get_alignment();
190         off_t base = ROUND_UP(current, alignment);
191
192         if (exfat_seek(dev, base, SEEK_SET) == (off_t) -1)
193         {
194                 exfat_error("seek to %"PRIu64" failed", base);
195                 return -1;
196         }
197         if (structure->order > 0)
198         {
199                 int rc = structure->write_data(dev, base);
200                 if (rc != 0)
201                 {
202                         exfat_error("%s creation failed: %s", structure->name,
203                                         strerror(rc));
204                         return -1;
205                 }
206                 structure->order--;
207         }
208         return base + structure->get_size();
209 }
210
211 static int write_structures(struct exfat_dev* dev)
212 {
213         off_t current;
214         size_t i;
215         int remainder;
216
217         do
218         {
219                 current = 0;
220                 remainder = 0;
221                 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
222                 {
223                         current = write_structure(dev, &structures[i], current);
224                         if (current == (off_t) -1)
225                                 return 1;
226                         remainder += structures[i].order;
227                 }
228         }
229         while (remainder > 0);
230         return 0;
231 }
232
233 static int get_spc_bits(int user_defined, off_t volume_size)
234 {
235         if (user_defined != -1)
236                 return user_defined;
237
238         if (volume_size < 256ull * 1024 * 1024)
239                 return 3;       /* 4 KB */
240         else if (volume_size < 32ull * 1024 * 1024 * 1024)
241                 return 6;       /* 32 KB */
242         else
243                 return 8;       /* 128 KB */
244 }
245
246 static int set_volume_label(const char* volume_label)
247 {
248         le16_t tmp[EXFAT_ENAME_MAX + 1];
249
250         if (volume_label == NULL)
251                 return 0;
252
253         memset(tmp, 0, sizeof(tmp));
254         if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
255                                 strlen(volume_label)) != 0)
256                 return 1;
257         memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
258         label_entry.length = utf16_length(tmp);
259         label_entry.type |= EXFAT_ENTRY_VALID;
260         return 0;
261 }
262
263 static uint32_t get_volume_serial(uint32_t user_defined)
264 {
265         struct timeval now;
266
267         if (user_defined != 0)
268                 return user_defined;
269
270         if (gettimeofday(&now, NULL) != 0)
271                 return 0;
272         return (now.tv_sec << 20) | now.tv_usec;
273 }
274
275 static int mkfs(const char* spec, int sector_bits, int spc_bits,
276                 const char* volume_label, uint32_t volume_serial, int first_sector)
277 {
278         struct exfat_dev* dev;
279         off_t volume_size;
280
281         dev = exfat_open(spec, 0);
282         if (dev == NULL)
283                 return 1;
284
285         volume_size = exfat_seek(dev, 0, SEEK_END);
286         if (volume_size == (off_t) -1)
287         {
288                 exfat_close(dev);
289                 exfat_error("seek failed");
290                 return 1;
291         }
292         spc_bits = get_spc_bits(spc_bits, volume_size);
293
294         if (set_volume_label(volume_label) != 0)
295         {
296                 exfat_close(dev);
297                 return 1;
298         }
299
300         volume_serial = get_volume_serial(volume_serial);
301         if (volume_serial == 0)
302         {
303                 exfat_close(dev);
304                 exfat_error("failed to get current time to form volume id");
305                 return 1;
306         }
307
308         if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
309                                 first_sector) != 0)
310         {
311                 exfat_close(dev);
312                 return 1;
313         }
314
315         printf("Creating... %2u%%", 0);
316         fflush(stdout);
317         if (erase_device(dev) != 0)
318         {
319                 exfat_close(dev);
320                 return 1;
321         }
322         if (write_structures(dev) != 0)
323         {
324                 exfat_close(dev);
325                 return 1;
326         }
327         puts("\b\b\b\bdone.");
328
329         printf("Flushing... ");
330         fflush(stdout);
331         if (exfat_fsync(dev) != 0)
332         {
333                 exfat_close(dev);
334                 return 1;
335         }
336         puts("done.");
337         if (exfat_close(dev) != 0)
338                 return 1;
339         printf("File system created successfully.\n");
340         return 0;
341 }
342
343 static int logarithm2(int n)
344 {
345         int i;
346
347         for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
348                 if ((1 << i) == n)
349                         return i;
350         return -1;
351 }
352
353 static void usage(const char* prog)
354 {
355         fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
356                         "[-p partition-first-sector] "
357                         "[-s sectors-per-cluster] [-v] <device>\n", prog);
358         exit(1);
359 }
360
361 int main(int argc, char* argv[])
362 {
363         const char* spec = NULL;
364         char** pp;
365         int spc_bits = -1;
366         const char* volume_label = NULL;
367         uint32_t volume_serial = 0;
368         int first_sector = 0;
369
370         printf("mkexfatfs %u.%u.%u\n",
371                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
372
373         for (pp = argv + 1; *pp; pp++)
374         {
375                 if (strcmp(*pp, "-s") == 0)
376                 {
377                         pp++;
378                         if (*pp == NULL)
379                                 usage(argv[0]);
380                         spc_bits = logarithm2(atoi(*pp));
381                         if (spc_bits < 0)
382                         {
383                                 exfat_error("invalid option value: `%s'", *pp);
384                                 return 1;
385                         }
386                 }
387                 else if (strcmp(*pp, "-n") == 0)
388                 {
389                         pp++;
390                         if (*pp == NULL)
391                                 usage(argv[0]);
392                         volume_label = *pp;
393                         /* TODO check length */
394                 }
395                 else if (strcmp(*pp, "-i") == 0)
396                 {
397                         pp++;
398                         if (*pp == NULL)
399                                 usage(argv[0]);
400                         volume_serial = strtol(*pp, NULL, 16);
401                 }
402                 else if (strcmp(*pp, "-p") == 0)
403                 {
404                         pp++;
405                         if (*pp == NULL)
406                                 usage(argv[0]);
407                         first_sector = atoi(*pp);
408                 }
409                 else if (strcmp(*pp, "-v") == 0)
410                 {
411                         puts("Copyright (C) 2010  Andrew Nayenko");
412                         return 0;
413                 }
414                 else if (spec == NULL)
415                         spec = *pp;
416                 else
417                         usage(argv[0]);
418         }
419         if (spec == NULL)
420                 usage(argv[0]);
421
422         return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);
423 }