Imported Upstream version 0.9.8
[sven/exfat-utils.git] / mkfs / mkexfat.c
1 /*
2         mkexfat.c (22.04.12)
3         FS creation engine.
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 <unistd.h>
23 #include <inttypes.h>
24 #include <stdio.h>
25 #include <string.h>
26 #include "mkexfat.h"
27
28 static int check_size(off_t volume_size)
29 {
30         const struct fs_object** pp;
31         off_t position = 0;
32
33         for (pp = objects; *pp; pp++)
34         {
35                 position = ROUND_UP(position, (*pp)->get_alignment());
36                 position += (*pp)->get_size();
37         }
38
39         if (position > volume_size)
40         {
41                 struct exfat_human_bytes vhb;
42
43                 exfat_humanize_bytes(volume_size, &vhb);
44                 exfat_error("too small device (%"PRIu64" %s)", vhb.value, vhb.unit);
45                 return 1;
46         }
47
48         return 0;
49
50 }
51
52 static int erase_object(struct exfat_dev* dev, const void* block,
53                 size_t block_size, off_t start, off_t size)
54 {
55         const off_t block_count = DIV_ROUND_UP(size, block_size);
56         off_t i;
57
58         if (exfat_seek(dev, start, SEEK_SET) == (off_t) -1)
59         {
60                 exfat_error("seek to 0x%"PRIx64" failed", start);
61                 return 1;
62         }
63         for (i = 0; i < size; i += block_size)
64         {
65                 if (exfat_write(dev, block, MIN(size - i, block_size)) < 0)
66                 {
67                         exfat_error("failed to erase block %"PRIu64"/%"PRIu64
68                                         " at 0x%"PRIx64, i + 1, block_count, start);
69                         return 1;
70                 }
71         }
72         return 0;
73 }
74
75 static int erase(struct exfat_dev* dev)
76 {
77         const struct fs_object** pp;
78         off_t position = 0;
79         const size_t block_size = 1024 * 1024;
80         void* block = malloc(block_size);
81
82         if (block == NULL)
83         {
84                 exfat_error("failed to allocate erase block of %zu bytes", block_size);
85                 return 1;
86         }
87         memset(block, 0, block_size);
88
89         for (pp = objects; *pp; pp++)
90         {
91                 position = ROUND_UP(position, (*pp)->get_alignment());
92                 if (erase_object(dev, block, block_size, position,
93                                 (*pp)->get_size()) != 0)
94                 {
95                         free(block);
96                         return 1;
97                 }
98                 position += (*pp)->get_size();
99         }
100
101         free(block);
102         return 0;
103 }
104
105 static int create(struct exfat_dev* dev)
106 {
107         const struct fs_object** pp;
108         off_t position = 0;
109
110         for (pp = objects; *pp; pp++)
111         {
112                 position = ROUND_UP(position, (*pp)->get_alignment());
113                 if (exfat_seek(dev, position, SEEK_SET) == (off_t) -1)
114                 {
115                         exfat_error("seek to 0x%"PRIx64" failed", position);
116                         return 1;
117                 }
118                 if ((*pp)->write(dev) != 0)
119                         return 1;
120                 position += (*pp)->get_size();
121         }
122         return 0;
123 }
124
125 int mkfs(struct exfat_dev* dev, off_t volume_size)
126 {
127         if (check_size(volume_size) != 0)
128                 return 1;
129
130         fputs("Creating... ", stdout);
131         fflush(stdout);
132         if (erase(dev) != 0)
133                 return 1;
134         if (create(dev) != 0)
135                 return 1;
136         puts("done.");
137
138         fputs("Flushing... ", stdout);
139         fflush(stdout);
140         if (exfat_fsync(dev) != 0)
141                 return 1;
142         puts("done.");
143
144         return 0;
145 }
146
147 off_t get_position(const struct fs_object* object)
148 {
149         const struct fs_object** pp;
150         off_t position = 0;
151
152         for (pp = objects; *pp; pp++)
153         {
154                 position = ROUND_UP(position, (*pp)->get_alignment());
155                 if (*pp == object)
156                         return position;
157                 position += (*pp)->get_size();
158         }
159         exfat_bug("unknown object");
160 }