Commit | Line | Data |
---|---|---|
84a1b7d3 DC |
1 | /* |
2 | * fs/sdcardfs/main.c | |
3 | * | |
4 | * Copyright (c) 2013 Samsung Electronics Co. Ltd | |
5 | * Authors: Daeho Jeong, Woojoong Lee, Seunghwan Hyun, | |
6 | * Sunghwan Yun, Sungjong Seo | |
7 | * | |
8 | * This program has been developed as a stackable file system based on | |
9 | * the WrapFS which written by | |
10 | * | |
11 | * Copyright (c) 1998-2011 Erez Zadok | |
12 | * Copyright (c) 2009 Shrikar Archak | |
13 | * Copyright (c) 2003-2011 Stony Brook University | |
14 | * Copyright (c) 2003-2011 The Research Foundation of SUNY | |
15 | * | |
16 | * This file is dual licensed. It may be redistributed and/or modified | |
17 | * under the terms of the Apache 2.0 License OR version 2 of the GNU | |
18 | * General Public License. | |
19 | */ | |
20 | ||
21 | #include "sdcardfs.h" | |
22 | #include <linux/module.h> | |
23 | #include <linux/types.h> | |
24 | #include <linux/parser.h> | |
25 | ||
26 | enum { | |
1e2d3bbc DR |
27 | Opt_fsuid, |
28 | Opt_fsgid, | |
84a1b7d3 | 29 | Opt_gid, |
84a1b7d3 | 30 | Opt_debug, |
84a1b7d3 | 31 | Opt_lower_fs, |
1e2d3bbc DR |
32 | Opt_mask, |
33 | Opt_multiuser, // May need? | |
34 | Opt_userid, | |
84a1b7d3 DC |
35 | Opt_reserved_mb, |
36 | Opt_err, | |
37 | }; | |
38 | ||
39 | static const match_table_t sdcardfs_tokens = { | |
1e2d3bbc DR |
40 | {Opt_fsuid, "fsuid=%u"}, |
41 | {Opt_fsgid, "fsgid=%u"}, | |
84a1b7d3 | 42 | {Opt_gid, "gid=%u"}, |
84a1b7d3 | 43 | {Opt_debug, "debug"}, |
1e2d3bbc DR |
44 | {Opt_mask, "mask=%u"}, |
45 | {Opt_userid, "userid=%d"}, | |
46 | {Opt_multiuser, "multiuser"}, | |
84a1b7d3 DC |
47 | {Opt_reserved_mb, "reserved_mb=%u"}, |
48 | {Opt_err, NULL} | |
49 | }; | |
50 | ||
51 | static int parse_options(struct super_block *sb, char *options, int silent, | |
52 | int *debug, struct sdcardfs_mount_options *opts) | |
53 | { | |
54 | char *p; | |
55 | substring_t args[MAX_OPT_ARGS]; | |
56 | int option; | |
84a1b7d3 DC |
57 | |
58 | /* by default, we use AID_MEDIA_RW as uid, gid */ | |
59 | opts->fs_low_uid = AID_MEDIA_RW; | |
60 | opts->fs_low_gid = AID_MEDIA_RW; | |
1e2d3bbc DR |
61 | opts->mask = 0; |
62 | opts->multiuser = false; | |
63 | opts->fs_user_id = 0; | |
64 | opts->gid = 0; | |
84a1b7d3 DC |
65 | /* by default, 0MB is reserved */ |
66 | opts->reserved_mb = 0; | |
67 | ||
68 | *debug = 0; | |
69 | ||
70 | if (!options) | |
71 | return 0; | |
72 | ||
73 | while ((p = strsep(&options, ",")) != NULL) { | |
74 | int token; | |
75 | if (!*p) | |
76 | continue; | |
77 | ||
78 | token = match_token(p, sdcardfs_tokens, args); | |
79 | ||
80 | switch (token) { | |
81 | case Opt_debug: | |
82 | *debug = 1; | |
83 | break; | |
1e2d3bbc | 84 | case Opt_fsuid: |
84a1b7d3 DC |
85 | if (match_int(&args[0], &option)) |
86 | return 0; | |
87 | opts->fs_low_uid = option; | |
88 | break; | |
1e2d3bbc | 89 | case Opt_fsgid: |
84a1b7d3 DC |
90 | if (match_int(&args[0], &option)) |
91 | return 0; | |
92 | opts->fs_low_gid = option; | |
93 | break; | |
1e2d3bbc | 94 | case Opt_gid: |
84a1b7d3 DC |
95 | if (match_int(&args[0], &option)) |
96 | return 0; | |
1e2d3bbc | 97 | opts->gid = option; |
84a1b7d3 | 98 | break; |
1e2d3bbc DR |
99 | case Opt_userid: |
100 | if (match_int(&args[0], &option)) | |
101 | return 0; | |
102 | opts->fs_user_id = option; | |
84a1b7d3 | 103 | break; |
1e2d3bbc DR |
104 | case Opt_mask: |
105 | if (match_int(&args[0], &option)) | |
106 | return 0; | |
107 | opts->mask = option; | |
108 | break; | |
109 | case Opt_multiuser: | |
110 | opts->multiuser = true; | |
84a1b7d3 | 111 | break; |
84a1b7d3 DC |
112 | case Opt_reserved_mb: |
113 | if (match_int(&args[0], &option)) | |
114 | return 0; | |
115 | opts->reserved_mb = option; | |
116 | break; | |
117 | /* unknown option */ | |
118 | default: | |
84a1b7d3 DC |
119 | if (!silent) { |
120 | printk( KERN_ERR "Unrecognized mount option \"%s\" " | |
121 | "or missing value", p); | |
122 | } | |
123 | return -EINVAL; | |
124 | } | |
125 | } | |
126 | ||
127 | if (*debug) { | |
128 | printk( KERN_INFO "sdcardfs : options - debug:%d\n", *debug); | |
129 | printk( KERN_INFO "sdcardfs : options - uid:%d\n", | |
130 | opts->fs_low_uid); | |
131 | printk( KERN_INFO "sdcardfs : options - gid:%d\n", | |
132 | opts->fs_low_gid); | |
133 | } | |
134 | ||
135 | return 0; | |
136 | } | |
137 | ||
e76fbcd4 | 138 | #if 0 |
84a1b7d3 DC |
139 | /* |
140 | * our custom d_alloc_root work-alike | |
141 | * | |
142 | * we can't use d_alloc_root if we want to use our own interpose function | |
143 | * unchanged, so we simply call our own "fake" d_alloc_root | |
144 | */ | |
145 | static struct dentry *sdcardfs_d_alloc_root(struct super_block *sb) | |
146 | { | |
147 | struct dentry *ret = NULL; | |
148 | ||
149 | if (sb) { | |
150 | static const struct qstr name = { | |
151 | .name = "/", | |
152 | .len = 1 | |
153 | }; | |
154 | ||
155 | ret = d_alloc(NULL, &name); | |
156 | if (ret) { | |
157 | d_set_d_op(ret, &sdcardfs_ci_dops); | |
158 | ret->d_sb = sb; | |
159 | ret->d_parent = ret; | |
160 | } | |
161 | } | |
162 | return ret; | |
163 | } | |
e76fbcd4 | 164 | #endif |
84a1b7d3 | 165 | |
1e2d3bbc DR |
166 | DEFINE_MUTEX(sdcardfs_super_list_lock); |
167 | LIST_HEAD(sdcardfs_super_list); | |
168 | EXPORT_SYMBOL_GPL(sdcardfs_super_list_lock); | |
169 | EXPORT_SYMBOL_GPL(sdcardfs_super_list); | |
170 | ||
84a1b7d3 DC |
171 | /* |
172 | * There is no need to lock the sdcardfs_super_info's rwsem as there is no | |
173 | * way anyone can have a reference to the superblock at this point in time. | |
174 | */ | |
175 | static int sdcardfs_read_super(struct super_block *sb, const char *dev_name, | |
176 | void *raw_data, int silent) | |
177 | { | |
178 | int err = 0; | |
179 | int debug; | |
180 | struct super_block *lower_sb; | |
181 | struct path lower_path; | |
182 | struct sdcardfs_sb_info *sb_info; | |
e76fbcd4 | 183 | struct inode *inode; |
84a1b7d3 DC |
184 | |
185 | printk(KERN_INFO "sdcardfs version 2.0\n"); | |
186 | ||
187 | if (!dev_name) { | |
188 | printk(KERN_ERR | |
189 | "sdcardfs: read_super: missing dev_name argument\n"); | |
190 | err = -EINVAL; | |
191 | goto out; | |
192 | } | |
193 | ||
194 | printk(KERN_INFO "sdcardfs: dev_name -> %s\n", dev_name); | |
195 | printk(KERN_INFO "sdcardfs: options -> %s\n", (char *)raw_data); | |
196 | ||
197 | /* parse lower path */ | |
198 | err = kern_path(dev_name, LOOKUP_FOLLOW | LOOKUP_DIRECTORY, | |
199 | &lower_path); | |
200 | if (err) { | |
1e2d3bbc | 201 | printk(KERN_ERR "sdcardfs: error accessing lower directory '%s'\n", dev_name); |
84a1b7d3 DC |
202 | goto out; |
203 | } | |
204 | ||
205 | /* allocate superblock private data */ | |
206 | sb->s_fs_info = kzalloc(sizeof(struct sdcardfs_sb_info), GFP_KERNEL); | |
207 | if (!SDCARDFS_SB(sb)) { | |
208 | printk(KERN_CRIT "sdcardfs: read_super: out of memory\n"); | |
209 | err = -ENOMEM; | |
210 | goto out_free; | |
211 | } | |
212 | ||
213 | sb_info = sb->s_fs_info; | |
84a1b7d3 DC |
214 | /* parse options */ |
215 | err = parse_options(sb, raw_data, silent, &debug, &sb_info->options); | |
216 | if (err) { | |
217 | printk(KERN_ERR "sdcardfs: invalid options\n"); | |
218 | goto out_freesbi; | |
219 | } | |
220 | ||
84a1b7d3 DC |
221 | /* set the lower superblock field of upper superblock */ |
222 | lower_sb = lower_path.dentry->d_sb; | |
223 | atomic_inc(&lower_sb->s_active); | |
224 | sdcardfs_set_lower_super(sb, lower_sb); | |
225 | ||
226 | /* inherit maxbytes from lower file system */ | |
227 | sb->s_maxbytes = lower_sb->s_maxbytes; | |
228 | ||
229 | /* | |
230 | * Our c/m/atime granularity is 1 ns because we may stack on file | |
231 | * systems whose granularity is as good. | |
232 | */ | |
233 | sb->s_time_gran = 1; | |
234 | ||
235 | sb->s_magic = SDCARDFS_SUPER_MAGIC; | |
236 | sb->s_op = &sdcardfs_sops; | |
237 | ||
e76fbcd4 | 238 | /* get a new inode and allocate our root dentry */ |
1e2d3bbc | 239 | inode = sdcardfs_iget(sb, lower_path.dentry->d_inode, 0); |
e76fbcd4 DC |
240 | if (IS_ERR(inode)) { |
241 | err = PTR_ERR(inode); | |
242 | goto out_sput; | |
243 | } | |
244 | sb->s_root = d_make_root(inode); | |
84a1b7d3 DC |
245 | if (!sb->s_root) { |
246 | err = -ENOMEM; | |
e76fbcd4 | 247 | goto out_iput; |
84a1b7d3 | 248 | } |
e76fbcd4 | 249 | d_set_d_op(sb->s_root, &sdcardfs_ci_dops); |
84a1b7d3 DC |
250 | |
251 | /* link the upper and lower dentries */ | |
252 | sb->s_root->d_fsdata = NULL; | |
253 | err = new_dentry_private_data(sb->s_root); | |
254 | if (err) | |
255 | goto out_freeroot; | |
256 | ||
257 | /* set the lower dentries for s_root */ | |
258 | sdcardfs_set_lower_path(sb->s_root, &lower_path); | |
259 | ||
e76fbcd4 DC |
260 | /* |
261 | * No need to call interpose because we already have a positive | |
262 | * dentry, which was instantiated by d_make_root. Just need to | |
263 | * d_rehash it. | |
264 | */ | |
265 | d_rehash(sb->s_root); | |
84a1b7d3 | 266 | |
e76fbcd4 | 267 | /* setup permission policy */ |
1e2d3bbc DR |
268 | sb_info->obbpath_s = kzalloc(PATH_MAX, GFP_KERNEL); |
269 | mutex_lock(&sdcardfs_super_list_lock); | |
270 | if(sb_info->options.multiuser) { | |
271 | setup_derived_state(sb->s_root->d_inode, PERM_PRE_ROOT, sb_info->options.fs_user_id, AID_ROOT, false); | |
272 | snprintf(sb_info->obbpath_s, PATH_MAX, "%s/obb", dev_name); | |
273 | /*err = prepare_dir(sb_info->obbpath_s, | |
e76fbcd4 | 274 | sb_info->options.fs_low_uid, |
1e2d3bbc DR |
275 | sb_info->options.fs_low_gid, 00755);*/ |
276 | } else { | |
277 | setup_derived_state(sb->s_root->d_inode, PERM_ROOT, sb_info->options.fs_low_uid, AID_ROOT, false); | |
278 | snprintf(sb_info->obbpath_s, PATH_MAX, "%s/Android/obb", dev_name); | |
84a1b7d3 | 279 | } |
e76fbcd4 | 280 | fix_derived_permission(sb->s_root->d_inode); |
1e2d3bbc DR |
281 | sb_info->sb = sb; |
282 | list_add(&sb_info->list, &sdcardfs_super_list); | |
283 | mutex_unlock(&sdcardfs_super_list_lock); | |
e76fbcd4 DC |
284 | |
285 | if (!silent) | |
286 | printk(KERN_INFO "sdcardfs: mounted on top of %s type %s\n", | |
287 | dev_name, lower_sb->s_type->name); | |
288 | goto out; /* all is well */ | |
84a1b7d3 | 289 | |
e76fbcd4 | 290 | /* no longer needed: free_dentry_private_data(sb->s_root); */ |
84a1b7d3 DC |
291 | out_freeroot: |
292 | dput(sb->s_root); | |
e76fbcd4 DC |
293 | out_iput: |
294 | iput(inode); | |
84a1b7d3 DC |
295 | out_sput: |
296 | /* drop refs we took earlier */ | |
297 | atomic_dec(&lower_sb->s_active); | |
84a1b7d3 DC |
298 | out_freesbi: |
299 | kfree(SDCARDFS_SB(sb)); | |
300 | sb->s_fs_info = NULL; | |
301 | out_free: | |
302 | path_put(&lower_path); | |
303 | ||
304 | out: | |
305 | return err; | |
306 | } | |
307 | ||
308 | /* A feature which supports mount_nodev() with options */ | |
309 | static struct dentry *mount_nodev_with_options(struct file_system_type *fs_type, | |
310 | int flags, const char *dev_name, void *data, | |
311 | int (*fill_super)(struct super_block *, const char *, void *, int)) | |
312 | ||
313 | { | |
314 | int error; | |
e76fbcd4 | 315 | struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL); |
84a1b7d3 DC |
316 | |
317 | if (IS_ERR(s)) | |
318 | return ERR_CAST(s); | |
319 | ||
320 | s->s_flags = flags; | |
321 | ||
322 | error = fill_super(s, dev_name, data, flags & MS_SILENT ? 1 : 0); | |
323 | if (error) { | |
324 | deactivate_locked_super(s); | |
325 | return ERR_PTR(error); | |
326 | } | |
327 | s->s_flags |= MS_ACTIVE; | |
328 | return dget(s->s_root); | |
329 | } | |
330 | ||
331 | struct dentry *sdcardfs_mount(struct file_system_type *fs_type, int flags, | |
332 | const char *dev_name, void *raw_data) | |
333 | { | |
334 | /* | |
335 | * dev_name is a lower_path_name, | |
336 | * raw_data is a option string. | |
337 | */ | |
338 | return mount_nodev_with_options(fs_type, flags, dev_name, | |
339 | raw_data, sdcardfs_read_super); | |
340 | } | |
341 | ||
1e2d3bbc DR |
342 | void sdcardfs_kill_sb(struct super_block *sb) { |
343 | struct sdcardfs_sb_info *sbi; | |
344 | if (sb->s_magic == SDCARDFS_SUPER_MAGIC) { | |
345 | sbi = SDCARDFS_SB(sb); | |
346 | mutex_lock(&sdcardfs_super_list_lock); | |
347 | list_del(&sbi->list); | |
348 | mutex_unlock(&sdcardfs_super_list_lock); | |
349 | } | |
350 | generic_shutdown_super(sb); | |
351 | } | |
352 | ||
84a1b7d3 DC |
353 | static struct file_system_type sdcardfs_fs_type = { |
354 | .owner = THIS_MODULE, | |
355 | .name = SDCARDFS_NAME, | |
356 | .mount = sdcardfs_mount, | |
1e2d3bbc | 357 | .kill_sb = sdcardfs_kill_sb, |
e76fbcd4 | 358 | .fs_flags = 0, |
84a1b7d3 DC |
359 | }; |
360 | ||
361 | static int __init init_sdcardfs_fs(void) | |
362 | { | |
363 | int err; | |
364 | ||
365 | pr_info("Registering sdcardfs " SDCARDFS_VERSION "\n"); | |
366 | ||
367 | err = sdcardfs_init_inode_cache(); | |
368 | if (err) | |
369 | goto out; | |
370 | err = sdcardfs_init_dentry_cache(); | |
371 | if (err) | |
372 | goto out; | |
373 | err = packagelist_init(); | |
374 | if (err) | |
375 | goto out; | |
376 | err = register_filesystem(&sdcardfs_fs_type); | |
377 | out: | |
378 | if (err) { | |
379 | sdcardfs_destroy_inode_cache(); | |
380 | sdcardfs_destroy_dentry_cache(); | |
381 | packagelist_exit(); | |
382 | } | |
383 | return err; | |
384 | } | |
385 | ||
386 | static void __exit exit_sdcardfs_fs(void) | |
387 | { | |
388 | sdcardfs_destroy_inode_cache(); | |
389 | sdcardfs_destroy_dentry_cache(); | |
390 | packagelist_exit(); | |
391 | unregister_filesystem(&sdcardfs_fs_type); | |
392 | pr_info("Completed sdcardfs module unload\n"); | |
393 | } | |
394 | ||
395 | MODULE_AUTHOR("Erez Zadok, Filesystems and Storage Lab, Stony Brook University" | |
396 | " (http://www.fsl.cs.sunysb.edu/)"); | |
397 | MODULE_DESCRIPTION("Wrapfs " SDCARDFS_VERSION | |
398 | " (http://wrapfs.filesystems.org/)"); | |
399 | MODULE_LICENSE("GPL"); | |
400 | ||
401 | module_init(init_sdcardfs_fs); | |
402 | module_exit(exit_sdcardfs_fs); |