Merge back cpufreq changes for v5.15.
[linux-2.6-microblaze.git] / fs / fuse / inode.c
index 393e36b..b9beb39 100644 (file)
@@ -350,8 +350,8 @@ retry:
                inode->i_generation = generation;
                fuse_init_inode(inode, attr);
                unlock_new_inode(inode);
-       } else if (inode_wrong_type(inode, attr->mode)) {
-               /* Inode has changed type, any I/O on the old should fail */
+       } else if (fuse_stale_inode(inode, generation, attr)) {
+               /* nodeid was reused, any I/O on the old inode should fail */
                fuse_make_bad(inode);
                iput(inode);
                goto retry;
@@ -506,6 +506,45 @@ static int fuse_statfs(struct dentry *dentry, struct kstatfs *buf)
        return err;
 }
 
+static int fuse_sync_fs(struct super_block *sb, int wait)
+{
+       struct fuse_mount *fm = get_fuse_mount_super(sb);
+       struct fuse_conn *fc = fm->fc;
+       struct fuse_syncfs_in inarg;
+       FUSE_ARGS(args);
+       int err;
+
+       /*
+        * Userspace cannot handle the wait == 0 case.  Avoid a
+        * gratuitous roundtrip.
+        */
+       if (!wait)
+               return 0;
+
+       /* The filesystem is being unmounted.  Nothing to do. */
+       if (!sb->s_root)
+               return 0;
+
+       if (!fc->sync_fs)
+               return 0;
+
+       memset(&inarg, 0, sizeof(inarg));
+       args.in_numargs = 1;
+       args.in_args[0].size = sizeof(inarg);
+       args.in_args[0].value = &inarg;
+       args.opcode = FUSE_SYNCFS;
+       args.nodeid = get_node_id(sb->s_root->d_inode);
+       args.out_numargs = 0;
+
+       err = fuse_simple_request(fm, &args);
+       if (err == -ENOSYS) {
+               fc->sync_fs = 0;
+               err = 0;
+       }
+
+       return err;
+}
+
 enum {
        OPT_SOURCE,
        OPT_SUBTYPE,
@@ -909,6 +948,7 @@ static const struct super_operations fuse_super_operations = {
        .put_super      = fuse_put_super,
        .umount_begin   = fuse_umount_begin,
        .statfs         = fuse_statfs,
+       .sync_fs        = fuse_sync_fs,
        .show_options   = fuse_show_options,
 };
 
@@ -1275,8 +1315,8 @@ static void fuse_sb_defaults(struct super_block *sb)
                sb->s_xattr = fuse_no_acl_xattr_handlers;
 }
 
-int fuse_fill_super_submount(struct super_block *sb,
-                            struct fuse_inode *parent_fi)
+static int fuse_fill_super_submount(struct super_block *sb,
+                                   struct fuse_inode *parent_fi)
 {
        struct fuse_mount *fm = get_fuse_mount_super(sb);
        struct super_block *parent_sb = parent_fi->inode.i_sb;
@@ -1313,6 +1353,58 @@ int fuse_fill_super_submount(struct super_block *sb,
        return 0;
 }
 
+/* Filesystem context private data holds the FUSE inode of the mount point */
+static int fuse_get_tree_submount(struct fs_context *fsc)
+{
+       struct fuse_mount *fm;
+       struct fuse_inode *mp_fi = fsc->fs_private;
+       struct fuse_conn *fc = get_fuse_conn(&mp_fi->inode);
+       struct super_block *sb;
+       int err;
+
+       fm = kzalloc(sizeof(struct fuse_mount), GFP_KERNEL);
+       if (!fm)
+               return -ENOMEM;
+
+       fsc->s_fs_info = fm;
+       sb = sget_fc(fsc, NULL, set_anon_super_fc);
+       if (IS_ERR(sb)) {
+               kfree(fm);
+               return PTR_ERR(sb);
+       }
+       fm->fc = fuse_conn_get(fc);
+
+       /* Initialize superblock, making @mp_fi its root */
+       err = fuse_fill_super_submount(sb, mp_fi);
+       if (err) {
+               fuse_conn_put(fc);
+               kfree(fm);
+               sb->s_fs_info = NULL;
+               deactivate_locked_super(sb);
+               return err;
+       }
+
+       down_write(&fc->killsb);
+       list_add_tail(&fm->fc_entry, &fc->mounts);
+       up_write(&fc->killsb);
+
+       sb->s_flags |= SB_ACTIVE;
+       fsc->root = dget(sb->s_root);
+
+       return 0;
+}
+
+static const struct fs_context_operations fuse_context_submount_ops = {
+       .get_tree       = fuse_get_tree_submount,
+};
+
+int fuse_init_fs_context_submount(struct fs_context *fsc)
+{
+       fsc->ops = &fuse_context_submount_ops;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(fuse_init_fs_context_submount);
+
 int fuse_fill_super_common(struct super_block *sb, struct fuse_fs_context *ctx)
 {
        struct fuse_dev *fud = NULL;