OSDN Git Service

d383b7de2f37cce2662793fdd17afaef527314cc
[android-x86/external-exfat.git] / libexfat / mount.c
1 /*
2  *  mount.c
3  *  exFAT file system implementation library.
4  *
5  *  Created by Andrew Nayenko on 22.10.09.
6  *  This software is distributed under the GNU General Public License 
7  *  version 3 or any later.
8  */
9
10 #include "exfat.h"
11 #include <string.h>
12 #include <stdlib.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <unistd.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #define _XOPEN_SOURCE /* for tzset() in Linux */
19 #include <time.h>
20
21 static uint64_t rootdir_size(const struct exfat* ef)
22 {
23         uint64_t clusters = 0;
24         cluster_t rootdir_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
25
26         while (!CLUSTER_INVALID(rootdir_cluster))
27         {
28                 clusters++;
29                 /* root directory cannot be contiguous because there is no flag
30                    to indicate this */
31                 rootdir_cluster = exfat_next_cluster(ef, ef->root, rootdir_cluster);
32         }
33         return clusters * CLUSTER_SIZE(*ef->sb);
34 }
35
36 static const char* get_option(const char* options, const char* option_name)
37 {
38         const char* p;
39         size_t length = strlen(option_name);
40
41         for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
42                 if ((p == options || p[-1] == ',') && p[length] == '=')
43                         return p + length + 1;
44         return NULL;
45 }
46
47 static int get_int_option(const char* options, const char* option_name,
48                 int base, int default_value)
49 {
50         const char* p = get_option(options, option_name);
51
52         if (p == NULL)
53                 return default_value;
54         return strtol(p, NULL, base);
55 }
56
57 static int match_option(const char* options, const char* option_name)
58 {
59         const char* p;
60         size_t length = strlen(option_name);
61
62         for (p = strstr(options, option_name); p; p = strstr(p + 1, option_name))
63                 if ((p == options || p[-1] == ',') &&
64                                 (p[length] == ',' || p[length] == '\0'))
65                         return 1;
66         return 0;
67 }
68
69 static void parse_options(struct exfat* ef, const char* options)
70 {
71         int sys_umask = umask(0);
72         int opt_umask;
73
74         umask(sys_umask); /* restore umask */
75         opt_umask = get_int_option(options, "umask", 8, sys_umask);
76         ef->dmask = get_int_option(options, "dmask", 8, opt_umask) & 0777;
77         ef->fmask = get_int_option(options, "fmask", 8, opt_umask) & 0777;
78
79         ef->uid = get_int_option(options, "uid", 10, geteuid());
80         ef->gid = get_int_option(options, "gid", 10, getegid());
81
82         ef->ro = match_option(options, "ro");
83         ef->noatime = match_option(options, "noatime");
84 }
85
86 int exfat_mount(struct exfat* ef, const char* spec, const char* options)
87 {
88         tzset();
89         memset(ef, 0, sizeof(struct exfat));
90
91         ef->sb = malloc(sizeof(struct exfat_super_block));
92         if (ef->sb == NULL)
93         {
94                 exfat_error("memory allocation failed");
95                 return -ENOMEM;
96         }
97         memset(ef->sb, 0, sizeof(struct exfat_super_block));
98
99         parse_options(ef, options);
100
101         ef->fd = open(spec, ef->ro ? O_RDONLY : O_RDWR);
102         if (ef->fd < 0)
103         {
104                 free(ef->sb);
105                 exfat_error("failed to open `%s'", spec);
106                 return -EIO;
107         }
108
109         exfat_read_raw(ef->sb, sizeof(struct exfat_super_block), 0, ef->fd);
110         if (memcmp(ef->sb->oem_name, "EXFAT   ", 8) != 0)
111         {
112                 close(ef->fd);
113                 free(ef->sb);
114                 exfat_error("exFAT file system is not found");
115                 return -EIO;
116         }
117
118         ef->zero_block = malloc(BLOCK_SIZE(*ef->sb));
119         if (ef->zero_block == NULL)
120         {
121                 close(ef->fd);
122                 free(ef->sb);
123                 exfat_error("failed to allocate zero block");
124                 return -ENOMEM;
125         }
126         memset(ef->zero_block, 0, BLOCK_SIZE(*ef->sb));
127
128         ef->root = malloc(sizeof(struct exfat_node));
129         if (ef->root == NULL)
130         {
131                 free(ef->zero_block);
132                 close(ef->fd);
133                 free(ef->sb);
134                 exfat_error("failed to allocate root node");
135                 return -ENOMEM;
136         }
137         memset(ef->root, 0, sizeof(struct exfat_node));
138         ef->root->flags = EXFAT_ATTRIB_DIR;
139         ef->root->start_cluster = le32_to_cpu(ef->sb->rootdir_cluster);
140         ef->root->fptr_cluster = ef->root->start_cluster;
141         ef->root->name[0] = cpu_to_le16('\0');
142         ef->root->size = rootdir_size(ef);
143         /* exFAT does not have time attributes for the root directory */
144         ef->root->mtime = 0;
145         ef->root->atime = 0;
146         /* always keep at least 1 reference to the root node */
147         exfat_get_node(ef->root);
148
149         return 0;
150 }
151
152 void exfat_unmount(struct exfat* ef)
153 {
154         exfat_put_node(ef, ef->root);
155         exfat_reset_cache(ef);
156         free(ef->root);
157         ef->root = NULL;
158         free(ef->zero_block);
159         ef->zero_block = NULL;
160         free(ef->cmap.chunk);
161         ef->cmap.chunk = NULL;
162         close(ef->fd);
163         ef->fd = 0;
164         free(ef->sb);
165         ef->sb = NULL;
166         free(ef->upcase);
167         ef->upcase = NULL;
168         ef->upcase_chars = 0;
169 }