|
10 | 10 |
|
11 | 11 | #include <linux/pagemap.h>
|
12 | 12 | #include <linux/file.h>
|
| 13 | +#include <linux/fs_context.h> |
13 | 14 | #include <linux/sched.h>
|
14 | 15 | #include <linux/namei.h>
|
15 | 16 | #include <linux/slab.h>
|
@@ -237,7 +238,8 @@ static int fuse_dentry_revalidate(struct dentry *entry, unsigned int flags)
|
237 | 238 | ret = -ENOENT;
|
238 | 239 | if (!ret) {
|
239 | 240 | fi = get_fuse_inode(inode);
|
240 |
| - if (outarg.nodeid != get_node_id(inode)) { |
| 241 | + if (outarg.nodeid != get_node_id(inode) || |
| 242 | + (bool) IS_AUTOMOUNT(inode) != (bool) (outarg.attr.flags & FUSE_ATTR_SUBMOUNT)) { |
241 | 243 | fuse_queue_forget(fm->fc, forget,
|
242 | 244 | outarg.nodeid, 1);
|
243 | 245 | goto invalid;
|
@@ -299,13 +301,87 @@ static int fuse_dentry_delete(const struct dentry *dentry)
|
299 | 301 | return time_before64(fuse_dentry_time(dentry), get_jiffies_64());
|
300 | 302 | }
|
301 | 303 |
|
| 304 | +/* |
| 305 | + * Create a fuse_mount object with a new superblock (with path->dentry |
| 306 | + * as the root), and return that mount so it can be auto-mounted on |
| 307 | + * @path. |
| 308 | + */ |
| 309 | +static struct vfsmount *fuse_dentry_automount(struct path *path) |
| 310 | +{ |
| 311 | + struct fs_context *fsc; |
| 312 | + struct fuse_mount *parent_fm = get_fuse_mount_super(path->mnt->mnt_sb); |
| 313 | + struct fuse_conn *fc = parent_fm->fc; |
| 314 | + struct fuse_mount *fm; |
| 315 | + struct vfsmount *mnt; |
| 316 | + struct fuse_inode *mp_fi = get_fuse_inode(d_inode(path->dentry)); |
| 317 | + struct super_block *sb; |
| 318 | + int err; |
| 319 | + |
| 320 | + fsc = fs_context_for_submount(path->mnt->mnt_sb->s_type, path->dentry); |
| 321 | + if (IS_ERR(fsc)) { |
| 322 | + err = PTR_ERR(fsc); |
| 323 | + goto out; |
| 324 | + } |
| 325 | + |
| 326 | + err = -ENOMEM; |
| 327 | + fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL); |
| 328 | + if (!fm) |
| 329 | + goto out_put_fsc; |
| 330 | + |
| 331 | + refcount_set(&fm->count, 1); |
| 332 | + fsc->s_fs_info = fm; |
| 333 | + sb = sget_fc(fsc, NULL, set_anon_super_fc); |
| 334 | + if (IS_ERR(sb)) { |
| 335 | + err = PTR_ERR(sb); |
| 336 | + fuse_mount_put(fm); |
| 337 | + goto out_put_fsc; |
| 338 | + } |
| 339 | + fm->fc = fuse_conn_get(fc); |
| 340 | + |
| 341 | + /* Initialize superblock, making @mp_fi its root */ |
| 342 | + err = fuse_fill_super_submount(sb, mp_fi); |
| 343 | + if (err) |
| 344 | + goto out_put_sb; |
| 345 | + |
| 346 | + sb->s_flags |= SB_ACTIVE; |
| 347 | + fsc->root = dget(sb->s_root); |
| 348 | + /* We are done configuring the superblock, so unlock it */ |
| 349 | + up_write(&sb->s_umount); |
| 350 | + |
| 351 | + down_write(&fc->killsb); |
| 352 | + list_add_tail(&fm->fc_entry, &fc->mounts); |
| 353 | + up_write(&fc->killsb); |
| 354 | + |
| 355 | + /* Create the submount */ |
| 356 | + mnt = vfs_create_mount(fsc); |
| 357 | + if (IS_ERR(mnt)) { |
| 358 | + err = PTR_ERR(mnt); |
| 359 | + goto out_put_fsc; |
| 360 | + } |
| 361 | + mntget(mnt); |
| 362 | + put_fs_context(fsc); |
| 363 | + return mnt; |
| 364 | + |
| 365 | +out_put_sb: |
| 366 | + /* |
| 367 | + * Only jump here when fsc->root is NULL and sb is still locked |
| 368 | + * (otherwise put_fs_context() will put the superblock) |
| 369 | + */ |
| 370 | + deactivate_locked_super(sb); |
| 371 | +out_put_fsc: |
| 372 | + put_fs_context(fsc); |
| 373 | +out: |
| 374 | + return ERR_PTR(err); |
| 375 | +} |
| 376 | + |
302 | 377 | const struct dentry_operations fuse_dentry_operations = {
|
303 | 378 | .d_revalidate = fuse_dentry_revalidate,
|
304 | 379 | .d_delete = fuse_dentry_delete,
|
305 | 380 | #if BITS_PER_LONG < 64
|
306 | 381 | .d_init = fuse_dentry_init,
|
307 | 382 | .d_release = fuse_dentry_release,
|
308 | 383 | #endif
|
| 384 | + .d_automount = fuse_dentry_automount, |
309 | 385 | };
|
310 | 386 |
|
311 | 387 | const struct dentry_operations fuse_root_dentry_operations = {
|
|
0 commit comments