|
@@ -18,7 +18,6 @@
|
|
|
#include <linux/seq_file.h>
|
|
|
#include <linux/posix_acl_xattr.h>
|
|
|
#include "overlayfs.h"
|
|
|
-#include "ovl_entry.h"
|
|
|
|
|
|
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
|
|
|
MODULE_DESCRIPTION("Overlay filesystem");
|
|
@@ -39,15 +38,20 @@ 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_entry_stack_free(struct ovl_entry *oe)
|
|
|
+{
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ for (i = 0; i < oe->numlower; i++)
|
|
|
+ dput(oe->lowerstack[i].dentry);
|
|
|
+}
|
|
|
+
|
|
|
static void ovl_dentry_release(struct dentry *dentry)
|
|
|
{
|
|
|
struct ovl_entry *oe = dentry->d_fsdata;
|
|
|
|
|
|
if (oe) {
|
|
|
- unsigned int i;
|
|
|
-
|
|
|
- for (i = 0; i < oe->numlower; i++)
|
|
|
- dput(oe->lowerstack[i].dentry);
|
|
|
+ ovl_entry_stack_free(oe);
|
|
|
kfree_rcu(oe, rcu);
|
|
|
}
|
|
|
}
|
|
@@ -207,39 +211,48 @@ static void ovl_destroy_inode(struct inode *inode)
|
|
|
call_rcu(&inode->i_rcu, ovl_i_callback);
|
|
|
}
|
|
|
|
|
|
-static void ovl_put_super(struct super_block *sb)
|
|
|
+static void ovl_free_fs(struct ovl_fs *ofs)
|
|
|
{
|
|
|
- struct ovl_fs *ufs = sb->s_fs_info;
|
|
|
unsigned i;
|
|
|
|
|
|
- dput(ufs->indexdir);
|
|
|
- dput(ufs->workdir);
|
|
|
- if (ufs->workdir_locked)
|
|
|
- ovl_inuse_unlock(ufs->workbasedir);
|
|
|
- dput(ufs->workbasedir);
|
|
|
- if (ufs->upper_mnt && ufs->upperdir_locked)
|
|
|
- ovl_inuse_unlock(ufs->upper_mnt->mnt_root);
|
|
|
- mntput(ufs->upper_mnt);
|
|
|
- for (i = 0; i < ufs->numlower; i++)
|
|
|
- mntput(ufs->lower_mnt[i]);
|
|
|
- kfree(ufs->lower_mnt);
|
|
|
-
|
|
|
- kfree(ufs->config.lowerdir);
|
|
|
- kfree(ufs->config.upperdir);
|
|
|
- kfree(ufs->config.workdir);
|
|
|
- put_cred(ufs->creator_cred);
|
|
|
- kfree(ufs);
|
|
|
+ dput(ofs->indexdir);
|
|
|
+ dput(ofs->workdir);
|
|
|
+ if (ofs->workdir_locked)
|
|
|
+ ovl_inuse_unlock(ofs->workbasedir);
|
|
|
+ dput(ofs->workbasedir);
|
|
|
+ if (ofs->upperdir_locked)
|
|
|
+ ovl_inuse_unlock(ofs->upper_mnt->mnt_root);
|
|
|
+ mntput(ofs->upper_mnt);
|
|
|
+ for (i = 0; i < ofs->numlower; i++) {
|
|
|
+ mntput(ofs->lower_layers[i].mnt);
|
|
|
+ free_anon_bdev(ofs->lower_layers[i].pseudo_dev);
|
|
|
+ }
|
|
|
+ kfree(ofs->lower_layers);
|
|
|
+
|
|
|
+ kfree(ofs->config.lowerdir);
|
|
|
+ kfree(ofs->config.upperdir);
|
|
|
+ kfree(ofs->config.workdir);
|
|
|
+ if (ofs->creator_cred)
|
|
|
+ put_cred(ofs->creator_cred);
|
|
|
+ kfree(ofs);
|
|
|
+}
|
|
|
+
|
|
|
+static void ovl_put_super(struct super_block *sb)
|
|
|
+{
|
|
|
+ struct ovl_fs *ofs = sb->s_fs_info;
|
|
|
+
|
|
|
+ ovl_free_fs(ofs);
|
|
|
}
|
|
|
|
|
|
static int ovl_sync_fs(struct super_block *sb, int wait)
|
|
|
{
|
|
|
- struct ovl_fs *ufs = sb->s_fs_info;
|
|
|
+ struct ovl_fs *ofs = sb->s_fs_info;
|
|
|
struct super_block *upper_sb;
|
|
|
int ret;
|
|
|
|
|
|
- if (!ufs->upper_mnt)
|
|
|
+ if (!ofs->upper_mnt)
|
|
|
return 0;
|
|
|
- upper_sb = ufs->upper_mnt->mnt_sb;
|
|
|
+ upper_sb = ofs->upper_mnt->mnt_sb;
|
|
|
if (!upper_sb->s_op->sync_fs)
|
|
|
return 0;
|
|
|
|
|
@@ -277,9 +290,9 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
|
}
|
|
|
|
|
|
/* Will this overlay be forced to mount/remount ro? */
|
|
|
-static bool ovl_force_readonly(struct ovl_fs *ufs)
|
|
|
+static bool ovl_force_readonly(struct ovl_fs *ofs)
|
|
|
{
|
|
|
- return (!ufs->upper_mnt || !ufs->workdir);
|
|
|
+ return (!ofs->upper_mnt || !ofs->workdir);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -291,29 +304,29 @@ static bool ovl_force_readonly(struct ovl_fs *ufs)
|
|
|
static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|
|
{
|
|
|
struct super_block *sb = dentry->d_sb;
|
|
|
- struct ovl_fs *ufs = sb->s_fs_info;
|
|
|
+ struct ovl_fs *ofs = sb->s_fs_info;
|
|
|
|
|
|
- seq_show_option(m, "lowerdir", ufs->config.lowerdir);
|
|
|
- if (ufs->config.upperdir) {
|
|
|
- seq_show_option(m, "upperdir", ufs->config.upperdir);
|
|
|
- seq_show_option(m, "workdir", ufs->config.workdir);
|
|
|
+ seq_show_option(m, "lowerdir", ofs->config.lowerdir);
|
|
|
+ if (ofs->config.upperdir) {
|
|
|
+ seq_show_option(m, "upperdir", ofs->config.upperdir);
|
|
|
+ seq_show_option(m, "workdir", ofs->config.workdir);
|
|
|
}
|
|
|
- if (ufs->config.default_permissions)
|
|
|
+ if (ofs->config.default_permissions)
|
|
|
seq_puts(m, ",default_permissions");
|
|
|
- if (ufs->config.redirect_dir != ovl_redirect_dir_def)
|
|
|
+ if (ofs->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)
|
|
|
+ ofs->config.redirect_dir ? "on" : "off");
|
|
|
+ if (ofs->config.index != ovl_index_def)
|
|
|
seq_printf(m, ",index=%s",
|
|
|
- ufs->config.index ? "on" : "off");
|
|
|
+ ofs->config.index ? "on" : "off");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
static int ovl_remount(struct super_block *sb, int *flags, char *data)
|
|
|
{
|
|
|
- struct ovl_fs *ufs = sb->s_fs_info;
|
|
|
+ struct ovl_fs *ofs = sb->s_fs_info;
|
|
|
|
|
|
- if (!(*flags & MS_RDONLY) && ovl_force_readonly(ufs))
|
|
|
+ if (!(*flags & MS_RDONLY) && ovl_force_readonly(ofs))
|
|
|
return -EROFS;
|
|
|
|
|
|
return 0;
|
|
@@ -451,13 +464,11 @@ 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 super_block *sb,
|
|
|
- struct ovl_fs *ufs,
|
|
|
- struct dentry *dentry,
|
|
|
+static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
|
|
const char *name, bool persist)
|
|
|
{
|
|
|
- struct inode *dir = dentry->d_inode;
|
|
|
- struct vfsmount *mnt = ufs->upper_mnt;
|
|
|
+ struct inode *dir = ofs->workbasedir->d_inode;
|
|
|
+ struct vfsmount *mnt = ofs->upper_mnt;
|
|
|
struct dentry *work;
|
|
|
int err;
|
|
|
bool retried = false;
|
|
@@ -471,7 +482,7 @@ static struct dentry *ovl_workdir_create(struct super_block *sb,
|
|
|
locked = true;
|
|
|
|
|
|
retry:
|
|
|
- work = lookup_one_len(name, dentry, strlen(name));
|
|
|
+ work = lookup_one_len(name, ofs->workbasedir, strlen(name));
|
|
|
|
|
|
if (!IS_ERR(work)) {
|
|
|
struct iattr attr = {
|
|
@@ -541,8 +552,7 @@ out_dput:
|
|
|
dput(work);
|
|
|
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;
|
|
|
+ ofs->config.workdir, name, -err);
|
|
|
work = NULL;
|
|
|
goto out_unlock;
|
|
|
}
|
|
@@ -585,7 +595,7 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
|
|
return 0;
|
|
|
|
|
|
out_put:
|
|
|
- path_put(path);
|
|
|
+ path_put_init(path);
|
|
|
out:
|
|
|
return err;
|
|
|
}
|
|
@@ -603,7 +613,7 @@ static int ovl_mount_dir(const char *name, struct path *path)
|
|
|
if (ovl_dentry_remote(path->dentry)) {
|
|
|
pr_err("overlayfs: filesystem on '%s' not supported as upperdir\n",
|
|
|
tmp);
|
|
|
- path_put(path);
|
|
|
+ path_put_init(path);
|
|
|
err = -EINVAL;
|
|
|
}
|
|
|
kfree(tmp);
|
|
@@ -655,7 +665,7 @@ static int ovl_lower_dir(const char *name, struct path *path,
|
|
|
return 0;
|
|
|
|
|
|
out_put:
|
|
|
- path_put(path);
|
|
|
+ path_put_init(path);
|
|
|
out:
|
|
|
return err;
|
|
|
}
|
|
@@ -826,129 +836,269 @@ static const struct xattr_handler *ovl_xattr_handlers[] = {
|
|
|
NULL
|
|
|
};
|
|
|
|
|
|
-static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
+static int ovl_get_upper(struct ovl_fs *ofs, struct path *upperpath)
|
|
|
{
|
|
|
- struct path upperpath = { };
|
|
|
- struct path workpath = { };
|
|
|
- struct dentry *root_dentry;
|
|
|
- struct ovl_entry *oe;
|
|
|
- struct ovl_fs *ufs;
|
|
|
- struct path *stack = NULL;
|
|
|
- char *lowertmp;
|
|
|
- char *lower;
|
|
|
- unsigned int numlower;
|
|
|
- unsigned int stacklen = 0;
|
|
|
- unsigned int i;
|
|
|
- bool remote = false;
|
|
|
- struct cred *cred;
|
|
|
+ struct vfsmount *upper_mnt;
|
|
|
int err;
|
|
|
|
|
|
- err = -ENOMEM;
|
|
|
- ufs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
|
|
|
- if (!ufs)
|
|
|
+ err = ovl_mount_dir(ofs->config.upperdir, upperpath);
|
|
|
+ if (err)
|
|
|
goto out;
|
|
|
|
|
|
- ufs->config.redirect_dir = ovl_redirect_dir_def;
|
|
|
- ufs->config.index = ovl_index_def;
|
|
|
- err = ovl_parse_opt((char *) data, &ufs->config);
|
|
|
+ /* Upper fs should not be r/o */
|
|
|
+ if (sb_rdonly(upperpath->mnt->mnt_sb)) {
|
|
|
+ pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n");
|
|
|
+ err = -EINVAL;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ err = ovl_check_namelen(upperpath, ofs, ofs->config.upperdir);
|
|
|
if (err)
|
|
|
- goto out_free_config;
|
|
|
+ goto out;
|
|
|
+
|
|
|
+ err = -EBUSY;
|
|
|
+ if (ovl_inuse_trylock(upperpath->dentry)) {
|
|
|
+ ofs->upperdir_locked = true;
|
|
|
+ } else if (ofs->config.index) {
|
|
|
+ pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
|
|
|
+ goto out;
|
|
|
+ } else {
|
|
|
+ pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ upper_mnt = clone_private_mount(upperpath);
|
|
|
+ err = PTR_ERR(upper_mnt);
|
|
|
+ if (IS_ERR(upper_mnt)) {
|
|
|
+ pr_err("overlayfs: failed to clone upperpath\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Don't inherit atime flags */
|
|
|
+ upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
|
|
|
+ ofs->upper_mnt = upper_mnt;
|
|
|
+ err = 0;
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int ovl_make_workdir(struct ovl_fs *ofs, struct path *workpath)
|
|
|
+{
|
|
|
+ struct dentry *temp;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ ofs->workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
|
|
|
+ if (!ofs->workdir)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Upper should support d_type, else whiteouts are visible. Given
|
|
|
+ * workdir and upper are on same fs, we can do iterate_dir() on
|
|
|
+ * workdir. This check requires successful creation of workdir in
|
|
|
+ * previous step.
|
|
|
+ */
|
|
|
+ err = ovl_check_d_type_supported(workpath);
|
|
|
+ if (err < 0)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We allowed this configuration and don't want to break users over
|
|
|
+ * kernel upgrade. So warn instead of erroring out.
|
|
|
+ */
|
|
|
+ if (!err)
|
|
|
+ pr_warn("overlayfs: upper fs needs to support d_type.\n");
|
|
|
+
|
|
|
+ /* Check if upper/work fs supports O_TMPFILE */
|
|
|
+ temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0);
|
|
|
+ ofs->tmpfile = !IS_ERR(temp);
|
|
|
+ if (ofs->tmpfile)
|
|
|
+ dput(temp);
|
|
|
+ else
|
|
|
+ pr_warn("overlayfs: upper fs does not support tmpfile.\n");
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Check if upper/work fs supports trusted.overlay.* xattr
|
|
|
+ */
|
|
|
+ err = ovl_do_setxattr(ofs->workdir, OVL_XATTR_OPAQUE, "0", 1, 0);
|
|
|
+ if (err) {
|
|
|
+ ofs->noxattr = true;
|
|
|
+ pr_warn("overlayfs: upper fs does not support xattr.\n");
|
|
|
+ } else {
|
|
|
+ vfs_removexattr(ofs->workdir, OVL_XATTR_OPAQUE);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Check if upper/work fs supports file handles */
|
|
|
+ if (ofs->config.index &&
|
|
|
+ !ovl_can_decode_fh(ofs->workdir->d_sb)) {
|
|
|
+ ofs->config.index = false;
|
|
|
+ pr_warn("overlayfs: upper fs does not support file handles, falling back to index=off.\n");
|
|
|
+ }
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ovl_get_workdir(struct ovl_fs *ofs, struct path *upperpath)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ struct path workpath = { };
|
|
|
+
|
|
|
+ err = ovl_mount_dir(ofs->config.workdir, &workpath);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
|
|
|
err = -EINVAL;
|
|
|
- if (!ufs->config.lowerdir) {
|
|
|
- if (!silent)
|
|
|
- pr_err("overlayfs: missing 'lowerdir'\n");
|
|
|
- goto out_free_config;
|
|
|
+ if (upperpath->mnt != workpath.mnt) {
|
|
|
+ pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+ if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) {
|
|
|
+ pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- sb->s_stack_depth = 0;
|
|
|
- sb->s_maxbytes = MAX_LFS_FILESIZE;
|
|
|
- if (ufs->config.upperdir) {
|
|
|
- if (!ufs->config.workdir) {
|
|
|
- pr_err("overlayfs: missing 'workdir'\n");
|
|
|
- goto out_free_config;
|
|
|
- }
|
|
|
+ err = -EBUSY;
|
|
|
+ if (ovl_inuse_trylock(workpath.dentry)) {
|
|
|
+ ofs->workdir_locked = true;
|
|
|
+ } else if (ofs->config.index) {
|
|
|
+ pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
|
|
|
+ goto out;
|
|
|
+ } else {
|
|
|
+ pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
|
|
+ }
|
|
|
|
|
|
- err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
|
|
|
- if (err)
|
|
|
- goto out_free_config;
|
|
|
+ ofs->workbasedir = dget(workpath.dentry);
|
|
|
+ err = ovl_make_workdir(ofs, &workpath);
|
|
|
+ if (err)
|
|
|
+ goto out;
|
|
|
|
|
|
- /* Upper fs should not be r/o */
|
|
|
- if (sb_rdonly(upperpath.mnt->mnt_sb)) {
|
|
|
- pr_err("overlayfs: upper fs is r/o, try multi-lower layers mount\n");
|
|
|
- err = -EINVAL;
|
|
|
- goto out_put_upperpath;
|
|
|
- }
|
|
|
+ err = 0;
|
|
|
+out:
|
|
|
+ path_put(&workpath);
|
|
|
|
|
|
- err = ovl_check_namelen(&upperpath, ufs, ufs->config.upperdir);
|
|
|
- if (err)
|
|
|
- goto out_put_upperpath;
|
|
|
-
|
|
|
- err = -EBUSY;
|
|
|
- if (ovl_inuse_trylock(upperpath.dentry)) {
|
|
|
- ufs->upperdir_locked = true;
|
|
|
- } else if (ufs->config.index) {
|
|
|
- pr_err("overlayfs: upperdir is in-use by another mount, mount with '-o index=off' to override exclusive upperdir protection.\n");
|
|
|
- goto out_put_upperpath;
|
|
|
- } else {
|
|
|
- pr_warn("overlayfs: upperdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
|
|
- }
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int ovl_get_indexdir(struct ovl_fs *ofs, struct ovl_entry *oe,
|
|
|
+ struct path *upperpath)
|
|
|
+{
|
|
|
+ int err;
|
|
|
|
|
|
- err = ovl_mount_dir(ufs->config.workdir, &workpath);
|
|
|
+ /* Verify lower root is upper root origin */
|
|
|
+ err = ovl_verify_origin(upperpath->dentry, oe->lowerstack[0].dentry,
|
|
|
+ false, true);
|
|
|
+ if (err) {
|
|
|
+ pr_err("overlayfs: failed to verify upper root origin\n");
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ ofs->indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
|
|
|
+ if (ofs->indexdir) {
|
|
|
+ /* Verify upper root is index dir origin */
|
|
|
+ err = ovl_verify_origin(ofs->indexdir, upperpath->dentry,
|
|
|
+ true, true);
|
|
|
if (err)
|
|
|
- goto out_unlock_upperdentry;
|
|
|
+ pr_err("overlayfs: failed to verify index dir origin\n");
|
|
|
|
|
|
- err = -EINVAL;
|
|
|
- if (upperpath.mnt != workpath.mnt) {
|
|
|
- pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
|
|
|
- goto out_put_workpath;
|
|
|
- }
|
|
|
- if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
|
|
|
- pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
|
|
|
- goto out_put_workpath;
|
|
|
+ /* Cleanup bad/stale/orphan index entries */
|
|
|
+ if (!err)
|
|
|
+ err = ovl_indexdir_cleanup(ofs->indexdir,
|
|
|
+ ofs->upper_mnt,
|
|
|
+ oe->lowerstack,
|
|
|
+ oe->numlower);
|
|
|
+ }
|
|
|
+ if (err || !ofs->indexdir)
|
|
|
+ pr_warn("overlayfs: try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
|
|
|
+
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int ovl_get_lower_layers(struct ovl_fs *ofs, struct path *stack,
|
|
|
+ unsigned int numlower)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ unsigned int i;
|
|
|
+
|
|
|
+ err = -ENOMEM;
|
|
|
+ ofs->lower_layers = kcalloc(numlower, sizeof(struct ovl_layer),
|
|
|
+ GFP_KERNEL);
|
|
|
+ if (ofs->lower_layers == NULL)
|
|
|
+ goto out;
|
|
|
+ for (i = 0; i < numlower; i++) {
|
|
|
+ struct vfsmount *mnt;
|
|
|
+ dev_t dev;
|
|
|
+
|
|
|
+ err = get_anon_bdev(&dev);
|
|
|
+ if (err) {
|
|
|
+ pr_err("overlayfs: failed to get anonymous bdev for lowerpath\n");
|
|
|
+ goto out;
|
|
|
}
|
|
|
|
|
|
- err = -EBUSY;
|
|
|
- if (ovl_inuse_trylock(workpath.dentry)) {
|
|
|
- ufs->workdir_locked = true;
|
|
|
- } else if (ufs->config.index) {
|
|
|
- pr_err("overlayfs: workdir is in-use by another mount, mount with '-o index=off' to override exclusive workdir protection.\n");
|
|
|
- goto out_put_workpath;
|
|
|
- } else {
|
|
|
- pr_warn("overlayfs: workdir is in-use by another mount, accessing files from both mounts will result in undefined behavior.\n");
|
|
|
+ mnt = clone_private_mount(&stack[i]);
|
|
|
+ err = PTR_ERR(mnt);
|
|
|
+ if (IS_ERR(mnt)) {
|
|
|
+ pr_err("overlayfs: failed to clone lowerpath\n");
|
|
|
+ free_anon_bdev(dev);
|
|
|
+ goto out;
|
|
|
}
|
|
|
+ /*
|
|
|
+ * Make lower layers R/O. That way fchmod/fchown on lower file
|
|
|
+ * will fail instead of modifying lower fs.
|
|
|
+ */
|
|
|
+ mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
|
|
|
|
|
|
- ufs->workbasedir = workpath.dentry;
|
|
|
- sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
|
|
|
+ ofs->lower_layers[ofs->numlower].mnt = mnt;
|
|
|
+ ofs->lower_layers[ofs->numlower].pseudo_dev = dev;
|
|
|
+ ofs->numlower++;
|
|
|
+
|
|
|
+ /* Check if all lower layers are on same sb */
|
|
|
+ if (i == 0)
|
|
|
+ ofs->same_sb = mnt->mnt_sb;
|
|
|
+ else if (ofs->same_sb != mnt->mnt_sb)
|
|
|
+ ofs->same_sb = NULL;
|
|
|
}
|
|
|
+ err = 0;
|
|
|
+out:
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
|
|
|
+ struct ovl_fs *ofs)
|
|
|
+{
|
|
|
+ int err;
|
|
|
+ char *lowertmp, *lower;
|
|
|
+ struct path *stack = NULL;
|
|
|
+ unsigned int stacklen, numlower = 0, i;
|
|
|
+ bool remote = false;
|
|
|
+ struct ovl_entry *oe;
|
|
|
+
|
|
|
err = -ENOMEM;
|
|
|
- lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
|
|
|
+ lowertmp = kstrdup(ofs->config.lowerdir, GFP_KERNEL);
|
|
|
if (!lowertmp)
|
|
|
- goto out_unlock_workdentry;
|
|
|
+ goto out_err;
|
|
|
|
|
|
err = -EINVAL;
|
|
|
stacklen = ovl_split_lowerdirs(lowertmp);
|
|
|
if (stacklen > OVL_MAX_STACK) {
|
|
|
pr_err("overlayfs: too many lower directories, limit is %d\n",
|
|
|
OVL_MAX_STACK);
|
|
|
- goto out_free_lowertmp;
|
|
|
- } else if (!ufs->config.upperdir && stacklen == 1) {
|
|
|
+ goto out_err;
|
|
|
+ } else if (!ofs->config.upperdir && stacklen == 1) {
|
|
|
pr_err("overlayfs: at least 2 lowerdir are needed while upperdir nonexistent\n");
|
|
|
- goto out_free_lowertmp;
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
|
|
|
err = -ENOMEM;
|
|
|
stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
|
|
|
if (!stack)
|
|
|
- goto out_free_lowertmp;
|
|
|
+ goto out_err;
|
|
|
|
|
|
err = -EINVAL;
|
|
|
lower = lowertmp;
|
|
|
for (numlower = 0; numlower < stacklen; numlower++) {
|
|
|
- err = ovl_lower_dir(lower, &stack[numlower], ufs,
|
|
|
+ err = ovl_lower_dir(lower, &stack[numlower], ofs,
|
|
|
&sb->s_stack_depth, &remote);
|
|
|
if (err)
|
|
|
- goto out_put_lowerpath;
|
|
|
+ goto out_err;
|
|
|
|
|
|
lower = strchr(lower, '\0') + 1;
|
|
|
}
|
|
@@ -957,190 +1107,144 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
sb->s_stack_depth++;
|
|
|
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
|
|
|
pr_err("overlayfs: maximum fs stacking depth exceeded\n");
|
|
|
- goto out_put_lowerpath;
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
|
|
|
- if (ufs->config.upperdir) {
|
|
|
- ufs->upper_mnt = clone_private_mount(&upperpath);
|
|
|
- err = PTR_ERR(ufs->upper_mnt);
|
|
|
- if (IS_ERR(ufs->upper_mnt)) {
|
|
|
- pr_err("overlayfs: failed to clone upperpath\n");
|
|
|
- goto out_put_lowerpath;
|
|
|
- }
|
|
|
+ err = ovl_get_lower_layers(ofs, stack, numlower);
|
|
|
+ if (err)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ err = -ENOMEM;
|
|
|
+ oe = ovl_alloc_entry(numlower);
|
|
|
+ if (!oe)
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ for (i = 0; i < numlower; i++) {
|
|
|
+ oe->lowerstack[i].dentry = dget(stack[i].dentry);
|
|
|
+ oe->lowerstack[i].layer = &ofs->lower_layers[i];
|
|
|
+ }
|
|
|
|
|
|
- /* Don't inherit atime flags */
|
|
|
- ufs->upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
|
|
|
+ if (remote)
|
|
|
+ sb->s_d_op = &ovl_reval_dentry_operations;
|
|
|
+ else
|
|
|
+ sb->s_d_op = &ovl_dentry_operations;
|
|
|
|
|
|
- sb->s_time_gran = ufs->upper_mnt->mnt_sb->s_time_gran;
|
|
|
+out:
|
|
|
+ for (i = 0; i < numlower; i++)
|
|
|
+ path_put(&stack[i]);
|
|
|
+ kfree(stack);
|
|
|
+ kfree(lowertmp);
|
|
|
|
|
|
- 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
|
|
|
- * iterate_dir() on workdir. This check requires successful
|
|
|
- * creation of workdir in previous step.
|
|
|
- */
|
|
|
- if (ufs->workdir) {
|
|
|
- struct dentry *temp;
|
|
|
-
|
|
|
- err = ovl_check_d_type_supported(&workpath);
|
|
|
- if (err < 0)
|
|
|
- goto out_put_workdir;
|
|
|
-
|
|
|
- /*
|
|
|
- * We allowed this configuration and don't want to
|
|
|
- * break users over kernel upgrade. So warn instead
|
|
|
- * of erroring out.
|
|
|
- */
|
|
|
- if (!err)
|
|
|
- pr_warn("overlayfs: upper fs needs to support d_type.\n");
|
|
|
-
|
|
|
- /* Check if upper/work fs supports O_TMPFILE */
|
|
|
- temp = ovl_do_tmpfile(ufs->workdir, S_IFREG | 0);
|
|
|
- ufs->tmpfile = !IS_ERR(temp);
|
|
|
- if (ufs->tmpfile)
|
|
|
- dput(temp);
|
|
|
- else
|
|
|
- pr_warn("overlayfs: upper fs does not support tmpfile.\n");
|
|
|
-
|
|
|
- /*
|
|
|
- * Check if upper/work fs supports trusted.overlay.*
|
|
|
- * xattr
|
|
|
- */
|
|
|
- err = ovl_do_setxattr(ufs->workdir, OVL_XATTR_OPAQUE,
|
|
|
- "0", 1, 0);
|
|
|
- if (err) {
|
|
|
- ufs->noxattr = true;
|
|
|
- pr_warn("overlayfs: upper fs does not support xattr.\n");
|
|
|
- } else {
|
|
|
- vfs_removexattr(ufs->workdir, OVL_XATTR_OPAQUE);
|
|
|
- }
|
|
|
+ return oe;
|
|
|
|
|
|
- /* 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");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+out_err:
|
|
|
+ oe = ERR_PTR(err);
|
|
|
+ goto out;
|
|
|
+}
|
|
|
+
|
|
|
+static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
+{
|
|
|
+ struct path upperpath = { };
|
|
|
+ struct dentry *root_dentry;
|
|
|
+ struct ovl_entry *oe;
|
|
|
+ struct ovl_fs *ofs;
|
|
|
+ struct cred *cred;
|
|
|
+ int err;
|
|
|
|
|
|
err = -ENOMEM;
|
|
|
- ufs->lower_mnt = kcalloc(numlower, sizeof(struct vfsmount *), GFP_KERNEL);
|
|
|
- if (ufs->lower_mnt == NULL)
|
|
|
- goto out_put_workdir;
|
|
|
- for (i = 0; i < numlower; i++) {
|
|
|
- struct vfsmount *mnt = clone_private_mount(&stack[i]);
|
|
|
+ ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
|
|
|
+ if (!ofs)
|
|
|
+ goto out;
|
|
|
|
|
|
- err = PTR_ERR(mnt);
|
|
|
- if (IS_ERR(mnt)) {
|
|
|
- pr_err("overlayfs: failed to clone lowerpath\n");
|
|
|
- goto out_put_lower_mnt;
|
|
|
- }
|
|
|
- /*
|
|
|
- * Make lower_mnt R/O. That way fchmod/fchown on lower file
|
|
|
- * will fail instead of modifying lower fs.
|
|
|
- */
|
|
|
- mnt->mnt_flags |= MNT_READONLY | MNT_NOATIME;
|
|
|
+ ofs->creator_cred = cred = prepare_creds();
|
|
|
+ if (!cred)
|
|
|
+ goto out_err;
|
|
|
|
|
|
- ufs->lower_mnt[ufs->numlower] = mnt;
|
|
|
- ufs->numlower++;
|
|
|
+ ofs->config.redirect_dir = ovl_redirect_dir_def;
|
|
|
+ ofs->config.index = ovl_index_def;
|
|
|
+ err = ovl_parse_opt((char *) data, &ofs->config);
|
|
|
+ if (err)
|
|
|
+ goto out_err;
|
|
|
|
|
|
- /* Check if all lower layers are on same sb */
|
|
|
- if (i == 0)
|
|
|
- ufs->same_sb = mnt->mnt_sb;
|
|
|
- else if (ufs->same_sb != mnt->mnt_sb)
|
|
|
- ufs->same_sb = NULL;
|
|
|
+ err = -EINVAL;
|
|
|
+ if (!ofs->config.lowerdir) {
|
|
|
+ if (!silent)
|
|
|
+ pr_err("overlayfs: missing 'lowerdir'\n");
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
|
|
|
- /* If the upper fs is nonexistent, we mark overlayfs r/o too */
|
|
|
- if (!ufs->upper_mnt)
|
|
|
- sb->s_flags |= MS_RDONLY;
|
|
|
- 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;
|
|
|
+ sb->s_stack_depth = 0;
|
|
|
+ sb->s_maxbytes = MAX_LFS_FILESIZE;
|
|
|
+ if (ofs->config.upperdir) {
|
|
|
+ if (!ofs->config.workdir) {
|
|
|
+ pr_err("overlayfs: missing 'workdir'\n");
|
|
|
+ goto out_err;
|
|
|
}
|
|
|
|
|
|
- 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");
|
|
|
+ err = ovl_get_upper(ofs, &upperpath);
|
|
|
+ if (err)
|
|
|
+ goto out_err;
|
|
|
|
|
|
- /* 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");
|
|
|
+ err = ovl_get_workdir(ofs, &upperpath);
|
|
|
if (err)
|
|
|
- goto out_put_indexdir;
|
|
|
+ goto out_err;
|
|
|
+
|
|
|
+ if (!ofs->workdir)
|
|
|
+ sb->s_flags |= MS_RDONLY;
|
|
|
+
|
|
|
+ sb->s_stack_depth = ofs->upper_mnt->mnt_sb->s_stack_depth;
|
|
|
+ sb->s_time_gran = ofs->upper_mnt->mnt_sb->s_time_gran;
|
|
|
+
|
|
|
}
|
|
|
+ oe = ovl_get_lowerstack(sb, ofs);
|
|
|
+ err = PTR_ERR(oe);
|
|
|
+ if (IS_ERR(oe))
|
|
|
+ goto out_err;
|
|
|
|
|
|
- /* Show index=off/on in /proc/mounts for any of the reasons above */
|
|
|
- if (!ufs->indexdir)
|
|
|
- ufs->config.index = false;
|
|
|
+ /* If the upper fs is nonexistent, we mark overlayfs r/o too */
|
|
|
+ if (!ofs->upper_mnt)
|
|
|
+ sb->s_flags |= MS_RDONLY;
|
|
|
+ else if (ofs->upper_mnt->mnt_sb != ofs->same_sb)
|
|
|
+ ofs->same_sb = NULL;
|
|
|
|
|
|
- if (remote)
|
|
|
- sb->s_d_op = &ovl_reval_dentry_operations;
|
|
|
- else
|
|
|
- sb->s_d_op = &ovl_dentry_operations;
|
|
|
+ if (!(ovl_force_readonly(ofs)) && ofs->config.index) {
|
|
|
+ err = ovl_get_indexdir(ofs, oe, &upperpath);
|
|
|
+ if (err)
|
|
|
+ goto out_free_oe;
|
|
|
|
|
|
- err = -ENOMEM;
|
|
|
- ufs->creator_cred = cred = prepare_creds();
|
|
|
- if (!cred)
|
|
|
- goto out_put_indexdir;
|
|
|
+ if (!ofs->indexdir)
|
|
|
+ sb->s_flags |= MS_RDONLY;
|
|
|
+ }
|
|
|
+
|
|
|
+ /* Show index=off/on in /proc/mounts for any of the reasons above */
|
|
|
+ if (!ofs->indexdir)
|
|
|
+ ofs->config.index = false;
|
|
|
|
|
|
/* Never override disk quota limits or use reserved space */
|
|
|
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
|
|
|
|
|
|
- err = -ENOMEM;
|
|
|
- oe = ovl_alloc_entry(numlower);
|
|
|
- if (!oe)
|
|
|
- goto out_put_cred;
|
|
|
-
|
|
|
sb->s_magic = OVERLAYFS_SUPER_MAGIC;
|
|
|
sb->s_op = &ovl_super_operations;
|
|
|
sb->s_xattr = ovl_xattr_handlers;
|
|
|
- sb->s_fs_info = ufs;
|
|
|
+ sb->s_fs_info = ofs;
|
|
|
sb->s_flags |= MS_POSIXACL | MS_NOREMOTELOCK;
|
|
|
|
|
|
+ err = -ENOMEM;
|
|
|
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, 0));
|
|
|
if (!root_dentry)
|
|
|
goto out_free_oe;
|
|
|
|
|
|
mntput(upperpath.mnt);
|
|
|
- for (i = 0; i < numlower; i++)
|
|
|
- mntput(stack[i].mnt);
|
|
|
- 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));
|
|
|
}
|
|
|
- for (i = 0; i < numlower; i++) {
|
|
|
- oe->lowerstack[i].dentry = stack[i].dentry;
|
|
|
- oe->lowerstack[i].mnt = ufs->lower_mnt[i];
|
|
|
- }
|
|
|
- kfree(stack);
|
|
|
|
|
|
root_dentry->d_fsdata = oe;
|
|
|
|
|
|
+ /* Root is always merge -> can have whiteouts */
|
|
|
+ ovl_set_flag(OVL_WHITEOUTS, d_inode(root_dentry));
|
|
|
ovl_inode_init(d_inode(root_dentry), upperpath.dentry,
|
|
|
ovl_dentry_lower(root_dentry));
|
|
|
|
|
@@ -1149,39 +1253,11 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
return 0;
|
|
|
|
|
|
out_free_oe:
|
|
|
+ ovl_entry_stack_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]);
|
|
|
- kfree(ufs->lower_mnt);
|
|
|
-out_put_workdir:
|
|
|
- dput(ufs->workdir);
|
|
|
- mntput(ufs->upper_mnt);
|
|
|
-out_put_lowerpath:
|
|
|
- for (i = 0; i < numlower; i++)
|
|
|
- path_put(&stack[i]);
|
|
|
- kfree(stack);
|
|
|
-out_free_lowertmp:
|
|
|
- kfree(lowertmp);
|
|
|
-out_unlock_workdentry:
|
|
|
- if (ufs->workdir_locked)
|
|
|
- ovl_inuse_unlock(workpath.dentry);
|
|
|
-out_put_workpath:
|
|
|
- path_put(&workpath);
|
|
|
-out_unlock_upperdentry:
|
|
|
- if (ufs->upperdir_locked)
|
|
|
- ovl_inuse_unlock(upperpath.dentry);
|
|
|
-out_put_upperpath:
|
|
|
+out_err:
|
|
|
path_put(&upperpath);
|
|
|
-out_free_config:
|
|
|
- kfree(ufs->config.lowerdir);
|
|
|
- kfree(ufs->config.upperdir);
|
|
|
- kfree(ufs->config.workdir);
|
|
|
- kfree(ufs);
|
|
|
+ ovl_free_fs(ofs);
|
|
|
out:
|
|
|
return err;
|
|
|
}
|