]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - fs/overlayfs/super.c
ovl: remove unneeded check for IS_ERR()
[mirror_ubuntu-bionic-kernel.git] / fs / overlayfs / super.c
index e0a51ea773eca3361509cc727c0e2c1332876d48..c88493b01d8dbd2b28ef38a411de7b53f29a7766 100644 (file)
@@ -34,6 +34,11 @@ module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
 MODULE_PARM_DESC(ovl_redirect_dir_def,
                 "Default to on or off for the redirect_dir feature");
 
+static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
+module_param_named(index, ovl_index_def, bool, 0644);
+MODULE_PARM_DESC(ovl_index_def,
+                "Default to on or off for the inodes index feature");
+
 static void ovl_dentry_release(struct dentry *dentry)
 {
        struct ovl_entry *oe = dentry->d_fsdata;
@@ -203,7 +208,12 @@ static void ovl_put_super(struct super_block *sb)
        struct ovl_fs *ufs = sb->s_fs_info;
        unsigned i;
 
+       dput(ufs->indexdir);
        dput(ufs->workdir);
+       ovl_inuse_unlock(ufs->workbasedir);
+       dput(ufs->workbasedir);
+       if (ufs->upper_mnt)
+               ovl_inuse_unlock(ufs->upper_mnt->mnt_root);
        mntput(ufs->upper_mnt);
        for (i = 0; i < ufs->numlower; i++)
                mntput(ufs->lower_mnt[i]);
@@ -261,6 +271,12 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
        return err;
 }
 
+/* Will this overlay be forced to mount/remount ro? */
+static bool ovl_force_readonly(struct ovl_fs *ufs)
+{
+       return (!ufs->upper_mnt || !ufs->workdir);
+}
+
 /**
  * ovl_show_options
  *
@@ -282,6 +298,9 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
        if (ufs->config.redirect_dir != ovl_redirect_dir_def)
                seq_printf(m, ",redirect_dir=%s",
                           ufs->config.redirect_dir ? "on" : "off");
+       if (ufs->config.index != ovl_index_def)
+               seq_printf(m, ",index=%s",
+                          ufs->config.index ? "on" : "off");
        return 0;
 }
 
@@ -289,7 +308,7 @@ static int ovl_remount(struct super_block *sb, int *flags, char *data)
 {
        struct ovl_fs *ufs = sb->s_fs_info;
 
-       if (!(*flags & MS_RDONLY) && (!ufs->upper_mnt || !ufs->workdir))
+       if (!(*flags & MS_RDONLY) && ovl_force_readonly(ufs))
                return -EROFS;
 
        return 0;
@@ -313,6 +332,8 @@ enum {
        OPT_DEFAULT_PERMISSIONS,
        OPT_REDIRECT_DIR_ON,
        OPT_REDIRECT_DIR_OFF,
+       OPT_INDEX_ON,
+       OPT_INDEX_OFF,
        OPT_ERR,
 };
 
@@ -323,6 +344,8 @@ static const match_table_t ovl_tokens = {
        {OPT_DEFAULT_PERMISSIONS,       "default_permissions"},
        {OPT_REDIRECT_DIR_ON,           "redirect_dir=on"},
        {OPT_REDIRECT_DIR_OFF,          "redirect_dir=off"},
+       {OPT_INDEX_ON,                  "index=on"},
+       {OPT_INDEX_OFF,                 "index=off"},
        {OPT_ERR,                       NULL}
 };
 
@@ -395,6 +418,14 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
                        config->redirect_dir = false;
                        break;
 
+               case OPT_INDEX_ON:
+                       config->index = true;
+                       break;
+
+               case OPT_INDEX_OFF:
+                       config->index = false;
+                       break;
+
                default:
                        pr_err("overlayfs: unrecognized mount option \"%s\" or missing value\n", p);
                        return -EINVAL;
@@ -413,23 +444,29 @@ static int ovl_parse_opt(char *opt, struct ovl_config *config)
 }
 
 #define OVL_WORKDIR_NAME "work"
+#define OVL_INDEXDIR_NAME "index"
 
-static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
-                                        struct dentry *dentry)
+static struct dentry *ovl_workdir_create(struct super_block *sb,
+                                        struct ovl_fs *ufs,
+                                        struct dentry *dentry,
+                                        const char *name, bool persist)
 {
        struct inode *dir = dentry->d_inode;
+       struct vfsmount *mnt = ufs->upper_mnt;
        struct dentry *work;
        int err;
        bool retried = false;
+       bool locked = false;
 
        err = mnt_want_write(mnt);
        if (err)
-               return ERR_PTR(err);
+               goto out_err;
 
        inode_lock_nested(dir, I_MUTEX_PARENT);
+       locked = true;
+
 retry:
-       work = lookup_one_len(OVL_WORKDIR_NAME, dentry,
-                             strlen(OVL_WORKDIR_NAME));
+       work = lookup_one_len(name, dentry, strlen(name));
 
        if (!IS_ERR(work)) {
                struct iattr attr = {
@@ -442,6 +479,9 @@ retry:
                        if (retried)
                                goto out_dput;
 
+                       if (persist)
+                               goto out_unlock;
+
                        retried = true;
                        ovl_workdir_cleanup(dir, mnt, work, 0);
                        dput(work);
@@ -481,16 +521,24 @@ retry:
                inode_unlock(work->d_inode);
                if (err)
                        goto out_dput;
+       } else {
+               err = PTR_ERR(work);
+               goto out_err;
        }
 out_unlock:
-       inode_unlock(dir);
        mnt_drop_write(mnt);
+       if (locked)
+               inode_unlock(dir);
 
        return work;
 
 out_dput:
        dput(work);
-       work = ERR_PTR(err);
+out_err:
+       pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n",
+               ufs->config.workdir, name, -err);
+       sb->s_flags |= MS_RDONLY;
+       work = NULL;
        goto out_unlock;
 }
 
@@ -590,6 +638,15 @@ static int ovl_lower_dir(const char *name, struct path *path,
        if (ovl_dentry_remote(path->dentry))
                *remote = true;
 
+       /*
+        * The inodes index feature needs to encode and decode file
+        * handles, so it requires that all layers support them.
+        */
+       if (ofs->config.index && !ovl_can_decode_fh(path->dentry->d_sb)) {
+               ofs->config.index = false;
+               pr_warn("overlayfs: fs on '%s' does not support file handles, falling back to index=off.\n", name);
+       }
+
        return 0;
 
 out_put:
@@ -787,6 +844,7 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                goto out;
 
        ufs->config.redirect_dir = ovl_redirect_dir_def;
+       ufs->config.index = ovl_index_def;
        err = ovl_parse_opt((char *) data, &ufs->config);
        if (err)
                goto out_free_config;
@@ -821,9 +879,15 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                if (err)
                        goto out_put_upperpath;
 
+               err = -EBUSY;
+               if (!ovl_inuse_trylock(upperpath.dentry)) {
+                       pr_err("overlayfs: upperdir is in-use by another mount\n");
+                       goto out_put_upperpath;
+               }
+
                err = ovl_mount_dir(ufs->config.workdir, &workpath);
                if (err)
-                       goto out_put_upperpath;
+                       goto out_unlock_upperdentry;
 
                err = -EINVAL;
                if (upperpath.mnt != workpath.mnt) {
@@ -834,12 +898,20 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
                        goto out_put_workpath;
                }
+
+               err = -EBUSY;
+               if (!ovl_inuse_trylock(workpath.dentry)) {
+                       pr_err("overlayfs: workdir is in-use by another mount\n");
+                       goto out_put_workpath;
+               }
+
+               ufs->workbasedir = workpath.dentry;
                sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
        }
        err = -ENOMEM;
        lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
        if (!lowertmp)
-               goto out_put_workpath;
+               goto out_unlock_workdentry;
 
        err = -EINVAL;
        stacklen = ovl_split_lowerdirs(lowertmp);
@@ -882,20 +954,14 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        pr_err("overlayfs: failed to clone upperpath\n");
                        goto out_put_lowerpath;
                }
+
                /* Don't inherit atime flags */
                ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
 
                sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran;
 
-               ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
-               err = PTR_ERR(ufs->workdir);
-               if (IS_ERR(ufs->workdir)) {
-                       pr_warn("overlayfs: failed to create directory %s/%s (errno: %i); mounting read-only\n",
-                               ufs->config.workdir, OVL_WORKDIR_NAME, -err);
-                       sb->s_flags |= MS_RDONLY;
-                       ufs->workdir = NULL;
-               }
-
+               ufs->workdir = ovl_workdir_create(sb, ufs, workpath.dentry,
+                                                 OVL_WORKDIR_NAME, false);
                /*
                 * Upper should support d_type, else whiteouts are visible.
                 * Given workdir and upper are on same fs, we can do
@@ -937,6 +1003,13 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
                        } else {
                                vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE);
                        }
+
+                       /* Check if upper/work fs supports file handles */
+                       if (ufs->config.index &&
+                           !ovl_can_decode_fh(ufs->workdir->d_sb)) {
+                               ufs->config.index = false;
+                               pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
+                       }
                }
        }
 
@@ -974,14 +1047,49 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        else if (ufs->upper_mnt->mnt_sb != ufs->same_sb)
                ufs->same_sb = NULL;
 
+       if (!(ovl_force_readonly(ufs)) && ufs->config.index) {
+               /* Verify lower root is upper root origin */
+               err = ovl_verify_origin(upperpath.dentry, ufs->lower_mnt[0],
+                                       stack[0].dentry, false, true);
+               if (err) {
+                       pr_err("overlayfs: failed to verify upper root origin\n");
+                       goto out_put_lower_mnt;
+               }
+
+               ufs->indexdir = ovl_workdir_create(sb, ufs, workpath.dentry,
+                                                  OVL_INDEXDIR_NAME, true);
+               if (ufs->indexdir) {
+                       /* Verify upper root is index dir origin */
+                       err = ovl_verify_origin(ufs->indexdir, ufs->upper_mnt,
+                                               upperpath.dentry, true, true);
+                       if (err)
+                               pr_err("overlayfs: failed to verify index dir origin\n");
+
+                       /* Cleanup bad/stale/orphan index entries */
+                       if (!err)
+                               err = ovl_indexdir_cleanup(ufs->indexdir,
+                                                          ufs->upper_mnt,
+                                                          stack, numlower);
+               }
+               if (err || !ufs->indexdir)
+                       pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
+               if (err)
+                       goto out_put_indexdir;
+       }
+
+       /* Show index=off/on in /proc/mounts for any of the reasons above */
+       if (!ufs->indexdir)
+               ufs->config.index = false;
+
        if (remote)
                sb->s_d_op = &ovl_reval_dentry_operations;
        else
                sb->s_d_op = &ovl_dentry_operations;
 
+       err = -ENOMEM;
        ufs->creator_cred = cred = prepare_creds();
        if (!cred)
-               goto out_put_lower_mnt;
+               goto out_put_indexdir;
 
        /* Never override disk quota limits or use reserved space */
        cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
@@ -1004,10 +1112,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
        mntput(upperpath.mnt);
        for (i = 0; i < numlower; i++)
                mntput(stack[i].mnt);
-       path_put(&workpath);
+       mntput(workpath.mnt);
        kfree(lowertmp);
 
        if (upperpath.dentry) {
+               oe->has_upper = true;
                if (ovl_is_impuredir(upperpath.dentry))
                        ovl_set_flag(OVL_IMPURE, d_inode(root_dentry));
        }
@@ -1030,6 +1139,8 @@ out_free_oe:
        kfree(oe);
 out_put_cred:
        put_cred(ufs->creator_cred);
+out_put_indexdir:
+       dput(ufs->indexdir);
 out_put_lower_mnt:
        for (i = 0; i < ufs->numlower; i++)
                mntput(ufs->lower_mnt[i]);
@@ -1043,8 +1154,12 @@ out_put_lowerpath:
        kfree(stack);
 out_free_lowertmp:
        kfree(lowertmp);
+out_unlock_workdentry:
+       ovl_inuse_unlock(workpath.dentry);
 out_put_workpath:
        path_put(&workpath);
+out_unlock_upperdentry:
+       ovl_inuse_unlock(upperpath.dentry);
 out_put_upperpath:
        path_put(&upperpath);
 out_free_config: