2019-06-04 16:11:33 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
/*
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 Novell Inc.
|
|
|
|
*/
|
|
|
|
|
2017-02-03 00:54:15 +08:00
|
|
|
#include <uapi/linux/magic.h>
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
#include <linux/fs.h>
|
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/xattr.h>
|
|
|
|
#include <linux/mount.h>
|
|
|
|
#include <linux/parser.h>
|
|
|
|
#include <linux/module.h>
|
2014-10-24 06:14:38 +08:00
|
|
|
#include <linux/statfs.h>
|
2014-10-24 06:14:38 +08:00
|
|
|
#include <linux/seq_file.h>
|
2016-07-29 18:05:24 +08:00
|
|
|
#include <linux/posix_acl_xattr.h>
|
2017-11-07 19:55:04 +08:00
|
|
|
#include <linux/exportfs.h>
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
#include "overlayfs.h"
|
|
|
|
|
|
|
|
MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
|
|
|
|
MODULE_DESCRIPTION("Overlay filesystem");
|
|
|
|
MODULE_LICENSE("GPL");
|
|
|
|
|
|
|
|
|
|
|
|
struct ovl_dir_cache;
|
|
|
|
|
2014-12-13 07:59:52 +08:00
|
|
|
#define OVL_MAX_STACK 500
|
|
|
|
|
2016-12-16 18:02:57 +08:00
|
|
|
static bool ovl_redirect_dir_def = IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_DIR);
|
|
|
|
module_param_named(redirect_dir, ovl_redirect_dir_def, bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(redirect_dir,
|
2016-12-16 18:02:57 +08:00
|
|
|
"Default to on or off for the redirect_dir feature");
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2017-12-11 18:28:10 +08:00
|
|
|
static bool ovl_redirect_always_follow =
|
|
|
|
IS_ENABLED(CONFIG_OVERLAY_FS_REDIRECT_ALWAYS_FOLLOW);
|
|
|
|
module_param_named(redirect_always_follow, ovl_redirect_always_follow,
|
|
|
|
bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(redirect_always_follow,
|
2017-12-11 18:28:10 +08:00
|
|
|
"Follow redirects even if redirect_dir feature is turned off");
|
|
|
|
|
2017-06-21 20:28:36 +08:00
|
|
|
static bool ovl_index_def = IS_ENABLED(CONFIG_OVERLAY_FS_INDEX);
|
|
|
|
module_param_named(index, ovl_index_def, bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(index,
|
2017-06-21 20:28:36 +08:00
|
|
|
"Default to on or off for the inodes index feature");
|
|
|
|
|
2018-01-19 17:26:53 +08:00
|
|
|
static bool ovl_nfs_export_def = IS_ENABLED(CONFIG_OVERLAY_FS_NFS_EXPORT);
|
|
|
|
module_param_named(nfs_export, ovl_nfs_export_def, bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(nfs_export,
|
2018-01-19 17:26:53 +08:00
|
|
|
"Default to on or off for the NFS export feature");
|
|
|
|
|
2018-03-29 14:08:18 +08:00
|
|
|
static bool ovl_xino_auto_def = IS_ENABLED(CONFIG_OVERLAY_FS_XINO_AUTO);
|
|
|
|
module_param_named(xino_auto, ovl_xino_auto_def, bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(xino_auto,
|
2018-03-29 14:08:18 +08:00
|
|
|
"Auto enable xino feature");
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-11 23:49:27 +08:00
|
|
|
static bool ovl_metacopy_def = IS_ENABLED(CONFIG_OVERLAY_FS_METACOPY);
|
|
|
|
module_param_named(metacopy, ovl_metacopy_def, bool, 0644);
|
2019-06-17 15:39:00 +08:00
|
|
|
MODULE_PARM_DESC(metacopy,
|
2018-05-11 23:49:27 +08:00
|
|
|
"Default to on or off for the metadata only copy up feature");
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static void ovl_dentry_release(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct ovl_entry *oe = dentry->d_fsdata;
|
|
|
|
|
|
|
|
if (oe) {
|
2017-11-10 16:39:15 +08:00
|
|
|
ovl_entry_stack_free(oe);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
kfree_rcu(oe, rcu);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-30 14:53:27 +08:00
|
|
|
static struct dentry *ovl_d_real(struct dentry *dentry,
|
2018-07-18 21:44:44 +08:00
|
|
|
const struct inode *inode)
|
2016-03-27 04:14:37 +08:00
|
|
|
{
|
|
|
|
struct dentry *real;
|
|
|
|
|
2018-07-18 21:44:41 +08:00
|
|
|
/* It's an overlay file */
|
|
|
|
if (inode && d_inode(dentry) == inode)
|
|
|
|
return dentry;
|
|
|
|
|
2016-12-16 18:02:55 +08:00
|
|
|
if (!d_is_reg(dentry)) {
|
2016-03-27 04:14:37 +08:00
|
|
|
if (!inode || inode == d_inode(dentry))
|
|
|
|
return dentry;
|
|
|
|
goto bug;
|
|
|
|
}
|
|
|
|
|
|
|
|
real = ovl_dentry_upper(dentry);
|
2018-05-11 23:49:31 +08:00
|
|
|
if (real && (inode == d_inode(real)))
|
2016-03-27 04:14:37 +08:00
|
|
|
return real;
|
|
|
|
|
2018-05-11 23:49:31 +08:00
|
|
|
if (real && !inode && ovl_has_upperdata(d_inode(dentry)))
|
|
|
|
return real;
|
|
|
|
|
|
|
|
real = ovl_dentry_lowerdata(dentry);
|
2016-03-27 04:14:37 +08:00
|
|
|
if (!real)
|
|
|
|
goto bug;
|
|
|
|
|
2016-11-29 17:20:24 +08:00
|
|
|
/* Handle recursion */
|
2018-07-18 21:44:44 +08:00
|
|
|
real = d_real(real, inode);
|
2016-11-29 17:20:24 +08:00
|
|
|
|
2016-03-27 04:14:37 +08:00
|
|
|
if (!inode || inode == d_inode(real))
|
|
|
|
return real;
|
|
|
|
bug:
|
2016-07-29 18:05:24 +08:00
|
|
|
WARN(1, "ovl_d_real(%pd4, %s:%lu): real dentry not found\n", dentry,
|
2016-03-27 04:14:37 +08:00
|
|
|
inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
|
|
|
|
return dentry;
|
|
|
|
}
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
static int ovl_revalidate_real(struct dentry *d, unsigned int flags, bool weak)
|
2015-06-22 19:53:48 +08:00
|
|
|
{
|
|
|
|
int ret = 1;
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
if (weak) {
|
|
|
|
if (d->d_flags & DCACHE_OP_WEAK_REVALIDATE)
|
|
|
|
ret = d->d_op->d_weak_revalidate(d, flags);
|
|
|
|
} else if (d->d_flags & DCACHE_OP_REVALIDATE) {
|
|
|
|
ret = d->d_op->d_revalidate(d, flags);
|
|
|
|
if (!ret) {
|
|
|
|
if (!(flags & LOOKUP_RCU))
|
|
|
|
d_invalidate(d);
|
|
|
|
ret = -ESTALE;
|
2015-06-22 19:53:48 +08:00
|
|
|
}
|
|
|
|
}
|
2020-03-17 22:04:22 +08:00
|
|
|
return ret;
|
2015-06-22 19:53:48 +08:00
|
|
|
}
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
static int ovl_dentry_revalidate_common(struct dentry *dentry,
|
|
|
|
unsigned int flags, bool weak)
|
2015-06-22 19:53:48 +08:00
|
|
|
{
|
|
|
|
struct ovl_entry *oe = dentry->d_fsdata;
|
2020-03-17 22:04:22 +08:00
|
|
|
struct dentry *upper;
|
2015-06-22 19:53:48 +08:00
|
|
|
unsigned int i;
|
|
|
|
int ret = 1;
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
upper = ovl_dentry_upper(dentry);
|
|
|
|
if (upper)
|
|
|
|
ret = ovl_revalidate_real(upper, flags, weak);
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
for (i = 0; ret > 0 && i < oe->numlower; i++) {
|
|
|
|
ret = ovl_revalidate_real(oe->lowerstack[i].dentry, flags,
|
|
|
|
weak);
|
2015-06-22 19:53:48 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
static int ovl_dentry_revalidate(struct dentry *dentry, unsigned int flags)
|
|
|
|
{
|
|
|
|
return ovl_dentry_revalidate_common(dentry, flags, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
|
|
|
|
{
|
|
|
|
return ovl_dentry_revalidate_common(dentry, flags, true);
|
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static const struct dentry_operations ovl_dentry_operations = {
|
|
|
|
.d_release = ovl_dentry_release,
|
2016-03-27 04:14:37 +08:00
|
|
|
.d_real = ovl_d_real,
|
2015-06-22 19:53:48 +08:00
|
|
|
.d_revalidate = ovl_dentry_revalidate,
|
|
|
|
.d_weak_revalidate = ovl_dentry_weak_revalidate,
|
|
|
|
};
|
|
|
|
|
2017-06-12 14:54:40 +08:00
|
|
|
static struct kmem_cache *ovl_inode_cachep;
|
|
|
|
|
|
|
|
static struct inode *ovl_alloc_inode(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct ovl_inode *oi = kmem_cache_alloc(ovl_inode_cachep, GFP_KERNEL);
|
|
|
|
|
2017-09-26 02:09:53 +08:00
|
|
|
if (!oi)
|
|
|
|
return NULL;
|
|
|
|
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->cache = NULL;
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->redirect = NULL;
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->version = 0;
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->flags = 0;
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->__upperdentry = NULL;
|
2017-07-05 04:03:16 +08:00
|
|
|
oi->lower = NULL;
|
2018-05-11 23:49:30 +08:00
|
|
|
oi->lowerdata = NULL;
|
2017-06-21 20:28:51 +08:00
|
|
|
mutex_init(&oi->lock);
|
2017-07-05 04:03:16 +08:00
|
|
|
|
2017-06-12 14:54:40 +08:00
|
|
|
return &oi->vfs_inode;
|
|
|
|
}
|
|
|
|
|
2019-04-16 10:52:17 +08:00
|
|
|
static void ovl_free_inode(struct inode *inode)
|
2017-06-12 14:54:40 +08:00
|
|
|
{
|
2019-04-16 10:52:17 +08:00
|
|
|
struct ovl_inode *oi = OVL_I(inode);
|
2017-06-12 14:54:40 +08:00
|
|
|
|
2019-04-16 10:52:17 +08:00
|
|
|
kfree(oi->redirect);
|
|
|
|
mutex_destroy(&oi->lock);
|
|
|
|
kmem_cache_free(ovl_inode_cachep, oi);
|
2017-06-12 14:54:40 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void ovl_destroy_inode(struct inode *inode)
|
|
|
|
{
|
2017-07-05 04:03:16 +08:00
|
|
|
struct ovl_inode *oi = OVL_I(inode);
|
|
|
|
|
|
|
|
dput(oi->__upperdentry);
|
2018-01-15 00:35:40 +08:00
|
|
|
iput(oi->lower);
|
2018-05-11 23:49:30 +08:00
|
|
|
if (S_ISDIR(inode->i_mode))
|
|
|
|
ovl_dir_cache_free(inode);
|
|
|
|
else
|
|
|
|
iput(oi->lowerdata);
|
2017-06-12 14:54:40 +08:00
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
static void ovl_free_fs(struct ovl_fs *ofs)
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
{
|
2020-06-04 16:48:19 +08:00
|
|
|
struct vfsmount **mounts;
|
2014-12-13 07:59:43 +08:00
|
|
|
unsigned i;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2019-07-12 20:24:34 +08:00
|
|
|
iput(ofs->workbasedir_trap);
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
iput(ofs->indexdir_trap);
|
|
|
|
iput(ofs->workdir_trap);
|
2020-04-24 10:55:17 +08:00
|
|
|
dput(ofs->whiteout);
|
2017-11-10 16:39:16 +08:00
|
|
|
dput(ofs->indexdir);
|
|
|
|
dput(ofs->workdir);
|
|
|
|
if (ofs->workdir_locked)
|
|
|
|
ovl_inuse_unlock(ofs->workbasedir);
|
|
|
|
dput(ofs->workbasedir);
|
|
|
|
if (ofs->upperdir_locked)
|
2020-06-04 16:48:19 +08:00
|
|
|
ovl_inuse_unlock(ovl_upper_mnt(ofs)->mnt_root);
|
2020-06-04 16:48:19 +08:00
|
|
|
|
|
|
|
/* Hack! Reuse ofs->layers as a vfsmount array before freeing it */
|
|
|
|
mounts = (struct vfsmount **) ofs->layers;
|
2020-06-04 16:48:19 +08:00
|
|
|
for (i = 0; i < ofs->numlayer; i++) {
|
2019-11-15 20:12:40 +08:00
|
|
|
iput(ofs->layers[i].trap);
|
2020-06-04 16:48:19 +08:00
|
|
|
mounts[i] = ofs->layers[i].mnt;
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
}
|
2020-06-04 16:48:19 +08:00
|
|
|
kern_unmount_array(mounts, ofs->numlayer);
|
2019-11-15 20:12:40 +08:00
|
|
|
kfree(ofs->layers);
|
2020-01-15 04:17:25 +08:00
|
|
|
for (i = 0; i < ofs->numfs; i++)
|
2020-01-15 03:59:22 +08:00
|
|
|
free_anon_bdev(ofs->fs[i].pseudo_dev);
|
|
|
|
kfree(ofs->fs);
|
2017-11-10 16:39:16 +08:00
|
|
|
|
|
|
|
kfree(ofs->config.lowerdir);
|
|
|
|
kfree(ofs->config.upperdir);
|
|
|
|
kfree(ofs->config.workdir);
|
2017-12-11 18:28:10 +08:00
|
|
|
kfree(ofs->config.redirect_mode);
|
2017-11-10 16:39:16 +08:00
|
|
|
if (ofs->creator_cred)
|
|
|
|
put_cred(ofs->creator_cred);
|
|
|
|
kfree(ofs);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
static void ovl_put_super(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct ovl_fs *ofs = sb->s_fs_info;
|
|
|
|
|
|
|
|
ovl_free_fs(ofs);
|
|
|
|
}
|
|
|
|
|
2017-11-29 10:01:32 +08:00
|
|
|
/* Sync real dirty inodes in upper filesystem (if it exists) */
|
2017-01-23 20:32:21 +08:00
|
|
|
static int ovl_sync_fs(struct super_block *sb, int wait)
|
|
|
|
{
|
2017-11-10 16:39:16 +08:00
|
|
|
struct ovl_fs *ofs = sb->s_fs_info;
|
2017-01-23 20:32:21 +08:00
|
|
|
struct super_block *upper_sb;
|
|
|
|
int ret;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (!ovl_upper_mnt(ofs))
|
2017-01-23 20:32:21 +08:00
|
|
|
return 0;
|
2017-11-29 10:01:32 +08:00
|
|
|
|
2020-09-01 02:15:29 +08:00
|
|
|
if (!ovl_should_sync(ofs))
|
|
|
|
return 0;
|
2017-11-29 10:01:32 +08:00
|
|
|
/*
|
2020-04-09 16:29:47 +08:00
|
|
|
* Not called for sync(2) call or an emergency sync (SB_I_SKIP_SYNC).
|
|
|
|
* All the super blocks will be iterated, including upper_sb.
|
2017-11-29 10:01:32 +08:00
|
|
|
*
|
|
|
|
* If this is a syncfs(2) call, then we do need to call
|
|
|
|
* sync_filesystem() on upper_sb, but enough if we do it when being
|
|
|
|
* called with wait == 1.
|
|
|
|
*/
|
|
|
|
if (!wait)
|
2017-01-23 20:32:21 +08:00
|
|
|
return 0;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
|
2017-11-29 10:01:32 +08:00
|
|
|
|
2017-01-23 20:32:21 +08:00
|
|
|
down_read(&upper_sb->s_umount);
|
2017-11-29 10:01:32 +08:00
|
|
|
ret = sync_filesystem(upper_sb);
|
2017-01-23 20:32:21 +08:00
|
|
|
up_read(&upper_sb->s_umount);
|
2017-11-29 10:01:32 +08:00
|
|
|
|
2017-01-23 20:32:21 +08:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2014-10-24 06:14:38 +08:00
|
|
|
/**
|
|
|
|
* ovl_statfs
|
|
|
|
* @sb: The overlayfs super block
|
|
|
|
* @buf: The struct kstatfs to fill in with stats
|
|
|
|
*
|
|
|
|
* Get the filesystem statistics. As writes always target the upper layer
|
2014-12-13 07:59:46 +08:00
|
|
|
* filesystem pass the statfs to the upper filesystem (if it exists)
|
2014-10-24 06:14:38 +08:00
|
|
|
*/
|
|
|
|
static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
|
|
|
|
{
|
|
|
|
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
|
|
|
struct dentry *root_dentry = dentry->d_sb->s_root;
|
|
|
|
struct path path;
|
|
|
|
int err;
|
|
|
|
|
2014-12-13 07:59:46 +08:00
|
|
|
ovl_path_real(root_dentry, &path);
|
2014-10-24 06:14:38 +08:00
|
|
|
|
|
|
|
err = vfs_statfs(&path, buf);
|
|
|
|
if (!err) {
|
2016-12-16 18:02:56 +08:00
|
|
|
buf->f_namelen = ofs->namelen;
|
2014-10-24 06:14:38 +08:00
|
|
|
buf->f_type = OVERLAYFS_SUPER_MAGIC;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-06-21 20:28:36 +08:00
|
|
|
/* Will this overlay be forced to mount/remount ro? */
|
2017-11-10 16:39:16 +08:00
|
|
|
static bool ovl_force_readonly(struct ovl_fs *ofs)
|
2017-06-21 20:28:36 +08:00
|
|
|
{
|
2020-06-04 16:48:19 +08:00
|
|
|
return (!ovl_upper_mnt(ofs) || !ofs->workdir);
|
2017-06-21 20:28:36 +08:00
|
|
|
}
|
|
|
|
|
2017-12-11 18:28:10 +08:00
|
|
|
static const char *ovl_redirect_mode_def(void)
|
|
|
|
{
|
|
|
|
return ovl_redirect_dir_def ? "on" : "off";
|
|
|
|
}
|
|
|
|
|
2018-03-29 14:08:18 +08:00
|
|
|
static const char * const ovl_xino_str[] = {
|
|
|
|
"off",
|
|
|
|
"auto",
|
|
|
|
"on",
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline int ovl_xino_def(void)
|
|
|
|
{
|
|
|
|
return ovl_xino_auto_def ? OVL_XINO_AUTO : OVL_XINO_OFF;
|
|
|
|
}
|
|
|
|
|
2014-10-24 06:14:38 +08:00
|
|
|
/**
|
|
|
|
* ovl_show_options
|
|
|
|
*
|
|
|
|
* Prints the mount options for a given superblock.
|
|
|
|
* Returns zero; does not fail.
|
|
|
|
*/
|
|
|
|
static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct super_block *sb = dentry->d_sb;
|
2017-11-10 16:39:16 +08:00
|
|
|
struct ovl_fs *ofs = sb->s_fs_info;
|
2014-10-24 06:14:38 +08:00
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
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);
|
2014-12-13 07:59:51 +08:00
|
|
|
}
|
2017-11-10 16:39:16 +08:00
|
|
|
if (ofs->config.default_permissions)
|
2015-10-12 23:11:44 +08:00
|
|
|
seq_puts(m, ",default_permissions");
|
2017-12-11 18:28:10 +08:00
|
|
|
if (strcmp(ofs->config.redirect_mode, ovl_redirect_mode_def()) != 0)
|
|
|
|
seq_printf(m, ",redirect_dir=%s", ofs->config.redirect_mode);
|
2017-11-10 16:39:16 +08:00
|
|
|
if (ofs->config.index != ovl_index_def)
|
2017-12-11 18:28:10 +08:00
|
|
|
seq_printf(m, ",index=%s", ofs->config.index ? "on" : "off");
|
2018-01-19 17:26:53 +08:00
|
|
|
if (ofs->config.nfs_export != ovl_nfs_export_def)
|
|
|
|
seq_printf(m, ",nfs_export=%s", ofs->config.nfs_export ?
|
|
|
|
"on" : "off");
|
2019-11-17 00:14:41 +08:00
|
|
|
if (ofs->config.xino != ovl_xino_def() && !ovl_same_fs(sb))
|
2018-03-29 14:08:18 +08:00
|
|
|
seq_printf(m, ",xino=%s", ovl_xino_str[ofs->config.xino]);
|
2018-05-11 23:49:27 +08:00
|
|
|
if (ofs->config.metacopy != ovl_metacopy_def)
|
|
|
|
seq_printf(m, ",metacopy=%s",
|
|
|
|
ofs->config.metacopy ? "on" : "off");
|
2020-09-01 02:15:29 +08:00
|
|
|
if (ofs->config.ovl_volatile)
|
|
|
|
seq_puts(m, ",volatile");
|
2014-10-24 06:14:38 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-01-03 01:26:49 +08:00
|
|
|
static int ovl_remount(struct super_block *sb, int *flags, char *data)
|
|
|
|
{
|
2017-11-10 16:39:16 +08:00
|
|
|
struct ovl_fs *ofs = sb->s_fs_info;
|
2020-04-22 12:28:43 +08:00
|
|
|
struct super_block *upper_sb;
|
|
|
|
int ret = 0;
|
2015-01-03 01:26:49 +08:00
|
|
|
|
2017-11-28 05:05:09 +08:00
|
|
|
if (!(*flags & SB_RDONLY) && ovl_force_readonly(ofs))
|
2015-01-03 01:26:49 +08:00
|
|
|
return -EROFS;
|
|
|
|
|
2020-04-22 12:28:43 +08:00
|
|
|
if (*flags & SB_RDONLY && !sb_rdonly(sb)) {
|
2020-06-04 16:48:19 +08:00
|
|
|
upper_sb = ovl_upper_mnt(ofs)->mnt_sb;
|
2020-09-01 02:15:29 +08:00
|
|
|
if (ovl_should_sync(ofs)) {
|
|
|
|
down_read(&upper_sb->s_umount);
|
|
|
|
ret = sync_filesystem(upper_sb);
|
|
|
|
up_read(&upper_sb->s_umount);
|
|
|
|
}
|
2020-04-22 12:28:43 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
2015-01-03 01:26:49 +08:00
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static const struct super_operations ovl_super_operations = {
|
2017-06-12 14:54:40 +08:00
|
|
|
.alloc_inode = ovl_alloc_inode,
|
2019-04-16 10:52:17 +08:00
|
|
|
.free_inode = ovl_free_inode,
|
2017-06-12 14:54:40 +08:00
|
|
|
.destroy_inode = ovl_destroy_inode,
|
|
|
|
.drop_inode = generic_delete_inode,
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
.put_super = ovl_put_super,
|
2017-01-23 20:32:21 +08:00
|
|
|
.sync_fs = ovl_sync_fs,
|
2014-10-24 06:14:38 +08:00
|
|
|
.statfs = ovl_statfs,
|
2014-10-24 06:14:38 +08:00
|
|
|
.show_options = ovl_show_options,
|
2015-01-03 01:26:49 +08:00
|
|
|
.remount_fs = ovl_remount,
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
enum {
|
|
|
|
OPT_LOWERDIR,
|
|
|
|
OPT_UPPERDIR,
|
|
|
|
OPT_WORKDIR,
|
2015-10-12 23:11:44 +08:00
|
|
|
OPT_DEFAULT_PERMISSIONS,
|
2017-12-11 18:28:10 +08:00
|
|
|
OPT_REDIRECT_DIR,
|
2017-06-21 20:28:36 +08:00
|
|
|
OPT_INDEX_ON,
|
|
|
|
OPT_INDEX_OFF,
|
2018-01-19 17:26:53 +08:00
|
|
|
OPT_NFS_EXPORT_ON,
|
|
|
|
OPT_NFS_EXPORT_OFF,
|
2018-03-29 14:08:18 +08:00
|
|
|
OPT_XINO_ON,
|
|
|
|
OPT_XINO_OFF,
|
|
|
|
OPT_XINO_AUTO,
|
2018-05-11 23:49:27 +08:00
|
|
|
OPT_METACOPY_ON,
|
|
|
|
OPT_METACOPY_OFF,
|
2020-09-01 02:15:29 +08:00
|
|
|
OPT_VOLATILE,
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
OPT_ERR,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const match_table_t ovl_tokens = {
|
|
|
|
{OPT_LOWERDIR, "lowerdir=%s"},
|
|
|
|
{OPT_UPPERDIR, "upperdir=%s"},
|
|
|
|
{OPT_WORKDIR, "workdir=%s"},
|
2015-10-12 23:11:44 +08:00
|
|
|
{OPT_DEFAULT_PERMISSIONS, "default_permissions"},
|
2017-12-11 18:28:10 +08:00
|
|
|
{OPT_REDIRECT_DIR, "redirect_dir=%s"},
|
2017-06-21 20:28:36 +08:00
|
|
|
{OPT_INDEX_ON, "index=on"},
|
|
|
|
{OPT_INDEX_OFF, "index=off"},
|
2018-01-19 17:26:53 +08:00
|
|
|
{OPT_NFS_EXPORT_ON, "nfs_export=on"},
|
|
|
|
{OPT_NFS_EXPORT_OFF, "nfs_export=off"},
|
2018-03-29 14:08:18 +08:00
|
|
|
{OPT_XINO_ON, "xino=on"},
|
|
|
|
{OPT_XINO_OFF, "xino=off"},
|
|
|
|
{OPT_XINO_AUTO, "xino=auto"},
|
2018-05-11 23:49:27 +08:00
|
|
|
{OPT_METACOPY_ON, "metacopy=on"},
|
|
|
|
{OPT_METACOPY_OFF, "metacopy=off"},
|
2020-09-01 02:15:29 +08:00
|
|
|
{OPT_VOLATILE, "volatile"},
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
{OPT_ERR, NULL}
|
|
|
|
};
|
|
|
|
|
2014-11-20 23:40:00 +08:00
|
|
|
static char *ovl_next_opt(char **s)
|
|
|
|
{
|
|
|
|
char *sbegin = *s;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
if (sbegin == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
for (p = sbegin; *p; p++) {
|
|
|
|
if (*p == '\\') {
|
|
|
|
p++;
|
|
|
|
if (!*p)
|
|
|
|
break;
|
|
|
|
} else if (*p == ',') {
|
|
|
|
*p = '\0';
|
|
|
|
*s = p + 1;
|
|
|
|
return sbegin;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*s = NULL;
|
|
|
|
return sbegin;
|
|
|
|
}
|
|
|
|
|
2017-12-11 18:28:10 +08:00
|
|
|
static int ovl_parse_redirect_mode(struct ovl_config *config, const char *mode)
|
|
|
|
{
|
|
|
|
if (strcmp(mode, "on") == 0) {
|
|
|
|
config->redirect_dir = true;
|
|
|
|
/*
|
|
|
|
* Does not make sense to have redirect creation without
|
|
|
|
* redirect following.
|
|
|
|
*/
|
|
|
|
config->redirect_follow = true;
|
|
|
|
} else if (strcmp(mode, "follow") == 0) {
|
|
|
|
config->redirect_follow = true;
|
|
|
|
} else if (strcmp(mode, "off") == 0) {
|
|
|
|
if (ovl_redirect_always_follow)
|
|
|
|
config->redirect_follow = true;
|
|
|
|
} else if (strcmp(mode, "nofollow") != 0) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("bad mount option \"redirect_dir=%s\"\n",
|
2017-12-11 18:28:10 +08:00
|
|
|
mode);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
|
|
|
{
|
|
|
|
char *p;
|
2018-05-11 23:49:27 +08:00
|
|
|
int err;
|
2018-11-02 04:31:39 +08:00
|
|
|
bool metacopy_opt = false, redirect_opt = false;
|
2020-04-09 23:58:34 +08:00
|
|
|
bool nfs_export_opt = false, index_opt = false;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2017-12-11 18:28:10 +08:00
|
|
|
config->redirect_mode = kstrdup(ovl_redirect_mode_def(), GFP_KERNEL);
|
|
|
|
if (!config->redirect_mode)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2014-11-20 23:40:00 +08:00
|
|
|
while ((p = ovl_next_opt(&opt)) != NULL) {
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
int token;
|
|
|
|
substring_t args[MAX_OPT_ARGS];
|
|
|
|
|
|
|
|
if (!*p)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
token = match_token(p, ovl_tokens, args);
|
|
|
|
switch (token) {
|
|
|
|
case OPT_UPPERDIR:
|
|
|
|
kfree(config->upperdir);
|
|
|
|
config->upperdir = match_strdup(&args[0]);
|
|
|
|
if (!config->upperdir)
|
|
|
|
return -ENOMEM;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_LOWERDIR:
|
|
|
|
kfree(config->lowerdir);
|
|
|
|
config->lowerdir = match_strdup(&args[0]);
|
|
|
|
if (!config->lowerdir)
|
|
|
|
return -ENOMEM;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_WORKDIR:
|
|
|
|
kfree(config->workdir);
|
|
|
|
config->workdir = match_strdup(&args[0]);
|
|
|
|
if (!config->workdir)
|
|
|
|
return -ENOMEM;
|
|
|
|
break;
|
|
|
|
|
2015-10-12 23:11:44 +08:00
|
|
|
case OPT_DEFAULT_PERMISSIONS:
|
|
|
|
config->default_permissions = true;
|
|
|
|
break;
|
|
|
|
|
2017-12-11 18:28:10 +08:00
|
|
|
case OPT_REDIRECT_DIR:
|
|
|
|
kfree(config->redirect_mode);
|
|
|
|
config->redirect_mode = match_strdup(&args[0]);
|
|
|
|
if (!config->redirect_mode)
|
|
|
|
return -ENOMEM;
|
2018-11-02 04:31:39 +08:00
|
|
|
redirect_opt = true;
|
2016-12-16 18:02:56 +08:00
|
|
|
break;
|
|
|
|
|
2017-06-21 20:28:36 +08:00
|
|
|
case OPT_INDEX_ON:
|
|
|
|
config->index = true;
|
2020-04-09 23:58:34 +08:00
|
|
|
index_opt = true;
|
2017-06-21 20:28:36 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_INDEX_OFF:
|
|
|
|
config->index = false;
|
2020-04-09 23:58:34 +08:00
|
|
|
index_opt = true;
|
2017-06-21 20:28:36 +08:00
|
|
|
break;
|
|
|
|
|
2018-01-19 17:26:53 +08:00
|
|
|
case OPT_NFS_EXPORT_ON:
|
|
|
|
config->nfs_export = true;
|
2020-04-09 23:58:34 +08:00
|
|
|
nfs_export_opt = true;
|
2018-01-19 17:26:53 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_NFS_EXPORT_OFF:
|
|
|
|
config->nfs_export = false;
|
2020-04-09 23:58:34 +08:00
|
|
|
nfs_export_opt = true;
|
2018-01-19 17:26:53 +08:00
|
|
|
break;
|
|
|
|
|
2018-03-29 14:08:18 +08:00
|
|
|
case OPT_XINO_ON:
|
|
|
|
config->xino = OVL_XINO_ON;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_XINO_OFF:
|
|
|
|
config->xino = OVL_XINO_OFF;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_XINO_AUTO:
|
|
|
|
config->xino = OVL_XINO_AUTO;
|
|
|
|
break;
|
|
|
|
|
2018-05-11 23:49:27 +08:00
|
|
|
case OPT_METACOPY_ON:
|
|
|
|
config->metacopy = true;
|
2018-11-02 04:31:39 +08:00
|
|
|
metacopy_opt = true;
|
2018-05-11 23:49:27 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case OPT_METACOPY_OFF:
|
|
|
|
config->metacopy = false;
|
2020-04-09 23:58:34 +08:00
|
|
|
metacopy_opt = true;
|
2018-05-11 23:49:27 +08:00
|
|
|
break;
|
|
|
|
|
2020-09-01 02:15:29 +08:00
|
|
|
case OPT_VOLATILE:
|
|
|
|
config->ovl_volatile = true;
|
|
|
|
break;
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
default:
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("unrecognized mount option \"%s\" or missing value\n",
|
|
|
|
p);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
2015-01-15 13:20:57 +08:00
|
|
|
|
2020-07-13 22:19:44 +08:00
|
|
|
/* Workdir/index are useless in non-upper mount */
|
|
|
|
if (!config->upperdir) {
|
|
|
|
if (config->workdir) {
|
|
|
|
pr_info("option \"workdir=%s\" is useless in a non-upper mount, ignore\n",
|
|
|
|
config->workdir);
|
|
|
|
kfree(config->workdir);
|
|
|
|
config->workdir = NULL;
|
|
|
|
}
|
|
|
|
if (config->index && index_opt) {
|
|
|
|
pr_info("option \"index=on\" is useless in a non-upper mount, ignore\n");
|
|
|
|
index_opt = false;
|
|
|
|
}
|
|
|
|
config->index = false;
|
2015-01-15 13:20:57 +08:00
|
|
|
}
|
|
|
|
|
2020-09-01 02:15:29 +08:00
|
|
|
if (!config->upperdir && config->ovl_volatile) {
|
|
|
|
pr_info("option \"volatile\" is meaningless in a non-upper mount, ignoring it.\n");
|
|
|
|
config->ovl_volatile = false;
|
|
|
|
}
|
|
|
|
|
2018-05-11 23:49:27 +08:00
|
|
|
err = ovl_parse_redirect_mode(config, config->redirect_mode);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2018-11-02 04:31:39 +08:00
|
|
|
/*
|
|
|
|
* This is to make the logic below simpler. It doesn't make any other
|
|
|
|
* difference, since config->redirect_dir is only used for upper.
|
|
|
|
*/
|
|
|
|
if (!config->upperdir && config->redirect_follow)
|
|
|
|
config->redirect_dir = true;
|
|
|
|
|
|
|
|
/* Resolve metacopy -> redirect_dir dependency */
|
|
|
|
if (config->metacopy && !config->redirect_dir) {
|
|
|
|
if (metacopy_opt && redirect_opt) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("conflicting options: metacopy=on,redirect_dir=%s\n",
|
2018-11-02 04:31:39 +08:00
|
|
|
config->redirect_mode);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (redirect_opt) {
|
|
|
|
/*
|
|
|
|
* There was an explicit redirect_dir=... that resulted
|
|
|
|
* in this conflict.
|
|
|
|
*/
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_info("disabling metacopy due to redirect_dir=%s\n",
|
2018-11-02 04:31:39 +08:00
|
|
|
config->redirect_mode);
|
|
|
|
config->metacopy = false;
|
|
|
|
} else {
|
|
|
|
/* Automatically enable redirect otherwise. */
|
|
|
|
config->redirect_follow = config->redirect_dir = true;
|
|
|
|
}
|
2018-05-11 23:49:27 +08:00
|
|
|
}
|
|
|
|
|
2020-04-09 23:58:34 +08:00
|
|
|
/* Resolve nfs_export -> index dependency */
|
|
|
|
if (config->nfs_export && !config->index) {
|
2020-07-13 22:19:44 +08:00
|
|
|
if (!config->upperdir && config->redirect_follow) {
|
|
|
|
pr_info("NFS export requires \"redirect_dir=nofollow\" on non-upper mount, falling back to nfs_export=off.\n");
|
|
|
|
config->nfs_export = false;
|
|
|
|
} else if (nfs_export_opt && index_opt) {
|
2020-04-09 23:58:34 +08:00
|
|
|
pr_err("conflicting options: nfs_export=on,index=off\n");
|
|
|
|
return -EINVAL;
|
2020-07-13 22:19:44 +08:00
|
|
|
} else if (index_opt) {
|
2020-04-09 23:58:34 +08:00
|
|
|
/*
|
|
|
|
* There was an explicit index=off that resulted
|
|
|
|
* in this conflict.
|
|
|
|
*/
|
|
|
|
pr_info("disabling nfs_export due to index=off\n");
|
|
|
|
config->nfs_export = false;
|
|
|
|
} else {
|
|
|
|
/* Automatically enable index otherwise. */
|
|
|
|
config->index = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Resolve nfs_export -> !metacopy dependency */
|
|
|
|
if (config->nfs_export && config->metacopy) {
|
|
|
|
if (nfs_export_opt && metacopy_opt) {
|
|
|
|
pr_err("conflicting options: nfs_export=on,metacopy=on\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
if (metacopy_opt) {
|
|
|
|
/*
|
|
|
|
* There was an explicit metacopy=on that resulted
|
|
|
|
* in this conflict.
|
|
|
|
*/
|
|
|
|
pr_info("disabling nfs_export due to metacopy=on\n");
|
|
|
|
config->nfs_export = false;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* There was an explicit nfs_export=on that resulted
|
|
|
|
* in this conflict.
|
|
|
|
*/
|
|
|
|
pr_info("disabling metacopy due to nfs_export=on\n");
|
|
|
|
config->metacopy = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 23:49:27 +08:00
|
|
|
return 0;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#define OVL_WORKDIR_NAME "work"
|
2017-06-21 20:28:36 +08:00
|
|
|
#define OVL_INDEXDIR_NAME "index"
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
|
2017-06-21 20:28:35 +08:00
|
|
|
const char *name, bool persist)
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
{
|
2017-11-10 16:39:16 +08:00
|
|
|
struct inode *dir = ofs->workbasedir->d_inode;
|
2020-06-04 16:48:19 +08:00
|
|
|
struct vfsmount *mnt = ovl_upper_mnt(ofs);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
struct dentry *work;
|
|
|
|
int err;
|
|
|
|
bool retried = false;
|
|
|
|
|
2016-01-23 04:40:57 +08:00
|
|
|
inode_lock_nested(dir, I_MUTEX_PARENT);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
retry:
|
2017-11-10 16:39:16 +08:00
|
|
|
work = lookup_one_len(name, ofs->workbasedir, strlen(name));
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
|
|
|
if (!IS_ERR(work)) {
|
2016-09-01 17:11:59 +08:00
|
|
|
struct iattr attr = {
|
|
|
|
.ia_valid = ATTR_MODE,
|
2016-12-05 01:33:17 +08:00
|
|
|
.ia_mode = S_IFDIR | 0,
|
2016-09-01 17:11:59 +08:00
|
|
|
};
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
|
|
|
if (work->d_inode) {
|
|
|
|
err = -EEXIST;
|
|
|
|
if (retried)
|
|
|
|
goto out_dput;
|
|
|
|
|
2017-06-21 20:28:35 +08:00
|
|
|
if (persist)
|
|
|
|
goto out_unlock;
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
retried = true;
|
2020-08-31 04:28:03 +08:00
|
|
|
err = ovl_workdir_cleanup(dir, mnt, work, 0);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
dput(work);
|
2020-08-31 04:28:03 +08:00
|
|
|
if (err == -EINVAL) {
|
|
|
|
work = ERR_PTR(err);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
2018-05-16 22:51:25 +08:00
|
|
|
work = ovl_create_real(dir, work, OVL_CATTR(attr.ia_mode));
|
|
|
|
err = PTR_ERR(work);
|
|
|
|
if (IS_ERR(work))
|
|
|
|
goto out_err;
|
2016-09-01 17:11:59 +08:00
|
|
|
|
2016-10-04 20:40:44 +08:00
|
|
|
/*
|
|
|
|
* Try to remove POSIX ACL xattrs from workdir. We are good if:
|
|
|
|
*
|
|
|
|
* a) success (there was a POSIX ACL xattr and was removed)
|
|
|
|
* b) -ENODATA (there was no POSIX ACL xattr)
|
|
|
|
* c) -EOPNOTSUPP (POSIX ACL xattrs are not supported)
|
|
|
|
*
|
|
|
|
* There are various other error values that could effectively
|
|
|
|
* mean that the xattr doesn't exist (e.g. -ERANGE is returned
|
|
|
|
* if the xattr name is too long), but the set of filesystems
|
|
|
|
* allowed as upper are limited to "normal" ones, where checking
|
|
|
|
* for the above two errors is sufficient.
|
|
|
|
*/
|
2016-09-01 17:11:59 +08:00
|
|
|
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_DEFAULT);
|
2016-09-05 19:55:20 +08:00
|
|
|
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
2016-09-01 17:11:59 +08:00
|
|
|
goto out_dput;
|
|
|
|
|
|
|
|
err = vfs_removexattr(work, XATTR_NAME_POSIX_ACL_ACCESS);
|
2016-09-05 19:55:20 +08:00
|
|
|
if (err && err != -ENODATA && err != -EOPNOTSUPP)
|
2016-09-01 17:11:59 +08:00
|
|
|
goto out_dput;
|
|
|
|
|
|
|
|
/* Clear any inherited mode bits */
|
|
|
|
inode_lock(work->d_inode);
|
|
|
|
err = notify_change(work, &attr, NULL);
|
|
|
|
inode_unlock(work->d_inode);
|
|
|
|
if (err)
|
|
|
|
goto out_dput;
|
2017-06-21 20:28:35 +08:00
|
|
|
} else {
|
|
|
|
err = PTR_ERR(work);
|
|
|
|
goto out_err;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
out_unlock:
|
2020-06-07 17:04:06 +08:00
|
|
|
inode_unlock(dir);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
return work;
|
|
|
|
|
|
|
|
out_dput:
|
|
|
|
dput(work);
|
2017-06-21 20:28:35 +08:00
|
|
|
out_err:
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("failed to create directory %s/%s (errno: %i); mounting read-only\n",
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->config.workdir, name, -err);
|
2017-06-21 20:28:35 +08:00
|
|
|
work = NULL;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:40:00 +08:00
|
|
|
static void ovl_unescape(char *s)
|
|
|
|
{
|
|
|
|
char *d = s;
|
|
|
|
|
|
|
|
for (;; s++, d++) {
|
|
|
|
if (*s == '\\')
|
|
|
|
s++;
|
|
|
|
*d = *s;
|
|
|
|
if (!*s)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-13 07:59:49 +08:00
|
|
|
static int ovl_mount_dir_noesc(const char *name, struct path *path)
|
|
|
|
{
|
2014-12-13 07:59:52 +08:00
|
|
|
int err = -EINVAL;
|
2014-12-13 07:59:49 +08:00
|
|
|
|
2014-12-13 07:59:52 +08:00
|
|
|
if (!*name) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("empty lowerdir\n");
|
2014-12-13 07:59:52 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2014-12-13 07:59:49 +08:00
|
|
|
err = kern_path(name, LOOKUP_FOLLOW, path);
|
|
|
|
if (err) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to resolve '%s': %i\n", name, err);
|
2014-12-13 07:59:49 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = -EINVAL;
|
2015-06-22 19:53:48 +08:00
|
|
|
if (ovl_dentry_weird(path->dentry)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("filesystem on '%s' not supported\n", name);
|
2014-12-13 07:59:49 +08:00
|
|
|
goto out_put;
|
|
|
|
}
|
2016-12-16 18:02:56 +08:00
|
|
|
if (!d_is_dir(path->dentry)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("'%s' not a directory\n", name);
|
2014-12-13 07:59:49 +08:00
|
|
|
goto out_put;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_put:
|
2017-11-09 17:23:28 +08:00
|
|
|
path_put_init(path);
|
2014-12-13 07:59:49 +08:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ovl_mount_dir(const char *name, struct path *path)
|
|
|
|
{
|
|
|
|
int err = -ENOMEM;
|
|
|
|
char *tmp = kstrdup(name, GFP_KERNEL);
|
|
|
|
|
|
|
|
if (tmp) {
|
|
|
|
ovl_unescape(tmp);
|
|
|
|
err = ovl_mount_dir_noesc(tmp, path);
|
2015-06-22 19:53:48 +08:00
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
if (!err && path->dentry->d_flags & DCACHE_OP_REAL) {
|
2020-03-17 22:04:22 +08:00
|
|
|
pr_err("filesystem on '%s' not supported as upperdir\n",
|
|
|
|
tmp);
|
|
|
|
path_put_init(path);
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
2014-12-13 07:59:49 +08:00
|
|
|
kfree(tmp);
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-12-16 18:02:56 +08:00
|
|
|
static int ovl_check_namelen(struct path *path, struct ovl_fs *ofs,
|
|
|
|
const char *name)
|
2014-12-13 07:59:49 +08:00
|
|
|
{
|
|
|
|
struct kstatfs statfs;
|
2016-12-16 18:02:56 +08:00
|
|
|
int err = vfs_statfs(path, &statfs);
|
|
|
|
|
|
|
|
if (err)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("statfs failed on '%s'\n", name);
|
2016-12-16 18:02:56 +08:00
|
|
|
else
|
|
|
|
ofs->namelen = max(ofs->namelen, statfs.f_namelen);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int ovl_lower_dir(const char *name, struct path *path,
|
2020-03-17 22:04:22 +08:00
|
|
|
struct ovl_fs *ofs, int *stack_depth)
|
2016-12-16 18:02:56 +08:00
|
|
|
{
|
2017-11-07 19:55:04 +08:00
|
|
|
int fh_type;
|
2016-12-16 18:02:56 +08:00
|
|
|
int err;
|
2014-12-13 07:59:49 +08:00
|
|
|
|
2014-12-13 07:59:52 +08:00
|
|
|
err = ovl_mount_dir_noesc(name, path);
|
2014-12-13 07:59:49 +08:00
|
|
|
if (err)
|
2020-06-04 16:48:19 +08:00
|
|
|
return err;
|
2014-12-13 07:59:49 +08:00
|
|
|
|
2016-12-16 18:02:56 +08:00
|
|
|
err = ovl_check_namelen(path, ofs, name);
|
|
|
|
if (err)
|
2020-06-04 16:48:19 +08:00
|
|
|
return err;
|
2016-12-16 18:02:56 +08:00
|
|
|
|
2014-12-13 07:59:49 +08:00
|
|
|
*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);
|
|
|
|
|
2017-06-21 20:28:36 +08:00
|
|
|
/*
|
2018-01-19 17:26:53 +08:00
|
|
|
* The inodes index feature and NFS export need to encode and decode
|
|
|
|
* file handles, so they require that all layers support them.
|
2017-06-21 20:28:36 +08:00
|
|
|
*/
|
2017-11-07 19:55:04 +08:00
|
|
|
fh_type = ovl_can_decode_fh(path->dentry->d_sb);
|
2018-01-19 17:26:53 +08:00
|
|
|
if ((ofs->config.nfs_export ||
|
2017-11-07 19:55:04 +08:00
|
|
|
(ofs->config.index && ofs->config.upperdir)) && !fh_type) {
|
2017-06-21 20:28:36 +08:00
|
|
|
ofs->config.index = false;
|
2018-01-19 17:26:53 +08:00
|
|
|
ofs->config.nfs_export = false;
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("fs on '%s' does not support file handles, falling back to index=off,nfs_export=off.\n",
|
2018-01-19 17:26:53 +08:00
|
|
|
name);
|
2017-06-21 20:28:36 +08:00
|
|
|
}
|
|
|
|
|
2017-11-07 19:55:04 +08:00
|
|
|
/* Check if lower fs has 32bit inode numbers */
|
|
|
|
if (fh_type != FILEID_INO32_GEN)
|
2019-11-17 00:14:41 +08:00
|
|
|
ofs->xino_mode = -1;
|
2017-11-07 19:55:04 +08:00
|
|
|
|
2014-12-13 07:59:49 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
/* Workdir should not be subdir of upperdir and vice versa */
|
|
|
|
static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
|
|
|
|
{
|
|
|
|
bool ok = false;
|
|
|
|
|
|
|
|
if (workdir != upperdir) {
|
|
|
|
ok = (lock_rename(workdir, upperdir) == NULL);
|
|
|
|
unlock_rename(workdir, upperdir);
|
|
|
|
}
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2014-12-13 07:59:52 +08:00
|
|
|
static unsigned int ovl_split_lowerdirs(char *str)
|
|
|
|
{
|
|
|
|
unsigned int ctr = 1;
|
|
|
|
char *s, *d;
|
|
|
|
|
|
|
|
for (s = d = str;; s++, d++) {
|
|
|
|
if (*s == '\\') {
|
|
|
|
s++;
|
|
|
|
} else if (*s == ':') {
|
|
|
|
*d = '\0';
|
|
|
|
ctr++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
*d = *s;
|
|
|
|
if (!*s)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return ctr;
|
|
|
|
}
|
|
|
|
|
2016-08-22 23:52:55 +08:00
|
|
|
static int __maybe_unused
|
|
|
|
ovl_posix_acl_xattr_get(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, void *buffer, size_t size)
|
|
|
|
{
|
2017-07-20 17:08:21 +08:00
|
|
|
return ovl_xattr_get(dentry, inode, handler->name, buffer, size);
|
2016-08-22 23:52:55 +08:00
|
|
|
}
|
|
|
|
|
2016-08-22 22:36:49 +08:00
|
|
|
static int __maybe_unused
|
|
|
|
ovl_posix_acl_xattr_set(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, const void *value,
|
|
|
|
size_t size, int flags)
|
2016-07-29 18:05:24 +08:00
|
|
|
{
|
|
|
|
struct dentry *workdir = ovl_workdir(dentry);
|
2017-07-05 04:03:16 +08:00
|
|
|
struct inode *realinode = ovl_inode_real(inode);
|
2016-07-29 18:05:24 +08:00
|
|
|
struct posix_acl *acl = NULL;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
/* Check that everything is OK before copy-up */
|
|
|
|
if (value) {
|
|
|
|
acl = posix_acl_from_xattr(&init_user_ns, value, size);
|
|
|
|
if (IS_ERR(acl))
|
|
|
|
return PTR_ERR(acl);
|
|
|
|
}
|
|
|
|
err = -EOPNOTSUPP;
|
|
|
|
if (!IS_POSIXACL(d_inode(workdir)))
|
|
|
|
goto out_acl_release;
|
|
|
|
if (!realinode->i_op->set_acl)
|
|
|
|
goto out_acl_release;
|
|
|
|
if (handler->flags == ACL_TYPE_DEFAULT && !S_ISDIR(inode->i_mode)) {
|
|
|
|
err = acl ? -EACCES : 0;
|
|
|
|
goto out_acl_release;
|
|
|
|
}
|
|
|
|
err = -EPERM;
|
|
|
|
if (!inode_owner_or_capable(inode))
|
|
|
|
goto out_acl_release;
|
|
|
|
|
|
|
|
posix_acl_release(acl);
|
|
|
|
|
2016-10-31 21:42:14 +08:00
|
|
|
/*
|
|
|
|
* Check if sgid bit needs to be cleared (actual setacl operation will
|
|
|
|
* be done with mounter's capabilities and so that won't do it for us).
|
|
|
|
*/
|
|
|
|
if (unlikely(inode->i_mode & S_ISGID) &&
|
|
|
|
handler->flags == ACL_TYPE_ACCESS &&
|
|
|
|
!in_group_p(inode->i_gid) &&
|
|
|
|
!capable_wrt_inode_uidgid(inode, CAP_FSETID)) {
|
|
|
|
struct iattr iattr = { .ia_valid = ATTR_KILL_SGID };
|
|
|
|
|
|
|
|
err = ovl_setattr(dentry, &iattr);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-07-20 17:08:21 +08:00
|
|
|
err = ovl_xattr_set(dentry, inode, handler->name, value, size, flags);
|
2016-09-01 17:12:00 +08:00
|
|
|
if (!err)
|
2017-07-05 04:03:16 +08:00
|
|
|
ovl_copyattr(ovl_inode_real(inode), inode);
|
2016-09-01 17:12:00 +08:00
|
|
|
|
|
|
|
return err;
|
2016-07-29 18:05:24 +08:00
|
|
|
|
|
|
|
out_acl_release:
|
|
|
|
posix_acl_release(acl);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2016-08-22 23:52:55 +08:00
|
|
|
static int ovl_own_xattr_get(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, void *buffer, size_t size)
|
|
|
|
{
|
2016-11-16 17:22:39 +08:00
|
|
|
return -EOPNOTSUPP;
|
2016-08-22 23:52:55 +08:00
|
|
|
}
|
|
|
|
|
2016-07-29 18:05:24 +08:00
|
|
|
static int ovl_own_xattr_set(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, const void *value,
|
|
|
|
size_t size, int flags)
|
|
|
|
{
|
2016-11-16 17:22:39 +08:00
|
|
|
return -EOPNOTSUPP;
|
2016-07-29 18:05:24 +08:00
|
|
|
}
|
|
|
|
|
2016-08-22 23:52:55 +08:00
|
|
|
static int ovl_other_xattr_get(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, void *buffer, size_t size)
|
|
|
|
{
|
2017-07-20 17:08:21 +08:00
|
|
|
return ovl_xattr_get(dentry, inode, name, buffer, size);
|
2016-08-22 23:52:55 +08:00
|
|
|
}
|
|
|
|
|
2016-08-22 23:22:11 +08:00
|
|
|
static int ovl_other_xattr_set(const struct xattr_handler *handler,
|
|
|
|
struct dentry *dentry, struct inode *inode,
|
|
|
|
const char *name, const void *value,
|
|
|
|
size_t size, int flags)
|
|
|
|
{
|
2017-07-20 17:08:21 +08:00
|
|
|
return ovl_xattr_set(dentry, inode, name, value, size, flags);
|
2016-08-22 23:22:11 +08:00
|
|
|
}
|
|
|
|
|
2016-08-22 22:36:49 +08:00
|
|
|
static const struct xattr_handler __maybe_unused
|
|
|
|
ovl_posix_acl_access_xattr_handler = {
|
2016-07-29 18:05:24 +08:00
|
|
|
.name = XATTR_NAME_POSIX_ACL_ACCESS,
|
|
|
|
.flags = ACL_TYPE_ACCESS,
|
2016-08-22 23:52:55 +08:00
|
|
|
.get = ovl_posix_acl_xattr_get,
|
2016-07-29 18:05:24 +08:00
|
|
|
.set = ovl_posix_acl_xattr_set,
|
|
|
|
};
|
|
|
|
|
2016-08-22 22:36:49 +08:00
|
|
|
static const struct xattr_handler __maybe_unused
|
|
|
|
ovl_posix_acl_default_xattr_handler = {
|
2016-07-29 18:05:24 +08:00
|
|
|
.name = XATTR_NAME_POSIX_ACL_DEFAULT,
|
|
|
|
.flags = ACL_TYPE_DEFAULT,
|
2016-08-22 23:52:55 +08:00
|
|
|
.get = ovl_posix_acl_xattr_get,
|
2016-07-29 18:05:24 +08:00
|
|
|
.set = ovl_posix_acl_xattr_set,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct xattr_handler ovl_own_xattr_handler = {
|
|
|
|
.prefix = OVL_XATTR_PREFIX,
|
2016-08-22 23:52:55 +08:00
|
|
|
.get = ovl_own_xattr_get,
|
2016-07-29 18:05:24 +08:00
|
|
|
.set = ovl_own_xattr_set,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct xattr_handler ovl_other_xattr_handler = {
|
|
|
|
.prefix = "", /* catch all */
|
2016-08-22 23:52:55 +08:00
|
|
|
.get = ovl_other_xattr_get,
|
2016-07-29 18:05:24 +08:00
|
|
|
.set = ovl_other_xattr_set,
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct xattr_handler *ovl_xattr_handlers[] = {
|
2016-08-22 22:36:49 +08:00
|
|
|
#ifdef CONFIG_FS_POSIX_ACL
|
2016-07-29 18:05:24 +08:00
|
|
|
&ovl_posix_acl_access_xattr_handler,
|
|
|
|
&ovl_posix_acl_default_xattr_handler,
|
2016-08-22 22:36:49 +08:00
|
|
|
#endif
|
2016-07-29 18:05:24 +08:00
|
|
|
&ovl_own_xattr_handler,
|
|
|
|
&ovl_other_xattr_handler,
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
static int ovl_setup_trap(struct super_block *sb, struct dentry *dir,
|
|
|
|
struct inode **ptrap, const char *name)
|
|
|
|
{
|
|
|
|
struct inode *trap;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
trap = ovl_get_trap_inode(sb, dir);
|
2019-06-17 20:39:29 +08:00
|
|
|
err = PTR_ERR_OR_ZERO(trap);
|
|
|
|
if (err) {
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
if (err == -ELOOP)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("conflicting %s path\n", name);
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
*ptrap = trap;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-07-12 20:24:34 +08:00
|
|
|
/*
|
|
|
|
* Determine how we treat concurrent use of upperdir/workdir based on the
|
|
|
|
* index feature. This is papering over mount leaks of container runtimes,
|
|
|
|
* for example, an old overlay mount is leaked and now its upperdir is
|
|
|
|
* attempted to be used as a lower layer in a new overlay mount.
|
|
|
|
*/
|
|
|
|
static int ovl_report_in_use(struct ovl_fs *ofs, const char *name)
|
|
|
|
{
|
|
|
|
if (ofs->config.index) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("%s is in-use as upperdir/workdir of another mount, mount with '-o index=off' to override exclusive upperdir protection.\n",
|
2019-07-12 20:24:34 +08:00
|
|
|
name);
|
|
|
|
return -EBUSY;
|
|
|
|
} else {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("%s is in-use as upperdir/workdir of another mount, accessing files from both mounts will result in undefined behavior.\n",
|
2019-07-12 20:24:34 +08:00
|
|
|
name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
|
2020-06-04 16:48:19 +08:00
|
|
|
struct ovl_layer *upper_layer, struct path *upperpath)
|
2017-11-09 17:23:28 +08:00
|
|
|
{
|
2017-11-10 16:39:15 +08:00
|
|
|
struct vfsmount *upper_mnt;
|
2017-11-09 17:23:28 +08:00
|
|
|
int err;
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
err = ovl_mount_dir(ofs->config.upperdir, upperpath);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/* Upper fs should not be r/o */
|
|
|
|
if (sb_rdonly(upperpath->mnt->mnt_sb)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("upper fs is r/o, try multi-lower layers mount\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
err = ovl_check_namelen(upperpath, ofs, ofs->config.upperdir);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
err = ovl_setup_trap(sb, upperpath->dentry, &upper_layer->trap,
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
"upperdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
upper_mnt = clone_private_mount(upperpath);
|
|
|
|
err = PTR_ERR(upper_mnt);
|
|
|
|
if (IS_ERR(upper_mnt)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to clone upperpath\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't inherit atime flags */
|
|
|
|
upper_mnt->mnt_flags &= ~(MNT_NOATIME | MNT_NODIRATIME | MNT_RELATIME);
|
2020-06-04 16:48:19 +08:00
|
|
|
upper_layer->mnt = upper_mnt;
|
|
|
|
upper_layer->idx = 0;
|
|
|
|
upper_layer->fsid = 0;
|
2018-09-10 17:43:29 +08:00
|
|
|
|
2020-04-23 19:06:55 +08:00
|
|
|
/*
|
|
|
|
* Inherit SB_NOSEC flag from upperdir.
|
|
|
|
*
|
|
|
|
* This optimization changes behavior when a security related attribute
|
|
|
|
* (suid/sgid/security.*) is changed on an underlying layer. This is
|
|
|
|
* okay because we don't yet have guarantees in that case, but it will
|
|
|
|
* need careful treatment once we want to honour changes to underlying
|
|
|
|
* filesystems.
|
|
|
|
*/
|
|
|
|
if (upper_mnt->mnt_sb->s_flags & SB_NOSEC)
|
|
|
|
sb->s_flags |= SB_NOSEC;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (ovl_inuse_trylock(ovl_upper_mnt(ofs)->mnt_root)) {
|
2018-09-10 17:43:29 +08:00
|
|
|
ofs->upperdir_locked = true;
|
|
|
|
} else {
|
2019-07-12 20:24:34 +08:00
|
|
|
err = ovl_report_in_use(ofs, "upperdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2018-09-10 17:43:29 +08:00
|
|
|
}
|
|
|
|
|
2017-11-09 17:23:28 +08:00
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-02-20 15:00:19 +08:00
|
|
|
/*
|
|
|
|
* Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and
|
|
|
|
* negative values if error is encountered.
|
|
|
|
*/
|
|
|
|
static int ovl_check_rename_whiteout(struct dentry *workdir)
|
|
|
|
{
|
|
|
|
struct inode *dir = d_inode(workdir);
|
|
|
|
struct dentry *temp;
|
|
|
|
struct dentry *dest;
|
|
|
|
struct dentry *whiteout;
|
|
|
|
struct name_snapshot name;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
inode_lock_nested(dir, I_MUTEX_PARENT);
|
|
|
|
|
|
|
|
temp = ovl_create_temp(workdir, OVL_CATTR(S_IFREG | 0));
|
|
|
|
err = PTR_ERR(temp);
|
|
|
|
if (IS_ERR(temp))
|
|
|
|
goto out_unlock;
|
|
|
|
|
|
|
|
dest = ovl_lookup_temp(workdir);
|
|
|
|
err = PTR_ERR(dest);
|
|
|
|
if (IS_ERR(dest)) {
|
|
|
|
dput(temp);
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Name is inline and stable - using snapshot as a copy helper */
|
|
|
|
take_dentry_name_snapshot(&name, temp);
|
|
|
|
err = ovl_do_rename(dir, temp, dir, dest, RENAME_WHITEOUT);
|
|
|
|
if (err) {
|
|
|
|
if (err == -EINVAL)
|
|
|
|
err = 0;
|
|
|
|
goto cleanup_temp;
|
|
|
|
}
|
|
|
|
|
|
|
|
whiteout = lookup_one_len(name.name.name, workdir, name.name.len);
|
|
|
|
err = PTR_ERR(whiteout);
|
|
|
|
if (IS_ERR(whiteout))
|
|
|
|
goto cleanup_temp;
|
|
|
|
|
|
|
|
err = ovl_is_whiteout(whiteout);
|
|
|
|
|
|
|
|
/* Best effort cleanup of whiteout and temp file */
|
|
|
|
if (err)
|
|
|
|
ovl_cleanup(dir, whiteout);
|
|
|
|
dput(whiteout);
|
|
|
|
|
|
|
|
cleanup_temp:
|
|
|
|
ovl_cleanup(dir, temp);
|
|
|
|
release_dentry_name_snapshot(&name);
|
|
|
|
dput(temp);
|
|
|
|
dput(dest);
|
|
|
|
|
|
|
|
out_unlock:
|
|
|
|
inode_unlock(dir);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-09-01 02:15:29 +08:00
|
|
|
static struct dentry *ovl_lookup_or_create(struct dentry *parent,
|
|
|
|
const char *name, umode_t mode)
|
|
|
|
{
|
|
|
|
size_t len = strlen(name);
|
|
|
|
struct dentry *child;
|
|
|
|
|
|
|
|
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
|
|
|
|
child = lookup_one_len(name, parent, len);
|
|
|
|
if (!IS_ERR(child) && !child->d_inode)
|
|
|
|
child = ovl_create_real(parent->d_inode, child,
|
|
|
|
OVL_CATTR(mode));
|
|
|
|
inode_unlock(parent->d_inode);
|
|
|
|
dput(parent);
|
|
|
|
|
|
|
|
return child;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Creates $workdir/work/incompat/volatile/dirty file if it is not already
|
|
|
|
* present.
|
|
|
|
*/
|
|
|
|
static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
|
|
|
|
{
|
|
|
|
unsigned int ctr;
|
|
|
|
struct dentry *d = dget(ofs->workbasedir);
|
|
|
|
static const char *const volatile_path[] = {
|
|
|
|
OVL_WORKDIR_NAME, "incompat", "volatile", "dirty"
|
|
|
|
};
|
|
|
|
const char *const *name = volatile_path;
|
|
|
|
|
|
|
|
for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
|
|
|
|
d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
|
|
|
|
if (IS_ERR(d))
|
|
|
|
return PTR_ERR(d);
|
|
|
|
}
|
|
|
|
dput(d);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|
|
|
struct path *workpath)
|
2017-11-09 17:23:28 +08:00
|
|
|
{
|
2020-06-04 16:48:19 +08:00
|
|
|
struct vfsmount *mnt = ovl_upper_mnt(ofs);
|
2020-08-31 04:28:03 +08:00
|
|
|
struct dentry *temp, *workdir;
|
2020-02-20 15:03:08 +08:00
|
|
|
bool rename_whiteout;
|
|
|
|
bool d_type;
|
2017-11-07 19:55:04 +08:00
|
|
|
int fh_type;
|
2017-11-09 17:23:28 +08:00
|
|
|
int err;
|
|
|
|
|
2018-01-04 00:54:41 +08:00
|
|
|
err = mnt_want_write(mnt);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2020-08-31 04:28:03 +08:00
|
|
|
workdir = ovl_workdir_create(ofs, OVL_WORKDIR_NAME, false);
|
|
|
|
err = PTR_ERR(workdir);
|
|
|
|
if (IS_ERR_OR_NULL(workdir))
|
2018-01-04 00:54:41 +08:00
|
|
|
goto out;
|
2017-11-09 17:23:28 +08:00
|
|
|
|
2020-08-31 04:28:03 +08:00
|
|
|
ofs->workdir = workdir;
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_setup_trap(sb, ofs->workdir, &ofs->workdir_trap, "workdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2017-11-09 17:23:28 +08:00
|
|
|
/*
|
|
|
|
* 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)
|
2018-01-04 00:54:41 +08:00
|
|
|
goto out;
|
2017-11-09 17:23:28 +08:00
|
|
|
|
2020-02-20 15:03:08 +08:00
|
|
|
d_type = err;
|
|
|
|
if (!d_type)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("upper fs needs to support d_type.\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
/* Check if upper/work fs supports O_TMPFILE */
|
2017-11-10 16:39:16 +08:00
|
|
|
temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0);
|
|
|
|
ofs->tmpfile = !IS_ERR(temp);
|
|
|
|
if (ofs->tmpfile)
|
2017-11-09 17:23:28 +08:00
|
|
|
dput(temp);
|
|
|
|
else
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("upper fs does not support tmpfile.\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
|
2020-02-20 15:00:19 +08:00
|
|
|
|
|
|
|
/* Check if upper/work fs supports RENAME_WHITEOUT */
|
|
|
|
err = ovl_check_rename_whiteout(ofs->workdir);
|
|
|
|
if (err < 0)
|
|
|
|
goto out;
|
|
|
|
|
2020-02-20 15:03:08 +08:00
|
|
|
rename_whiteout = err;
|
|
|
|
if (!rename_whiteout)
|
2020-02-20 15:00:19 +08:00
|
|
|
pr_warn("upper fs does not support RENAME_WHITEOUT.\n");
|
|
|
|
|
2017-11-09 17:23:28 +08:00
|
|
|
/*
|
|
|
|
* Check if upper/work fs supports trusted.overlay.* xattr
|
|
|
|
*/
|
2020-09-02 16:58:49 +08:00
|
|
|
err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err) {
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->noxattr = true;
|
2017-09-19 17:14:18 +08:00
|
|
|
ofs->config.index = false;
|
2018-05-11 23:49:27 +08:00
|
|
|
ofs->config.metacopy = false;
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("upper fs does not support xattr, falling back to index=off and metacopy=off.\n");
|
2018-01-04 00:54:41 +08:00
|
|
|
err = 0;
|
2017-11-09 17:23:28 +08:00
|
|
|
} else {
|
2020-09-02 16:58:49 +08:00
|
|
|
ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
|
|
|
|
2020-02-20 15:03:08 +08:00
|
|
|
/*
|
|
|
|
* We allowed sub-optimal upper fs configuration and don't want to break
|
|
|
|
* users over kernel upgrade, but we never allowed remote upper fs, so
|
|
|
|
* we can enforce strict requirements for remote upper fs.
|
|
|
|
*/
|
|
|
|
if (ovl_dentry_remote(ofs->workdir) &&
|
|
|
|
(!d_type || !rename_whiteout || ofs->noxattr)) {
|
|
|
|
pr_err("upper fs missing required features.\n");
|
|
|
|
err = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-09-01 02:15:29 +08:00
|
|
|
/*
|
|
|
|
* For volatile mount, create a incompat/volatile/dirty file to keep
|
|
|
|
* track of it.
|
|
|
|
*/
|
|
|
|
if (ofs->config.ovl_volatile) {
|
|
|
|
err = ovl_create_volatile_dirty(ofs);
|
|
|
|
if (err < 0) {
|
|
|
|
pr_err("Failed to create volatile/dirty file.\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-09 17:23:28 +08:00
|
|
|
/* Check if upper/work fs supports file handles */
|
2017-11-07 19:55:04 +08:00
|
|
|
fh_type = ovl_can_decode_fh(ofs->workdir->d_sb);
|
|
|
|
if (ofs->config.index && !fh_type) {
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->config.index = false;
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("upper fs does not support file handles, falling back to index=off.\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
|
|
|
|
2017-11-07 19:55:04 +08:00
|
|
|
/* Check if upper fs has 32bit inode numbers */
|
|
|
|
if (fh_type != FILEID_INO32_GEN)
|
2019-11-17 00:14:41 +08:00
|
|
|
ofs->xino_mode = -1;
|
2017-11-07 19:55:04 +08:00
|
|
|
|
2018-01-19 17:26:53 +08:00
|
|
|
/* NFS export of r/w mount depends on index */
|
|
|
|
if (ofs->config.nfs_export && !ofs->config.index) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("NFS export requires \"index=on\", falling back to nfs_export=off.\n");
|
2018-01-19 17:26:53 +08:00
|
|
|
ofs->config.nfs_export = false;
|
|
|
|
}
|
2018-01-04 00:54:41 +08:00
|
|
|
out:
|
|
|
|
mnt_drop_write(mnt);
|
|
|
|
return err;
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
static int ovl_get_workdir(struct super_block *sb, struct ovl_fs *ofs,
|
|
|
|
struct path *upperpath)
|
2017-11-10 16:39:15 +08:00
|
|
|
{
|
|
|
|
int err;
|
2017-11-10 16:39:15 +08:00
|
|
|
struct path workpath = { };
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
err = ovl_mount_dir(ofs->config.workdir, &workpath);
|
2017-11-10 16:39:15 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
2017-11-10 16:39:15 +08:00
|
|
|
if (upperpath->mnt != workpath.mnt) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("workdir and upperdir must reside under the same mount\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2017-11-10 16:39:15 +08:00
|
|
|
if (!ovl_workdir_ok(workpath.dentry, upperpath->dentry)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("workdir and upperdir must be separate subtrees\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2018-09-10 17:43:29 +08:00
|
|
|
ofs->workbasedir = dget(workpath.dentry);
|
|
|
|
|
|
|
|
if (ovl_inuse_trylock(ofs->workbasedir)) {
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->workdir_locked = true;
|
2017-11-10 16:39:15 +08:00
|
|
|
} else {
|
2019-07-12 20:24:34 +08:00
|
|
|
err = ovl_report_in_use(ofs, "workdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
2017-11-10 16:39:15 +08:00
|
|
|
}
|
|
|
|
|
2019-07-12 20:24:34 +08:00
|
|
|
err = ovl_setup_trap(sb, ofs->workbasedir, &ofs->workbasedir_trap,
|
|
|
|
"workdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_make_workdir(sb, ofs, &workpath);
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
out:
|
2017-11-10 16:39:15 +08:00
|
|
|
path_put(&workpath);
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
static int ovl_get_indexdir(struct super_block *sb, struct ovl_fs *ofs,
|
|
|
|
struct ovl_entry *oe, struct path *upperpath)
|
2017-11-09 17:23:28 +08:00
|
|
|
{
|
2020-06-04 16:48:19 +08:00
|
|
|
struct vfsmount *mnt = ovl_upper_mnt(ofs);
|
2020-08-31 04:28:03 +08:00
|
|
|
struct dentry *indexdir;
|
2017-11-09 17:23:28 +08:00
|
|
|
int err;
|
|
|
|
|
2018-01-04 00:54:41 +08:00
|
|
|
err = mnt_want_write(mnt);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2017-11-09 17:23:28 +08:00
|
|
|
/* Verify lower root is upper root origin */
|
2020-09-02 16:58:49 +08:00
|
|
|
err = ovl_verify_origin(ofs, upperpath->dentry,
|
|
|
|
oe->lowerstack[0].dentry, true);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to verify upper root origin\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-07-13 22:19:43 +08:00
|
|
|
/* index dir will act also as workdir */
|
|
|
|
iput(ofs->workdir_trap);
|
|
|
|
ofs->workdir_trap = NULL;
|
|
|
|
dput(ofs->workdir);
|
|
|
|
ofs->workdir = NULL;
|
2020-08-31 04:28:03 +08:00
|
|
|
indexdir = ovl_workdir_create(ofs, OVL_INDEXDIR_NAME, true);
|
|
|
|
if (IS_ERR(indexdir)) {
|
|
|
|
err = PTR_ERR(indexdir);
|
|
|
|
} else if (indexdir) {
|
|
|
|
ofs->indexdir = indexdir;
|
|
|
|
ofs->workdir = dget(indexdir);
|
ovl: fix oops in ovl_indexdir_cleanup() with nfs_export=on
Mounting with nfs_export=on, xfstests overlay/031 triggers a kernel panic
since v5.8-rc1 overlayfs updates.
overlayfs: orphan index entry (index/00fb1..., ftype=4000, nlink=2)
BUG: kernel NULL pointer dereference, address: 0000000000000030
RIP: 0010:ovl_cleanup_and_whiteout+0x28/0x220 [overlay]
Bisect point at commit c21c839b8448 ("ovl: whiteout inode sharing")
Minimal reproducer:
--------------------------------------------------
rm -rf l u w m
mkdir -p l u w m
mkdir -p l/testdir
touch l/testdir/testfile
mount -t overlay -o lowerdir=l,upperdir=u,workdir=w,nfs_export=on overlay m
echo 1 > m/testdir/testfile
umount m
rm -rf u/testdir
mount -t overlay -o lowerdir=l,upperdir=u,workdir=w,nfs_export=on overlay m
umount m
--------------------------------------------------
When mount with nfs_export=on, and fail to verify an orphan index, we're
cleaning this index from indexdir by calling ovl_cleanup_and_whiteout().
This dereferences ofs->workdir, that was earlier set to NULL.
The design was that ovl->workdir will point at ovl->indexdir, but we are
assigning ofs->indexdir to ofs->workdir only after ovl_indexdir_cleanup().
There is no reason not to do it sooner, because once we get success from
ofs->indexdir = ovl_workdir_create(... there is no turning back.
Reported-and-tested-by: Murphy Zhou <jencce.kernel@gmail.com>
Fixes: c21c839b8448 ("ovl: whiteout inode sharing")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2020-06-21 14:37:59 +08:00
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_setup_trap(sb, ofs->indexdir, &ofs->indexdir_trap,
|
|
|
|
"indexdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2018-01-11 16:47:03 +08:00
|
|
|
/*
|
|
|
|
* Verify upper root is exclusively associated with index dir.
|
|
|
|
* Older kernels stored upper fh in "trusted.overlay.origin"
|
|
|
|
* xattr. If that xattr exists, verify that it is a match to
|
|
|
|
* upper dir file handle. In any case, verify or set xattr
|
|
|
|
* "trusted.overlay.upper" to indicate that index may have
|
|
|
|
* directory entries.
|
|
|
|
*/
|
2020-09-02 16:58:49 +08:00
|
|
|
if (ovl_check_origin_xattr(ofs, ofs->indexdir)) {
|
|
|
|
err = ovl_verify_set_fh(ofs, ofs->indexdir,
|
|
|
|
OVL_XATTR_ORIGIN,
|
2018-01-11 16:47:03 +08:00
|
|
|
upperpath->dentry, true, false);
|
|
|
|
if (err)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to verify index dir 'origin' xattr\n");
|
2018-01-11 16:47:03 +08:00
|
|
|
}
|
2020-09-02 16:58:49 +08:00
|
|
|
err = ovl_verify_upper(ofs, ofs->indexdir, upperpath->dentry,
|
|
|
|
true);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to verify index dir 'upper' xattr\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
/* Cleanup bad/stale/orphan index entries */
|
|
|
|
if (!err)
|
2017-12-13 04:40:46 +08:00
|
|
|
err = ovl_indexdir_cleanup(ofs);
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
2017-11-10 16:39:16 +08:00
|
|
|
if (err || !ofs->indexdir)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("try deleting index dir or mounting with '-o index=off' to disable inodes index.\n");
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
out:
|
2018-01-04 00:54:41 +08:00
|
|
|
mnt_drop_write(mnt);
|
2017-11-09 17:23:28 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2018-09-03 14:12:09 +08:00
|
|
|
static bool ovl_lower_uuid_ok(struct ovl_fs *ofs, const uuid_t *uuid)
|
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (!ofs->config.nfs_export && !ovl_upper_mnt(ofs))
|
2018-09-03 14:12:09 +08:00
|
|
|
return true;
|
|
|
|
|
2020-07-08 21:16:13 +08:00
|
|
|
/*
|
|
|
|
* We allow using single lower with null uuid for index and nfs_export
|
|
|
|
* for example to support those features with single lower squashfs.
|
|
|
|
* To avoid regressions in setups of overlay with re-formatted lower
|
|
|
|
* squashfs, do not allow decoding origin with lower null uuid unless
|
|
|
|
* user opted-in to one of the new features that require following the
|
|
|
|
* lower inode of non-dir upper.
|
|
|
|
*/
|
|
|
|
if (!ofs->config.index && !ofs->config.metacopy && !ofs->config.xino &&
|
|
|
|
uuid_is_null(uuid))
|
|
|
|
return false;
|
|
|
|
|
2019-11-17 00:52:20 +08:00
|
|
|
for (i = 0; i < ofs->numfs; i++) {
|
2018-09-03 14:12:09 +08:00
|
|
|
/*
|
|
|
|
* We use uuid to associate an overlay lower file handle with a
|
|
|
|
* lower layer, so we can accept lower fs with null uuid as long
|
|
|
|
* as all lower layers with null uuid are on the same fs.
|
2019-11-15 04:28:41 +08:00
|
|
|
* if we detect multiple lower fs with the same uuid, we
|
|
|
|
* disable lower file handle decoding on all of them.
|
2018-09-03 14:12:09 +08:00
|
|
|
*/
|
2019-11-17 00:52:20 +08:00
|
|
|
if (ofs->fs[i].is_lower &&
|
|
|
|
uuid_equal(&ofs->fs[i].sb->s_uuid, uuid)) {
|
2020-01-15 03:59:22 +08:00
|
|
|
ofs->fs[i].bad_uuid = true;
|
2018-09-03 14:12:09 +08:00
|
|
|
return false;
|
2019-11-15 04:28:41 +08:00
|
|
|
}
|
2018-09-03 14:12:09 +08:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-03-29 01:22:41 +08:00
|
|
|
/* Get a unique fsid for the layer */
|
2018-09-03 14:12:09 +08:00
|
|
|
static int ovl_get_fsid(struct ovl_fs *ofs, const struct path *path)
|
2018-03-29 01:22:41 +08:00
|
|
|
{
|
2018-09-03 14:12:09 +08:00
|
|
|
struct super_block *sb = path->mnt->mnt_sb;
|
2018-03-29 01:22:41 +08:00
|
|
|
unsigned int i;
|
|
|
|
dev_t dev;
|
|
|
|
int err;
|
2019-11-15 04:28:41 +08:00
|
|
|
bool bad_uuid = false;
|
2018-03-29 01:22:41 +08:00
|
|
|
|
2020-01-15 03:59:22 +08:00
|
|
|
for (i = 0; i < ofs->numfs; i++) {
|
|
|
|
if (ofs->fs[i].sb == sb)
|
|
|
|
return i;
|
2018-03-29 01:22:41 +08:00
|
|
|
}
|
|
|
|
|
2018-09-03 14:12:09 +08:00
|
|
|
if (!ovl_lower_uuid_ok(ofs, &sb->s_uuid)) {
|
2019-11-15 04:28:41 +08:00
|
|
|
bad_uuid = true;
|
|
|
|
if (ofs->config.index || ofs->config.nfs_export) {
|
|
|
|
ofs->config.index = false;
|
|
|
|
ofs->config.nfs_export = false;
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("%s uuid detected in lower fs '%pd2', falling back to index=off,nfs_export=off.\n",
|
2019-11-15 04:28:41 +08:00
|
|
|
uuid_is_null(&sb->s_uuid) ? "null" :
|
|
|
|
"conflicting",
|
|
|
|
path->dentry);
|
|
|
|
}
|
2018-09-03 14:12:09 +08:00
|
|
|
}
|
|
|
|
|
2018-03-29 01:22:41 +08:00
|
|
|
err = get_anon_bdev(&dev);
|
|
|
|
if (err) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to get anonymous bdev for lowerpath\n");
|
2018-03-29 01:22:41 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2020-01-15 03:59:22 +08:00
|
|
|
ofs->fs[ofs->numfs].sb = sb;
|
|
|
|
ofs->fs[ofs->numfs].pseudo_dev = dev;
|
|
|
|
ofs->fs[ofs->numfs].bad_uuid = bad_uuid;
|
2018-03-29 01:22:41 +08:00
|
|
|
|
2020-01-15 03:59:22 +08:00
|
|
|
return ofs->numfs++;
|
2018-03-29 01:22:41 +08:00
|
|
|
}
|
|
|
|
|
2019-11-15 20:12:40 +08:00
|
|
|
static int ovl_get_layers(struct super_block *sb, struct ovl_fs *ofs,
|
2020-06-04 16:48:19 +08:00
|
|
|
struct path *stack, unsigned int numlower,
|
|
|
|
struct ovl_layer *layers)
|
2017-11-10 16:39:15 +08:00
|
|
|
{
|
|
|
|
int err;
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
err = -ENOMEM;
|
2020-01-15 03:59:22 +08:00
|
|
|
ofs->fs = kcalloc(numlower + 1, sizeof(struct ovl_sb), GFP_KERNEL);
|
|
|
|
if (ofs->fs == NULL)
|
2018-03-29 01:22:41 +08:00
|
|
|
goto out;
|
|
|
|
|
2020-01-15 03:59:22 +08:00
|
|
|
/* idx/fsid 0 are reserved for upper fs even with lower only overlay */
|
|
|
|
ofs->numfs++;
|
|
|
|
|
|
|
|
/*
|
2020-01-15 04:17:25 +08:00
|
|
|
* All lower layers that share the same fs as upper layer, use the same
|
|
|
|
* pseudo_dev as upper layer. Allocate fs[0].pseudo_dev even for lower
|
|
|
|
* only overlay to simplify ovl_fs_free().
|
2019-11-17 00:52:20 +08:00
|
|
|
* is_lower will be set if upper fs is shared with a lower layer.
|
2020-01-15 03:59:22 +08:00
|
|
|
*/
|
2020-01-15 04:17:25 +08:00
|
|
|
err = get_anon_bdev(&ofs->fs[0].pseudo_dev);
|
|
|
|
if (err) {
|
|
|
|
pr_err("failed to get anonymous bdev for upper fs\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (ovl_upper_mnt(ofs)) {
|
|
|
|
ofs->fs[0].sb = ovl_upper_mnt(ofs)->mnt_sb;
|
2019-11-17 00:52:20 +08:00
|
|
|
ofs->fs[0].is_lower = false;
|
2020-01-15 03:59:22 +08:00
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
for (i = 0; i < numlower; i++) {
|
|
|
|
struct vfsmount *mnt;
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
struct inode *trap;
|
2018-03-29 01:22:41 +08:00
|
|
|
int fsid;
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2018-09-03 14:12:09 +08:00
|
|
|
err = fsid = ovl_get_fsid(ofs, &stack[i]);
|
2018-03-29 01:22:41 +08:00
|
|
|
if (err < 0)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out;
|
|
|
|
|
2020-06-16 16:30:43 +08:00
|
|
|
/*
|
|
|
|
* Check if lower root conflicts with this overlay layers before
|
|
|
|
* checking if it is in-use as upperdir/workdir of "another"
|
|
|
|
* mount, because we do not bother to check in ovl_is_inuse() if
|
|
|
|
* the upperdir/workdir is in fact in-use by our
|
|
|
|
* upperdir/workdir.
|
|
|
|
*/
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_setup_trap(sb, stack[i].dentry, &trap, "lowerdir");
|
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
|
2019-07-12 20:24:34 +08:00
|
|
|
if (ovl_is_inuse(stack[i].dentry)) {
|
|
|
|
err = ovl_report_in_use(ofs, "lowerdir");
|
2020-06-16 16:30:43 +08:00
|
|
|
if (err) {
|
|
|
|
iput(trap);
|
2019-07-12 20:24:34 +08:00
|
|
|
goto out;
|
2020-06-16 16:30:43 +08:00
|
|
|
}
|
2019-07-12 20:24:34 +08:00
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
mnt = clone_private_mount(&stack[i]);
|
|
|
|
err = PTR_ERR(mnt);
|
|
|
|
if (IS_ERR(mnt)) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("failed to clone lowerpath\n");
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
iput(trap);
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out;
|
|
|
|
}
|
2018-03-29 01:22:41 +08:00
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
2020-01-24 16:46:45 +08:00
|
|
|
layers[ofs->numlayer].trap = trap;
|
|
|
|
layers[ofs->numlayer].mnt = mnt;
|
|
|
|
layers[ofs->numlayer].idx = ofs->numlayer;
|
|
|
|
layers[ofs->numlayer].fsid = fsid;
|
|
|
|
layers[ofs->numlayer].fs = &ofs->fs[fsid];
|
2019-11-15 20:12:40 +08:00
|
|
|
ofs->numlayer++;
|
2019-11-17 00:52:20 +08:00
|
|
|
ofs->fs[fsid].is_lower = true;
|
2017-11-10 16:39:15 +08:00
|
|
|
}
|
2017-11-07 19:55:04 +08:00
|
|
|
|
2018-03-29 14:08:18 +08:00
|
|
|
/*
|
|
|
|
* When all layers on same fs, overlay can use real inode numbers.
|
2020-02-21 22:34:45 +08:00
|
|
|
* With mount option "xino=<on|auto>", mounter declares that there are
|
|
|
|
* enough free high bits in underlying fs to hold the unique fsid.
|
2018-03-29 14:08:18 +08:00
|
|
|
* If overlayfs does encounter underlying inodes using the high xino
|
|
|
|
* bits reserved for fsid, it emits a warning and uses the original
|
2020-02-21 22:34:44 +08:00
|
|
|
* inode number or a non persistent inode number allocated from a
|
|
|
|
* dedicated range.
|
2018-03-29 14:08:18 +08:00
|
|
|
*/
|
2020-06-04 16:48:19 +08:00
|
|
|
if (ofs->numfs - !ovl_upper_mnt(ofs) == 1) {
|
2019-11-17 00:14:41 +08:00
|
|
|
if (ofs->config.xino == OVL_XINO_ON)
|
|
|
|
pr_info("\"xino=on\" is useless with all layers on same fs, ignore.\n");
|
|
|
|
ofs->xino_mode = 0;
|
2020-02-21 22:34:42 +08:00
|
|
|
} else if (ofs->config.xino == OVL_XINO_OFF) {
|
|
|
|
ofs->xino_mode = -1;
|
2020-02-21 22:34:45 +08:00
|
|
|
} else if (ofs->xino_mode < 0) {
|
2018-03-29 14:08:18 +08:00
|
|
|
/*
|
2020-01-15 03:59:22 +08:00
|
|
|
* This is a roundup of number of bits needed for encoding
|
2020-02-21 22:34:44 +08:00
|
|
|
* fsid, where fsid 0 is reserved for upper fs (even with
|
|
|
|
* lower only overlay) +1 extra bit is reserved for the non
|
|
|
|
* persistent inode number range that is used for resolving
|
|
|
|
* xino lower bits overflow.
|
2018-03-29 14:08:18 +08:00
|
|
|
*/
|
2020-02-21 22:34:44 +08:00
|
|
|
BUILD_BUG_ON(ilog2(OVL_MAX_STACK) > 30);
|
|
|
|
ofs->xino_mode = ilog2(ofs->numfs - 1) + 2;
|
2018-03-29 14:08:18 +08:00
|
|
|
}
|
|
|
|
|
2019-11-17 00:14:41 +08:00
|
|
|
if (ofs->xino_mode > 0) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_info("\"xino\" feature enabled using %d upper inode bits.\n",
|
2019-11-17 00:14:41 +08:00
|
|
|
ofs->xino_mode);
|
2018-03-29 14:08:18 +08:00
|
|
|
}
|
2017-11-07 19:55:04 +08:00
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
err = 0;
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb,
|
2020-06-04 16:48:19 +08:00
|
|
|
const char *lower, unsigned int numlower,
|
|
|
|
struct ovl_fs *ofs, struct ovl_layer *layers)
|
2017-11-09 17:23:28 +08:00
|
|
|
{
|
|
|
|
int err;
|
2017-11-10 16:39:15 +08:00
|
|
|
struct path *stack = NULL;
|
2020-06-04 16:48:19 +08:00
|
|
|
unsigned int i;
|
2017-11-10 16:39:15 +08:00
|
|
|
struct ovl_entry *oe;
|
2017-11-09 17:23:28 +08:00
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (!ofs->config.upperdir && numlower == 1) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n");
|
2020-06-04 16:48:19 +08:00
|
|
|
return ERR_PTR(-EINVAL);
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (!stack)
|
2020-06-04 16:48:19 +08:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
err = -EINVAL;
|
2020-06-04 16:48:19 +08:00
|
|
|
for (i = 0; i < numlower; i++) {
|
|
|
|
err = ovl_lower_dir(lower, &stack[i], ofs, &sb->s_stack_depth);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
lower = strchr(lower, '\0') + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = -EINVAL;
|
|
|
|
sb->s_stack_depth++;
|
|
|
|
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("maximum fs stacking depth exceeded\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2017-11-09 17:23:28 +08:00
|
|
|
}
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
err = ovl_get_layers(sb, ofs, stack, numlower, layers);
|
2017-11-10 16:39:15 +08:00
|
|
|
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);
|
2019-11-15 20:12:40 +08:00
|
|
|
oe->lowerstack[i].layer = &ofs->layers[i+1];
|
2017-11-10 16:39:15 +08:00
|
|
|
}
|
2017-11-09 17:23:28 +08:00
|
|
|
|
|
|
|
out:
|
|
|
|
for (i = 0; i < numlower; i++)
|
|
|
|
path_put(&stack[i]);
|
|
|
|
kfree(stack);
|
2017-11-10 16:39:15 +08:00
|
|
|
|
|
|
|
return oe;
|
|
|
|
|
|
|
|
out_err:
|
|
|
|
oe = ERR_PTR(err);
|
2017-11-09 17:23:28 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
/*
|
|
|
|
* Check if this layer root is a descendant of:
|
|
|
|
* - another layer of this overlayfs instance
|
|
|
|
* - upper/work dir of any overlayfs instance
|
|
|
|
*/
|
2019-07-12 20:24:34 +08:00
|
|
|
static int ovl_check_layer(struct super_block *sb, struct ovl_fs *ofs,
|
|
|
|
struct dentry *dentry, const char *name)
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
{
|
2019-06-18 21:06:16 +08:00
|
|
|
struct dentry *next = dentry, *parent;
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
int err = 0;
|
|
|
|
|
2019-06-18 21:06:16 +08:00
|
|
|
if (!dentry)
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
return 0;
|
|
|
|
|
2019-06-18 21:06:16 +08:00
|
|
|
parent = dget_parent(next);
|
|
|
|
|
|
|
|
/* Walk back ancestors to root (inclusive) looking for traps */
|
|
|
|
while (!err && parent != next) {
|
2019-07-12 20:24:34 +08:00
|
|
|
if (ovl_lookup_trap_inode(sb, parent)) {
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = -ELOOP;
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("overlapping %s path\n", name);
|
2019-07-12 20:24:34 +08:00
|
|
|
} else if (ovl_is_inuse(parent)) {
|
|
|
|
err = ovl_report_in_use(ofs, name);
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
}
|
|
|
|
next = parent;
|
2019-06-18 21:06:16 +08:00
|
|
|
parent = dget_parent(next);
|
|
|
|
dput(next);
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
}
|
|
|
|
|
2019-06-18 21:06:16 +08:00
|
|
|
dput(parent);
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if any of the layers or work dirs overlap.
|
|
|
|
*/
|
|
|
|
static int ovl_check_overlapping_layers(struct super_block *sb,
|
|
|
|
struct ovl_fs *ofs)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
if (ovl_upper_mnt(ofs)) {
|
|
|
|
err = ovl_check_layer(sb, ofs, ovl_upper_mnt(ofs)->mnt_root,
|
2019-07-12 20:24:34 +08:00
|
|
|
"upperdir");
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Checking workbasedir avoids hitting ovl_is_inuse(parent) of
|
|
|
|
* this instance and covers overlapping work and index dirs,
|
|
|
|
* unless work or index dir have been moved since created inside
|
|
|
|
* workbasedir. In that case, we already have their traps in
|
|
|
|
* inode cache and we will catch that case on lookup.
|
|
|
|
*/
|
2019-07-12 20:24:34 +08:00
|
|
|
err = ovl_check_layer(sb, ofs, ofs->workbasedir, "workdir");
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2019-11-15 20:12:40 +08:00
|
|
|
for (i = 1; i < ofs->numlayer; i++) {
|
2019-07-12 20:24:34 +08:00
|
|
|
err = ovl_check_layer(sb, ofs,
|
2019-11-15 20:12:40 +08:00
|
|
|
ofs->layers[i].mnt->mnt_root,
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
"lowerdir");
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-11-19 23:49:17 +08:00
|
|
|
static struct dentry *ovl_get_root(struct super_block *sb,
|
|
|
|
struct dentry *upperdentry,
|
|
|
|
struct ovl_entry *oe)
|
|
|
|
{
|
|
|
|
struct dentry *root;
|
2019-11-19 21:31:46 +08:00
|
|
|
struct ovl_path *lowerpath = &oe->lowerstack[0];
|
|
|
|
unsigned long ino = d_inode(lowerpath->dentry)->i_ino;
|
|
|
|
int fsid = lowerpath->layer->fsid;
|
|
|
|
struct ovl_inode_params oip = {
|
|
|
|
.upperdentry = upperdentry,
|
|
|
|
.lowerpath = lowerpath,
|
|
|
|
};
|
2019-11-19 23:49:17 +08:00
|
|
|
|
|
|
|
root = d_make_root(ovl_new_inode(sb, S_IFDIR, 0));
|
|
|
|
if (!root)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
root->d_fsdata = oe;
|
|
|
|
|
|
|
|
if (upperdentry) {
|
2019-11-19 21:31:46 +08:00
|
|
|
/* Root inode uses upper st_ino/i_ino */
|
|
|
|
ino = d_inode(upperdentry)->i_ino;
|
|
|
|
fsid = 0;
|
2019-11-19 23:49:17 +08:00
|
|
|
ovl_dentry_set_upper_alias(root);
|
2020-09-02 16:58:49 +08:00
|
|
|
if (ovl_is_impuredir(sb, upperdentry))
|
2019-11-19 23:49:17 +08:00
|
|
|
ovl_set_flag(OVL_IMPURE, d_inode(root));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Root is always merge -> can have whiteouts */
|
|
|
|
ovl_set_flag(OVL_WHITEOUTS, d_inode(root));
|
|
|
|
ovl_dentry_set_flag(OVL_E_CONNECTED, root);
|
|
|
|
ovl_set_upperdata(d_inode(root));
|
2019-11-19 21:31:46 +08:00
|
|
|
ovl_inode_init(d_inode(root), &oip, ino, fsid);
|
2020-03-17 22:04:22 +08:00
|
|
|
ovl_dentry_update_reval(root, upperdentry, DCACHE_OP_WEAK_REVALIDATE);
|
2019-11-19 23:49:17 +08:00
|
|
|
|
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static int ovl_fill_super(struct super_block *sb, void *data, int silent)
|
|
|
|
{
|
2017-03-30 05:02:19 +08:00
|
|
|
struct path upperpath = { };
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
struct dentry *root_dentry;
|
2017-11-10 16:39:15 +08:00
|
|
|
struct ovl_entry *oe;
|
2017-11-10 16:39:16 +08:00
|
|
|
struct ovl_fs *ofs;
|
2020-06-04 16:48:19 +08:00
|
|
|
struct ovl_layer *layers;
|
2017-01-11 02:30:21 +08:00
|
|
|
struct cred *cred;
|
2020-06-04 16:48:19 +08:00
|
|
|
char *splitlower = NULL;
|
|
|
|
unsigned int numlower;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
int err;
|
|
|
|
|
2020-03-17 22:04:22 +08:00
|
|
|
sb->s_d_op = &ovl_dentry_operations;
|
|
|
|
|
2014-10-24 06:14:38 +08:00
|
|
|
err = -ENOMEM;
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL);
|
|
|
|
if (!ofs)
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
goto out;
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->creator_cred = cred = prepare_creds();
|
2017-11-10 16:39:15 +08:00
|
|
|
if (!cred)
|
|
|
|
goto out_err;
|
|
|
|
|
2020-04-24 10:55:17 +08:00
|
|
|
/* Is there a reason anyone would want not to share whiteouts? */
|
|
|
|
ofs->share_whiteout = true;
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->config.index = ovl_index_def;
|
2018-01-19 17:26:53 +08:00
|
|
|
ofs->config.nfs_export = ovl_nfs_export_def;
|
2018-03-29 14:08:18 +08:00
|
|
|
ofs->config.xino = ovl_xino_def();
|
2018-05-11 23:49:27 +08:00
|
|
|
ofs->config.metacopy = ovl_metacopy_def;
|
2017-11-10 16:39:16 +08:00
|
|
|
err = ovl_parse_opt((char *) data, &ofs->config);
|
2014-10-24 06:14:38 +08:00
|
|
|
if (err)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2014-10-24 06:14:38 +08:00
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
err = -EINVAL;
|
2017-11-10 16:39:16 +08:00
|
|
|
if (!ofs->config.lowerdir) {
|
2015-06-30 01:18:56 +08:00
|
|
|
if (!silent)
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("missing 'lowerdir'\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
err = -ENOMEM;
|
|
|
|
splitlower = kstrdup(ofs->config.lowerdir, GFP_KERNEL);
|
|
|
|
if (!splitlower)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
numlower = ovl_split_lowerdirs(splitlower);
|
|
|
|
if (numlower > OVL_MAX_STACK) {
|
|
|
|
pr_err("too many lower directories, limit is %d\n",
|
|
|
|
OVL_MAX_STACK);
|
|
|
|
goto out_err;
|
|
|
|
}
|
|
|
|
|
|
|
|
layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), GFP_KERNEL);
|
|
|
|
if (!layers)
|
|
|
|
goto out_err;
|
|
|
|
|
|
|
|
ofs->layers = layers;
|
|
|
|
/* Layer 0 is reserved for upper even if there's no upper */
|
|
|
|
ofs->numlayer = 1;
|
|
|
|
|
2014-12-13 07:59:51 +08:00
|
|
|
sb->s_stack_depth = 0;
|
2015-12-11 23:30:49 +08:00
|
|
|
sb->s_maxbytes = MAX_LFS_FILESIZE;
|
2020-02-21 22:34:43 +08:00
|
|
|
atomic_long_set(&ofs->last_ino, 1);
|
2017-11-07 19:55:04 +08:00
|
|
|
/* Assume underlaying fs uses 32bit inodes unless proven otherwise */
|
2020-02-21 22:34:42 +08:00
|
|
|
if (ofs->config.xino != OVL_XINO_OFF) {
|
2019-11-17 00:14:41 +08:00
|
|
|
ofs->xino_mode = BITS_PER_LONG - 32;
|
2020-02-21 22:34:42 +08:00
|
|
|
if (!ofs->xino_mode) {
|
|
|
|
pr_warn("xino not supported on 32bit kernel, falling back to xino=off.\n");
|
|
|
|
ofs->config.xino = OVL_XINO_OFF;
|
|
|
|
}
|
|
|
|
}
|
2018-03-29 14:08:18 +08:00
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
/* alloc/destroy_inode needed for setting up traps in inode cache */
|
|
|
|
sb->s_op = &ovl_super_operations;
|
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
if (ofs->config.upperdir) {
|
|
|
|
if (!ofs->config.workdir) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_err("missing 'workdir'\n");
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2014-12-13 07:59:51 +08:00
|
|
|
}
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
err = ovl_get_upper(sb, ofs, &layers[0], &upperpath);
|
2014-12-13 07:59:51 +08:00
|
|
|
if (err)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2017-06-21 20:28:33 +08:00
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_get_workdir(sb, ofs, &upperpath);
|
2017-11-09 17:23:28 +08:00
|
|
|
if (err)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2017-11-10 16:39:16 +08:00
|
|
|
if (!ofs->workdir)
|
2017-11-28 05:05:09 +08:00
|
|
|
sb->s_flags |= SB_RDONLY;
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2020-06-04 16:48:19 +08:00
|
|
|
sb->s_stack_depth = ovl_upper_mnt(ofs)->mnt_sb->s_stack_depth;
|
|
|
|
sb->s_time_gran = ovl_upper_mnt(ofs)->mnt_sb->s_time_gran;
|
2017-11-10 16:39:15 +08:00
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
2020-06-04 16:48:19 +08:00
|
|
|
oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers);
|
2017-11-10 16:39:15 +08:00
|
|
|
err = PTR_ERR(oe);
|
|
|
|
if (IS_ERR(oe))
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_err;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2015-01-15 13:20:57 +08:00
|
|
|
/* If the upper fs is nonexistent, we mark overlayfs r/o too */
|
2020-06-04 16:48:19 +08:00
|
|
|
if (!ovl_upper_mnt(ofs))
|
2017-11-28 05:05:09 +08:00
|
|
|
sb->s_flags |= SB_RDONLY;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2020-07-13 22:19:43 +08:00
|
|
|
if (!ovl_force_readonly(ofs) && ofs->config.index) {
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_get_indexdir(sb, ofs, oe, &upperpath);
|
2017-06-21 20:28:38 +08:00
|
|
|
if (err)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_free_oe;
|
2017-11-10 16:39:15 +08:00
|
|
|
|
2017-09-19 17:14:18 +08:00
|
|
|
/* Force r/o mount with no index dir */
|
ovl: fix oops in ovl_indexdir_cleanup() with nfs_export=on
Mounting with nfs_export=on, xfstests overlay/031 triggers a kernel panic
since v5.8-rc1 overlayfs updates.
overlayfs: orphan index entry (index/00fb1..., ftype=4000, nlink=2)
BUG: kernel NULL pointer dereference, address: 0000000000000030
RIP: 0010:ovl_cleanup_and_whiteout+0x28/0x220 [overlay]
Bisect point at commit c21c839b8448 ("ovl: whiteout inode sharing")
Minimal reproducer:
--------------------------------------------------
rm -rf l u w m
mkdir -p l u w m
mkdir -p l/testdir
touch l/testdir/testfile
mount -t overlay -o lowerdir=l,upperdir=u,workdir=w,nfs_export=on overlay m
echo 1 > m/testdir/testfile
umount m
rm -rf u/testdir
mount -t overlay -o lowerdir=l,upperdir=u,workdir=w,nfs_export=on overlay m
umount m
--------------------------------------------------
When mount with nfs_export=on, and fail to verify an orphan index, we're
cleaning this index from indexdir by calling ovl_cleanup_and_whiteout().
This dereferences ofs->workdir, that was earlier set to NULL.
The design was that ovl->workdir will point at ovl->indexdir, but we are
assigning ofs->indexdir to ofs->workdir only after ovl_indexdir_cleanup().
There is no reason not to do it sooner, because once we get success from
ofs->indexdir = ovl_workdir_create(... there is no turning back.
Reported-and-tested-by: Murphy Zhou <jencce.kernel@gmail.com>
Fixes: c21c839b8448 ("ovl: whiteout inode sharing")
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2020-06-21 14:37:59 +08:00
|
|
|
if (!ofs->indexdir)
|
2017-11-28 05:05:09 +08:00
|
|
|
sb->s_flags |= SB_RDONLY;
|
2017-06-21 20:28:36 +08:00
|
|
|
}
|
|
|
|
|
ovl: detect overlapping layers
Overlapping overlay layers are not supported and can cause unexpected
behavior, but overlayfs does not currently check or warn about these
configurations.
User is not supposed to specify the same directory for upper and
lower dirs or for different lower layers and user is not supposed to
specify directories that are descendants of each other for overlay
layers, but that is exactly what this zysbot repro did:
https://syzkaller.appspot.com/x/repro.syz?x=12c7a94f400000
Moving layer root directories into other layers while overlayfs
is mounted could also result in unexpected behavior.
This commit places "traps" in the overlay inode hash table.
Those traps are dummy overlay inodes that are hashed by the layers
root inodes.
On mount, the hash table trap entries are used to verify that overlay
layers are not overlapping. While at it, we also verify that overlay
layers are not overlapping with directories "in-use" by other overlay
instances as upperdir/workdir.
On lookup, the trap entries are used to verify that overlay layers
root inodes have not been moved into other layers after mount.
Some examples:
$ ./run --ov --samefs -s
...
( mkdir -p base/upper/0/u base/upper/0/w base/lower lower upper mnt
mount -o bind base/lower lower
mount -o bind base/upper upper
mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w)
$ umount mnt
$ mount -t overlay none mnt ...
-o lowerdir=base,upperdir=upper/0/u,workdir=upper/0/w
[ 94.434900] overlayfs: overlapping upperdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=upper/0/u,upperdir=upper/0/u,workdir=upper/0/w
[ 151.350132] overlayfs: conflicting lowerdir path
mount: none is already mounted or mnt busy
$ mount -t overlay none mnt ...
-o lowerdir=lower:lower/a,upperdir=upper/0/u,workdir=upper/0/w
[ 201.205045] overlayfs: overlapping lowerdir path
mount: mount overlay on mnt failed: Too many levels of symbolic links
$ mount -t overlay none mnt ...
-o lowerdir=lower,upperdir=upper/0/u,workdir=upper/0/w
$ mv base/upper/0/ base/lower/
$ find mnt/0
mnt/0
mnt/0/w
find: 'mnt/0/w/work': Too many levels of symbolic links
find: 'mnt/0/u': Too many levels of symbolic links
Reported-by: syzbot+9c69c282adc4edd2b540@syzkaller.appspotmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
2019-04-18 22:42:08 +08:00
|
|
|
err = ovl_check_overlapping_layers(sb, ofs);
|
|
|
|
if (err)
|
|
|
|
goto out_free_oe;
|
|
|
|
|
2017-09-19 17:14:18 +08:00
|
|
|
/* Show index=off in /proc/mounts for forced r/o mount */
|
2018-01-19 17:26:53 +08:00
|
|
|
if (!ofs->indexdir) {
|
2017-11-10 16:39:16 +08:00
|
|
|
ofs->config.index = false;
|
2020-06-04 16:48:19 +08:00
|
|
|
if (ovl_upper_mnt(ofs) && ofs->config.nfs_export) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("NFS export requires an index dir, falling back to nfs_export=off.\n");
|
2018-01-19 17:26:53 +08:00
|
|
|
ofs->config.nfs_export = false;
|
|
|
|
}
|
|
|
|
}
|
2017-06-21 20:28:36 +08:00
|
|
|
|
2018-05-11 23:49:27 +08:00
|
|
|
if (ofs->config.metacopy && ofs->config.nfs_export) {
|
2019-12-16 19:12:32 +08:00
|
|
|
pr_warn("NFS export is not supported with metadata only copy up, falling back to nfs_export=off.\n");
|
2018-05-11 23:49:27 +08:00
|
|
|
ofs->config.nfs_export = false;
|
|
|
|
}
|
|
|
|
|
2017-10-02 16:31:42 +08:00
|
|
|
if (ofs->config.nfs_export)
|
|
|
|
sb->s_export_op = &ovl_export_operations;
|
|
|
|
|
2017-01-11 02:30:21 +08:00
|
|
|
/* Never override disk quota limits or use reserved space */
|
|
|
|
cap_lower(cred->cap_effective, CAP_SYS_RESOURCE);
|
|
|
|
|
2016-10-14 09:03:36 +08:00
|
|
|
sb->s_magic = OVERLAYFS_SUPER_MAGIC;
|
|
|
|
sb->s_xattr = ovl_xattr_handlers;
|
2017-11-10 16:39:16 +08:00
|
|
|
sb->s_fs_info = ofs;
|
2018-07-18 21:44:43 +08:00
|
|
|
sb->s_flags |= SB_POSIXACL;
|
2020-04-09 16:29:47 +08:00
|
|
|
sb->s_iflags |= SB_I_SKIP_SYNC;
|
2016-10-14 09:03:36 +08:00
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
err = -ENOMEM;
|
2019-11-19 23:49:17 +08:00
|
|
|
root_dentry = ovl_get_root(sb, upperpath.dentry, oe);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
if (!root_dentry)
|
2017-11-10 16:39:15 +08:00
|
|
|
goto out_free_oe;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
|
|
|
mntput(upperpath.mnt);
|
2020-06-04 16:48:19 +08:00
|
|
|
kfree(splitlower);
|
2015-12-09 23:11:59 +08:00
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
sb->s_root = root_dentry;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
2017-11-10 16:39:15 +08:00
|
|
|
out_free_oe:
|
|
|
|
ovl_entry_stack_free(oe);
|
2017-07-24 14:57:54 +08:00
|
|
|
kfree(oe);
|
2017-11-10 16:39:15 +08:00
|
|
|
out_err:
|
2020-06-04 16:48:19 +08:00
|
|
|
kfree(splitlower);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
path_put(&upperpath);
|
2017-11-10 16:39:16 +08:00
|
|
|
ovl_free_fs(ofs);
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
|
|
|
|
const char *dev_name, void *raw_data)
|
|
|
|
{
|
|
|
|
return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct file_system_type ovl_fs_type = {
|
|
|
|
.owner = THIS_MODULE,
|
2014-11-20 23:39:59 +08:00
|
|
|
.name = "overlay",
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
.mount = ovl_mount,
|
|
|
|
.kill_sb = kill_anon_super,
|
|
|
|
};
|
2014-11-20 23:39:59 +08:00
|
|
|
MODULE_ALIAS_FS("overlay");
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
|
2017-06-12 14:54:40 +08:00
|
|
|
static void ovl_inode_init_once(void *foo)
|
|
|
|
{
|
|
|
|
struct ovl_inode *oi = foo;
|
|
|
|
|
|
|
|
inode_init_once(&oi->vfs_inode);
|
|
|
|
}
|
|
|
|
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
static int __init ovl_init(void)
|
|
|
|
{
|
2017-06-12 14:54:40 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
ovl_inode_cachep = kmem_cache_create("ovl_inode",
|
|
|
|
sizeof(struct ovl_inode), 0,
|
|
|
|
(SLAB_RECLAIM_ACCOUNT|
|
|
|
|
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
|
|
|
|
ovl_inode_init_once);
|
|
|
|
if (ovl_inode_cachep == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2019-11-20 17:45:26 +08:00
|
|
|
err = ovl_aio_request_cache_init();
|
|
|
|
if (!err) {
|
|
|
|
err = register_filesystem(&ovl_fs_type);
|
|
|
|
if (!err)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
ovl_aio_request_cache_destroy();
|
|
|
|
}
|
|
|
|
kmem_cache_destroy(ovl_inode_cachep);
|
2017-06-12 14:54:40 +08:00
|
|
|
|
|
|
|
return err;
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void __exit ovl_exit(void)
|
|
|
|
{
|
|
|
|
unregister_filesystem(&ovl_fs_type);
|
2017-06-12 14:54:40 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure all delayed rcu free inodes are flushed before we
|
|
|
|
* destroy cache.
|
|
|
|
*/
|
|
|
|
rcu_barrier();
|
|
|
|
kmem_cache_destroy(ovl_inode_cachep);
|
2019-11-20 17:45:26 +08:00
|
|
|
ovl_aio_request_cache_destroy();
|
overlay filesystem
Overlayfs allows one, usually read-write, directory tree to be
overlaid onto another, read-only directory tree. All modifications
go to the upper, writable layer.
This type of mechanism is most often used for live CDs but there's a
wide variety of other uses.
The implementation differs from other "union filesystem"
implementations in that after a file is opened all operations go
directly to the underlying, lower or upper, filesystems. This
simplifies the implementation and allows native performance in these
cases.
The dentry tree is duplicated from the underlying filesystems, this
enables fast cached lookups without adding special support into the
VFS. This uses slightly more memory than union mounts, but dentries
are relatively small.
Currently inodes are duplicated as well, but it is a possible
optimization to share inodes for non-directories.
Opening non directories results in the open forwarded to the
underlying filesystem. This makes the behavior very similar to union
mounts (with the same limitations vs. fchmod/fchown on O_RDONLY file
descriptors).
Usage:
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper/upper,workdir=/upper/work /overlay
The following cotributions have been folded into this patch:
Neil Brown <neilb@suse.de>:
- minimal remount support
- use correct seek function for directories
- initialise is_real before use
- rename ovl_fill_cache to ovl_dir_read
Felix Fietkau <nbd@openwrt.org>:
- fix a deadlock in ovl_dir_read_merged
- fix a deadlock in ovl_remove_whiteouts
Erez Zadok <ezk@fsl.cs.sunysb.edu>
- fix cleanup after WARN_ON
Sedat Dilek <sedat.dilek@googlemail.com>
- fix up permission to confirm to new API
Robin Dong <hao.bigrat@gmail.com>
- fix possible leak in ovl_new_inode
- create new inode in ovl_link
Andy Whitcroft <apw@canonical.com>
- switch to __inode_permission()
- copy up i_uid/i_gid from the underlying inode
AV:
- ovl_copy_up_locked() - dput(ERR_PTR(...)) on two failure exits
- ovl_clear_empty() - one failure exit forgetting to do unlock_rename(),
lack of check for udir being the parent of upper, dropping and regaining
the lock on udir (which would require _another_ check for parent being
right).
- bogus d_drop() in copyup and rename [fix from your mail]
- copyup/remove and copyup/rename races [fix from your mail]
- ovl_dir_fsync() leaving ERR_PTR() in ->realfile
- ovl_entry_free() is pointless - it's just a kfree_rcu()
- fold ovl_do_lookup() into ovl_lookup()
- manually assigning ->d_op is wrong. Just use ->s_d_op.
[patches picked from Miklos]:
* copyup/remove and copyup/rename races
* bogus d_drop() in copyup and rename
Also thanks to the following people for testing and reporting bugs:
Jordi Pujol <jordipujolp@gmail.com>
Andy Whitcroft <apw@canonical.com>
Michal Suchanek <hramrach@centrum.cz>
Felix Fietkau <nbd@openwrt.org>
Erez Zadok <ezk@fsl.cs.sunysb.edu>
Randy Dunlap <rdunlap@xenotime.net>
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
2014-10-24 06:14:38 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module_init(ovl_init);
|
|
|
|
module_exit(ovl_exit);
|