OSDN Git Service

847b6194a3ea8c5fd0b4d374da834bc8514d6c63
[android-x86/external-exfat.git] / libexfat / mount.c
1 /*
2         mount.c (22.10.09)
3         exFAT file system implementation library.
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 "exfat.h"
22 #include <string.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #define _XOPEN_SOURCE /* for tzset() in Linux */
30 #include <time.h>
31
32 static uint64_t rootdir_size(const struct exfat* ef)
33 {
34         uint64_t clusters = 0;
35         cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
36
37         while (!CLUSTER_INVALID(rootdir_cluster))
38         {
39                 clusters++;
40                 /* root directory cannot be contiguous because there is no flag
41                    to indicate this */
42                 rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
43         }
44         return clusters * CLUSTER_SIZE(*ef->sb);
45 }
46
47 static const char* get_option(const char* options, const char* option_name)
48 {
49         const char* p;
50         size_t length = strlen(option_name);
51
52         for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
53                 if ((p == options || p[-1] == ',') && p[length] == '=')
54                         return p + length + 1;
55         return NULL;
56 }
57
58 static int get_int_option(const char* options, const char* option_name,
59                 int base, int default_value)
60 {
61         const char* p = get_option(options, option_name);
62
63         if (p == NULL)
64                 return default_value;
65         return strtol(p, NULL, base);
66 }
67
68 static int match_option(const char* options, const char* option_name)
69 {
70         const char* p;
71         size_t length = strlen(option_name);
72
73         for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
74                 if ((p == options || p[-1] == ',') &&
75                                 (p[length] == ',' || p[length] == '\0'))
76                         return 1;
77         return 0;
78 }
79
80 static void parse_options(struct exfat* ef, const char* options)
81 {
82         int sys_umask = umask(0);
83         int opt_umask;
84
85         umask(sys_umask); /* restore umask */
86         opt_umask = get_int_option(options, "umask", 8, sys_umask);
87         ef->dmask = get_int_option(options, "dmask", 8, opt_umask) & 0777;
88         ef->fmask = get_int_option(options, "fmask", 8, opt_umask) & 0777;
89
90         ef->uid = get_int_option(options, "uid", 10, geteuid());
91         ef->gid = get_int_option(options, "gid", 10, getegid());
92
93         ef->ro = match_option(options, "ro");
94         ef->noatime = match_option(options, "noatime");
95 }
96
97 static int verify_vbr_checksum(void* sector, off_t sector_size, int fd)
98 {
99         uint32_t vbr_checksum;
100         int i;
101
102         exfat_read_raw(sector, sector_size, 0, fd);
103         vbr_checksum = exfat_vbr_start_checksum(sector, sector_size);
104         for (i = 1; i < 11; i++)
105         {
106                 exfat_read_raw(sector, sector_size, i * sector_size, fd);
107                 vbr_checksum = exfat_vbr_add_checksum(sector, sector_size,
108                                 vbr_checksum);
109         }
110         exfat_read_raw(sector, sector_size, i * sector_size, fd);
111         for (i = 0; i < sector_size / sizeof(vbr_checksum); i++)
112                 if (le32_to_cpu(((const le32_t*) sector)[i]) != vbr_checksum)
113                 {
114                         exfat_error("invalid VBR checksum 0x%x (expected 0x%x)",
115                                         le32_to_cpu(((const le32_t*) sector)[i]), vbr_checksum);
116                         return 1;
117                 }
118         return 0;
119 }
120
121 int exfat_mount(struct exfat* ef, const char* spec, const char* options)
122 {
123         int rc;
124         struct stat stbuf;
125
126         tzset();
127         memset(ef, 0, sizeof(struct exfat));
128
129         ef->sb = malloc(sizeof(struct exfat_super_block));
130         if (ef->sb == NULL)
131         {
132                 exfat_error("memory allocation failed");
133                 return -ENOMEM;
134         }
135         memset(ef->sb, 0, sizeof(struct exfat_super_block));
136
137         parse_options(ef, options);
138
139         ef->fd = open(spec, ef->ro ? O_RDONLY : O_RDWR);
140         if (ef->fd < 0)
141         {
142                 free(ef->sb);
143                 exfat_error("failed to open `%s'", spec);
144                 return -EIO;
145         }
146         if (fstat(ef->fd, &stbuf) != 0)
147         {
148                 close(ef->fd);
149                 free(ef->sb);
150                 exfat_error("failed to fstat `%s'", spec);
151                 return -EIO;
152         }
153         if (!S_ISBLK(stbuf.st_mode) && !S_ISREG(stbuf.st_mode))
154         {
155                 close(ef->fd);
156                 free(ef->sb);
157                 exfat_error("`%s' is neither a block device, nor a regular file",
158                                 spec);
159                 return -EIO;
160         }
161
162         exfat_read_raw(ef->sb, sizeof(struct exfat_super_block), 0, ef->fd);
163         if (memcmp(ef->sb->oem_name, "EXFAT   ", 8) != 0)
164         {
165                 close(ef->fd);
166                 free(ef->sb);
167                 exfat_error("exFAT file system is not found");
168                 return -EIO;
169         }
170         if (ef->sb->version.major != 1 || ef->sb->version.minor != 0)
171         {
172                 close(ef->fd);
173                 exfat_error("unsupported exFAT version: %hhu.%hhu",
174                                 ef->sb->version.major, ef->sb->version.minor);
175                 free(ef->sb);
176                 return -EIO;
177         }
178         if (ef->sb->fat_count != 1)
179         {
180                 close(ef->fd);
181                 free(ef->sb);
182                 exfat_error("unsupported FAT count: %hhu", ef->sb->fat_count);
183                 return -EIO;
184         }
185         /* officially exFAT supports cluster size up to 32 MB */
186         if ((int) ef->sb->sector_bits + (int) ef->sb->spc_bits > 25)
187         {
188                 close(ef->fd);
189                 free(ef->sb);
190                 exfat_error("too big cluster size: 2^%d",
191                                 (int) ef->sb->sector_bits + (int) ef->sb->spc_bits);
192                 return -EIO;
193         }
194
195         ef->zero_sector = malloc(SECTOR_SIZE(*ef->sb));
196         if (ef->zero_sector == NULL)
197         {
198                 close(ef->fd);
199                 free(ef->sb);
200                 exfat_error("failed to allocate zero sector");
201                 return -ENOMEM;
202         }
203         /* use zero_sector as a temporary buffer for VBR checksum verification */
204         if (verify_vbr_checksum(ef->zero_sector, SECTOR_SIZE(*ef->sb), ef->fd) != 0)
205         {
206                 free(ef->zero_sector);
207                 close(ef->fd);
208                 free(ef->sb);
209                 return -EIO;
210         }
211         memset(ef->zero_sector, 0, SECTOR_SIZE(*ef->sb));
212
213         ef->root = malloc(sizeof(struct exfat_node));
214         if (ef->root == NULL)
215         {
216                 free(ef->zero_sector);
217                 close(ef->fd);
218                 free(ef->sb);
219                 exfat_error("failed to allocate root node");
220                 return -ENOMEM;
221         }
222         memset(ef->root, 0, sizeof(struct exfat_node));
223         ef->root->flags = EXFAT_ATTRIB_DIR;
224         ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
225         ef->root->fptr_cluster = ef->root->start_cluster;
226         ef->root->name[0] = cpu_to_le16('\0');
227         ef->root->size = rootdir_size(ef);
228         /* exFAT does not have time attributes for the root directory */
229         ef->root->mtime = 0;
230         ef->root->atime = 0;
231         /* always keep at least 1 reference to the root node */
232         exfat_get_node(ef->root);
233
234         rc = exfat_cache_directory(ef, ef->root);
235         if (rc != 0)
236                 goto error;
237         if (ef->upcase == NULL)
238         {
239                 exfat_error("upcase table is not found");
240                 goto error;
241         }
242         if (ef->cmap.chunk == NULL)
243         {
244                 exfat_error("clusters bitmap is not found");
245                 goto error;
246         }
247
248         return 0;
249
250 error:
251         exfat_put_node(ef, ef->root);
252         exfat_reset_cache(ef);
253         free(ef->root);
254         free(ef->zero_sector);
255         close(ef->fd);
256         free(ef->sb);
257         return -EIO;
258 }
259
260 void exfat_unmount(struct exfat* ef)
261 {
262         exfat_put_node(ef, ef->root);
263         exfat_reset_cache(ef);
264         free(ef->root);
265         ef->root = NULL;
266         free(ef->zero_sector);
267         ef->zero_sector = NULL;
268         free(ef->cmap.chunk);
269         ef->cmap.chunk = NULL;
270         if (fsync(ef->fd) < 0)
271                 exfat_error("fsync failed");
272         if (close(ef->fd) < 0)
273                 exfat_error("close failed");
274         ef->fd = 0;
275         free(ef->sb);
276         ef->sb = NULL;
277         free(ef->upcase);
278         ef->upcase = NULL;
279         ef->upcase_chars = 0;
280 }