OSDN Git Service

Update copyright years.
[android-x86/external-exfat.git] / mkfs / main.c
1 /*
2         main.c (15.08.10)
3         Creates exFAT file system.
4
5         Copyright (C) 2011, 2012  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 get_spc_bits(int sector_bits, int user_defined, off_t volume_size)
53 {
54         int i;
55
56         if (user_defined != -1)
57                 return user_defined;
58
59         if (volume_size < 256ull * 1024 * 1024)
60                 return MAX(0, 12 - sector_bits);        /* 4 KB */
61         if (volume_size < 32ull * 1024 * 1024 * 1024)
62                 return MAX(0, 15 - sector_bits);        /* 32 KB */
63
64         for (i = 17; ; i++)                                             /* 128 KB or more */
65                 if (DIV_ROUND_UP(volume_size, 1 << i) <= EXFAT_LAST_DATA_CLUSTER)
66                         return MAX(0, i - sector_bits);
67 }
68
69 static int init_sb(off_t volume_size, int sector_bits, int spc_bits,
70                 uint32_t volume_serial, int first_sector)
71 {
72         uint32_t clusters_max;
73         uint32_t fat_sectors;
74         uint32_t allocated_clusters;
75
76         if ((volume_size >> sector_bits >> spc_bits) > EXFAT_LAST_DATA_CLUSTER)
77         {
78                 struct exfat_human_bytes chb, vhb;
79
80                 exfat_humanize_bytes(1 << sector_bits << spc_bits, &chb);
81                 exfat_humanize_bytes(volume_size, &vhb);
82                 exfat_error("cluster size %"PRIu64" %s is too small for "
83                                 "%"PRIu64" %s volume, try -s %d",
84                                 chb.value, chb.unit,
85                                 vhb.value, vhb.unit,
86                                 1 << get_spc_bits(sector_bits, -1, volume_size));
87                 return 1;
88         }
89
90         clusters_max = (volume_size >> sector_bits >> spc_bits);
91         fat_sectors = DIV_ROUND_UP((off_t) clusters_max * sizeof(cluster_t),
92                         1 << sector_bits);
93
94         memset(&sb, 0, sizeof(struct exfat_super_block));
95         sb.jump[0] = 0xeb;
96         sb.jump[1] = 0x76;
97         sb.jump[2] = 0x90;
98         memcpy(sb.oem_name, "EXFAT   ", sizeof(sb.oem_name));
99         sb.sector_start = cpu_to_le64(first_sector);
100         sb.sector_count = cpu_to_le64(volume_size >> sector_bits);
101         sb.fat_sector_start = cpu_to_le32(128); /* FIXME */
102         sb.fat_sector_count = cpu_to_le32(ROUND_UP(
103                         le32_to_cpu(sb.fat_sector_start) + fat_sectors, 1 << spc_bits) -
104                         le32_to_cpu(sb.fat_sector_start));
105         /* cluster_sector_start will be set later */
106         sb.cluster_count = cpu_to_le32(clusters_max -
107                         ((le32_to_cpu(sb.fat_sector_start) +
108                           le32_to_cpu(sb.fat_sector_count)) >> spc_bits));
109         /* rootdir_cluster will be set later */
110         sb.volume_serial = cpu_to_le32(volume_serial);
111         sb.version.major = 1;
112         sb.version.minor = 0;
113         sb.volume_state = cpu_to_le16(0);
114         sb.sector_bits = sector_bits;
115         sb.spc_bits = spc_bits;
116         sb.fat_count = 1;
117         sb.drive_no = 0x80;
118         sb.allocated_percent = 0;
119         sb.boot_signature = cpu_to_le16(0xaa55);
120
121         allocated_clusters =
122                         DIV_ROUND_UP(cbm_size(), CLUSTER_SIZE(sb)) +
123                         DIV_ROUND_UP(uct_size(), CLUSTER_SIZE(sb)) +
124                         DIV_ROUND_UP(rootdir_size(), CLUSTER_SIZE(sb));
125         if (clusters_max < ((le32_to_cpu(sb.fat_sector_start) +
126                         le32_to_cpu(sb.fat_sector_count)) >> spc_bits) +
127                         allocated_clusters)
128         {
129                 exfat_error("too small volume (%"PRIu64" bytes)", volume_size);
130                 return 1;
131         }
132         exfat_print_info(&sb, le32_to_cpu(sb.cluster_count) -
133                         allocated_clusters);
134         return 0;
135 }
136
137 static int erase_device(struct exfat_dev* dev)
138 {
139         off_t erase_size;
140         off_t erase_blocks;
141         long block_size;
142         void* block;
143         off_t i;
144
145         block_size = sysconf(_SC_PAGESIZE);
146         if (block_size < 1)
147                 block_size = 0x1000;
148
149         erase_size = ((uint64_t)
150                         le32_to_cpu(sb.fat_sector_start) +
151                         le32_to_cpu(sb.fat_sector_count)) * SECTOR_SIZE(sb);
152         erase_size = ROUND_UP(erase_size, cbm_alignment());
153         erase_size += cbm_size();
154         erase_size = ROUND_UP(erase_size, uct_alignment());
155         erase_size += uct_size();
156         erase_size = ROUND_UP(erase_size, rootdir_alignment());
157         erase_size += rootdir_size();
158
159         erase_blocks = DIV_ROUND_UP(erase_size, block_size);
160
161         if (exfat_seek(dev, 0, SEEK_SET) == (off_t) -1)
162         {
163                 exfat_error("seek failed");
164                 return 1;
165         }
166
167         block = malloc(block_size);
168         if (block == NULL)
169         {
170                 exfat_error("failed to allocate erase block");
171                 return 1;
172         }
173         memset(block, 0, block_size);
174
175         for (i = 0; i < erase_blocks; i++)
176         {
177                 if (exfat_write(dev, block, block_size) < 0)
178                 {
179                         free(block);
180                         exfat_error("failed to erase block %"PRIu64, i);
181                         return 1;
182                 }
183                 if (i * 100 / erase_blocks != (i + 1) * 100 / erase_blocks)
184                 {
185                         printf("\b\b\b%2"PRIu64"%%", (i + 1) * 100 / erase_blocks);
186                         fflush(stdout);
187                 }
188         }
189         free(block);
190         return 0;
191 }
192
193 /*
194  * exFAT layout:
195  * - Volume Boot Record (VBR)
196  *   - Main Boot Sector (MBR)
197  *   - Main Extended Boot Sectors (MEBS)
198  *   - OEM Parameters
199  *   - Reserved sector
200  *   - Checksum sector
201  * - Volume Boot Record copy
202  * - File Allocation Table (FAT)
203  * - Clusters heap
204  *   - Clusters bitmap
205  *   - Upper case table
206  *   - Root directory
207  */
208 #define FS_OBJECT(order, name) \
209         {#name, order, name##_alignment, name##_size, name##_write}
210 static struct exfat_structure structures[] =
211 {
212         FS_OBJECT(3, vbr),
213         FS_OBJECT(3, vbr),
214         FS_OBJECT(2, fat),
215         FS_OBJECT(1, cbm),
216         FS_OBJECT(1, uct),
217         FS_OBJECT(1, rootdir)
218 };
219 #undef FS_OBJECT
220
221 static off_t write_structure(struct exfat_dev* dev,
222                 struct exfat_structure* structure, off_t current)
223 {
224         off_t alignment = structure->get_alignment();
225         off_t base = ROUND_UP(current, alignment);
226
227         if (exfat_seek(dev, base, SEEK_SET) == (off_t) -1)
228         {
229                 exfat_error("seek to %"PRIu64" failed", base);
230                 return -1;
231         }
232         if (structure->order > 0)
233         {
234                 int rc = structure->write_data(dev, base);
235                 if (rc != 0)
236                 {
237                         exfat_error("%s creation failed: %s", structure->name,
238                                         strerror(rc));
239                         return -1;
240                 }
241                 structure->order--;
242         }
243         return base + structure->get_size();
244 }
245
246 static int write_structures(struct exfat_dev* dev)
247 {
248         off_t current;
249         size_t i;
250         int remainder;
251
252         do
253         {
254                 current = 0;
255                 remainder = 0;
256                 for (i = 0; i < sizeof(structures) / sizeof(structures[0]); i++)
257                 {
258                         current = write_structure(dev, &structures[i], current);
259                         if (current == (off_t) -1)
260                                 return 1;
261                         remainder += structures[i].order;
262                 }
263         }
264         while (remainder > 0);
265         return 0;
266 }
267
268 static int set_volume_label(const char* volume_label)
269 {
270         le16_t tmp[EXFAT_ENAME_MAX + 1];
271
272         if (volume_label == NULL)
273                 return 0;
274
275         memset(tmp, 0, sizeof(tmp));
276         if (utf8_to_utf16(tmp, volume_label, EXFAT_ENAME_MAX,
277                                 strlen(volume_label)) != 0)
278                 return 1;
279         memcpy(label_entry.name, tmp, EXFAT_ENAME_MAX * sizeof(le16_t));
280         label_entry.length = utf16_length(tmp);
281         label_entry.type |= EXFAT_ENTRY_VALID;
282         return 0;
283 }
284
285 static uint32_t get_volume_serial(uint32_t user_defined)
286 {
287         struct timeval now;
288
289         if (user_defined != 0)
290                 return user_defined;
291
292         if (gettimeofday(&now, NULL) != 0)
293                 return 0;
294         return (now.tv_sec << 20) | now.tv_usec;
295 }
296
297 static int mkfs(const char* spec, int sector_bits, int spc_bits,
298                 const char* volume_label, uint32_t volume_serial, int first_sector)
299 {
300         struct exfat_dev* dev;
301         off_t volume_size;
302
303         dev = exfat_open(spec, 0);
304         if (dev == NULL)
305                 return 1;
306
307         volume_size = exfat_seek(dev, 0, SEEK_END);
308         if (volume_size == (off_t) -1)
309         {
310                 exfat_close(dev);
311                 exfat_error("seek failed");
312                 return 1;
313         }
314         spc_bits = get_spc_bits(sector_bits, spc_bits, volume_size);
315
316         if (set_volume_label(volume_label) != 0)
317         {
318                 exfat_close(dev);
319                 return 1;
320         }
321
322         volume_serial = get_volume_serial(volume_serial);
323         if (volume_serial == 0)
324         {
325                 exfat_close(dev);
326                 exfat_error("failed to get current time to form volume id");
327                 return 1;
328         }
329
330         if (init_sb(volume_size, sector_bits, spc_bits, volume_serial,
331                                 first_sector) != 0)
332         {
333                 exfat_close(dev);
334                 return 1;
335         }
336
337         printf("Creating... %2u%%", 0);
338         fflush(stdout);
339         if (erase_device(dev) != 0)
340         {
341                 exfat_close(dev);
342                 return 1;
343         }
344         if (write_structures(dev) != 0)
345         {
346                 exfat_close(dev);
347                 return 1;
348         }
349         puts("\b\b\b\bdone.");
350
351         printf("Flushing... ");
352         fflush(stdout);
353         if (exfat_fsync(dev) != 0)
354         {
355                 exfat_close(dev);
356                 return 1;
357         }
358         puts("done.");
359         if (exfat_close(dev) != 0)
360                 return 1;
361         printf("File system created successfully.\n");
362         return 0;
363 }
364
365 static int logarithm2(int n)
366 {
367         int i;
368
369         for (i = 0; i < sizeof(int) * CHAR_BIT - 1; i++)
370                 if ((1 << i) == n)
371                         return i;
372         return -1;
373 }
374
375 static void usage(const char* prog)
376 {
377         fprintf(stderr, "Usage: %s [-i volume-id] [-n label] "
378                         "[-p partition-first-sector] "
379                         "[-s sectors-per-cluster] [-v] <device>\n", prog);
380         exit(1);
381 }
382
383 int main(int argc, char* argv[])
384 {
385         const char* spec = NULL;
386         char** pp;
387         int spc_bits = -1;
388         const char* volume_label = NULL;
389         uint32_t volume_serial = 0;
390         int first_sector = 0;
391
392         printf("mkexfatfs %u.%u.%u\n",
393                         EXFAT_VERSION_MAJOR, EXFAT_VERSION_MINOR, EXFAT_VERSION_PATCH);
394
395         for (pp = argv + 1; *pp; pp++)
396         {
397                 if (strcmp(*pp, "-s") == 0)
398                 {
399                         pp++;
400                         if (*pp == NULL)
401                                 usage(argv[0]);
402                         spc_bits = logarithm2(atoi(*pp));
403                         if (spc_bits < 0)
404                         {
405                                 exfat_error("invalid option value: `%s'", *pp);
406                                 return 1;
407                         }
408                 }
409                 else if (strcmp(*pp, "-n") == 0)
410                 {
411                         pp++;
412                         if (*pp == NULL)
413                                 usage(argv[0]);
414                         volume_label = *pp;
415                         /* TODO check length */
416                 }
417                 else if (strcmp(*pp, "-i") == 0)
418                 {
419                         pp++;
420                         if (*pp == NULL)
421                                 usage(argv[0]);
422                         volume_serial = strtol(*pp, NULL, 16);
423                 }
424                 else if (strcmp(*pp, "-p") == 0)
425                 {
426                         pp++;
427                         if (*pp == NULL)
428                                 usage(argv[0]);
429                         first_sector = atoi(*pp);
430                 }
431                 else if (strcmp(*pp, "-v") == 0)
432                 {
433                         puts("Copyright (C) 2011, 2012  Andrew Nayenko");
434                         return 0;
435                 }
436                 else if (spec == NULL)
437                         spec = *pp;
438                 else
439                         usage(argv[0]);
440         }
441         if (spec == NULL)
442                 usage(argv[0]);
443
444         return mkfs(spec, 9, spc_bits, volume_label, volume_serial, first_sector);
445 }