OSDN Git Service

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