Commit ce7b9fac authored by Al Viro's avatar Al Viro

Merge branch 'overlayfs-next' of...

Merge branch 'overlayfs-next' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs into for-next
parents a95104fd 4330397e
...@@ -159,6 +159,22 @@ overlay filesystem (though an operation on the name of the file such as ...@@ -159,6 +159,22 @@ overlay filesystem (though an operation on the name of the file such as
rename or unlink will of course be noticed and handled). rename or unlink will of course be noticed and handled).
Multiple lower layers
---------------------
Multiple lower layers can now be given using the the colon (":") as a
separator character between the directory names. For example:
mount -t overlay overlay -olowerdir=/lower1:/lower2:/lower3 /merged
As the example shows, "upperdir=" and "workdir=" may be omitted. In
that case the overlay will be read-only.
The specified lower directories will be stacked beginning from the
rightmost one and going left. In the above example lower1 will be the
top, lower2 the middle and lower3 the bottom layer.
Non-standard behavior Non-standard behavior
--------------------- ---------------------
...@@ -196,3 +212,15 @@ Changes to the underlying filesystems while part of a mounted overlay ...@@ -196,3 +212,15 @@ Changes to the underlying filesystems while part of a mounted overlay
filesystem are not allowed. If the underlying filesystem is changed, filesystem are not allowed. If the underlying filesystem is changed,
the behavior of the overlay is undefined, though it will not result in the behavior of the overlay is undefined, though it will not result in
a crash or deadlock. a crash or deadlock.
Testsuite
---------
There's testsuite developed by David Howells at:
git://git.infradead.org/users/dhowells/unionmount-testsuite.git
Run as root:
# cd unionmount-testsuite
# ./run --ov
...@@ -191,7 +191,6 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -191,7 +191,6 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
ovl_set_timestamps(upperdentry, stat); ovl_set_timestamps(upperdentry, stat);
return err; return err;
} }
static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
...@@ -385,7 +384,7 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -385,7 +384,7 @@ int ovl_copy_up(struct dentry *dentry)
struct kstat stat; struct kstat stat;
enum ovl_path_type type = ovl_path_type(dentry); enum ovl_path_type type = ovl_path_type(dentry);
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
break; break;
next = dget(dentry); next = dget(dentry);
...@@ -394,7 +393,7 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -394,7 +393,7 @@ int ovl_copy_up(struct dentry *dentry)
parent = dget_parent(next); parent = dget_parent(next);
type = ovl_path_type(parent); type = ovl_path_type(parent);
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
break; break;
dput(next); dput(next);
......
...@@ -118,14 +118,14 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry, ...@@ -118,14 +118,14 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry,
static int ovl_set_opaque(struct dentry *upperdentry) static int ovl_set_opaque(struct dentry *upperdentry)
{ {
return ovl_do_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0); return ovl_do_setxattr(upperdentry, OVL_XATTR_OPAQUE, "y", 1, 0);
} }
static void ovl_remove_opaque(struct dentry *upperdentry) static void ovl_remove_opaque(struct dentry *upperdentry)
{ {
int err; int err;
err = ovl_do_removexattr(upperdentry, ovl_opaque_xattr); err = ovl_do_removexattr(upperdentry, OVL_XATTR_OPAQUE);
if (err) { if (err) {
pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n", pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n",
upperdentry->d_name.name, err); upperdentry->d_name.name, err);
...@@ -152,7 +152,7 @@ static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry, ...@@ -152,7 +152,7 @@ static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry,
* correct link count. nlink=1 seems to pacify 'find' and * correct link count. nlink=1 seems to pacify 'find' and
* other utilities. * other utilities.
*/ */
if (type == OVL_PATH_MERGE) if (OVL_TYPE_MERGE(type))
stat->nlink = 1; stat->nlink = 1;
return 0; return 0;
...@@ -506,7 +506,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) ...@@ -506,7 +506,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
struct dentry *opaquedir = NULL; struct dentry *opaquedir = NULL;
int err; int err;
if (is_dir) { if (is_dir && OVL_TYPE_MERGE_OR_LOWER(ovl_path_type(dentry))) {
opaquedir = ovl_check_empty_and_clear(dentry); opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) if (IS_ERR(opaquedir))
...@@ -630,7 +630,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -630,7 +630,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out_drop_write; goto out_drop_write;
type = ovl_path_type(dentry); type = ovl_path_type(dentry);
if (type == OVL_PATH_PURE_UPPER) { if (OVL_TYPE_PURE_UPPER(type)) {
err = ovl_remove_upper(dentry, is_dir); err = ovl_remove_upper(dentry, is_dir);
} else { } else {
const struct cred *old_cred; const struct cred *old_cred;
...@@ -712,7 +712,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -712,7 +712,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
/* Don't copy up directory trees */ /* Don't copy up directory trees */
old_type = ovl_path_type(old); old_type = ovl_path_type(old);
err = -EXDEV; err = -EXDEV;
if ((old_type == OVL_PATH_LOWER || old_type == OVL_PATH_MERGE) && is_dir) if (OVL_TYPE_MERGE_OR_LOWER(old_type) && is_dir)
goto out; goto out;
if (new->d_inode) { if (new->d_inode) {
...@@ -725,25 +725,25 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -725,25 +725,25 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
new_type = ovl_path_type(new); new_type = ovl_path_type(new);
err = -EXDEV; err = -EXDEV;
if (!overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) if (!overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir)
goto out; goto out;
err = 0; err = 0;
if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) { if (!OVL_TYPE_UPPER(new_type) && !OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_lower(old)->d_inode == if (ovl_dentry_lower(old)->d_inode ==
ovl_dentry_lower(new)->d_inode) ovl_dentry_lower(new)->d_inode)
goto out; goto out;
} }
if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) { if (OVL_TYPE_UPPER(new_type) && OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_upper(old)->d_inode == if (ovl_dentry_upper(old)->d_inode ==
ovl_dentry_upper(new)->d_inode) ovl_dentry_upper(new)->d_inode)
goto out; goto out;
} }
} else { } else {
if (ovl_dentry_is_opaque(new)) if (ovl_dentry_is_opaque(new))
new_type = OVL_PATH_UPPER; new_type = __OVL_PATH_UPPER;
else else
new_type = OVL_PATH_PURE_UPPER; new_type = __OVL_PATH_UPPER | __OVL_PATH_PURE;
} }
err = ovl_want_write(old); err = ovl_want_write(old);
...@@ -763,8 +763,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -763,8 +763,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
goto out_drop_write; goto out_drop_write;
} }
old_opaque = old_type != OVL_PATH_PURE_UPPER; old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
new_opaque = new_type != OVL_PATH_PURE_UPPER; new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
if (old_opaque || new_opaque) { if (old_opaque || new_opaque) {
err = -ENOMEM; err = -ENOMEM;
...@@ -787,7 +787,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old, ...@@ -787,7 +787,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
old_cred = override_creds(override_cred); old_cred = override_creds(override_cred);
} }
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) { if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
opaquedir = ovl_check_empty_and_clear(new); opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) { if (IS_ERR(opaquedir)) {
......
...@@ -205,7 +205,7 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz) ...@@ -205,7 +205,7 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
static bool ovl_is_private_xattr(const char *name) static bool ovl_is_private_xattr(const char *name)
{ {
return strncmp(name, "trusted.overlay.", 14) == 0; return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
} }
int ovl_setxattr(struct dentry *dentry, const char *name, int ovl_setxattr(struct dentry *dentry, const char *name,
...@@ -238,7 +238,10 @@ int ovl_setxattr(struct dentry *dentry, const char *name, ...@@ -238,7 +238,10 @@ int ovl_setxattr(struct dentry *dentry, const char *name,
static bool ovl_need_xattr_filter(struct dentry *dentry, static bool ovl_need_xattr_filter(struct dentry *dentry,
enum ovl_path_type type) enum ovl_path_type type)
{ {
return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode); if ((type & (__OVL_PATH_PURE | __OVL_PATH_UPPER)) == __OVL_PATH_UPPER)
return S_ISDIR(dentry->d_inode->i_mode);
else
return false;
} }
ssize_t ovl_getxattr(struct dentry *dentry, const char *name, ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
...@@ -299,7 +302,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name) ...@@ -299,7 +302,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name)) if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write; goto out_drop_write;
if (type == OVL_PATH_LOWER) { if (!OVL_TYPE_UPPER(type)) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0); err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0) if (err < 0)
goto out_drop_write; goto out_drop_write;
...@@ -321,7 +324,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name) ...@@ -321,7 +324,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
struct dentry *realdentry) struct dentry *realdentry)
{ {
if (type != OVL_PATH_LOWER) if (OVL_TYPE_UPPER(type))
return false; return false;
if (special_file(realdentry->d_inode->i_mode)) if (special_file(realdentry->d_inode->i_mode))
...@@ -430,5 +433,4 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, ...@@ -430,5 +433,4 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
} }
return inode; return inode;
} }
...@@ -12,13 +12,20 @@ ...@@ -12,13 +12,20 @@
struct ovl_entry; struct ovl_entry;
enum ovl_path_type { enum ovl_path_type {
OVL_PATH_PURE_UPPER, __OVL_PATH_PURE = (1 << 0),
OVL_PATH_UPPER, __OVL_PATH_UPPER = (1 << 1),
OVL_PATH_MERGE, __OVL_PATH_MERGE = (1 << 2),
OVL_PATH_LOWER,
}; };
extern const char *ovl_opaque_xattr; #define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER)
#define OVL_TYPE_MERGE(type) ((type) & __OVL_PATH_MERGE)
#define OVL_TYPE_PURE_UPPER(type) ((type) & __OVL_PATH_PURE)
#define OVL_TYPE_MERGE_OR_LOWER(type) \
(OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
#define OVL_XATTR_PRE_NAME "trusted.overlay."
#define OVL_XATTR_PRE_LEN 16
#define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque"
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{ {
...@@ -130,6 +137,7 @@ void ovl_dentry_version_inc(struct dentry *dentry); ...@@ -130,6 +137,7 @@ void ovl_dentry_version_inc(struct dentry *dentry);
void ovl_path_upper(struct dentry *dentry, struct path *path); void ovl_path_upper(struct dentry *dentry, struct path *path);
void ovl_path_lower(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path);
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
......
...@@ -24,7 +24,6 @@ struct ovl_cache_entry { ...@@ -24,7 +24,6 @@ struct ovl_cache_entry {
struct list_head l_node; struct list_head l_node;
struct rb_node node; struct rb_node node;
bool is_whiteout; bool is_whiteout;
bool is_cursor;
char name[]; char name[];
}; };
...@@ -40,6 +39,7 @@ struct ovl_readdir_data { ...@@ -40,6 +39,7 @@ struct ovl_readdir_data {
struct rb_root root; struct rb_root root;
struct list_head *list; struct list_head *list;
struct list_head middle; struct list_head middle;
struct dentry *dir;
int count; int count;
int err; int err;
}; };
...@@ -48,7 +48,7 @@ struct ovl_dir_file { ...@@ -48,7 +48,7 @@ struct ovl_dir_file {
bool is_real; bool is_real;
bool is_upper; bool is_upper;
struct ovl_dir_cache *cache; struct ovl_dir_cache *cache;
struct ovl_cache_entry cursor; struct list_head *cursor;
struct file *realfile; struct file *realfile;
struct file *upperfile; struct file *upperfile;
}; };
...@@ -79,23 +79,49 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root, ...@@ -79,23 +79,49 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
return NULL; return NULL;
} }
static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len, static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
const char *name, int len,
u64 ino, unsigned int d_type) u64 ino, unsigned int d_type)
{ {
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]); size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
p = kmalloc(size, GFP_KERNEL); p = kmalloc(size, GFP_KERNEL);
if (p) { if (!p)
memcpy(p->name, name, len); return NULL;
p->name[len] = '\0';
p->len = len; memcpy(p->name, name, len);
p->type = d_type; p->name[len] = '\0';
p->ino = ino; p->len = len;
p->is_whiteout = false; p->type = d_type;
p->is_cursor = false; p->ino = ino;
} p->is_whiteout = false;
if (d_type == DT_CHR) {
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
kfree(p);
return NULL;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
dentry = lookup_one_len(name, dir, len);
if (!IS_ERR(dentry)) {
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
revert_creds(old_cred);
put_cred(override_cred);
}
return p; return p;
} }
...@@ -122,7 +148,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd, ...@@ -122,7 +148,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
return 0; return 0;
} }
p = ovl_cache_entry_new(name, len, ino, d_type); p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
if (p == NULL) if (p == NULL)
return -ENOMEM; return -ENOMEM;
...@@ -143,7 +169,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd, ...@@ -143,7 +169,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
if (p) { if (p) {
list_move_tail(&p->l_node, &rdd->middle); list_move_tail(&p->l_node, &rdd->middle);
} else { } else {
p = ovl_cache_entry_new(name, namelen, ino, d_type); p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
if (p == NULL) if (p == NULL)
rdd->err = -ENOMEM; rdd->err = -ENOMEM;
else else
...@@ -168,7 +194,6 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry) ...@@ -168,7 +194,6 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
{ {
struct ovl_dir_cache *cache = od->cache; struct ovl_dir_cache *cache = od->cache;
list_del_init(&od->cursor.l_node);
WARN_ON(cache->refcount <= 0); WARN_ON(cache->refcount <= 0);
cache->refcount--; cache->refcount--;
if (!cache->refcount) { if (!cache->refcount) {
...@@ -204,6 +229,7 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -204,6 +229,7 @@ static inline int ovl_dir_read(struct path *realpath,
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
rdd->dir = realpath->dentry;
rdd->ctx.pos = 0; rdd->ctx.pos = 0;
do { do {
rdd->count = 0; rdd->count = 0;
...@@ -227,108 +253,58 @@ static void ovl_dir_reset(struct file *file) ...@@ -227,108 +253,58 @@ static void ovl_dir_reset(struct file *file)
if (cache && ovl_dentry_version_get(dentry) != cache->version) { if (cache && ovl_dentry_version_get(dentry) != cache->version) {
ovl_cache_put(od, dentry); ovl_cache_put(od, dentry);
od->cache = NULL; od->cache = NULL;
od->cursor = NULL;
} }
WARN_ON(!od->is_real && type != OVL_PATH_MERGE); WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type));
if (od->is_real && type == OVL_PATH_MERGE) if (od->is_real && OVL_TYPE_MERGE(type))
od->is_real = false; od->is_real = false;
} }
static int ovl_dir_mark_whiteouts(struct dentry *dir,
struct ovl_readdir_data *rdd)
{
struct ovl_cache_entry *p;
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
ovl_cache_free(rdd->list);
return -ENOMEM;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry(p, rdd->list, l_node) {
if (p->is_cursor)
continue;
if (p->type != DT_CHR)
continue;
dentry = lookup_one_len(p->name, dir, p->len);
if (IS_ERR(dentry))
continue;
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
mutex_unlock(&dir->d_inode->i_mutex);
revert_creds(old_cred);
put_cred(override_cred);
return 0;
}
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list) static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{ {
int err; int err;
struct path lowerpath; struct path realpath;
struct path upperpath;
struct ovl_readdir_data rdd = { struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge, .ctx.actor = ovl_fill_merge,
.list = list, .list = list,
.root = RB_ROOT, .root = RB_ROOT,
.is_merge = false, .is_merge = false,
}; };
int idx, next;
ovl_path_lower(dentry, &lowerpath); for (idx = 0; idx != -1; idx = next) {
ovl_path_upper(dentry, &upperpath); next = ovl_path_next(idx, dentry, &realpath);
if (upperpath.dentry) { if (next != -1) {
err = ovl_dir_read(&upperpath, &rdd); err = ovl_dir_read(&realpath, &rdd);
if (err)
goto out;
if (lowerpath.dentry) {
err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (err) if (err)
goto out; break;
} else {
/*
* Insert lowest layer entries before upper ones, this
* allows offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(&realpath, &rdd);
list_del(&rdd.middle);
} }
} }
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);
list_del(&rdd.middle);
}
out:
return err; return err;
} }
static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos) static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
{ {
struct ovl_cache_entry *p; struct list_head *p;
loff_t off = 0; loff_t off = 0;
list_for_each_entry(p, &od->cache->entries, l_node) { list_for_each(p, &od->cache->entries) {
if (p->is_cursor)
continue;
if (off >= pos) if (off >= pos)
break; break;
off++; off++;
} }
list_move_tail(&od->cursor.l_node, &p->l_node); /* Cursor is safe since the cache is stable */
od->cursor = p;
} }
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry) static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
...@@ -367,6 +343,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -367,6 +343,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
{ {
struct ovl_dir_file *od = file->private_data; struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
struct ovl_cache_entry *p;
if (!ctx->pos) if (!ctx->pos)
ovl_dir_reset(file); ovl_dir_reset(file);
...@@ -385,19 +362,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx) ...@@ -385,19 +362,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
ovl_seek_cursor(od, ctx->pos); ovl_seek_cursor(od, ctx->pos);
} }
while (od->cursor.l_node.next != &od->cache->entries) { while (od->cursor != &od->cache->entries) {
struct ovl_cache_entry *p; p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
if (!p->is_whiteout)
p = list_entry(od->cursor.l_node.next, struct ovl_cache_entry, l_node); if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
/* Skip cursors */ break;
if (!p->is_cursor) { od->cursor = p->l_node.next;
if (!p->is_whiteout) { ctx->pos++;
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break;
}
ctx->pos++;
}
list_move(&od->cursor.l_node, &p->l_node);
} }
return 0; return 0;
} }
...@@ -452,7 +423,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end, ...@@ -452,7 +423,7 @@ 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 * 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_LOWER) { if (!od->is_upper && OVL_TYPE_UPPER(ovl_path_type(dentry))) {
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
realfile = lockless_dereference(od->upperfile); realfile = lockless_dereference(od->upperfile);
...@@ -516,11 +487,9 @@ static int ovl_dir_open(struct inode *inode, struct file *file) ...@@ -516,11 +487,9 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
kfree(od); kfree(od);
return PTR_ERR(realfile); return PTR_ERR(realfile);
} }
INIT_LIST_HEAD(&od->cursor.l_node);
od->realfile = realfile; od->realfile = realfile;
od->is_real = (type != OVL_PATH_MERGE); od->is_real = !OVL_TYPE_MERGE(type);
od->is_upper = (type != OVL_PATH_LOWER); od->is_upper = OVL_TYPE_UPPER(type);
od->cursor.is_cursor = true;
file->private_data = od; file->private_data = od;
return 0; return 0;
......
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment