forked from luck/tmp_suning_uos_patched
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull vfs fixes from Al Viro: "Assorted fixes, most in overlayfs land" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: ovl: ovl_dir_fsync() cleanup ovl: update MAINTAINERS ovl: pass dentry into ovl_dir_read_merged() ovl: use lockless_dereference() for upperdentry ovl: allow filenames with comma ovl: fix race in private xattr checks ovl: fix remove/copy-up race ovl: rename filesystem type to "overlay" isofs: avoid unused function warning vfs: fix reference leak in d_prune_aliases()
This commit is contained in:
commit
ecde00642c
|
@ -64,7 +64,7 @@ is formed.
|
|||
At mount time, the two directories given as mount options "lowerdir" and
|
||||
"upperdir" are combined into a merged directory:
|
||||
|
||||
mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\
|
||||
mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
|
||||
workdir=/work /merged
|
||||
|
||||
The "workdir" needs to be an empty directory on the same filesystem
|
||||
|
|
|
@ -6888,11 +6888,12 @@ F: drivers/scsi/osd/
|
|||
F: include/scsi/osd_*
|
||||
F: fs/exofs/
|
||||
|
||||
OVERLAYFS FILESYSTEM
|
||||
OVERLAY FILESYSTEM
|
||||
M: Miklos Szeredi <miklos@szeredi.hu>
|
||||
L: linux-fsdevel@vger.kernel.org
|
||||
L: linux-unionfs@vger.kernel.org
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs.git
|
||||
S: Supported
|
||||
F: fs/overlayfs/*
|
||||
F: fs/overlayfs/
|
||||
F: Documentation/filesystems/overlayfs.txt
|
||||
|
||||
P54 WIRELESS DRIVER
|
||||
|
|
|
@ -104,7 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
|
|||
obj-$(CONFIG_AUTOFS4_FS) += autofs4/
|
||||
obj-$(CONFIG_ADFS_FS) += adfs/
|
||||
obj-$(CONFIG_FUSE_FS) += fuse/
|
||||
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/
|
||||
obj-$(CONFIG_OVERLAY_FS) += overlayfs/
|
||||
obj-$(CONFIG_UDF_FS) += udf/
|
||||
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
|
||||
obj-$(CONFIG_OMFS_FS) += omfs/
|
||||
|
|
|
@ -778,6 +778,7 @@ void d_prune_aliases(struct inode *inode)
|
|||
struct dentry *parent = lock_parent(dentry);
|
||||
if (likely(!dentry->d_lockref.count)) {
|
||||
__dentry_kill(dentry);
|
||||
dput(parent);
|
||||
goto restart;
|
||||
}
|
||||
if (parent)
|
||||
|
|
|
@ -170,27 +170,6 @@ struct iso9660_options{
|
|||
s32 sbsector;
|
||||
};
|
||||
|
||||
/*
|
||||
* Compute the hash for the isofs name corresponding to the dentry.
|
||||
*/
|
||||
static int
|
||||
isofs_hash_common(struct qstr *qstr, int ms)
|
||||
{
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
len = qstr->len;
|
||||
name = qstr->name;
|
||||
if (ms) {
|
||||
while (len && name[len-1] == '.')
|
||||
len--;
|
||||
}
|
||||
|
||||
qstr->hash = full_name_hash(name, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the hash for the isofs name corresponding to the dentry.
|
||||
*/
|
||||
|
@ -263,6 +242,27 @@ isofs_dentry_cmpi(const struct dentry *parent, const struct dentry *dentry,
|
|||
}
|
||||
|
||||
#ifdef CONFIG_JOLIET
|
||||
/*
|
||||
* Compute the hash for the isofs name corresponding to the dentry.
|
||||
*/
|
||||
static int
|
||||
isofs_hash_common(struct qstr *qstr, int ms)
|
||||
{
|
||||
const char *name;
|
||||
int len;
|
||||
|
||||
len = qstr->len;
|
||||
name = qstr->name;
|
||||
if (ms) {
|
||||
while (len && name[len-1] == '.')
|
||||
len--;
|
||||
}
|
||||
|
||||
qstr->hash = full_name_hash(name, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
isofs_hash_ms(const struct dentry *dentry, struct qstr *qstr)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
config OVERLAYFS_FS
|
||||
config OVERLAY_FS
|
||||
tristate "Overlay filesystem support"
|
||||
help
|
||||
An overlay filesystem combines two filesystems - an 'upper' filesystem
|
||||
|
|
|
@ -2,6 +2,6 @@
|
|||
# Makefile for the overlay filesystem.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o
|
||||
obj-$(CONFIG_OVERLAY_FS) += overlay.o
|
||||
|
||||
overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o
|
||||
overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
|
||||
|
|
|
@ -284,8 +284,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
|
|||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
|
||||
enum ovl_path_type type)
|
||||
static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct dentry *ret = NULL;
|
||||
|
@ -294,8 +293,17 @@ static struct dentry *ovl_check_empty_and_clear(struct dentry *dentry,
|
|||
err = ovl_check_empty_dir(dentry, &list);
|
||||
if (err)
|
||||
ret = ERR_PTR(err);
|
||||
else if (type == OVL_PATH_MERGE)
|
||||
ret = ovl_clear_empty(dentry, &list);
|
||||
else {
|
||||
/*
|
||||
* If no upperdentry then skip clearing whiteouts.
|
||||
*
|
||||
* Can race with copy-up, since we don't hold the upperdir
|
||||
* mutex. Doesn't matter, since copy-up can't create a
|
||||
* non-empty directory from an empty one.
|
||||
*/
|
||||
if (ovl_dentry_upper(dentry))
|
||||
ret = ovl_clear_empty(dentry, &list);
|
||||
}
|
||||
|
||||
ovl_cache_free(&list);
|
||||
|
||||
|
@ -487,8 +495,7 @@ static int ovl_link(struct dentry *old, struct inode *newdir,
|
|||
return err;
|
||||
}
|
||||
|
||||
static int ovl_remove_and_whiteout(struct dentry *dentry,
|
||||
enum ovl_path_type type, bool is_dir)
|
||||
static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
|
||||
{
|
||||
struct dentry *workdir = ovl_workdir(dentry);
|
||||
struct inode *wdir = workdir->d_inode;
|
||||
|
@ -500,7 +507,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
|
|||
int err;
|
||||
|
||||
if (is_dir) {
|
||||
opaquedir = ovl_check_empty_and_clear(dentry, type);
|
||||
opaquedir = ovl_check_empty_and_clear(dentry);
|
||||
err = PTR_ERR(opaquedir);
|
||||
if (IS_ERR(opaquedir))
|
||||
goto out;
|
||||
|
@ -515,9 +522,10 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
|
|||
if (IS_ERR(whiteout))
|
||||
goto out_unlock;
|
||||
|
||||
if (type == OVL_PATH_LOWER) {
|
||||
upper = ovl_dentry_upper(dentry);
|
||||
if (!upper) {
|
||||
upper = lookup_one_len(dentry->d_name.name, upperdir,
|
||||
dentry->d_name.len);
|
||||
dentry->d_name.len);
|
||||
err = PTR_ERR(upper);
|
||||
if (IS_ERR(upper))
|
||||
goto kill_whiteout;
|
||||
|
@ -529,7 +537,6 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
|
|||
} else {
|
||||
int flags = 0;
|
||||
|
||||
upper = ovl_dentry_upper(dentry);
|
||||
if (opaquedir)
|
||||
upper = opaquedir;
|
||||
err = -ESTALE;
|
||||
|
@ -648,7 +655,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
|
|||
cap_raise(override_cred->cap_effective, CAP_CHOWN);
|
||||
old_cred = override_creds(override_cred);
|
||||
|
||||
err = ovl_remove_and_whiteout(dentry, type, is_dir);
|
||||
err = ovl_remove_and_whiteout(dentry, is_dir);
|
||||
|
||||
revert_creds(old_cred);
|
||||
put_cred(override_cred);
|
||||
|
@ -781,7 +788,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
|
|||
}
|
||||
|
||||
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) {
|
||||
opaquedir = ovl_check_empty_and_clear(new, new_type);
|
||||
opaquedir = ovl_check_empty_and_clear(new);
|
||||
err = PTR_ERR(opaquedir);
|
||||
if (IS_ERR(opaquedir)) {
|
||||
opaquedir = NULL;
|
||||
|
|
|
@ -235,26 +235,36 @@ int ovl_setxattr(struct dentry *dentry, const char *name,
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool ovl_need_xattr_filter(struct dentry *dentry,
|
||||
enum ovl_path_type type)
|
||||
{
|
||||
return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
|
||||
}
|
||||
|
||||
ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
|
||||
ovl_is_private_xattr(name))
|
||||
struct path realpath;
|
||||
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
|
||||
|
||||
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
|
||||
return -ENODATA;
|
||||
|
||||
return vfs_getxattr(ovl_dentry_real(dentry), name, value, size);
|
||||
return vfs_getxattr(realpath.dentry, name, value, size);
|
||||
}
|
||||
|
||||
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size)
|
||||
{
|
||||
struct path realpath;
|
||||
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
|
||||
ssize_t res;
|
||||
int off;
|
||||
|
||||
res = vfs_listxattr(ovl_dentry_real(dentry), list, size);
|
||||
res = vfs_listxattr(realpath.dentry, list, size);
|
||||
if (res <= 0 || size == 0)
|
||||
return res;
|
||||
|
||||
if (ovl_path_type(dentry->d_parent) != OVL_PATH_MERGE)
|
||||
if (!ovl_need_xattr_filter(dentry, type))
|
||||
return res;
|
||||
|
||||
/* filter out private xattrs */
|
||||
|
@ -279,17 +289,16 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
|
|||
{
|
||||
int err;
|
||||
struct path realpath;
|
||||
enum ovl_path_type type;
|
||||
enum ovl_path_type type = ovl_path_real(dentry, &realpath);
|
||||
|
||||
err = ovl_want_write(dentry);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (ovl_path_type(dentry->d_parent) == OVL_PATH_MERGE &&
|
||||
ovl_is_private_xattr(name))
|
||||
err = -ENODATA;
|
||||
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
|
||||
goto out_drop_write;
|
||||
|
||||
type = ovl_path_real(dentry, &realpath);
|
||||
if (type == OVL_PATH_LOWER) {
|
||||
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
|
||||
if (err < 0)
|
||||
|
|
|
@ -274,11 +274,11 @@ static int ovl_dir_mark_whiteouts(struct dentry *dir,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static inline int ovl_dir_read_merged(struct path *upperpath,
|
||||
struct path *lowerpath,
|
||||
struct list_head *list)
|
||||
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
|
||||
{
|
||||
int err;
|
||||
struct path lowerpath;
|
||||
struct path upperpath;
|
||||
struct ovl_readdir_data rdd = {
|
||||
.ctx.actor = ovl_fill_merge,
|
||||
.list = list,
|
||||
|
@ -286,25 +286,28 @@ static inline int ovl_dir_read_merged(struct path *upperpath,
|
|||
.is_merge = false,
|
||||
};
|
||||
|
||||
if (upperpath->dentry) {
|
||||
err = ovl_dir_read(upperpath, &rdd);
|
||||
ovl_path_lower(dentry, &lowerpath);
|
||||
ovl_path_upper(dentry, &upperpath);
|
||||
|
||||
if (upperpath.dentry) {
|
||||
err = ovl_dir_read(&upperpath, &rdd);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (lowerpath->dentry) {
|
||||
err = ovl_dir_mark_whiteouts(upperpath->dentry, &rdd);
|
||||
if (lowerpath.dentry) {
|
||||
err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
|
||||
if (err)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
if (lowerpath->dentry) {
|
||||
if (lowerpath.dentry) {
|
||||
/*
|
||||
* Insert lowerpath entries before upperpath ones, this allows
|
||||
* offsets to be reasonably constant
|
||||
*/
|
||||
list_add(&rdd.middle, rdd.list);
|
||||
rdd.is_merge = true;
|
||||
err = ovl_dir_read(lowerpath, &rdd);
|
||||
err = ovl_dir_read(&lowerpath, &rdd);
|
||||
list_del(&rdd.middle);
|
||||
}
|
||||
out:
|
||||
|
@ -329,8 +332,6 @@ static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
|
|||
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
struct path lowerpath;
|
||||
struct path upperpath;
|
||||
struct ovl_dir_cache *cache;
|
||||
|
||||
cache = ovl_dir_cache(dentry);
|
||||
|
@ -347,10 +348,7 @@ static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
|
|||
cache->refcount = 1;
|
||||
INIT_LIST_HEAD(&cache->entries);
|
||||
|
||||
ovl_path_lower(dentry, &lowerpath);
|
||||
ovl_path_upper(dentry, &upperpath);
|
||||
|
||||
res = ovl_dir_read_merged(&upperpath, &lowerpath, &cache->entries);
|
||||
res = ovl_dir_read_merged(dentry, &cache->entries);
|
||||
if (res) {
|
||||
ovl_cache_free(&cache->entries);
|
||||
kfree(cache);
|
||||
|
@ -452,10 +450,10 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
|
|||
/*
|
||||
* Need to check if we started out being a lower dir, but got copied up
|
||||
*/
|
||||
if (!od->is_upper && ovl_path_type(dentry) == OVL_PATH_MERGE) {
|
||||
if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) {
|
||||
struct inode *inode = file_inode(file);
|
||||
|
||||
realfile =lockless_dereference(od->upperfile);
|
||||
realfile = lockless_dereference(od->upperfile);
|
||||
if (!realfile) {
|
||||
struct path upperpath;
|
||||
|
||||
|
@ -538,14 +536,9 @@ const struct file_operations ovl_dir_operations = {
|
|||
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
|
||||
{
|
||||
int err;
|
||||
struct path lowerpath;
|
||||
struct path upperpath;
|
||||
struct ovl_cache_entry *p;
|
||||
|
||||
ovl_path_upper(dentry, &upperpath);
|
||||
ovl_path_lower(dentry, &lowerpath);
|
||||
|
||||
err = ovl_dir_read_merged(&upperpath, &lowerpath, list);
|
||||
err = ovl_dir_read_merged(dentry, list);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ MODULE_AUTHOR("Miklos Szeredi <miklos@szeredi.hu>");
|
|||
MODULE_DESCRIPTION("Overlay filesystem");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define OVERLAYFS_SUPER_MAGIC 0x794c764f
|
||||
#define OVERLAYFS_SUPER_MAGIC 0x794c7630
|
||||
|
||||
struct ovl_config {
|
||||
char *lowerdir;
|
||||
|
@ -84,12 +84,7 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry)
|
|||
|
||||
static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
|
||||
{
|
||||
struct dentry *upperdentry = ACCESS_ONCE(oe->__upperdentry);
|
||||
/*
|
||||
* Make sure to order reads to upperdentry wrt ovl_dentry_update()
|
||||
*/
|
||||
smp_read_barrier_depends();
|
||||
return upperdentry;
|
||||
return lockless_dereference(oe->__upperdentry);
|
||||
}
|
||||
|
||||
void ovl_path_upper(struct dentry *dentry, struct path *path)
|
||||
|
@ -462,11 +457,34 @@ static const match_table_t ovl_tokens = {
|
|||
{OPT_ERR, NULL}
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static int ovl_parse_opt(char *opt, struct ovl_config *config)
|
||||
{
|
||||
char *p;
|
||||
|
||||
while ((p = strsep(&opt, ",")) != NULL) {
|
||||
while ((p = ovl_next_opt(&opt)) != NULL) {
|
||||
int token;
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
||||
|
@ -554,15 +572,34 @@ static struct dentry *ovl_workdir_create(struct vfsmount *mnt,
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
static void ovl_unescape(char *s)
|
||||
{
|
||||
char *d = s;
|
||||
|
||||
for (;; s++, d++) {
|
||||
if (*s == '\\')
|
||||
s++;
|
||||
*d = *s;
|
||||
if (!*s)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int ovl_mount_dir(const char *name, struct path *path)
|
||||
{
|
||||
int err;
|
||||
char *tmp = kstrdup(name, GFP_KERNEL);
|
||||
|
||||
err = kern_path(name, LOOKUP_FOLLOW, path);
|
||||
if (!tmp)
|
||||
return -ENOMEM;
|
||||
|
||||
ovl_unescape(tmp);
|
||||
err = kern_path(tmp, LOOKUP_FOLLOW, path);
|
||||
if (err) {
|
||||
pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
|
||||
pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err);
|
||||
err = -EINVAL;
|
||||
}
|
||||
kfree(tmp);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -776,11 +813,11 @@ static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags,
|
|||
|
||||
static struct file_system_type ovl_fs_type = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "overlayfs",
|
||||
.name = "overlay",
|
||||
.mount = ovl_mount,
|
||||
.kill_sb = kill_anon_super,
|
||||
};
|
||||
MODULE_ALIAS_FS("overlayfs");
|
||||
MODULE_ALIAS_FS("overlay");
|
||||
|
||||
static int __init ovl_init(void)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue
Block a user