diff options
Diffstat (limited to 'fs/sdcardfs/inode.c')
-rw-r--r-- | fs/sdcardfs/inode.c | 408 |
1 files changed, 259 insertions, 149 deletions
diff --git a/fs/sdcardfs/inode.c b/fs/sdcardfs/inode.c index 2528da0d3ae1..f15cb11ca8fd 100644 --- a/fs/sdcardfs/inode.c +++ b/fs/sdcardfs/inode.c | |||
@@ -19,18 +19,25 @@ | |||
19 | */ | 19 | */ |
20 | 20 | ||
21 | #include "sdcardfs.h" | 21 | #include "sdcardfs.h" |
22 | #include <linux/fs_struct.h> | ||
23 | #include <linux/ratelimit.h> | ||
22 | 24 | ||
23 | /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ | 25 | /* Do not directly use this function. Use OVERRIDE_CRED() instead. */ |
24 | const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) | 26 | const struct cred *override_fsids(struct sdcardfs_sb_info *sbi, struct sdcardfs_inode_info *info) |
25 | { | 27 | { |
26 | struct cred * cred; | 28 | struct cred *cred; |
27 | const struct cred * old_cred; | 29 | const struct cred *old_cred; |
30 | uid_t uid; | ||
28 | 31 | ||
29 | cred = prepare_creds(); | 32 | cred = prepare_creds(); |
30 | if (!cred) | 33 | if (!cred) |
31 | return NULL; | 34 | return NULL; |
32 | 35 | ||
33 | cred->fsuid = make_kuid(&init_user_ns, sbi->options.fs_low_uid); | 36 | if (info->under_obb) |
37 | uid = AID_MEDIA_OBB; | ||
38 | else | ||
39 | uid = multiuser_get_uid(info->userid, sbi->options.fs_low_uid); | ||
40 | cred->fsuid = make_kuid(&init_user_ns, uid); | ||
34 | cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); | 41 | cred->fsgid = make_kgid(&init_user_ns, sbi->options.fs_low_gid); |
35 | 42 | ||
36 | old_cred = override_creds(cred); | 43 | old_cred = override_creds(cred); |
@@ -39,9 +46,9 @@ const struct cred * override_fsids(struct sdcardfs_sb_info* sbi) | |||
39 | } | 46 | } |
40 | 47 | ||
41 | /* Do not directly use this function, use REVERT_CRED() instead. */ | 48 | /* Do not directly use this function, use REVERT_CRED() instead. */ |
42 | void revert_fsids(const struct cred * old_cred) | 49 | void revert_fsids(const struct cred *old_cred) |
43 | { | 50 | { |
44 | const struct cred * cur_cred; | 51 | const struct cred *cur_cred; |
45 | 52 | ||
46 | cur_cred = current->cred; | 53 | cur_cred = current->cred; |
47 | revert_creds(old_cred); | 54 | revert_creds(old_cred); |
@@ -53,28 +60,39 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, | |||
53 | { | 60 | { |
54 | int err; | 61 | int err; |
55 | struct dentry *lower_dentry; | 62 | struct dentry *lower_dentry; |
63 | struct vfsmount *lower_dentry_mnt; | ||
56 | struct dentry *lower_parent_dentry = NULL; | 64 | struct dentry *lower_parent_dentry = NULL; |
57 | struct path lower_path; | 65 | struct path lower_path; |
58 | const struct cred *saved_cred = NULL; | 66 | const struct cred *saved_cred = NULL; |
67 | struct fs_struct *saved_fs; | ||
68 | struct fs_struct *copied_fs; | ||
59 | 69 | ||
60 | if(!check_caller_access_to_name(dir, dentry->d_name.name)) { | 70 | if (!check_caller_access_to_name(dir, &dentry->d_name)) { |
61 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
62 | " dentry: %s, task:%s\n", | ||
63 | __func__, dentry->d_name.name, current->comm); | ||
64 | err = -EACCES; | 71 | err = -EACCES; |
65 | goto out_eacces; | 72 | goto out_eacces; |
66 | } | 73 | } |
67 | 74 | ||
68 | /* save current_cred and override it */ | 75 | /* save current_cred and override it */ |
69 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); | 76 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); |
70 | 77 | ||
71 | sdcardfs_get_lower_path(dentry, &lower_path); | 78 | sdcardfs_get_lower_path(dentry, &lower_path); |
72 | lower_dentry = lower_path.dentry; | 79 | lower_dentry = lower_path.dentry; |
80 | lower_dentry_mnt = lower_path.mnt; | ||
73 | lower_parent_dentry = lock_parent(lower_dentry); | 81 | lower_parent_dentry = lock_parent(lower_dentry); |
74 | 82 | ||
75 | /* set last 16bytes of mode field to 0664 */ | 83 | /* set last 16bytes of mode field to 0664 */ |
76 | mode = (mode & S_IFMT) | 00664; | 84 | mode = (mode & S_IFMT) | 00664; |
77 | err = vfs_create(d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); | 85 | |
86 | /* temporarily change umask for lower fs write */ | ||
87 | saved_fs = current->fs; | ||
88 | copied_fs = copy_fs_struct(current->fs); | ||
89 | if (!copied_fs) { | ||
90 | err = -ENOMEM; | ||
91 | goto out_unlock; | ||
92 | } | ||
93 | current->fs = copied_fs; | ||
94 | current->fs->umask = 0; | ||
95 | err = vfs_create2(lower_dentry_mnt, d_inode(lower_parent_dentry), lower_dentry, mode, want_excl); | ||
78 | if (err) | 96 | if (err) |
79 | goto out; | 97 | goto out; |
80 | 98 | ||
@@ -83,8 +101,12 @@ static int sdcardfs_create(struct inode *dir, struct dentry *dentry, | |||
83 | goto out; | 101 | goto out; |
84 | fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); | 102 | fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); |
85 | fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); | 103 | fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); |
104 | fixup_lower_ownership(dentry, dentry->d_name.name); | ||
86 | 105 | ||
87 | out: | 106 | out: |
107 | current->fs = saved_fs; | ||
108 | free_fs_struct(copied_fs); | ||
109 | out_unlock: | ||
88 | unlock_dir(lower_parent_dentry); | 110 | unlock_dir(lower_parent_dentry); |
89 | sdcardfs_put_lower_path(dentry, &lower_path); | 111 | sdcardfs_put_lower_path(dentry, &lower_path); |
90 | REVERT_CRED(saved_cred); | 112 | REVERT_CRED(saved_cred); |
@@ -138,28 +160,27 @@ static int sdcardfs_unlink(struct inode *dir, struct dentry *dentry) | |||
138 | { | 160 | { |
139 | int err; | 161 | int err; |
140 | struct dentry *lower_dentry; | 162 | struct dentry *lower_dentry; |
163 | struct vfsmount *lower_mnt; | ||
141 | struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); | 164 | struct inode *lower_dir_inode = sdcardfs_lower_inode(dir); |
142 | struct dentry *lower_dir_dentry; | 165 | struct dentry *lower_dir_dentry; |
143 | struct path lower_path; | 166 | struct path lower_path; |
144 | const struct cred *saved_cred = NULL; | 167 | const struct cred *saved_cred = NULL; |
145 | 168 | ||
146 | if(!check_caller_access_to_name(dir, dentry->d_name.name)) { | 169 | if (!check_caller_access_to_name(dir, &dentry->d_name)) { |
147 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
148 | " dentry: %s, task:%s\n", | ||
149 | __func__, dentry->d_name.name, current->comm); | ||
150 | err = -EACCES; | 170 | err = -EACCES; |
151 | goto out_eacces; | 171 | goto out_eacces; |
152 | } | 172 | } |
153 | 173 | ||
154 | /* save current_cred and override it */ | 174 | /* save current_cred and override it */ |
155 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); | 175 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); |
156 | 176 | ||
157 | sdcardfs_get_lower_path(dentry, &lower_path); | 177 | sdcardfs_get_lower_path(dentry, &lower_path); |
158 | lower_dentry = lower_path.dentry; | 178 | lower_dentry = lower_path.dentry; |
179 | lower_mnt = lower_path.mnt; | ||
159 | dget(lower_dentry); | 180 | dget(lower_dentry); |
160 | lower_dir_dentry = lock_parent(lower_dentry); | 181 | lower_dir_dentry = lock_parent(lower_dentry); |
161 | 182 | ||
162 | err = vfs_unlink(lower_dir_inode, lower_dentry, NULL); | 183 | err = vfs_unlink2(lower_mnt, lower_dir_inode, lower_dentry, NULL); |
163 | 184 | ||
164 | /* | 185 | /* |
165 | * Note: unlinking on top of NFS can cause silly-renamed files. | 186 | * Note: unlinking on top of NFS can cause silly-renamed files. |
@@ -219,14 +240,15 @@ out: | |||
219 | } | 240 | } |
220 | #endif | 241 | #endif |
221 | 242 | ||
222 | static int touch(char *abs_path, mode_t mode) { | 243 | static int touch(char *abs_path, mode_t mode) |
244 | { | ||
223 | struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); | 245 | struct file *filp = filp_open(abs_path, O_RDWR|O_CREAT|O_EXCL|O_NOFOLLOW, mode); |
246 | |||
224 | if (IS_ERR(filp)) { | 247 | if (IS_ERR(filp)) { |
225 | if (PTR_ERR(filp) == -EEXIST) { | 248 | if (PTR_ERR(filp) == -EEXIST) { |
226 | return 0; | 249 | return 0; |
227 | } | 250 | } else { |
228 | else { | 251 | pr_err("sdcardfs: failed to open(%s): %ld\n", |
229 | printk(KERN_ERR "sdcardfs: failed to open(%s): %ld\n", | ||
230 | abs_path, PTR_ERR(filp)); | 252 | abs_path, PTR_ERR(filp)); |
231 | return PTR_ERR(filp); | 253 | return PTR_ERR(filp); |
232 | } | 254 | } |
@@ -240,31 +262,29 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode | |||
240 | int err; | 262 | int err; |
241 | int make_nomedia_in_obb = 0; | 263 | int make_nomedia_in_obb = 0; |
242 | struct dentry *lower_dentry; | 264 | struct dentry *lower_dentry; |
265 | struct vfsmount *lower_mnt; | ||
243 | struct dentry *lower_parent_dentry = NULL; | 266 | struct dentry *lower_parent_dentry = NULL; |
244 | struct path lower_path; | 267 | struct path lower_path; |
245 | struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); | 268 | struct sdcardfs_sb_info *sbi = SDCARDFS_SB(dentry->d_sb); |
246 | const struct cred *saved_cred = NULL; | 269 | const struct cred *saved_cred = NULL; |
247 | struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); | 270 | struct sdcardfs_inode_info *pi = SDCARDFS_I(dir); |
248 | char *page_buf; | ||
249 | char *nomedia_dir_name; | ||
250 | char *nomedia_fullpath; | ||
251 | int fullpath_namelen; | ||
252 | int touch_err = 0; | 271 | int touch_err = 0; |
272 | struct fs_struct *saved_fs; | ||
273 | struct fs_struct *copied_fs; | ||
274 | struct qstr q_obb = QSTR_LITERAL("obb"); | ||
275 | struct qstr q_data = QSTR_LITERAL("data"); | ||
253 | 276 | ||
254 | if(!check_caller_access_to_name(dir, dentry->d_name.name)) { | 277 | if (!check_caller_access_to_name(dir, &dentry->d_name)) { |
255 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
256 | " dentry: %s, task:%s\n", | ||
257 | __func__, dentry->d_name.name, current->comm); | ||
258 | err = -EACCES; | 278 | err = -EACCES; |
259 | goto out_eacces; | 279 | goto out_eacces; |
260 | } | 280 | } |
261 | 281 | ||
262 | /* save current_cred and override it */ | 282 | /* save current_cred and override it */ |
263 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); | 283 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); |
264 | 284 | ||
265 | /* check disk space */ | 285 | /* check disk space */ |
266 | if (!check_min_free_space(dentry, 0, 1)) { | 286 | if (!check_min_free_space(dentry, 0, 1)) { |
267 | printk(KERN_INFO "sdcardfs: No minimum free space.\n"); | 287 | pr_err("sdcardfs: No minimum free space.\n"); |
268 | err = -ENOSPC; | 288 | err = -ENOSPC; |
269 | goto out_revert; | 289 | goto out_revert; |
270 | } | 290 | } |
@@ -272,87 +292,83 @@ static int sdcardfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode | |||
272 | /* the lower_dentry is negative here */ | 292 | /* the lower_dentry is negative here */ |
273 | sdcardfs_get_lower_path(dentry, &lower_path); | 293 | sdcardfs_get_lower_path(dentry, &lower_path); |
274 | lower_dentry = lower_path.dentry; | 294 | lower_dentry = lower_path.dentry; |
295 | lower_mnt = lower_path.mnt; | ||
275 | lower_parent_dentry = lock_parent(lower_dentry); | 296 | lower_parent_dentry = lock_parent(lower_dentry); |
276 | 297 | ||
277 | /* set last 16bytes of mode field to 0775 */ | 298 | /* set last 16bytes of mode field to 0775 */ |
278 | mode = (mode & S_IFMT) | 00775; | 299 | mode = (mode & S_IFMT) | 00775; |
279 | err = vfs_mkdir(d_inode(lower_parent_dentry), lower_dentry, mode); | ||
280 | 300 | ||
281 | if (err) | 301 | /* temporarily change umask for lower fs write */ |
302 | saved_fs = current->fs; | ||
303 | copied_fs = copy_fs_struct(current->fs); | ||
304 | if (!copied_fs) { | ||
305 | err = -ENOMEM; | ||
306 | unlock_dir(lower_parent_dentry); | ||
307 | goto out_unlock; | ||
308 | } | ||
309 | current->fs = copied_fs; | ||
310 | current->fs->umask = 0; | ||
311 | err = vfs_mkdir2(lower_mnt, d_inode(lower_parent_dentry), lower_dentry, mode); | ||
312 | |||
313 | if (err) { | ||
314 | unlock_dir(lower_parent_dentry); | ||
282 | goto out; | 315 | goto out; |
316 | } | ||
283 | 317 | ||
284 | /* if it is a local obb dentry, setup it with the base obbpath */ | 318 | /* if it is a local obb dentry, setup it with the base obbpath */ |
285 | if(need_graft_path(dentry)) { | 319 | if (need_graft_path(dentry)) { |
286 | 320 | ||
287 | err = setup_obb_dentry(dentry, &lower_path); | 321 | err = setup_obb_dentry(dentry, &lower_path); |
288 | if(err) { | 322 | if (err) { |
289 | /* if the sbi->obbpath is not available, the lower_path won't be | 323 | /* if the sbi->obbpath is not available, the lower_path won't be |
290 | * changed by setup_obb_dentry() but the lower path is saved to | 324 | * changed by setup_obb_dentry() but the lower path is saved to |
291 | * its orig_path. this dentry will be revalidated later. | 325 | * its orig_path. this dentry will be revalidated later. |
292 | * but now, the lower_path should be NULL */ | 326 | * but now, the lower_path should be NULL |
327 | */ | ||
293 | sdcardfs_put_reset_lower_path(dentry); | 328 | sdcardfs_put_reset_lower_path(dentry); |
294 | 329 | ||
295 | /* the newly created lower path which saved to its orig_path or | 330 | /* the newly created lower path which saved to its orig_path or |
296 | * the lower_path is the base obbpath. | 331 | * the lower_path is the base obbpath. |
297 | * therefore, an additional path_get is required */ | 332 | * therefore, an additional path_get is required |
333 | */ | ||
298 | path_get(&lower_path); | 334 | path_get(&lower_path); |
299 | } else | 335 | } else |
300 | make_nomedia_in_obb = 1; | 336 | make_nomedia_in_obb = 1; |
301 | } | 337 | } |
302 | 338 | ||
303 | err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); | 339 | err = sdcardfs_interpose(dentry, dir->i_sb, &lower_path, pi->userid); |
304 | if (err) | 340 | if (err) { |
341 | unlock_dir(lower_parent_dentry); | ||
305 | goto out; | 342 | goto out; |
343 | } | ||
306 | 344 | ||
307 | fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); | 345 | fsstack_copy_attr_times(dir, sdcardfs_lower_inode(dir)); |
308 | fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); | 346 | fsstack_copy_inode_size(dir, d_inode(lower_parent_dentry)); |
309 | /* update number of links on parent directory */ | 347 | /* update number of links on parent directory */ |
310 | set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); | 348 | set_nlink(dir, sdcardfs_lower_inode(dir)->i_nlink); |
311 | 349 | fixup_lower_ownership(dentry, dentry->d_name.name); | |
312 | if ((!sbi->options.multiuser) && (!strcasecmp(dentry->d_name.name, "obb")) | 350 | unlock_dir(lower_parent_dentry); |
351 | if ((!sbi->options.multiuser) && (qstr_case_eq(&dentry->d_name, &q_obb)) | ||
313 | && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) | 352 | && (pi->perm == PERM_ANDROID) && (pi->userid == 0)) |
314 | make_nomedia_in_obb = 1; | 353 | make_nomedia_in_obb = 1; |
315 | 354 | ||
316 | /* When creating /Android/data and /Android/obb, mark them as .nomedia */ | 355 | /* When creating /Android/data and /Android/obb, mark them as .nomedia */ |
317 | if (make_nomedia_in_obb || | 356 | if (make_nomedia_in_obb || |
318 | ((pi->perm == PERM_ANDROID) && (!strcasecmp(dentry->d_name.name, "data")))) { | 357 | ((pi->perm == PERM_ANDROID) && (qstr_case_eq(&dentry->d_name, &q_data)))) { |
319 | 358 | REVERT_CRED(saved_cred); | |
320 | page_buf = (char *)__get_free_page(GFP_KERNEL); | 359 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(d_inode(dentry))); |
321 | if (!page_buf) { | 360 | set_fs_pwd(current->fs, &lower_path); |
322 | printk(KERN_ERR "sdcardfs: failed to allocate page buf\n"); | 361 | touch_err = touch(".nomedia", 0664); |
323 | goto out; | ||
324 | } | ||
325 | |||
326 | nomedia_dir_name = d_absolute_path(&lower_path, page_buf, PAGE_SIZE); | ||
327 | if (IS_ERR(nomedia_dir_name)) { | ||
328 | free_page((unsigned long)page_buf); | ||
329 | printk(KERN_ERR "sdcardfs: failed to get .nomedia dir name\n"); | ||
330 | goto out; | ||
331 | } | ||
332 | |||
333 | fullpath_namelen = page_buf + PAGE_SIZE - nomedia_dir_name - 1; | ||
334 | fullpath_namelen += strlen("/.nomedia"); | ||
335 | nomedia_fullpath = kzalloc(fullpath_namelen + 1, GFP_KERNEL); | ||
336 | if (!nomedia_fullpath) { | ||
337 | free_page((unsigned long)page_buf); | ||
338 | printk(KERN_ERR "sdcardfs: failed to allocate .nomedia fullpath buf\n"); | ||
339 | goto out; | ||
340 | } | ||
341 | |||
342 | strcpy(nomedia_fullpath, nomedia_dir_name); | ||
343 | free_page((unsigned long)page_buf); | ||
344 | strcat(nomedia_fullpath, "/.nomedia"); | ||
345 | touch_err = touch(nomedia_fullpath, 0664); | ||
346 | if (touch_err) { | 362 | if (touch_err) { |
347 | printk(KERN_ERR "sdcardfs: failed to touch(%s): %d\n", | 363 | pr_err("sdcardfs: failed to create .nomedia in %s: %d\n", |
348 | nomedia_fullpath, touch_err); | 364 | lower_path.dentry->d_name.name, touch_err); |
349 | kfree(nomedia_fullpath); | ||
350 | goto out; | 365 | goto out; |
351 | } | 366 | } |
352 | kfree(nomedia_fullpath); | ||
353 | } | 367 | } |
354 | out: | 368 | out: |
355 | unlock_dir(lower_parent_dentry); | 369 | current->fs = saved_fs; |
370 | free_fs_struct(copied_fs); | ||
371 | out_unlock: | ||
356 | sdcardfs_put_lower_path(dentry, &lower_path); | 372 | sdcardfs_put_lower_path(dentry, &lower_path); |
357 | out_revert: | 373 | out_revert: |
358 | REVERT_CRED(saved_cred); | 374 | REVERT_CRED(saved_cred); |
@@ -364,29 +380,29 @@ static int sdcardfs_rmdir(struct inode *dir, struct dentry *dentry) | |||
364 | { | 380 | { |
365 | struct dentry *lower_dentry; | 381 | struct dentry *lower_dentry; |
366 | struct dentry *lower_dir_dentry; | 382 | struct dentry *lower_dir_dentry; |
383 | struct vfsmount *lower_mnt; | ||
367 | int err; | 384 | int err; |
368 | struct path lower_path; | 385 | struct path lower_path; |
369 | const struct cred *saved_cred = NULL; | 386 | const struct cred *saved_cred = NULL; |
370 | 387 | ||
371 | if(!check_caller_access_to_name(dir, dentry->d_name.name)) { | 388 | if (!check_caller_access_to_name(dir, &dentry->d_name)) { |
372 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
373 | " dentry: %s, task:%s\n", | ||
374 | __func__, dentry->d_name.name, current->comm); | ||
375 | err = -EACCES; | 389 | err = -EACCES; |
376 | goto out_eacces; | 390 | goto out_eacces; |
377 | } | 391 | } |
378 | 392 | ||
379 | /* save current_cred and override it */ | 393 | /* save current_cred and override it */ |
380 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred); | 394 | OVERRIDE_CRED(SDCARDFS_SB(dir->i_sb), saved_cred, SDCARDFS_I(dir)); |
381 | 395 | ||
382 | /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry | 396 | /* sdcardfs_get_real_lower(): in case of remove an user's obb dentry |
383 | * the dentry on the original path should be deleted. */ | 397 | * the dentry on the original path should be deleted. |
398 | */ | ||
384 | sdcardfs_get_real_lower(dentry, &lower_path); | 399 | sdcardfs_get_real_lower(dentry, &lower_path); |
385 | 400 | ||
386 | lower_dentry = lower_path.dentry; | 401 | lower_dentry = lower_path.dentry; |
402 | lower_mnt = lower_path.mnt; | ||
387 | lower_dir_dentry = lock_parent(lower_dentry); | 403 | lower_dir_dentry = lock_parent(lower_dentry); |
388 | 404 | ||
389 | err = vfs_rmdir(d_inode(lower_dir_dentry), lower_dentry); | 405 | err = vfs_rmdir2(lower_mnt, d_inode(lower_dir_dentry), lower_dentry); |
390 | if (err) | 406 | if (err) |
391 | goto out; | 407 | goto out; |
392 | 408 | ||
@@ -450,27 +466,25 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
450 | struct dentry *lower_new_dentry = NULL; | 466 | struct dentry *lower_new_dentry = NULL; |
451 | struct dentry *lower_old_dir_dentry = NULL; | 467 | struct dentry *lower_old_dir_dentry = NULL; |
452 | struct dentry *lower_new_dir_dentry = NULL; | 468 | struct dentry *lower_new_dir_dentry = NULL; |
469 | struct vfsmount *lower_mnt = NULL; | ||
453 | struct dentry *trap = NULL; | 470 | struct dentry *trap = NULL; |
454 | struct dentry *new_parent = NULL; | ||
455 | struct path lower_old_path, lower_new_path; | 471 | struct path lower_old_path, lower_new_path; |
456 | const struct cred *saved_cred = NULL; | 472 | const struct cred *saved_cred = NULL; |
457 | 473 | ||
458 | if(!check_caller_access_to_name(old_dir, old_dentry->d_name.name) || | 474 | if (!check_caller_access_to_name(old_dir, &old_dentry->d_name) || |
459 | !check_caller_access_to_name(new_dir, new_dentry->d_name.name)) { | 475 | !check_caller_access_to_name(new_dir, &new_dentry->d_name)) { |
460 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
461 | " new_dentry: %s, task:%s\n", | ||
462 | __func__, new_dentry->d_name.name, current->comm); | ||
463 | err = -EACCES; | 476 | err = -EACCES; |
464 | goto out_eacces; | 477 | goto out_eacces; |
465 | } | 478 | } |
466 | 479 | ||
467 | /* save current_cred and override it */ | 480 | /* save current_cred and override it */ |
468 | OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred); | 481 | OVERRIDE_CRED(SDCARDFS_SB(old_dir->i_sb), saved_cred, SDCARDFS_I(new_dir)); |
469 | 482 | ||
470 | sdcardfs_get_real_lower(old_dentry, &lower_old_path); | 483 | sdcardfs_get_real_lower(old_dentry, &lower_old_path); |
471 | sdcardfs_get_lower_path(new_dentry, &lower_new_path); | 484 | sdcardfs_get_lower_path(new_dentry, &lower_new_path); |
472 | lower_old_dentry = lower_old_path.dentry; | 485 | lower_old_dentry = lower_old_path.dentry; |
473 | lower_new_dentry = lower_new_path.dentry; | 486 | lower_new_dentry = lower_new_path.dentry; |
487 | lower_mnt = lower_old_path.mnt; | ||
474 | lower_old_dir_dentry = dget_parent(lower_old_dentry); | 488 | lower_old_dir_dentry = dget_parent(lower_old_dentry); |
475 | lower_new_dir_dentry = dget_parent(lower_new_dentry); | 489 | lower_new_dir_dentry = dget_parent(lower_new_dentry); |
476 | 490 | ||
@@ -486,7 +500,8 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
486 | goto out; | 500 | goto out; |
487 | } | 501 | } |
488 | 502 | ||
489 | err = vfs_rename(d_inode(lower_old_dir_dentry), lower_old_dentry, | 503 | err = vfs_rename2(lower_mnt, |
504 | d_inode(lower_old_dir_dentry), lower_old_dentry, | ||
490 | d_inode(lower_new_dir_dentry), lower_new_dentry, | 505 | d_inode(lower_new_dir_dentry), lower_new_dentry, |
491 | NULL, 0); | 506 | NULL, 0); |
492 | if (err) | 507 | if (err) |
@@ -499,25 +514,11 @@ static int sdcardfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
499 | if (new_dir != old_dir) { | 514 | if (new_dir != old_dir) { |
500 | sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); | 515 | sdcardfs_copy_and_fix_attrs(old_dir, d_inode(lower_old_dir_dentry)); |
501 | fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); | 516 | fsstack_copy_inode_size(old_dir, d_inode(lower_old_dir_dentry)); |
502 | |||
503 | /* update the derived permission of the old_dentry | ||
504 | * with its new parent | ||
505 | */ | ||
506 | new_parent = dget_parent(new_dentry); | ||
507 | if(new_parent) { | ||
508 | if(d_inode(old_dentry)) { | ||
509 | update_derived_permission_lock(old_dentry); | ||
510 | } | ||
511 | dput(new_parent); | ||
512 | } | ||
513 | } | 517 | } |
514 | /* At this point, not all dentry information has been moved, so | 518 | get_derived_permission_new(new_dentry->d_parent, old_dentry, &new_dentry->d_name); |
515 | * we pass along new_dentry for the name.*/ | 519 | fixup_tmp_permissions(d_inode(old_dentry)); |
516 | mutex_lock(&d_inode(old_dentry)->i_mutex); | 520 | fixup_lower_ownership(old_dentry, new_dentry->d_name.name); |
517 | get_derived_permission_new(new_dentry->d_parent, old_dentry, new_dentry); | 521 | d_invalidate(old_dentry); /* Can't fixup ownership recursively :( */ |
518 | fix_derived_permission(d_inode(old_dentry)); | ||
519 | get_derive_permissions_recursive(old_dentry); | ||
520 | mutex_unlock(&d_inode(old_dentry)->i_mutex); | ||
521 | out: | 522 | out: |
522 | unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); | 523 | unlock_rename(lower_old_dir_dentry, lower_new_dir_dentry); |
523 | dput(lower_old_dir_dentry); | 524 | dput(lower_old_dir_dentry); |
@@ -586,16 +587,63 @@ static const char *sdcardfs_follow_link(struct dentry *dentry, void **cookie) | |||
586 | } | 587 | } |
587 | #endif | 588 | #endif |
588 | 589 | ||
589 | static int sdcardfs_permission(struct inode *inode, int mask) | 590 | static int sdcardfs_permission_wrn(struct inode *inode, int mask) |
591 | { | ||
592 | WARN_RATELIMIT(1, "sdcardfs does not support permission. Use permission2.\n"); | ||
593 | return -EINVAL; | ||
594 | } | ||
595 | |||
596 | void copy_attrs(struct inode *dest, const struct inode *src) | ||
597 | { | ||
598 | dest->i_mode = src->i_mode; | ||
599 | dest->i_uid = src->i_uid; | ||
600 | dest->i_gid = src->i_gid; | ||
601 | dest->i_rdev = src->i_rdev; | ||
602 | dest->i_atime = src->i_atime; | ||
603 | dest->i_mtime = src->i_mtime; | ||
604 | dest->i_ctime = src->i_ctime; | ||
605 | dest->i_blkbits = src->i_blkbits; | ||
606 | dest->i_flags = src->i_flags; | ||
607 | #ifdef CONFIG_FS_POSIX_ACL | ||
608 | dest->i_acl = src->i_acl; | ||
609 | #endif | ||
610 | #ifdef CONFIG_SECURITY | ||
611 | dest->i_security = src->i_security; | ||
612 | #endif | ||
613 | } | ||
614 | |||
615 | static int sdcardfs_permission(struct vfsmount *mnt, struct inode *inode, int mask) | ||
590 | { | 616 | { |
591 | int err; | 617 | int err; |
618 | struct inode tmp; | ||
619 | struct inode *top = grab_top(SDCARDFS_I(inode)); | ||
620 | |||
621 | if (!top) { | ||
622 | release_top(SDCARDFS_I(inode)); | ||
623 | WARN(1, "Top value was null!\n"); | ||
624 | return -EINVAL; | ||
625 | } | ||
592 | 626 | ||
593 | /* | 627 | /* |
594 | * Permission check on sdcardfs inode. | 628 | * Permission check on sdcardfs inode. |
595 | * Calling process should have AID_SDCARD_RW permission | 629 | * Calling process should have AID_SDCARD_RW permission |
630 | * Since generic_permission only needs i_mode, i_uid, | ||
631 | * i_gid, and i_sb, we can create a fake inode to pass | ||
632 | * this information down in. | ||
633 | * | ||
634 | * The underlying code may attempt to take locks in some | ||
635 | * cases for features we're not using, but if that changes, | ||
636 | * locks must be dealt with to avoid undefined behavior. | ||
596 | */ | 637 | */ |
597 | err = generic_permission(inode, mask); | 638 | copy_attrs(&tmp, inode); |
598 | 639 | tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); | |
640 | tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); | ||
641 | tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); | ||
642 | release_top(SDCARDFS_I(inode)); | ||
643 | tmp.i_sb = inode->i_sb; | ||
644 | if (IS_POSIXACL(inode)) | ||
645 | pr_warn("%s: This may be undefined behavior...\n", __func__); | ||
646 | err = generic_permission(&tmp, mask); | ||
599 | /* XXX | 647 | /* XXX |
600 | * Original sdcardfs code calls inode_permission(lower_inode,.. ) | 648 | * Original sdcardfs code calls inode_permission(lower_inode,.. ) |
601 | * for checking inode permission. But doing such things here seems | 649 | * for checking inode permission. But doing such things here seems |
@@ -612,6 +660,7 @@ static int sdcardfs_permission(struct inode *inode, int mask) | |||
612 | * we check it with AID_MEDIA_RW permission | 660 | * we check it with AID_MEDIA_RW permission |
613 | */ | 661 | */ |
614 | struct inode *lower_inode; | 662 | struct inode *lower_inode; |
663 | |||
615 | OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); | 664 | OVERRIDE_CRED(SDCARDFS_SB(inode->sb)); |
616 | 665 | ||
617 | lower_inode = sdcardfs_lower_inode(inode); | 666 | lower_inode = sdcardfs_lower_inode(inode); |
@@ -624,47 +673,86 @@ static int sdcardfs_permission(struct inode *inode, int mask) | |||
624 | 673 | ||
625 | } | 674 | } |
626 | 675 | ||
627 | static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) | 676 | static int sdcardfs_setattr_wrn(struct dentry *dentry, struct iattr *ia) |
677 | { | ||
678 | WARN_RATELIMIT(1, "sdcardfs does not support setattr. User setattr2.\n"); | ||
679 | return -EINVAL; | ||
680 | } | ||
681 | |||
682 | static int sdcardfs_setattr(struct vfsmount *mnt, struct dentry *dentry, struct iattr *ia) | ||
628 | { | 683 | { |
629 | int err; | 684 | int err; |
630 | struct dentry *lower_dentry; | 685 | struct dentry *lower_dentry; |
686 | struct vfsmount *lower_mnt; | ||
631 | struct inode *inode; | 687 | struct inode *inode; |
632 | struct inode *lower_inode; | 688 | struct inode *lower_inode; |
633 | struct path lower_path; | 689 | struct path lower_path; |
634 | struct iattr lower_ia; | 690 | struct iattr lower_ia; |
635 | struct dentry *parent; | 691 | struct dentry *parent; |
692 | struct inode tmp; | ||
693 | struct inode *top; | ||
694 | const struct cred *saved_cred = NULL; | ||
636 | 695 | ||
637 | inode = d_inode(dentry); | 696 | inode = d_inode(dentry); |
697 | top = grab_top(SDCARDFS_I(inode)); | ||
698 | |||
699 | if (!top) { | ||
700 | release_top(SDCARDFS_I(inode)); | ||
701 | return -EINVAL; | ||
702 | } | ||
703 | |||
704 | /* | ||
705 | * Permission check on sdcardfs inode. | ||
706 | * Calling process should have AID_SDCARD_RW permission | ||
707 | * Since generic_permission only needs i_mode, i_uid, | ||
708 | * i_gid, and i_sb, we can create a fake inode to pass | ||
709 | * this information down in. | ||
710 | * | ||
711 | * The underlying code may attempt to take locks in some | ||
712 | * cases for features we're not using, but if that changes, | ||
713 | * locks must be dealt with to avoid undefined behavior. | ||
714 | * | ||
715 | */ | ||
716 | copy_attrs(&tmp, inode); | ||
717 | tmp.i_uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); | ||
718 | tmp.i_gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); | ||
719 | tmp.i_mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); | ||
720 | tmp.i_size = i_size_read(inode); | ||
721 | release_top(SDCARDFS_I(inode)); | ||
722 | tmp.i_sb = inode->i_sb; | ||
638 | 723 | ||
639 | /* | 724 | /* |
640 | * Check if user has permission to change inode. We don't check if | 725 | * Check if user has permission to change inode. We don't check if |
641 | * this user can change the lower inode: that should happen when | 726 | * this user can change the lower inode: that should happen when |
642 | * calling notify_change on the lower inode. | 727 | * calling notify_change on the lower inode. |
643 | */ | 728 | */ |
644 | err = inode_change_ok(inode, ia); | 729 | /* prepare our own lower struct iattr (with the lower file) */ |
730 | memcpy(&lower_ia, ia, sizeof(lower_ia)); | ||
731 | /* Allow touch updating timestamps. A previous permission check ensures | ||
732 | * we have write access. Changes to mode, owner, and group are ignored | ||
733 | */ | ||
734 | ia->ia_valid |= ATTR_FORCE; | ||
735 | err = inode_change_ok(&tmp, ia); | ||
645 | 736 | ||
646 | /* no vfs_XXX operations required, cred overriding will be skipped. wj*/ | ||
647 | if (!err) { | 737 | if (!err) { |
648 | /* check the Android group ID */ | 738 | /* check the Android group ID */ |
649 | parent = dget_parent(dentry); | 739 | parent = dget_parent(dentry); |
650 | if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { | 740 | if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) |
651 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
652 | " dentry: %s, task:%s\n", | ||
653 | __func__, dentry->d_name.name, current->comm); | ||
654 | err = -EACCES; | 741 | err = -EACCES; |
655 | } | ||
656 | dput(parent); | 742 | dput(parent); |
657 | } | 743 | } |
658 | 744 | ||
659 | if (err) | 745 | if (err) |
660 | goto out_err; | 746 | goto out_err; |
661 | 747 | ||
748 | /* save current_cred and override it */ | ||
749 | OVERRIDE_CRED(SDCARDFS_SB(dentry->d_sb), saved_cred, SDCARDFS_I(inode)); | ||
750 | |||
662 | sdcardfs_get_lower_path(dentry, &lower_path); | 751 | sdcardfs_get_lower_path(dentry, &lower_path); |
663 | lower_dentry = lower_path.dentry; | 752 | lower_dentry = lower_path.dentry; |
753 | lower_mnt = lower_path.mnt; | ||
664 | lower_inode = sdcardfs_lower_inode(inode); | 754 | lower_inode = sdcardfs_lower_inode(inode); |
665 | 755 | ||
666 | /* prepare our own lower struct iattr (with the lower file) */ | ||
667 | memcpy(&lower_ia, ia, sizeof(lower_ia)); | ||
668 | if (ia->ia_valid & ATTR_FILE) | 756 | if (ia->ia_valid & ATTR_FILE) |
669 | lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); | 757 | lower_ia.ia_file = sdcardfs_lower_file(ia->ia_file); |
670 | 758 | ||
@@ -681,7 +769,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) | |||
681 | if (current->mm) | 769 | if (current->mm) |
682 | down_write(¤t->mm->mmap_sem); | 770 | down_write(¤t->mm->mmap_sem); |
683 | if (ia->ia_valid & ATTR_SIZE) { | 771 | if (ia->ia_valid & ATTR_SIZE) { |
684 | err = inode_newsize_ok(inode, ia->ia_size); | 772 | err = inode_newsize_ok(&tmp, ia->ia_size); |
685 | if (err) { | 773 | if (err) { |
686 | if (current->mm) | 774 | if (current->mm) |
687 | up_write(¤t->mm->mmap_sem); | 775 | up_write(¤t->mm->mmap_sem); |
@@ -704,7 +792,7 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) | |||
704 | * tries to open(), unlink(), then ftruncate() a file. | 792 | * tries to open(), unlink(), then ftruncate() a file. |
705 | */ | 793 | */ |
706 | mutex_lock(&d_inode(lower_dentry)->i_mutex); | 794 | mutex_lock(&d_inode(lower_dentry)->i_mutex); |
707 | err = notify_change(lower_dentry, &lower_ia, /* note: lower_ia */ | 795 | err = notify_change2(lower_mnt, lower_dentry, &lower_ia, /* note: lower_ia */ |
708 | NULL); | 796 | NULL); |
709 | mutex_unlock(&d_inode(lower_dentry)->i_mutex); | 797 | mutex_unlock(&d_inode(lower_dentry)->i_mutex); |
710 | if (current->mm) | 798 | if (current->mm) |
@@ -723,48 +811,68 @@ static int sdcardfs_setattr(struct dentry *dentry, struct iattr *ia) | |||
723 | 811 | ||
724 | out: | 812 | out: |
725 | sdcardfs_put_lower_path(dentry, &lower_path); | 813 | sdcardfs_put_lower_path(dentry, &lower_path); |
814 | REVERT_CRED(saved_cred); | ||
726 | out_err: | 815 | out_err: |
727 | return err; | 816 | return err; |
728 | } | 817 | } |
729 | 818 | ||
819 | static int sdcardfs_fillattr(struct vfsmount *mnt, | ||
820 | struct inode *inode, struct kstat *stat) | ||
821 | { | ||
822 | struct sdcardfs_inode_info *info = SDCARDFS_I(inode); | ||
823 | struct inode *top = grab_top(info); | ||
824 | |||
825 | if (!top) | ||
826 | return -EINVAL; | ||
827 | |||
828 | stat->dev = inode->i_sb->s_dev; | ||
829 | stat->ino = inode->i_ino; | ||
830 | stat->mode = (inode->i_mode & S_IFMT) | get_mode(mnt, SDCARDFS_I(top)); | ||
831 | stat->nlink = inode->i_nlink; | ||
832 | stat->uid = make_kuid(&init_user_ns, SDCARDFS_I(top)->d_uid); | ||
833 | stat->gid = make_kgid(&init_user_ns, get_gid(mnt, SDCARDFS_I(top))); | ||
834 | stat->rdev = inode->i_rdev; | ||
835 | stat->size = i_size_read(inode); | ||
836 | stat->atime = inode->i_atime; | ||
837 | stat->mtime = inode->i_mtime; | ||
838 | stat->ctime = inode->i_ctime; | ||
839 | stat->blksize = (1 << inode->i_blkbits); | ||
840 | stat->blocks = inode->i_blocks; | ||
841 | release_top(info); | ||
842 | return 0; | ||
843 | } | ||
844 | |||
730 | static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, | 845 | static int sdcardfs_getattr(struct vfsmount *mnt, struct dentry *dentry, |
731 | struct kstat *stat) | 846 | struct kstat *stat) |
732 | { | 847 | { |
733 | struct dentry *lower_dentry; | 848 | struct kstat lower_stat; |
734 | struct inode *inode; | ||
735 | struct inode *lower_inode; | ||
736 | struct path lower_path; | 849 | struct path lower_path; |
737 | struct dentry *parent; | 850 | struct dentry *parent; |
851 | int err; | ||
738 | 852 | ||
739 | parent = dget_parent(dentry); | 853 | parent = dget_parent(dentry); |
740 | if(!check_caller_access_to_name(d_inode(parent), dentry->d_name.name)) { | 854 | if (!check_caller_access_to_name(d_inode(parent), &dentry->d_name)) { |
741 | printk(KERN_INFO "%s: need to check the caller's gid in packages.list\n" | ||
742 | " dentry: %s, task:%s\n", | ||
743 | __func__, dentry->d_name.name, current->comm); | ||
744 | dput(parent); | 855 | dput(parent); |
745 | return -EACCES; | 856 | return -EACCES; |
746 | } | 857 | } |
747 | dput(parent); | 858 | dput(parent); |
748 | 859 | ||
749 | inode = d_inode(dentry); | ||
750 | |||
751 | sdcardfs_get_lower_path(dentry, &lower_path); | 860 | sdcardfs_get_lower_path(dentry, &lower_path); |
752 | lower_dentry = lower_path.dentry; | 861 | err = vfs_getattr(&lower_path, &lower_stat); |
753 | lower_inode = sdcardfs_lower_inode(inode); | 862 | if (err) |
754 | 863 | goto out; | |
755 | 864 | sdcardfs_copy_and_fix_attrs(d_inode(dentry), | |
756 | sdcardfs_copy_and_fix_attrs(inode, lower_inode); | 865 | d_inode(lower_path.dentry)); |
757 | fsstack_copy_inode_size(inode, lower_inode); | 866 | err = sdcardfs_fillattr(mnt, d_inode(dentry), stat); |
758 | 867 | stat->blocks = lower_stat.blocks; | |
759 | 868 | out: | |
760 | generic_fillattr(inode, stat); | ||
761 | sdcardfs_put_lower_path(dentry, &lower_path); | 869 | sdcardfs_put_lower_path(dentry, &lower_path); |
762 | return 0; | 870 | return err; |
763 | } | 871 | } |
764 | 872 | ||
765 | const struct inode_operations sdcardfs_symlink_iops = { | 873 | const struct inode_operations sdcardfs_symlink_iops = { |
766 | .permission = sdcardfs_permission, | 874 | .permission2 = sdcardfs_permission, |
767 | .setattr = sdcardfs_setattr, | 875 | .setattr2 = sdcardfs_setattr, |
768 | /* XXX Following operations are implemented, | 876 | /* XXX Following operations are implemented, |
769 | * but FUSE(sdcard) or FAT does not support them | 877 | * but FUSE(sdcard) or FAT does not support them |
770 | * These methods are *NOT* perfectly tested. | 878 | * These methods are *NOT* perfectly tested. |
@@ -777,14 +885,14 @@ const struct inode_operations sdcardfs_symlink_iops = { | |||
777 | const struct inode_operations sdcardfs_dir_iops = { | 885 | const struct inode_operations sdcardfs_dir_iops = { |
778 | .create = sdcardfs_create, | 886 | .create = sdcardfs_create, |
779 | .lookup = sdcardfs_lookup, | 887 | .lookup = sdcardfs_lookup, |
780 | #if 0 | 888 | .permission = sdcardfs_permission_wrn, |
781 | .permission = sdcardfs_permission, | 889 | .permission2 = sdcardfs_permission, |
782 | #endif | ||
783 | .unlink = sdcardfs_unlink, | 890 | .unlink = sdcardfs_unlink, |
784 | .mkdir = sdcardfs_mkdir, | 891 | .mkdir = sdcardfs_mkdir, |
785 | .rmdir = sdcardfs_rmdir, | 892 | .rmdir = sdcardfs_rmdir, |
786 | .rename = sdcardfs_rename, | 893 | .rename = sdcardfs_rename, |
787 | .setattr = sdcardfs_setattr, | 894 | .setattr = sdcardfs_setattr_wrn, |
895 | .setattr2 = sdcardfs_setattr, | ||
788 | .getattr = sdcardfs_getattr, | 896 | .getattr = sdcardfs_getattr, |
789 | /* XXX Following operations are implemented, | 897 | /* XXX Following operations are implemented, |
790 | * but FUSE(sdcard) or FAT does not support them | 898 | * but FUSE(sdcard) or FAT does not support them |
@@ -796,7 +904,9 @@ const struct inode_operations sdcardfs_dir_iops = { | |||
796 | }; | 904 | }; |
797 | 905 | ||
798 | const struct inode_operations sdcardfs_main_iops = { | 906 | const struct inode_operations sdcardfs_main_iops = { |
799 | .permission = sdcardfs_permission, | 907 | .permission = sdcardfs_permission_wrn, |
800 | .setattr = sdcardfs_setattr, | 908 | .permission2 = sdcardfs_permission, |
909 | .setattr = sdcardfs_setattr_wrn, | ||
910 | .setattr2 = sdcardfs_setattr, | ||
801 | .getattr = sdcardfs_getattr, | 911 | .getattr = sdcardfs_getattr, |
802 | }; | 912 | }; |