OSDN Git Service

Unify CLI help syntax.
[android-x86/external-exfat.git] / mkfs / main.c
1 /*
2         main.c (15.08.10)
3         Creates exFAT file system.
4
5         Copyright (C) 2009, 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 block_bits, int bpc_bits,
55                 uint32_t volume_serial)
56 {
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;
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.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);
78         sb.version.major = 1;
79         sb.version.minor = 0;
80         sb.volume_state = cpu_to_le16(0);
81         sb.block_bits = block_bits;
82         sb.bpc_bits = bpc_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         exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
93                         allocated_clusters);
94         return 0;
95 }
96
97 static int erase_device(int fd)
98 {
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);
105         uint64_t i;
106         void* block;
107
108         if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
109         {
110                 exfat_error("seek failed");
111                 return 1;
112         }
113
114         block = malloc(BLOCK_SIZE(sb));
115         if (block == NULL)
116         {
117                 exfat_error("failed to allocate erase block");
118                 return 1;
119         }
120         memset(block, 0, BLOCK_SIZE(sb));
121
122         for (i = 0; i < erase_blocks; i++)
123         {
124                 if (write(fd, block, BLOCK_SIZE(sb)) == -1)
125                 {
126                         free(block);
127                         exfat_error("failed to erase block %"PRIu64, i);
128                         return 1;
129                 }
130                 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
131                 {
132                         printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
133                         fflush(stdout);
134                 }
135         }
136         free(block);
137         return 0;
138 }
139
140 /*
141  * exFAT layout:
142  * - Volume Boot Record (VBR)
143  *   - Main Boot Sector (MBR)
144  *   - Main Extended Boot Sectors (MEBS)
145  *   - OEM Parameters
146  *   - Reserved sector
147  *   - Checksum sector
148  * - Volume Boot Record copy
149  * - File Allocation Table (FAT)
150  * - Clusters heap
151  *   - Clusters bitmap
152  *   - Upper case table
153  *   - Root directory
154  */
155 #define FS_OBJECT(order, name) \
156         {#name, order, name##_alignment, name##_size, name##_write}
157 static struct exfat_structure structures[] =
158 {
159         FS_OBJECT(3, vbr),
160         FS_OBJECT(3, vbr),
161         FS_OBJECT(2, fat),
162         FS_OBJECT(1, cbm),
163         FS_OBJECT(1, uct),
164         FS_OBJECT(1, rootdir)
165 };
166 #undef FS_OBJECT
167
168 static off_t write_structure(int fd, struct exfat_structure* structure,
169                 off_t current)
170 {
171         off_t alignment = structure->get_alignment();
172         off_t base = ROUND_UP(current, alignment);
173
174         if (lseek(fd, base, SEEK_SET) == (off_t) -1)
175         {
176                 exfat_error("seek to %"PRIu64" failed", base);
177                 return -1;
178         }
179         if (structure->order > 0)
180         {
181                 int rc = structure->write_data(base, fd);
182                 if (rc != 0)
183                 {
184                         exfat_error("%s creation failed: %s", structure->name,
185                                         strerror(rc));
186                         return -1;
187                 }
188                 structure->order--;
189         }
190         return base + structure->get_size();
191 }
192
193 static int write_structures(int fd)
194 {
195         off_t current;
196         size_t i;
197         int remainder;
198
199         do
200         {
201                 current = 0;
202                 remainder = 0;
203                 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
204                 {
205                         current = write_structure(fd, &structures[i], current);
206                         if (current == (off_t) -1)
207                                 return 1;
208                         remainder += structures[i].order;
209                 }
210         }
211         while (remainder > 0);
212         return 0;
213 }
214
215 static int get_bpc_bits(int user_defined, off_t volume_size)
216 {
217         if (user_defined != -1)
218                 return user_defined;
219
220         if (volume_size < 256ull * 1024 * 1024)
221                 return 3;       /* 4 KB */
222         else if (volume_size < 32ull * 1024 * 1024 * 1024)
223                 return 6;       /* 32 KB */
224         else
225                 return 8;       /* 128 KB */
226 }
227
228 static int set_volume_label(int fd, const char* volume_label)
229 {
230         le16_t tmp[EXFAT_ENAME_MAX + 1];
231
232         if (volume_label == NULL)
233                 return 0;
234
235         memset(tmp, 0, sizeof(tmp));
236         if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
237                                 strlen(volume_label)) != 0)
238         {
239                 close(fd);
240                 return 1;
241         }
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;
245         return 0;
246 }
247
248 static uint32_t get_volume_serial(uint32_t user_defined)
249 {
250         struct timeval now;
251
252         if (user_defined != 0)
253                 return user_defined;
254
255         if (gettimeofday(&now, NULL) != 0)
256                 return 0;
257         return (now.tv_sec << 20) | now.tv_usec;
258 }
259
260 static int mkfs(const char* spec, int block_bits, int bpc_bits,
261                 const char* volume_label, uint32_t volume_serial)
262 {
263         int fd;
264         off_t volume_size;
265         char spec_abs[PATH_MAX];
266
267         if (realpath(spec, spec_abs) == NULL)
268         {
269                 exfat_error("failed to get absolute path for `%s'", spec);
270                 return 1;
271         }
272
273         fd = open(spec_abs, O_RDWR);
274         if (fd < 0)
275         {
276                 exfat_error("failed to open special file `%s'", spec_abs);
277                 return 1;
278         }
279
280         volume_size = lseek(fd, 0, SEEK_END);
281         if (volume_size == (off_t) -1)
282         {
283                 close(fd);
284                 exfat_error("seek failed");
285                 return 1;
286         }
287         bpc_bits = get_bpc_bits(bpc_bits, volume_size);
288
289         if (set_volume_label(fd, volume_label) != 0)
290         {
291                 close(fd);
292                 return 1;
293         }
294
295         volume_serial = get_volume_serial(volume_serial);
296         if (volume_serial == 0)
297         {
298                 close(fd);
299                 exfat_error("failed to get current time to form volume id");
300                 return 1;
301         }
302
303         init_sb(volume_size, block_bits, bpc_bits, volume_serial);
304
305         printf("Creating... %2u%%", 0);
306         fflush(stdout);
307         if (erase_device(fd) != 0)
308         {
309                 close(fd);
310                 return 1;
311         }
312         if (write_structures(fd) != 0)
313         {
314                 close(fd);
315                 return 1;
316         }
317         puts("\b\b\b\bdone.");
318
319         printf("Flushing... ");
320         fflush(stdout);
321         if (fsync(fd) < 0)
322         {
323                 close(fd);
324                 exfat_error("fsync failed for `%s'", spec_abs);
325                 return 1;
326         }
327         puts("done.");
328         if (close(fd) < 0)
329         {
330                 exfat_error("close failed for `%s'", spec_abs);
331                 return 1;
332         }
333         printf("File system created successfully.\n");
334         return 0;
335 }
336
337 static int logarithm2(int n)
338 {
339         int i;
340
341         for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
342                 if ((1 << i) == n)
343                         return i;
344         return -1;
345 }
346
347 static void usage(const char* prog)
348 {
349         fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
350                         "[-s sectors-per-cluster] <device>\n", prog);
351         exit(1);
352 }
353
354 int main(int argc, char* argv[])
355 {
356         const char* spec = NULL;
357         char** pp;
358         int bpc_bits = -1;
359         const char* volume_label = NULL;
360         uint32_t volume_serial = 0;
361
362         for (pp = argv + 1; *pp; pp++)
363         {
364                 if (strcmp(*pp, "-s") == 0)
365                 {
366                         pp++;
367                         if (*pp == NULL)
368                                 usage(argv[0]);
369                         bpc_bits = logarithm2(atoi(*pp));
370                         if (bpc_bits < 0)
371                         {
372                                 exfat_error("invalid option value: `%s'", *pp);
373                                 return 1;
374                         }
375                 }
376                 else if (strcmp(*pp, "-n") == 0)
377                 {
378                         pp++;
379                         if (*pp == NULL)
380                                 usage(argv[0]);
381                         volume_label = *pp;
382                         /* TODO check length */
383                 }
384                 else if (strcmp(*pp, "-i") == 0)
385                 {
386                         pp++;
387                         if (*pp == NULL)
388                                 usage(argv[0]);
389                         volume_serial = strtol(*pp, NULL, 16);
390                 }
391                 else if (**pp == '-')
392                 {
393                         exfat_error("unrecognized option `%s'", *pp);
394                         return 1;
395                 }
396                 else
397                         spec = *pp;
398         }
399         if (spec == NULL)
400                 usage(argv[0]);
401
402         printf("mkexfatfs %u.%u.%u\n",
403                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
404
405         return mkfs(spec, 9, bpc_bits, volume_label, volume_serial);
406 }