Commit 2c5ca23f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'ovl-update-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs

Pull overlayfs updates from Miklos Szeredi:

 - Support idmapped layers in overlayfs (Christian Brauner)

 - Add a fix to exportfs that is relevant to open_by_handle_at(2) as
   well

 - Introduce new lookup helpers that allow passing mnt_userns into
   inode_permission()

* tag 'ovl-update-5.19' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs:
  ovl: support idmapped layers
  ovl: handle idmappings in ovl_xattr_{g,s}et()
  ovl: handle idmappings in layer open helpers
  ovl: handle idmappings in ovl_permission()
  ovl: use ovl_copy_{real,upper}attr() wrappers
  ovl: store lower path in ovl_inode
  ovl: handle idmappings for layer lookup
  ovl: handle idmappings for layer fileattrs
  ovl: use ovl_path_getxattr() wrapper
  ovl: use ovl_lookup_upper() wrapper
  ovl: use ovl_do_notify_change() wrapper
  ovl: pass layer mnt to ovl_open_realfile()
  ovl: pass ofs to setattr operations
  ovl: handle idmappings in creation operations
  ovl: add ovl_upper_mnt_userns() wrapper
  ovl: pass ofs to creation operations
  ovl: use wrappers to all vfs_*xattr() calls
  exportfs: support idmapped mounts
  fs: add two trivial lookup helpers
parents 73d15ba6 bc70682a
...@@ -145,7 +145,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt, ...@@ -145,7 +145,7 @@ static struct dentry *reconnect_one(struct vfsmount *mnt,
if (err) if (err)
goto out_err; goto out_err;
dprintk("%s: found name: %s\n", __func__, nbuf); dprintk("%s: found name: %s\n", __func__, nbuf);
tmp = lookup_one_len_unlocked(nbuf, parent, strlen(nbuf)); tmp = lookup_one_unlocked(mnt_user_ns(mnt), nbuf, parent, strlen(nbuf));
if (IS_ERR(tmp)) { if (IS_ERR(tmp)) {
dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp)); dprintk("%s: lookup failed: %d\n", __func__, PTR_ERR(tmp));
err = PTR_ERR(tmp); err = PTR_ERR(tmp);
...@@ -525,7 +525,8 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len, ...@@ -525,7 +525,8 @@ exportfs_decode_fh_raw(struct vfsmount *mnt, struct fid *fid, int fh_len,
} }
inode_lock(target_dir->d_inode); inode_lock(target_dir->d_inode);
nresult = lookup_one_len(nbuf, target_dir, strlen(nbuf)); nresult = lookup_one(mnt_user_ns(mnt), nbuf,
target_dir, strlen(nbuf));
if (!IS_ERR(nresult)) { if (!IS_ERR(nresult)) {
if (unlikely(nresult->d_inode != result->d_inode)) { if (unlikely(nresult->d_inode != result->d_inode)) {
dput(nresult); dput(nresult);
......
...@@ -2769,7 +2769,8 @@ struct dentry *lookup_one(struct user_namespace *mnt_userns, const char *name, ...@@ -2769,7 +2769,8 @@ struct dentry *lookup_one(struct user_namespace *mnt_userns, const char *name,
EXPORT_SYMBOL(lookup_one); EXPORT_SYMBOL(lookup_one);
/** /**
* lookup_one_len_unlocked - filesystem helper to lookup single pathname component * lookup_one_unlocked - filesystem helper to lookup single pathname component
* @mnt_userns: idmapping of the mount the lookup is performed from
* @name: pathname component to lookup * @name: pathname component to lookup
* @base: base directory to lookup from * @base: base directory to lookup from
* @len: maximum length @len should be interpreted to * @len: maximum length @len should be interpreted to
...@@ -2780,14 +2781,15 @@ EXPORT_SYMBOL(lookup_one); ...@@ -2780,14 +2781,15 @@ EXPORT_SYMBOL(lookup_one);
* Unlike lookup_one_len, it should be called without the parent * Unlike lookup_one_len, it should be called without the parent
* i_mutex held, and will take the i_mutex itself if necessary. * i_mutex held, and will take the i_mutex itself if necessary.
*/ */
struct dentry *lookup_one_len_unlocked(const char *name, struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
struct dentry *base, int len) const char *name, struct dentry *base,
int len)
{ {
struct qstr this; struct qstr this;
int err; int err;
struct dentry *ret; struct dentry *ret;
err = lookup_one_common(&init_user_ns, name, base, len, &this); err = lookup_one_common(mnt_userns, name, base, len, &this);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
...@@ -2796,6 +2798,59 @@ struct dentry *lookup_one_len_unlocked(const char *name, ...@@ -2796,6 +2798,59 @@ struct dentry *lookup_one_len_unlocked(const char *name,
ret = lookup_slow(&this, base, 0); ret = lookup_slow(&this, base, 0);
return ret; return ret;
} }
EXPORT_SYMBOL(lookup_one_unlocked);
/**
* lookup_one_positive_unlocked - filesystem helper to lookup single
* pathname component
* @mnt_userns: idmapping of the mount the lookup is performed from
* @name: pathname component to lookup
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
* This helper will yield ERR_PTR(-ENOENT) on negatives. The helper returns
* known positive or ERR_PTR(). This is what most of the users want.
*
* Note that pinned negative with unlocked parent _can_ become positive at any
* time, so callers of lookup_one_unlocked() need to be very careful; pinned
* positives have >d_inode stable, so this one avoids such problems.
*
* Note that this routine is purely a helper for filesystem usage and should
* not be called by generic code.
*
* The helper should be called without i_mutex held.
*/
struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
const char *name,
struct dentry *base, int len)
{
struct dentry *ret = lookup_one_unlocked(mnt_userns, name, base, len);
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
dput(ret);
ret = ERR_PTR(-ENOENT);
}
return ret;
}
EXPORT_SYMBOL(lookup_one_positive_unlocked);
/**
* lookup_one_len_unlocked - filesystem helper to lookup single pathname component
* @name: pathname component to lookup
* @base: base directory to lookup from
* @len: maximum length @len should be interpreted to
*
* Note that this routine is purely a helper for filesystem usage and should
* not be called by generic code.
*
* Unlike lookup_one_len, it should be called without the parent
* i_mutex held, and will take the i_mutex itself if necessary.
*/
struct dentry *lookup_one_len_unlocked(const char *name,
struct dentry *base, int len)
{
return lookup_one_unlocked(&init_user_ns, name, base, len);
}
EXPORT_SYMBOL(lookup_one_len_unlocked); EXPORT_SYMBOL(lookup_one_len_unlocked);
/* /*
...@@ -2809,12 +2864,7 @@ EXPORT_SYMBOL(lookup_one_len_unlocked); ...@@ -2809,12 +2864,7 @@ EXPORT_SYMBOL(lookup_one_len_unlocked);
struct dentry *lookup_positive_unlocked(const char *name, struct dentry *lookup_positive_unlocked(const char *name,
struct dentry *base, int len) struct dentry *base, int len)
{ {
struct dentry *ret = lookup_one_len_unlocked(name, base, len); return lookup_one_positive_unlocked(&init_user_ns, name, base, len);
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
dput(ret);
ret = ERR_PTR(-ENOENT);
}
return ret;
} }
EXPORT_SYMBOL(lookup_positive_unlocked); EXPORT_SYMBOL(lookup_positive_unlocked);
......
...@@ -44,9 +44,9 @@ static bool ovl_must_copy_xattr(const char *name) ...@@ -44,9 +44,9 @@ static bool ovl_must_copy_xattr(const char *name)
!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN); !strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN);
} }
int ovl_copy_xattr(struct super_block *sb, struct dentry *old, int ovl_copy_xattr(struct super_block *sb, struct path *oldpath, struct dentry *new)
struct dentry *new)
{ {
struct dentry *old = oldpath->dentry;
ssize_t list_size, size, value_size = 0; ssize_t list_size, size, value_size = 0;
char *buf, *name, *value = NULL; char *buf, *name, *value = NULL;
int error = 0; int error = 0;
...@@ -94,9 +94,9 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old, ...@@ -94,9 +94,9 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
continue; /* Discard */ continue; /* Discard */
} }
retry: retry:
size = vfs_getxattr(&init_user_ns, old, name, value, value_size); size = ovl_do_getxattr(oldpath, name, value, value_size);
if (size == -ERANGE) if (size == -ERANGE)
size = vfs_getxattr(&init_user_ns, old, name, NULL, 0); size = ovl_do_getxattr(oldpath, name, NULL, 0);
if (size < 0) { if (size < 0) {
error = size; error = size;
...@@ -117,7 +117,7 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old, ...@@ -117,7 +117,7 @@ int ovl_copy_xattr(struct super_block *sb, struct dentry *old,
goto retry; goto retry;
} }
error = vfs_setxattr(&init_user_ns, new, name, value, size, 0); error = ovl_do_setxattr(OVL_FS(sb), new, name, value, size, 0);
if (error) { if (error) {
if (error != -EOPNOTSUPP || ovl_must_copy_xattr(name)) if (error != -EOPNOTSUPP || ovl_must_copy_xattr(name))
break; break;
...@@ -292,17 +292,19 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old, ...@@ -292,17 +292,19 @@ static int ovl_copy_up_data(struct ovl_fs *ofs, struct path *old,
return error; return error;
} }
static int ovl_set_size(struct dentry *upperdentry, struct kstat *stat) static int ovl_set_size(struct ovl_fs *ofs,
struct dentry *upperdentry, struct kstat *stat)
{ {
struct iattr attr = { struct iattr attr = {
.ia_valid = ATTR_SIZE, .ia_valid = ATTR_SIZE,
.ia_size = stat->size, .ia_size = stat->size,
}; };
return notify_change(&init_user_ns, upperdentry, &attr, NULL); return ovl_do_notify_change(ofs, upperdentry, &attr);
} }
static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) static int ovl_set_timestamps(struct ovl_fs *ofs, struct dentry *upperdentry,
struct kstat *stat)
{ {
struct iattr attr = { struct iattr attr = {
.ia_valid = .ia_valid =
...@@ -311,10 +313,11 @@ static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat) ...@@ -311,10 +313,11 @@ static int ovl_set_timestamps(struct dentry *upperdentry, struct kstat *stat)
.ia_mtime = stat->mtime, .ia_mtime = stat->mtime,
}; };
return notify_change(&init_user_ns, upperdentry, &attr, NULL); return ovl_do_notify_change(ofs, upperdentry, &attr);
} }
int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upperdentry,
struct kstat *stat)
{ {
int err = 0; int err = 0;
...@@ -323,7 +326,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -323,7 +326,7 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
.ia_valid = ATTR_MODE, .ia_valid = ATTR_MODE,
.ia_mode = stat->mode, .ia_mode = stat->mode,
}; };
err = notify_change(&init_user_ns, upperdentry, &attr, NULL); err = ovl_do_notify_change(ofs, upperdentry, &attr);
} }
if (!err) { if (!err) {
struct iattr attr = { struct iattr attr = {
...@@ -331,10 +334,10 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat) ...@@ -331,10 +334,10 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
.ia_uid = stat->uid, .ia_uid = stat->uid,
.ia_gid = stat->gid, .ia_gid = stat->gid,
}; };
err = notify_change(&init_user_ns, upperdentry, &attr, NULL); err = ovl_do_notify_change(ofs, upperdentry, &attr);
} }
if (!err) if (!err)
ovl_set_timestamps(upperdentry, stat); ovl_set_timestamps(ofs, upperdentry, stat);
return err; return err;
} }
...@@ -433,7 +436,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper, ...@@ -433,7 +436,7 @@ static int ovl_set_upper_fh(struct ovl_fs *ofs, struct dentry *upper,
if (IS_ERR(fh)) if (IS_ERR(fh))
return PTR_ERR(fh); return PTR_ERR(fh);
err = ovl_do_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len); err = ovl_setxattr(ofs, index, OVL_XATTR_UPPER, fh->buf, fh->fb.len);
kfree(fh); kfree(fh);
return err; return err;
...@@ -474,7 +477,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin, ...@@ -474,7 +477,7 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
if (err) if (err)
return err; return err;
temp = ovl_create_temp(indexdir, OVL_CATTR(S_IFDIR | 0)); temp = ovl_create_temp(ofs, indexdir, OVL_CATTR(S_IFDIR | 0));
err = PTR_ERR(temp); err = PTR_ERR(temp);
if (IS_ERR(temp)) if (IS_ERR(temp))
goto free_name; goto free_name;
...@@ -483,16 +486,16 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin, ...@@ -483,16 +486,16 @@ static int ovl_create_index(struct dentry *dentry, struct dentry *origin,
if (err) if (err)
goto out; goto out;
index = lookup_one_len(name.name, indexdir, name.len); index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index); err = PTR_ERR(index);
} else { } else {
err = ovl_do_rename(dir, temp, dir, index, 0); err = ovl_do_rename(ofs, dir, temp, dir, index, 0);
dput(index); dput(index);
} }
out: out:
if (err) if (err)
ovl_cleanup(dir, temp); ovl_cleanup(ofs, dir, temp);
dput(temp); dput(temp);
free_name: free_name:
kfree(name.name); kfree(name.name);
...@@ -519,6 +522,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -519,6 +522,7 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
int err; int err;
struct dentry *upper; struct dentry *upper;
struct dentry *upperdir = ovl_dentry_upper(c->parent); struct dentry *upperdir = ovl_dentry_upper(c->parent);
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *udir = d_inode(upperdir); struct inode *udir = d_inode(upperdir);
/* Mark parent "impure" because it may now contain non-pure upper */ /* Mark parent "impure" because it may now contain non-pure upper */
...@@ -531,16 +535,16 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c) ...@@ -531,16 +535,16 @@ static int ovl_link_up(struct ovl_copy_up_ctx *c)
return err; return err;
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
upper = lookup_one_len(c->dentry->d_name.name, upperdir, upper = ovl_lookup_upper(ofs, c->dentry->d_name.name, upperdir,
c->dentry->d_name.len); c->dentry->d_name.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (!IS_ERR(upper)) { if (!IS_ERR(upper)) {
err = ovl_do_link(ovl_dentry_upper(c->dentry), udir, upper); err = ovl_do_link(ofs, ovl_dentry_upper(c->dentry), udir, upper);
dput(upper); dput(upper);
if (!err) { if (!err) {
/* Restore timestamps on parent (best effort) */ /* Restore timestamps on parent (best effort) */
ovl_set_timestamps(upperdir, &c->pstat); ovl_set_timestamps(ofs, upperdir, &c->pstat);
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
} }
} }
...@@ -578,7 +582,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -578,7 +582,7 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
return err; return err;
} }
err = ovl_copy_xattr(c->dentry->d_sb, c->lowerpath.dentry, temp); err = ovl_copy_xattr(c->dentry->d_sb, &c->lowerpath, temp);
if (err) if (err)
return err; return err;
...@@ -614,9 +618,9 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp) ...@@ -614,9 +618,9 @@ static int ovl_copy_up_inode(struct ovl_copy_up_ctx *c, struct dentry *temp)
inode_lock(temp->d_inode); inode_lock(temp->d_inode);
if (S_ISREG(c->stat.mode)) if (S_ISREG(c->stat.mode))
err = ovl_set_size(temp, &c->stat); err = ovl_set_size(ofs, temp, &c->stat);
if (!err) if (!err)
err = ovl_set_attr(temp, &c->stat); err = ovl_set_attr(ofs, temp, &c->stat);
inode_unlock(temp->d_inode); inode_unlock(temp->d_inode);
return err; return err;
...@@ -656,6 +660,7 @@ static void ovl_revert_cu_creds(struct ovl_cu_creds *cc) ...@@ -656,6 +660,7 @@ static void ovl_revert_cu_creds(struct ovl_cu_creds *cc)
*/ */
static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
{ {
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *inode; struct inode *inode;
struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir); struct inode *udir = d_inode(c->destdir), *wdir = d_inode(c->workdir);
struct dentry *temp, *upper; struct dentry *temp, *upper;
...@@ -677,7 +682,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -677,7 +682,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
if (err) if (err)
goto unlock; goto unlock;
temp = ovl_create_temp(c->workdir, &cattr); temp = ovl_create_temp(ofs, c->workdir, &cattr);
ovl_revert_cu_creds(&cc); ovl_revert_cu_creds(&cc);
err = PTR_ERR(temp); err = PTR_ERR(temp);
...@@ -694,12 +699,13 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -694,12 +699,13 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
goto cleanup; goto cleanup;
} }
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len); upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
c->destname.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (IS_ERR(upper)) if (IS_ERR(upper))
goto cleanup; goto cleanup;
err = ovl_do_rename(wdir, temp, udir, upper, 0); err = ovl_do_rename(ofs, wdir, temp, udir, upper, 0);
dput(upper); dput(upper);
if (err) if (err)
goto cleanup; goto cleanup;
...@@ -716,7 +722,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -716,7 +722,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
return err; return err;
cleanup: cleanup:
ovl_cleanup(wdir, temp); ovl_cleanup(ofs, wdir, temp);
dput(temp); dput(temp);
goto unlock; goto unlock;
} }
...@@ -724,6 +730,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c) ...@@ -724,6 +730,7 @@ static int ovl_copy_up_workdir(struct ovl_copy_up_ctx *c)
/* Copyup using O_TMPFILE which does not require cross dir locking */ /* Copyup using O_TMPFILE which does not require cross dir locking */
static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
{ {
struct ovl_fs *ofs = OVL_FS(c->dentry->d_sb);
struct inode *udir = d_inode(c->destdir); struct inode *udir = d_inode(c->destdir);
struct dentry *temp, *upper; struct dentry *temp, *upper;
struct ovl_cu_creds cc; struct ovl_cu_creds cc;
...@@ -733,7 +740,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -733,7 +740,7 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
if (err) if (err)
return err; return err;
temp = ovl_do_tmpfile(c->workdir, c->stat.mode); temp = ovl_do_tmpfile(ofs, c->workdir, c->stat.mode);
ovl_revert_cu_creds(&cc); ovl_revert_cu_creds(&cc);
if (IS_ERR(temp)) if (IS_ERR(temp))
...@@ -745,10 +752,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c) ...@@ -745,10 +752,11 @@ static int ovl_copy_up_tmpfile(struct ovl_copy_up_ctx *c)
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
upper = lookup_one_len(c->destname.name, c->destdir, c->destname.len); upper = ovl_lookup_upper(ofs, c->destname.name, c->destdir,
c->destname.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (!IS_ERR(upper)) { if (!IS_ERR(upper)) {
err = ovl_do_link(temp, udir, upper); err = ovl_do_link(ofs, temp, udir, upper);
dput(upper); dput(upper);
} }
inode_unlock(udir); inode_unlock(udir);
...@@ -836,7 +844,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c) ...@@ -836,7 +844,7 @@ static int ovl_do_copy_up(struct ovl_copy_up_ctx *c)
/* Restore timestamps on parent (best effort) */ /* Restore timestamps on parent (best effort) */
inode_lock(udir); inode_lock(udir);
ovl_set_timestamps(c->destdir, &c->pstat); ovl_set_timestamps(ofs, c->destdir, &c->pstat);
inode_unlock(udir); inode_unlock(udir);
ovl_dentry_set_upper_alias(c->dentry); ovl_dentry_set_upper_alias(c->dentry);
...@@ -865,12 +873,12 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode, ...@@ -865,12 +873,12 @@ static bool ovl_need_meta_copy_up(struct dentry *dentry, umode_t mode,
return true; return true;
} }
static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value) static ssize_t ovl_getxattr_value(struct path *path, char *name, char **value)
{ {
ssize_t res; ssize_t res;
char *buf; char *buf;
res = vfs_getxattr(&init_user_ns, dentry, name, NULL, 0); res = ovl_do_getxattr(path, name, NULL, 0);
if (res == -ENODATA || res == -EOPNOTSUPP) if (res == -ENODATA || res == -EOPNOTSUPP)
res = 0; res = 0;
...@@ -879,7 +887,7 @@ static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value) ...@@ -879,7 +887,7 @@ static ssize_t ovl_getxattr(struct dentry *dentry, char *name, char **value)
if (!buf) if (!buf)
return -ENOMEM; return -ENOMEM;
res = vfs_getxattr(&init_user_ns, dentry, name, buf, res); res = ovl_do_getxattr(path, name, buf, res);
if (res < 0) if (res < 0)
kfree(buf); kfree(buf);
else else
...@@ -906,7 +914,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) ...@@ -906,7 +914,7 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
return -EIO; return -EIO;
if (c->stat.size) { if (c->stat.size) {
err = cap_size = ovl_getxattr(upperpath.dentry, XATTR_NAME_CAPS, err = cap_size = ovl_getxattr_value(&upperpath, XATTR_NAME_CAPS,
&capability); &capability);
if (cap_size < 0) if (cap_size < 0)
goto out; goto out;
...@@ -921,14 +929,14 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c) ...@@ -921,14 +929,14 @@ static int ovl_copy_up_meta_inode_data(struct ovl_copy_up_ctx *c)
* don't want that to happen for normal copy-up operation. * don't want that to happen for normal copy-up operation.
*/ */
if (capability) { if (capability) {
err = vfs_setxattr(&init_user_ns, upperpath.dentry, err = ovl_do_setxattr(ofs, upperpath.dentry, XATTR_NAME_CAPS,
XATTR_NAME_CAPS, capability, cap_size, 0); capability, cap_size, 0);
if (err) if (err)
goto out_free; goto out_free;
} }
err = ovl_do_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY); err = ovl_removexattr(ofs, upperpath.dentry, OVL_XATTR_METACOPY);
if (err) if (err)
goto out_free; goto out_free;
......
...@@ -23,15 +23,15 @@ MODULE_PARM_DESC(redirect_max, ...@@ -23,15 +23,15 @@ MODULE_PARM_DESC(redirect_max,
static int ovl_set_redirect(struct dentry *dentry, bool samedir); static int ovl_set_redirect(struct dentry *dentry, bool samedir);
int ovl_cleanup(struct inode *wdir, struct dentry *wdentry) int ovl_cleanup(struct ovl_fs *ofs, struct inode *wdir, struct dentry *wdentry)
{ {
int err; int err;
dget(wdentry); dget(wdentry);
if (d_is_dir(wdentry)) if (d_is_dir(wdentry))
err = ovl_do_rmdir(wdir, wdentry); err = ovl_do_rmdir(ofs, wdir, wdentry);
else else
err = ovl_do_unlink(wdir, wdentry); err = ovl_do_unlink(ofs, wdir, wdentry);
dput(wdentry); dput(wdentry);
if (err) { if (err) {
...@@ -42,7 +42,7 @@ int ovl_cleanup(struct inode *wdir, struct dentry *wdentry) ...@@ -42,7 +42,7 @@ int ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
return err; return err;
} }
struct dentry *ovl_lookup_temp(struct dentry *workdir) struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir)
{ {
struct dentry *temp; struct dentry *temp;
char name[20]; char name[20];
...@@ -51,7 +51,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir) ...@@ -51,7 +51,7 @@ struct dentry *ovl_lookup_temp(struct dentry *workdir)
/* counter is allowed to wrap, since temp dentries are ephemeral */ /* counter is allowed to wrap, since temp dentries are ephemeral */
snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id)); snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));
temp = lookup_one_len(name, workdir, strlen(name)); temp = ovl_lookup_upper(ofs, name, workdir, strlen(name));
if (!IS_ERR(temp) && temp->d_inode) { if (!IS_ERR(temp) && temp->d_inode) {
pr_err("workdir/%s already exists\n", name); pr_err("workdir/%s already exists\n", name);
dput(temp); dput(temp);
...@@ -70,11 +70,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) ...@@ -70,11 +70,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
struct inode *wdir = workdir->d_inode; struct inode *wdir = workdir->d_inode;
if (!ofs->whiteout) { if (!ofs->whiteout) {
whiteout = ovl_lookup_temp(workdir); whiteout = ovl_lookup_temp(ofs, workdir);
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto out; goto out;
err = ovl_do_whiteout(wdir, whiteout); err = ovl_do_whiteout(ofs, wdir, whiteout);
if (err) { if (err) {
dput(whiteout); dput(whiteout);
whiteout = ERR_PTR(err); whiteout = ERR_PTR(err);
...@@ -84,11 +84,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs) ...@@ -84,11 +84,11 @@ static struct dentry *ovl_whiteout(struct ovl_fs *ofs)
} }
if (ofs->share_whiteout) { if (ofs->share_whiteout) {
whiteout = ovl_lookup_temp(workdir); whiteout = ovl_lookup_temp(ofs, workdir);
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto out; goto out;
err = ovl_do_link(ofs->whiteout, wdir, whiteout); err = ovl_do_link(ofs, ofs->whiteout, wdir, whiteout);
if (!err) if (!err)
goto out; goto out;
...@@ -122,27 +122,28 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir, ...@@ -122,27 +122,28 @@ int ovl_cleanup_and_whiteout(struct ovl_fs *ofs, struct inode *dir,
if (d_is_dir(dentry)) if (d_is_dir(dentry))
flags = RENAME_EXCHANGE; flags = RENAME_EXCHANGE;
err = ovl_do_rename(wdir, whiteout, dir, dentry, flags); err = ovl_do_rename(ofs, wdir, whiteout, dir, dentry, flags);
if (err) if (err)
goto kill_whiteout; goto kill_whiteout;
if (flags) if (flags)
ovl_cleanup(wdir, dentry); ovl_cleanup(ofs, wdir, dentry);
out: out:
dput(whiteout); dput(whiteout);
return err; return err;
kill_whiteout: kill_whiteout:
ovl_cleanup(wdir, whiteout); ovl_cleanup(ofs, wdir, whiteout);
goto out; goto out;
} }
int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode) int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
struct dentry **newdentry, umode_t mode)
{ {
int err; int err;
struct dentry *d, *dentry = *newdentry; struct dentry *d, *dentry = *newdentry;
err = ovl_do_mkdir(dir, dentry, mode); err = ovl_do_mkdir(ofs, dir, dentry, mode);
if (err) if (err)
return err; return err;
...@@ -154,7 +155,7 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode) ...@@ -154,7 +155,7 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode)
* to it unhashed and negative. If that happens, try to * to it unhashed and negative. If that happens, try to
* lookup a new hashed and positive dentry. * lookup a new hashed and positive dentry.
*/ */
d = lookup_one_len(dentry->d_name.name, dentry->d_parent, d = ovl_lookup_upper(ofs, dentry->d_name.name, dentry->d_parent,
dentry->d_name.len); dentry->d_name.len);
if (IS_ERR(d)) { if (IS_ERR(d)) {
pr_warn("failed lookup after mkdir (%pd2, err=%i).\n", pr_warn("failed lookup after mkdir (%pd2, err=%i).\n",
...@@ -167,8 +168,8 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode) ...@@ -167,8 +168,8 @@ int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode)
return 0; return 0;
} }
struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry, struct dentry *ovl_create_real(struct ovl_fs *ofs, struct inode *dir,
struct ovl_cattr *attr) struct dentry *newdentry, struct ovl_cattr *attr)
{ {
int err; int err;
...@@ -180,28 +181,28 @@ struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry, ...@@ -180,28 +181,28 @@ struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
goto out; goto out;
if (attr->hardlink) { if (attr->hardlink) {
err = ovl_do_link(attr->hardlink, dir, newdentry); err = ovl_do_link(ofs, attr->hardlink, dir, newdentry);
} else { } else {
switch (attr->mode & S_IFMT) { switch (attr->mode & S_IFMT) {
case S_IFREG: case S_IFREG:
err = ovl_do_create(dir, newdentry, attr->mode); err = ovl_do_create(ofs, dir, newdentry, attr->mode);
break; break;
case S_IFDIR: case S_IFDIR:
/* mkdir is special... */ /* mkdir is special... */
err = ovl_mkdir_real(dir, &newdentry, attr->mode); err = ovl_mkdir_real(ofs, dir, &newdentry, attr->mode);
break; break;
case S_IFCHR: case S_IFCHR:
case S_IFBLK: case S_IFBLK:
case S_IFIFO: case S_IFIFO:
case S_IFSOCK: case S_IFSOCK:
err = ovl_do_mknod(dir, newdentry, attr->mode, err = ovl_do_mknod(ofs, dir, newdentry, attr->mode,
attr->rdev); attr->rdev);
break; break;
case S_IFLNK: case S_IFLNK:
err = ovl_do_symlink(dir, newdentry, attr->link); err = ovl_do_symlink(ofs, dir, newdentry, attr->link);
break; break;
default: default:
...@@ -223,10 +224,11 @@ struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry, ...@@ -223,10 +224,11 @@ struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry,
return newdentry; return newdentry;
} }
struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr) struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
struct ovl_cattr *attr)
{ {
return ovl_create_real(d_inode(workdir), ovl_lookup_temp(workdir), return ovl_create_real(ofs, d_inode(workdir),
attr); ovl_lookup_temp(ofs, workdir), attr);
} }
static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper, static int ovl_set_opaque_xerr(struct dentry *dentry, struct dentry *upper,
...@@ -330,10 +332,9 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, ...@@ -330,10 +332,9 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
attr->mode &= ~current_umask(); attr->mode &= ~current_umask();
inode_lock_nested(udir, I_MUTEX_PARENT); inode_lock_nested(udir, I_MUTEX_PARENT);
newdentry = ovl_create_real(udir, newdentry = ovl_create_real(ofs, udir,
lookup_one_len(dentry->d_name.name, ovl_lookup_upper(ofs, dentry->d_name.name,
upperdir, upperdir, dentry->d_name.len),
dentry->d_name.len),
attr); attr);
err = PTR_ERR(newdentry); err = PTR_ERR(newdentry);
if (IS_ERR(newdentry)) if (IS_ERR(newdentry))
...@@ -353,7 +354,7 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, ...@@ -353,7 +354,7 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
return err; return err;
out_cleanup: out_cleanup:
ovl_cleanup(udir, newdentry); ovl_cleanup(ofs, udir, newdentry);
dput(newdentry); dput(newdentry);
goto out_unlock; goto out_unlock;
} }
...@@ -361,6 +362,7 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode, ...@@ -361,6 +362,7 @@ static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
static struct dentry *ovl_clear_empty(struct dentry *dentry, static struct dentry *ovl_clear_empty(struct dentry *dentry,
struct list_head *list) struct list_head *list)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode; struct inode *wdir = workdir->d_inode;
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
...@@ -391,12 +393,12 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry, ...@@ -391,12 +393,12 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
if (upper->d_parent->d_inode != udir) if (upper->d_parent->d_inode != udir)
goto out_unlock; goto out_unlock;
opaquedir = ovl_create_temp(workdir, OVL_CATTR(stat.mode)); opaquedir = ovl_create_temp(ofs, workdir, OVL_CATTR(stat.mode));
err = PTR_ERR(opaquedir); err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) if (IS_ERR(opaquedir))
goto out_unlock; goto out_unlock;
err = ovl_copy_xattr(dentry->d_sb, upper, opaquedir); err = ovl_copy_xattr(dentry->d_sb, &upperpath, opaquedir);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
...@@ -405,17 +407,17 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry, ...@@ -405,17 +407,17 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
goto out_cleanup; goto out_cleanup;
inode_lock(opaquedir->d_inode); inode_lock(opaquedir->d_inode);
err = ovl_set_attr(opaquedir, &stat); err = ovl_set_attr(ofs, opaquedir, &stat);
inode_unlock(opaquedir->d_inode); inode_unlock(opaquedir->d_inode);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
err = ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE); err = ovl_do_rename(ofs, wdir, opaquedir, udir, upper, RENAME_EXCHANGE);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
ovl_cleanup_whiteouts(upper, list); ovl_cleanup_whiteouts(ofs, upper, list);
ovl_cleanup(wdir, upper); ovl_cleanup(ofs, wdir, upper);
unlock_rename(workdir, upperdir); unlock_rename(workdir, upperdir);
/* dentry's upper doesn't match now, get rid of it */ /* dentry's upper doesn't match now, get rid of it */
...@@ -424,7 +426,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry, ...@@ -424,7 +426,7 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
return opaquedir; return opaquedir;
out_cleanup: out_cleanup:
ovl_cleanup(wdir, opaquedir); ovl_cleanup(ofs, wdir, opaquedir);
dput(opaquedir); dput(opaquedir);
out_unlock: out_unlock:
unlock_rename(workdir, upperdir); unlock_rename(workdir, upperdir);
...@@ -432,8 +434,8 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry, ...@@ -432,8 +434,8 @@ static struct dentry *ovl_clear_empty(struct dentry *dentry,
return ERR_PTR(err); return ERR_PTR(err);
} }
static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name, static int ovl_set_upper_acl(struct ovl_fs *ofs, struct dentry *upperdentry,
const struct posix_acl *acl) const char *name, const struct posix_acl *acl)
{ {
void *buffer; void *buffer;
size_t size; size_t size;
...@@ -451,7 +453,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name, ...@@ -451,7 +453,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
if (err < 0) if (err < 0)
goto out_free; goto out_free;
err = vfs_setxattr(&init_user_ns, upperdentry, name, buffer, size, XATTR_CREATE); err = ovl_do_setxattr(ofs, upperdentry, name, buffer, size, XATTR_CREATE);
out_free: out_free:
kfree(buffer); kfree(buffer);
return err; return err;
...@@ -460,6 +462,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name, ...@@ -460,6 +462,7 @@ static int ovl_set_upper_acl(struct dentry *upperdentry, const char *name,
static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
struct ovl_cattr *cattr) struct ovl_cattr *cattr)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *workdir = ovl_workdir(dentry); struct dentry *workdir = ovl_workdir(dentry);
struct inode *wdir = workdir->d_inode; struct inode *wdir = workdir->d_inode;
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
...@@ -484,7 +487,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -484,7 +487,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err) if (err)
goto out; goto out;
upper = lookup_one_len(dentry->d_name.name, upperdir, upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
dentry->d_name.len); dentry->d_name.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (IS_ERR(upper)) if (IS_ERR(upper))
...@@ -494,7 +497,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -494,7 +497,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper))) if (d_is_negative(upper) || !IS_WHITEOUT(d_inode(upper)))
goto out_dput; goto out_dput;
newdentry = ovl_create_temp(workdir, cattr); newdentry = ovl_create_temp(ofs, workdir, cattr);
err = PTR_ERR(newdentry); err = PTR_ERR(newdentry);
if (IS_ERR(newdentry)) if (IS_ERR(newdentry))
goto out_dput; goto out_dput;
...@@ -510,19 +513,19 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -510,19 +513,19 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
.ia_mode = cattr->mode, .ia_mode = cattr->mode,
}; };
inode_lock(newdentry->d_inode); inode_lock(newdentry->d_inode);
err = notify_change(&init_user_ns, newdentry, &attr, NULL); err = ovl_do_notify_change(ofs, newdentry, &attr);
inode_unlock(newdentry->d_inode); inode_unlock(newdentry->d_inode);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
} }
if (!hardlink) { if (!hardlink) {
err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_ACCESS, err = ovl_set_upper_acl(ofs, newdentry,
acl); XATTR_NAME_POSIX_ACL_ACCESS, acl);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
err = ovl_set_upper_acl(newdentry, XATTR_NAME_POSIX_ACL_DEFAULT, err = ovl_set_upper_acl(ofs, newdentry,
default_acl); XATTR_NAME_POSIX_ACL_DEFAULT, default_acl);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
} }
...@@ -532,20 +535,20 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -532,20 +535,20 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
if (err) if (err)
goto out_cleanup; goto out_cleanup;
err = ovl_do_rename(wdir, newdentry, udir, upper, err = ovl_do_rename(ofs, wdir, newdentry, udir, upper,
RENAME_EXCHANGE); RENAME_EXCHANGE);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
ovl_cleanup(wdir, upper); ovl_cleanup(ofs, wdir, upper);
} else { } else {
err = ovl_do_rename(wdir, newdentry, udir, upper, 0); err = ovl_do_rename(ofs, wdir, newdentry, udir, upper, 0);
if (err) if (err)
goto out_cleanup; goto out_cleanup;
} }
err = ovl_instantiate(dentry, inode, newdentry, hardlink); err = ovl_instantiate(dentry, inode, newdentry, hardlink);
if (err) { if (err) {
ovl_cleanup(udir, newdentry); ovl_cleanup(ofs, udir, newdentry);
dput(newdentry); dput(newdentry);
} }
out_dput: out_dput:
...@@ -560,7 +563,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, ...@@ -560,7 +563,7 @@ static int ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode,
return err; return err;
out_cleanup: out_cleanup:
ovl_cleanup(wdir, newdentry); ovl_cleanup(ofs, wdir, newdentry);
dput(newdentry); dput(newdentry);
goto out_dput; goto out_dput;
} }
...@@ -767,7 +770,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -767,7 +770,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
if (err) if (err)
goto out_dput; goto out_dput;
upper = lookup_one_len(dentry->d_name.name, upperdir, upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
dentry->d_name.len); dentry->d_name.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (IS_ERR(upper)) if (IS_ERR(upper))
...@@ -800,6 +803,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, ...@@ -800,6 +803,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry,
static int ovl_remove_upper(struct dentry *dentry, bool is_dir, static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
struct list_head *list) struct list_head *list)
{ {
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent); struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);
struct inode *dir = upperdir->d_inode; struct inode *dir = upperdir->d_inode;
struct dentry *upper; struct dentry *upper;
...@@ -814,7 +818,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir, ...@@ -814,7 +818,7 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
} }
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
upper = lookup_one_len(dentry->d_name.name, upperdir, upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,
dentry->d_name.len); dentry->d_name.len);
err = PTR_ERR(upper); err = PTR_ERR(upper);
if (IS_ERR(upper)) if (IS_ERR(upper))
...@@ -826,9 +830,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir, ...@@ -826,9 +830,9 @@ static int ovl_remove_upper(struct dentry *dentry, bool is_dir,
goto out_dput_upper; goto out_dput_upper;
if (is_dir) if (is_dir)
err = vfs_rmdir(&init_user_ns, dir, upper); err = ovl_do_rmdir(ofs, dir, upper);
else else
err = vfs_unlink(&init_user_ns, dir, upper, NULL); err = ovl_do_unlink(ofs, dir, upper);
ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry)); ovl_dir_modified(dentry->d_parent, ovl_type_origin(dentry));
/* /*
...@@ -880,7 +884,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -880,7 +884,6 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{ {
int err; int err;
const struct cred *old_cred; const struct cred *old_cred;
struct dentry *upperdentry;
bool lower_positive = ovl_lower_positive(dentry); bool lower_positive = ovl_lower_positive(dentry);
LIST_HEAD(list); LIST_HEAD(list);
...@@ -923,9 +926,8 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir) ...@@ -923,9 +926,8 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
* Note: we fail to update ctime if there was no copy-up, only a * Note: we fail to update ctime if there was no copy-up, only a
* whiteout * whiteout
*/ */
upperdentry = ovl_dentry_upper(dentry); if (ovl_dentry_upper(dentry))
if (upperdentry) ovl_copyattr(d_inode(dentry));
ovl_copyattr(d_inode(upperdentry), d_inode(dentry));
out_drop_write: out_drop_write:
ovl_drop_write(dentry); ovl_drop_write(dentry);
...@@ -1095,6 +1097,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, ...@@ -1095,6 +1097,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
bool samedir = olddir == newdir; bool samedir = olddir == newdir;
struct dentry *opaquedir = NULL; struct dentry *opaquedir = NULL;
const struct cred *old_cred = NULL; const struct cred *old_cred = NULL;
struct ovl_fs *ofs = OVL_FS(old->d_sb);
LIST_HEAD(list); LIST_HEAD(list);
err = -EINVAL; err = -EINVAL;
...@@ -1189,7 +1192,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, ...@@ -1189,7 +1192,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
trap = lock_rename(new_upperdir, old_upperdir); trap = lock_rename(new_upperdir, old_upperdir);
olddentry = lookup_one_len(old->d_name.name, old_upperdir, olddentry = ovl_lookup_upper(ofs, old->d_name.name, old_upperdir,
old->d_name.len); old->d_name.len);
err = PTR_ERR(olddentry); err = PTR_ERR(olddentry);
if (IS_ERR(olddentry)) if (IS_ERR(olddentry))
...@@ -1199,7 +1202,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, ...@@ -1199,7 +1202,7 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
if (!ovl_matches_upper(old, olddentry)) if (!ovl_matches_upper(old, olddentry))
goto out_dput_old; goto out_dput_old;
newdentry = lookup_one_len(new->d_name.name, new_upperdir, newdentry = ovl_lookup_upper(ofs, new->d_name.name, new_upperdir,
new->d_name.len); new->d_name.len);
err = PTR_ERR(newdentry); err = PTR_ERR(newdentry);
if (IS_ERR(newdentry)) if (IS_ERR(newdentry))
...@@ -1251,13 +1254,13 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, ...@@ -1251,13 +1254,13 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
if (err) if (err)
goto out_dput; goto out_dput;
err = ovl_do_rename(old_upperdir->d_inode, olddentry, err = ovl_do_rename(ofs, old_upperdir->d_inode, olddentry,
new_upperdir->d_inode, newdentry, flags); new_upperdir->d_inode, newdentry, flags);
if (err) if (err)
goto out_dput; goto out_dput;
if (cleanup_whiteout) if (cleanup_whiteout)
ovl_cleanup(old_upperdir->d_inode, newdentry); ovl_cleanup(ofs, old_upperdir->d_inode, newdentry);
if (overwrite && d_inode(new)) { if (overwrite && d_inode(new)) {
if (new_is_dir) if (new_is_dir)
...@@ -1272,9 +1275,9 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir, ...@@ -1272,9 +1275,9 @@ static int ovl_rename(struct user_namespace *mnt_userns, struct inode *olddir,
(d_inode(new) && ovl_type_origin(new))); (d_inode(new) && ovl_type_origin(new)));
/* copy ctime: */ /* copy ctime: */
ovl_copyattr(d_inode(olddentry), d_inode(old)); ovl_copyattr(d_inode(old));
if (d_inode(new) && ovl_dentry_upper(new)) if (d_inode(new) && ovl_dentry_upper(new))
ovl_copyattr(d_inode(newdentry), d_inode(new)); ovl_copyattr(d_inode(new));
out_dput: out_dput:
dput(newdentry); dput(newdentry);
......
...@@ -391,6 +391,11 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected, ...@@ -391,6 +391,11 @@ static struct dentry *ovl_lookup_real_one(struct dentry *connected,
* pointer because we hold no lock on the real dentry. * pointer because we hold no lock on the real dentry.
*/ */
take_dentry_name_snapshot(&name, real); take_dentry_name_snapshot(&name, real);
/*
* No mnt_userns handling here: it's an internal lookup. Could skip
* permission checking altogether, but for now just use non-mnt_userns
* transformed ids.
*/
this = lookup_one_len(name.name.name, connected, name.name.len); this = lookup_one_len(name.name.name, connected, name.name.len);
release_dentry_name_snapshot(&name); release_dentry_name_snapshot(&name);
err = PTR_ERR(this); err = PTR_ERR(this);
......
...@@ -38,9 +38,11 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode) ...@@ -38,9 +38,11 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
#define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY) #define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY)
static struct file *ovl_open_realfile(const struct file *file, static struct file *ovl_open_realfile(const struct file *file,
struct inode *realinode) struct path *realpath)
{ {
struct inode *realinode = d_inode(realpath->dentry);
struct inode *inode = file_inode(file); struct inode *inode = file_inode(file);
struct user_namespace *real_mnt_userns;
struct file *realfile; struct file *realfile;
const struct cred *old_cred; const struct cred *old_cred;
int flags = file->f_flags | OVL_OPEN_FLAGS; int flags = file->f_flags | OVL_OPEN_FLAGS;
...@@ -51,11 +53,12 @@ static struct file *ovl_open_realfile(const struct file *file, ...@@ -51,11 +53,12 @@ static struct file *ovl_open_realfile(const struct file *file,
acc_mode |= MAY_APPEND; acc_mode |= MAY_APPEND;
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
err = inode_permission(&init_user_ns, realinode, MAY_OPEN | acc_mode); real_mnt_userns = mnt_user_ns(realpath->mnt);
err = inode_permission(real_mnt_userns, realinode, MAY_OPEN | acc_mode);
if (err) { if (err) {
realfile = ERR_PTR(err); realfile = ERR_PTR(err);
} else { } else {
if (!inode_owner_or_capable(&init_user_ns, realinode)) if (!inode_owner_or_capable(real_mnt_userns, realinode))
flags &= ~O_NOATIME; flags &= ~O_NOATIME;
realfile = open_with_fake_path(&file->f_path, flags, realinode, realfile = open_with_fake_path(&file->f_path, flags, realinode,
...@@ -101,21 +104,21 @@ static int ovl_change_flags(struct file *file, unsigned int flags) ...@@ -101,21 +104,21 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
static int ovl_real_fdget_meta(const struct file *file, struct fd *real, static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
bool allow_meta) bool allow_meta)
{ {
struct inode *inode = file_inode(file); struct dentry *dentry = file_dentry(file);
struct inode *realinode; struct path realpath;
real->flags = 0; real->flags = 0;
real->file = file->private_data; real->file = file->private_data;
if (allow_meta) if (allow_meta)
realinode = ovl_inode_real(inode); ovl_path_real(dentry, &realpath);
else else
realinode = ovl_inode_realdata(inode); ovl_path_realdata(dentry, &realpath);
/* Has it been copied up since we'd opened it? */ /* Has it been copied up since we'd opened it? */
if (unlikely(file_inode(real->file) != realinode)) { if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) {
real->flags = FDPUT_FPUT; real->flags = FDPUT_FPUT;
real->file = ovl_open_realfile(file, realinode); real->file = ovl_open_realfile(file, &realpath);
return PTR_ERR_OR_ZERO(real->file); return PTR_ERR_OR_ZERO(real->file);
} }
...@@ -141,17 +144,20 @@ static int ovl_real_fdget(const struct file *file, struct fd *real) ...@@ -141,17 +144,20 @@ static int ovl_real_fdget(const struct file *file, struct fd *real)
static int ovl_open(struct inode *inode, struct file *file) static int ovl_open(struct inode *inode, struct file *file)
{ {
struct dentry *dentry = file_dentry(file);
struct file *realfile; struct file *realfile;
struct path realpath;
int err; int err;
err = ovl_maybe_copy_up(file_dentry(file), file->f_flags); err = ovl_maybe_copy_up(dentry, file->f_flags);
if (err) if (err)
return err; return err;
/* No longer need these flags, so don't pass them on to underlying fs */ /* No longer need these flags, so don't pass them on to underlying fs */
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC); file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
realfile = ovl_open_realfile(file, ovl_inode_realdata(inode)); ovl_path_realdata(dentry, &realpath);
realfile = ovl_open_realfile(file, &realpath);
if (IS_ERR(realfile)) if (IS_ERR(realfile))
return PTR_ERR(realfile); return PTR_ERR(realfile);
...@@ -270,7 +276,7 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req) ...@@ -270,7 +276,7 @@ static void ovl_aio_cleanup_handler(struct ovl_aio_req *aio_req)
__sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb, __sb_writers_acquired(file_inode(iocb->ki_filp)->i_sb,
SB_FREEZE_WRITE); SB_FREEZE_WRITE);
file_end_write(iocb->ki_filp); file_end_write(iocb->ki_filp);
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(inode);
} }
orig_iocb->ki_pos = iocb->ki_pos; orig_iocb->ki_pos = iocb->ki_pos;
...@@ -352,7 +358,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -352,7 +358,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
inode_lock(inode); inode_lock(inode);
/* Update mode */ /* Update mode */
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(inode);
ret = file_remove_privs(file); ret = file_remove_privs(file);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
...@@ -376,7 +382,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) ...@@ -376,7 +382,7 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter)
ovl_iocb_to_rwf(ifl)); ovl_iocb_to_rwf(ifl));
file_end_write(real.file); file_end_write(real.file);
/* Update size */ /* Update size */
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(inode);
} else { } else {
struct ovl_aio_req *aio_req; struct ovl_aio_req *aio_req;
...@@ -426,12 +432,11 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, ...@@ -426,12 +432,11 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
struct fd real; struct fd real;
const struct cred *old_cred; const struct cred *old_cred;
struct inode *inode = file_inode(out); struct inode *inode = file_inode(out);
struct inode *realinode = ovl_inode_real(inode);
ssize_t ret; ssize_t ret;
inode_lock(inode); inode_lock(inode);
/* Update mode */ /* Update mode */
ovl_copyattr(realinode, inode); ovl_copyattr(inode);
ret = file_remove_privs(out); ret = file_remove_privs(out);
if (ret) if (ret)
goto out_unlock; goto out_unlock;
...@@ -447,7 +452,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out, ...@@ -447,7 +452,7 @@ static ssize_t ovl_splice_write(struct pipe_inode_info *pipe, struct file *out,
file_end_write(real.file); file_end_write(real.file);
/* Update size */ /* Update size */
ovl_copyattr(realinode, inode); ovl_copyattr(inode);
revert_creds(old_cred); revert_creds(old_cred);
fdput(real); fdput(real);
...@@ -521,7 +526,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len ...@@ -521,7 +526,7 @@ static long ovl_fallocate(struct file *file, int mode, loff_t offset, loff_t len
revert_creds(old_cred); revert_creds(old_cred);
/* Update size */ /* Update size */
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(inode);
fdput(real); fdput(real);
...@@ -593,7 +598,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in, ...@@ -593,7 +598,7 @@ static loff_t ovl_copyfile(struct file *file_in, loff_t pos_in,
revert_creds(old_cred); revert_creds(old_cred);
/* Update size */ /* Update size */
ovl_copyattr(ovl_inode_real(inode_out), inode_out); ovl_copyattr(inode_out);
fdput(real_in); fdput(real_in);
fdput(real_out); fdput(real_out);
......
...@@ -21,6 +21,7 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -21,6 +21,7 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
struct iattr *attr) struct iattr *attr)
{ {
int err; int err;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
bool full_copy_up = false; bool full_copy_up = false;
struct dentry *upperdentry; struct dentry *upperdentry;
const struct cred *old_cred; const struct cred *old_cred;
...@@ -77,10 +78,10 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry, ...@@ -77,10 +78,10 @@ int ovl_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
inode_lock(upperdentry->d_inode); inode_lock(upperdentry->d_inode);
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
err = notify_change(&init_user_ns, upperdentry, attr, NULL); err = ovl_do_notify_change(ofs, upperdentry, attr);
revert_creds(old_cred); revert_creds(old_cred);
if (!err) if (!err)
ovl_copyattr(upperdentry->d_inode, dentry->d_inode); ovl_copyattr(dentry->d_inode);
inode_unlock(upperdentry->d_inode); inode_unlock(upperdentry->d_inode);
if (winode) if (winode)
...@@ -279,12 +280,14 @@ int ovl_permission(struct user_namespace *mnt_userns, ...@@ -279,12 +280,14 @@ int ovl_permission(struct user_namespace *mnt_userns,
struct inode *inode, int mask) struct inode *inode, int mask)
{ {
struct inode *upperinode = ovl_inode_upper(inode); struct inode *upperinode = ovl_inode_upper(inode);
struct inode *realinode = upperinode ?: ovl_inode_lower(inode); struct inode *realinode;
struct path realpath;
const struct cred *old_cred; const struct cred *old_cred;
int err; int err;
/* Careful in RCU walk mode */ /* Careful in RCU walk mode */
if (!realinode) { ovl_i_path_real(inode, &realpath);
if (!realpath.dentry) {
WARN_ON(!(mask & MAY_NOT_BLOCK)); WARN_ON(!(mask & MAY_NOT_BLOCK));
return -ECHILD; return -ECHILD;
} }
...@@ -297,6 +300,7 @@ int ovl_permission(struct user_namespace *mnt_userns, ...@@ -297,6 +300,7 @@ int ovl_permission(struct user_namespace *mnt_userns,
if (err) if (err)
return err; return err;
realinode = d_inode(realpath.dentry);
old_cred = ovl_override_creds(inode->i_sb); old_cred = ovl_override_creds(inode->i_sb);
if (!upperinode && if (!upperinode &&
!special_file(realinode->i_mode) && mask & MAY_WRITE) { !special_file(realinode->i_mode) && mask & MAY_WRITE) {
...@@ -304,7 +308,7 @@ int ovl_permission(struct user_namespace *mnt_userns, ...@@ -304,7 +308,7 @@ int ovl_permission(struct user_namespace *mnt_userns,
/* Make sure mounter can read file for copy up later */ /* Make sure mounter can read file for copy up later */
mask |= MAY_READ; mask |= MAY_READ;
} }
err = inode_permission(&init_user_ns, realinode, mask); err = inode_permission(mnt_user_ns(realpath.mnt), realinode, mask);
revert_creds(old_cred); revert_creds(old_cred);
return err; return err;
...@@ -342,8 +346,10 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, ...@@ -342,8 +346,10 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
const void *value, size_t size, int flags) const void *value, size_t size, int flags)
{ {
int err; int err;
struct ovl_fs *ofs = OVL_FS(dentry->d_sb);
struct dentry *upperdentry = ovl_i_dentry_upper(inode); struct dentry *upperdentry = ovl_i_dentry_upper(inode);
struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry); struct dentry *realdentry = upperdentry ?: ovl_dentry_lower(dentry);
struct path realpath;
const struct cred *old_cred; const struct cred *old_cred;
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
...@@ -351,8 +357,9 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, ...@@ -351,8 +357,9 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
goto out; goto out;
if (!value && !upperdentry) { if (!value && !upperdentry) {
ovl_path_lower(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getxattr(&init_user_ns, realdentry, name, NULL, 0); err = vfs_getxattr(mnt_user_ns(realpath.mnt), realdentry, name, NULL, 0);
revert_creds(old_cred); revert_creds(old_cred);
if (err < 0) if (err < 0)
goto out_drop_write; goto out_drop_write;
...@@ -367,17 +374,17 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name, ...@@ -367,17 +374,17 @@ int ovl_xattr_set(struct dentry *dentry, struct inode *inode, const char *name,
} }
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
if (value) if (value) {
err = vfs_setxattr(&init_user_ns, realdentry, name, value, size, err = ovl_do_setxattr(ofs, realdentry, name, value, size,
flags); flags);
else { } else {
WARN_ON(flags != XATTR_REPLACE); WARN_ON(flags != XATTR_REPLACE);
err = vfs_removexattr(&init_user_ns, realdentry, name); err = ovl_do_removexattr(ofs, realdentry, name);
} }
revert_creds(old_cred); revert_creds(old_cred);
/* copy c/mtime */ /* copy c/mtime */
ovl_copyattr(d_inode(realdentry), inode); ovl_copyattr(inode);
out_drop_write: out_drop_write:
ovl_drop_write(dentry); ovl_drop_write(dentry);
...@@ -390,11 +397,11 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name, ...@@ -390,11 +397,11 @@ int ovl_xattr_get(struct dentry *dentry, struct inode *inode, const char *name,
{ {
ssize_t res; ssize_t res;
const struct cred *old_cred; const struct cred *old_cred;
struct dentry *realdentry = struct path realpath;
ovl_i_dentry_upper(inode) ?: ovl_dentry_lower(dentry);
ovl_i_path_real(inode, &realpath);
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
res = vfs_getxattr(&init_user_ns, realdentry, name, value, size); res = vfs_getxattr(mnt_user_ns(realpath.mnt), realpath.dentry, name, value, size);
revert_creds(old_cred); revert_creds(old_cred);
return res; return res;
} }
...@@ -535,7 +542,7 @@ int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa) ...@@ -535,7 +542,7 @@ int ovl_real_fileattr_set(struct path *realpath, struct fileattr *fa)
if (err) if (err)
return err; return err;
return vfs_fileattr_set(&init_user_ns, realpath->dentry, fa); return vfs_fileattr_set(mnt_user_ns(realpath->mnt), realpath->dentry, fa);
} }
int ovl_fileattr_set(struct user_namespace *mnt_userns, int ovl_fileattr_set(struct user_namespace *mnt_userns,
...@@ -579,7 +586,7 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns, ...@@ -579,7 +586,7 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
inode_set_flags(inode, flags, OVL_COPY_I_FLAGS_MASK); inode_set_flags(inode, flags, OVL_COPY_I_FLAGS_MASK);
/* Update ctime */ /* Update ctime */
ovl_copyattr(ovl_inode_real(inode), inode); ovl_copyattr(inode);
} }
ovl_drop_write(dentry); ovl_drop_write(dentry);
out: out:
...@@ -777,16 +784,19 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip, ...@@ -777,16 +784,19 @@ void ovl_inode_init(struct inode *inode, struct ovl_inode_params *oip,
unsigned long ino, int fsid) unsigned long ino, int fsid)
{ {
struct inode *realinode; struct inode *realinode;
struct ovl_inode *oi = OVL_I(inode);
if (oip->upperdentry) if (oip->upperdentry)
OVL_I(inode)->__upperdentry = oip->upperdentry; oi->__upperdentry = oip->upperdentry;
if (oip->lowerpath && oip->lowerpath->dentry) if (oip->lowerpath && oip->lowerpath->dentry) {
OVL_I(inode)->lower = igrab(d_inode(oip->lowerpath->dentry)); oi->lowerpath.dentry = dget(oip->lowerpath->dentry);
oi->lowerpath.layer = oip->lowerpath->layer;
}
if (oip->lowerdata) if (oip->lowerdata)
OVL_I(inode)->lowerdata = igrab(d_inode(oip->lowerdata)); oi->lowerdata = igrab(d_inode(oip->lowerdata));
realinode = ovl_inode_real(inode); realinode = ovl_inode_real(inode);
ovl_copyattr(realinode, inode); ovl_copyattr(inode);
ovl_copyflags(realinode, inode); ovl_copyflags(realinode, inode);
ovl_map_ino(inode, ino, fsid); ovl_map_ino(inode, ino, fsid);
} }
...@@ -871,7 +881,7 @@ static int ovl_set_nlink_common(struct dentry *dentry, ...@@ -871,7 +881,7 @@ static int ovl_set_nlink_common(struct dentry *dentry,
if (WARN_ON(len >= sizeof(buf))) if (WARN_ON(len >= sizeof(buf)))
return -EIO; return -EIO;
return ovl_do_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry), return ovl_setxattr(OVL_FS(inode->i_sb), ovl_dentry_upper(dentry),
OVL_XATTR_NLINK, buf, len); OVL_XATTR_NLINK, buf, len);
} }
...@@ -897,7 +907,7 @@ unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry, ...@@ -897,7 +907,7 @@ unsigned int ovl_get_nlink(struct ovl_fs *ofs, struct dentry *lowerdentry,
if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1) if (!lowerdentry || !upperdentry || d_inode(lowerdentry)->i_nlink == 1)
return fallback; return fallback;
err = ovl_do_getxattr(ofs, upperdentry, OVL_XATTR_NLINK, err = ovl_getxattr_upper(ofs, upperdentry, OVL_XATTR_NLINK,
&buf, sizeof(buf) - 1); &buf, sizeof(buf) - 1);
if (err < 0) if (err < 0)
goto fail; goto fail;
...@@ -1102,6 +1112,10 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1102,6 +1112,10 @@ struct inode *ovl_get_inode(struct super_block *sb,
struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL; struct inode *realinode = upperdentry ? d_inode(upperdentry) : NULL;
struct inode *inode; struct inode *inode;
struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL; struct dentry *lowerdentry = lowerpath ? lowerpath->dentry : NULL;
struct path realpath = {
.dentry = upperdentry ?: lowerdentry,
.mnt = upperdentry ? ovl_upper_mnt(ofs) : lowerpath->layer->mnt,
};
bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry, bool bylower = ovl_hash_bylower(sb, upperdentry, lowerdentry,
oip->index); oip->index);
int fsid = bylower ? lowerpath->layer->fsid : 0; int fsid = bylower ? lowerpath->layer->fsid : 0;
...@@ -1175,7 +1189,7 @@ struct inode *ovl_get_inode(struct super_block *sb, ...@@ -1175,7 +1189,7 @@ struct inode *ovl_get_inode(struct super_block *sb,
/* Check for non-merge dir that may have whiteouts */ /* Check for non-merge dir that may have whiteouts */
if (is_dir) { if (is_dir) {
if (((upperdentry && lowerdentry) || oip->numlower > 1) || if (((upperdentry && lowerdentry) || oip->numlower > 1) ||
ovl_check_origin_xattr(ofs, upperdentry ?: lowerdentry)) { ovl_path_check_origin_xattr(ofs, &realpath)) {
ovl_set_flag(OVL_WHITEOUTS, inode); ovl_set_flag(OVL_WHITEOUTS, inode);
} }
} }
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
struct ovl_lookup_data { struct ovl_lookup_data {
struct super_block *sb; struct super_block *sb;
struct vfsmount *mnt;
struct qstr name; struct qstr name;
bool is_dir; bool is_dir;
bool opaque; bool opaque;
...@@ -25,14 +26,14 @@ struct ovl_lookup_data { ...@@ -25,14 +26,14 @@ struct ovl_lookup_data {
bool metacopy; bool metacopy;
}; };
static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d, static int ovl_check_redirect(struct path *path, struct ovl_lookup_data *d,
size_t prelen, const char *post) size_t prelen, const char *post)
{ {
int res; int res;
char *buf; char *buf;
struct ovl_fs *ofs = OVL_FS(d->sb); struct ovl_fs *ofs = OVL_FS(d->sb);
buf = ovl_get_redirect_xattr(ofs, dentry, prelen + strlen(post)); buf = ovl_get_redirect_xattr(ofs, path, prelen + strlen(post));
if (IS_ERR_OR_NULL(buf)) if (IS_ERR_OR_NULL(buf))
return PTR_ERR(buf); return PTR_ERR(buf);
...@@ -105,13 +106,13 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len) ...@@ -105,13 +106,13 @@ int ovl_check_fb_len(struct ovl_fb *fb, int fb_len)
return 0; return 0;
} }
static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry, static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *upperdentry,
enum ovl_xattr ox) enum ovl_xattr ox)
{ {
int res, err; int res, err;
struct ovl_fh *fh = NULL; struct ovl_fh *fh = NULL;
res = ovl_do_getxattr(ofs, dentry, ox, NULL, 0); res = ovl_getxattr_upper(ofs, upperdentry, ox, NULL, 0);
if (res < 0) { if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP) if (res == -ENODATA || res == -EOPNOTSUPP)
return NULL; return NULL;
...@@ -125,7 +126,7 @@ static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -125,7 +126,7 @@ static struct ovl_fh *ovl_get_fh(struct ovl_fs *ofs, struct dentry *dentry,
if (!fh) if (!fh)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
res = ovl_do_getxattr(ofs, dentry, ox, fh->buf, res); res = ovl_getxattr_upper(ofs, upperdentry, ox, fh->buf, res);
if (res < 0) if (res < 0)
goto fail; goto fail;
...@@ -193,16 +194,17 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh, ...@@ -193,16 +194,17 @@ struct dentry *ovl_decode_real_fh(struct ovl_fs *ofs, struct ovl_fh *fh,
return real; return real;
} }
static bool ovl_is_opaquedir(struct super_block *sb, struct dentry *dentry) static bool ovl_is_opaquedir(struct ovl_fs *ofs, struct path *path)
{ {
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_OPAQUE); return ovl_path_check_dir_xattr(ofs, path, OVL_XATTR_OPAQUE);
} }
static struct dentry *ovl_lookup_positive_unlocked(const char *name, static struct dentry *ovl_lookup_positive_unlocked(struct ovl_lookup_data *d,
const char *name,
struct dentry *base, int len, struct dentry *base, int len,
bool drop_negative) bool drop_negative)
{ {
struct dentry *ret = lookup_one_len_unlocked(name, base, len); struct dentry *ret = lookup_one_unlocked(mnt_user_ns(d->mnt), name, base, len);
if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) { if (!IS_ERR(ret) && d_flags_negative(smp_load_acquire(&ret->d_flags))) {
if (drop_negative && ret->d_lockref.count == 1) { if (drop_negative && ret->d_lockref.count == 1) {
...@@ -224,10 +226,11 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -224,10 +226,11 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
struct dentry **ret, bool drop_negative) struct dentry **ret, bool drop_negative)
{ {
struct dentry *this; struct dentry *this;
struct path path;
int err; int err;
bool last_element = !post[0]; bool last_element = !post[0];
this = ovl_lookup_positive_unlocked(name, base, namelen, drop_negative); this = ovl_lookup_positive_unlocked(d, name, base, namelen, drop_negative);
if (IS_ERR(this)) { if (IS_ERR(this)) {
err = PTR_ERR(this); err = PTR_ERR(this);
this = NULL; this = NULL;
...@@ -253,12 +256,15 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -253,12 +256,15 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
d->stop = true; d->stop = true;
goto put_and_out; goto put_and_out;
} }
path.dentry = this;
path.mnt = d->mnt;
if (!d_can_lookup(this)) { if (!d_can_lookup(this)) {
if (d->is_dir || !last_element) { if (d->is_dir || !last_element) {
d->stop = true; d->stop = true;
goto put_and_out; goto put_and_out;
} }
err = ovl_check_metacopy_xattr(OVL_FS(d->sb), this); err = ovl_check_metacopy_xattr(OVL_FS(d->sb), &path);
if (err < 0) if (err < 0)
goto out_err; goto out_err;
...@@ -278,14 +284,14 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d, ...@@ -278,14 +284,14 @@ static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
if (d->last) if (d->last)
goto out; goto out;
if (ovl_is_opaquedir(d->sb, this)) { if (ovl_is_opaquedir(OVL_FS(d->sb), &path)) {
d->stop = true; d->stop = true;
if (last_element) if (last_element)
d->opaque = true; d->opaque = true;
goto out; goto out;
} }
} }
err = ovl_check_redirect(this, d, prelen, post); err = ovl_check_redirect(&path, d, prelen, post);
if (err) if (err)
goto out_err; goto out_err;
out: out:
...@@ -464,7 +470,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -464,7 +470,7 @@ int ovl_verify_set_fh(struct ovl_fs *ofs, struct dentry *dentry,
err = ovl_verify_fh(ofs, dentry, ox, fh); err = ovl_verify_fh(ofs, dentry, ox, fh);
if (set && err == -ENODATA) if (set && err == -ENODATA)
err = ovl_do_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len); err = ovl_setxattr(ofs, dentry, ox, fh->buf, fh->fb.len);
if (err) if (err)
goto fail; goto fail;
...@@ -704,7 +710,8 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper, ...@@ -704,7 +710,8 @@ struct dentry *ovl_lookup_index(struct ovl_fs *ofs, struct dentry *upper,
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
index = lookup_positive_unlocked(name.name, ofs->indexdir, name.len); index = lookup_one_positive_unlocked(ovl_upper_mnt_userns(ofs), name.name,
ofs->indexdir, name.len);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index); err = PTR_ERR(index);
if (err == -ENOENT) { if (err == -ENOENT) {
...@@ -856,6 +863,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -856,6 +863,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
old_cred = ovl_override_creds(dentry->d_sb); old_cred = ovl_override_creds(dentry->d_sb);
upperdir = ovl_dentry_upper(dentry->d_parent); upperdir = ovl_dentry_upper(dentry->d_parent);
if (upperdir) { if (upperdir) {
d.mnt = ovl_upper_mnt(ofs);
err = ovl_lookup_layer(upperdir, &d, &upperdentry, true); err = ovl_lookup_layer(upperdir, &d, &upperdentry, true);
if (err) if (err)
goto out; goto out;
...@@ -911,6 +919,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -911,6 +919,7 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
else else
d.last = lower.layer->idx == roe->numlower; d.last = lower.layer->idx == roe->numlower;
d.mnt = lower.layer->mnt;
err = ovl_lookup_layer(lower.dentry, &d, &this, false); err = ovl_lookup_layer(lower.dentry, &d, &this, false);
if (err) if (err)
goto out_put; goto out_put;
...@@ -1071,14 +1080,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, ...@@ -1071,14 +1080,18 @@ struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
if (upperdentry) if (upperdentry)
ovl_dentry_set_upper_alias(dentry); ovl_dentry_set_upper_alias(dentry);
else if (index) { else if (index) {
upperdentry = dget(index); struct path upperpath = {
upperredirect = ovl_get_redirect_xattr(ofs, upperdentry, 0); .dentry = upperdentry = dget(index),
.mnt = ovl_upper_mnt(ofs),
};
upperredirect = ovl_get_redirect_xattr(ofs, &upperpath, 0);
if (IS_ERR(upperredirect)) { if (IS_ERR(upperredirect)) {
err = PTR_ERR(upperredirect); err = PTR_ERR(upperredirect);
upperredirect = NULL; upperredirect = NULL;
goto out_free_oe; goto out_free_oe;
} }
err = ovl_check_metacopy_xattr(ofs, upperdentry); err = ovl_check_metacopy_xattr(ofs, &upperpath);
if (err < 0) if (err < 0)
goto out_free_oe; goto out_free_oe;
uppermetacopy = err; uppermetacopy = err;
...@@ -1163,8 +1176,8 @@ bool ovl_lower_positive(struct dentry *dentry) ...@@ -1163,8 +1176,8 @@ bool ovl_lower_positive(struct dentry *dentry)
struct dentry *this; struct dentry *this;
struct dentry *lowerdir = poe->lowerstack[i].dentry; struct dentry *lowerdir = poe->lowerstack[i].dentry;
this = lookup_positive_unlocked(name->name, lowerdir, this = lookup_one_positive_unlocked(mnt_user_ns(poe->lowerstack[i].layer->mnt),
name->len); name->name, lowerdir, name->len);
if (IS_ERR(this)) { if (IS_ERR(this)) {
switch (PTR_ERR(this)) { switch (PTR_ERR(this)) {
case -ENOENT: case -ENOENT:
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/uuid.h> #include <linux/uuid.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h>
#include "ovl_entry.h" #include "ovl_entry.h"
#undef pr_fmt #undef pr_fmt
...@@ -122,109 +123,180 @@ static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox) ...@@ -122,109 +123,180 @@ static inline const char *ovl_xattr(struct ovl_fs *ofs, enum ovl_xattr ox)
return ovl_xattr_table[ox][ofs->config.userxattr]; return ovl_xattr_table[ox][ofs->config.userxattr];
} }
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry) /*
* When changing ownership of an upper object map the intended ownership
* according to the upper layer's idmapping. When an upper mount idmaps files
* that are stored on-disk as owned by id 1001 to id 1000 this means stat on
* this object will report it as being owned by id 1000 when calling stat via
* the upper mount.
* In order to change ownership of an object so stat reports id 1000 when
* called on an idmapped upper mount the value written to disk - i.e., the
* value stored in ia_*id - must 1001. The mount mapping helper will thus take
* care to map 1000 to 1001.
* The mnt idmapping helpers are nops if the upper layer isn't idmapped.
*/
static inline int ovl_do_notify_change(struct ovl_fs *ofs,
struct dentry *upperdentry,
struct iattr *attr)
{ {
int err = vfs_rmdir(&init_user_ns, dir, dentry); struct user_namespace *upper_mnt_userns = ovl_upper_mnt_userns(ofs);
struct user_namespace *fs_userns = i_user_ns(d_inode(upperdentry));
if (attr->ia_valid & ATTR_UID)
attr->ia_uid = mapped_kuid_user(upper_mnt_userns,
fs_userns, attr->ia_uid);
if (attr->ia_valid & ATTR_GID)
attr->ia_gid = mapped_kgid_user(upper_mnt_userns,
fs_userns, attr->ia_gid);
return notify_change(upper_mnt_userns, upperdentry, attr, NULL);
}
static inline int ovl_do_rmdir(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry)
{
int err = vfs_rmdir(ovl_upper_mnt_userns(ofs), dir, dentry);
pr_debug("rmdir(%pd2) = %i\n", dentry, err); pr_debug("rmdir(%pd2) = %i\n", dentry, err);
return err; return err;
} }
static inline int ovl_do_unlink(struct inode *dir, struct dentry *dentry) static inline int ovl_do_unlink(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry)
{ {
int err = vfs_unlink(&init_user_ns, dir, dentry, NULL); int err = vfs_unlink(ovl_upper_mnt_userns(ofs), dir, dentry, NULL);
pr_debug("unlink(%pd2) = %i\n", dentry, err); pr_debug("unlink(%pd2) = %i\n", dentry, err);
return err; return err;
} }
static inline int ovl_do_link(struct dentry *old_dentry, struct inode *dir, static inline int ovl_do_link(struct ovl_fs *ofs, struct dentry *old_dentry,
struct dentry *new_dentry) struct inode *dir, struct dentry *new_dentry)
{ {
int err = vfs_link(old_dentry, &init_user_ns, dir, new_dentry, NULL); int err = vfs_link(old_dentry, ovl_upper_mnt_userns(ofs), dir, new_dentry, NULL);
pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err); pr_debug("link(%pd2, %pd2) = %i\n", old_dentry, new_dentry, err);
return err; return err;
} }
static inline int ovl_do_create(struct inode *dir, struct dentry *dentry, static inline int ovl_do_create(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
int err = vfs_create(&init_user_ns, dir, dentry, mode, true); int err = vfs_create(ovl_upper_mnt_userns(ofs), dir, dentry, mode, true);
pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("create(%pd2, 0%o) = %i\n", dentry, mode, err);
return err; return err;
} }
static inline int ovl_do_mkdir(struct inode *dir, struct dentry *dentry, static inline int ovl_do_mkdir(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry,
umode_t mode) umode_t mode)
{ {
int err = vfs_mkdir(&init_user_ns, dir, dentry, mode); int err = vfs_mkdir(ovl_upper_mnt_userns(ofs), dir, dentry, mode);
pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("mkdir(%pd2, 0%o) = %i\n", dentry, mode, err);
return err; return err;
} }
static inline int ovl_do_mknod(struct inode *dir, struct dentry *dentry, static inline int ovl_do_mknod(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry,
umode_t mode, dev_t dev) umode_t mode, dev_t dev)
{ {
int err = vfs_mknod(&init_user_ns, dir, dentry, mode, dev); int err = vfs_mknod(ovl_upper_mnt_userns(ofs), dir, dentry, mode, dev);
pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err); pr_debug("mknod(%pd2, 0%o, 0%o) = %i\n", dentry, mode, dev, err);
return err; return err;
} }
static inline int ovl_do_symlink(struct inode *dir, struct dentry *dentry, static inline int ovl_do_symlink(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry,
const char *oldname) const char *oldname)
{ {
int err = vfs_symlink(&init_user_ns, dir, dentry, oldname); int err = vfs_symlink(ovl_upper_mnt_userns(ofs), dir, dentry, oldname);
pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err); pr_debug("symlink(\"%s\", %pd2) = %i\n", oldname, dentry, err);
return err; return err;
} }
static inline ssize_t ovl_do_getxattr(struct ovl_fs *ofs, struct dentry *dentry, static inline ssize_t ovl_do_getxattr(struct path *path, const char *name,
enum ovl_xattr ox, void *value, void *value, size_t size)
size_t size)
{ {
const char *name = ovl_xattr(ofs, ox); int err, len;
int err = vfs_getxattr(&init_user_ns, dentry, name, value, size);
int len = (value && err > 0) ? err : 0; WARN_ON(path->dentry->d_sb != path->mnt->mnt_sb);
err = vfs_getxattr(mnt_user_ns(path->mnt), path->dentry,
name, value, size);
len = (value && err > 0) ? err : 0;
pr_debug("getxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n", pr_debug("getxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
dentry, name, min(len, 48), value, size, err); path->dentry, name, min(len, 48), value, size, err);
return err; return err;
} }
static inline ssize_t ovl_getxattr_upper(struct ovl_fs *ofs,
struct dentry *upperdentry,
enum ovl_xattr ox, void *value,
size_t size)
{
struct path upperpath = {
.dentry = upperdentry,
.mnt = ovl_upper_mnt(ofs),
};
return ovl_do_getxattr(&upperpath, ovl_xattr(ofs, ox), value, size);
}
static inline ssize_t ovl_path_getxattr(struct ovl_fs *ofs,
struct path *path,
enum ovl_xattr ox, void *value,
size_t size)
{
return ovl_do_getxattr(path, ovl_xattr(ofs, ox), value, size);
}
static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry, static inline int ovl_do_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
const char *name, const void *value,
size_t size, int flags)
{
int err = vfs_setxattr(ovl_upper_mnt_userns(ofs), dentry, name, value, size, flags);
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, %d) = %i\n",
dentry, name, min((int)size, 48), value, size, flags, err);
return err;
}
static inline int ovl_setxattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox, const void *value, enum ovl_xattr ox, const void *value,
size_t size) size_t size)
{ {
const char *name = ovl_xattr(ofs, ox); return ovl_do_setxattr(ofs, dentry, ovl_xattr(ofs, ox), value, size, 0);
int err = vfs_setxattr(&init_user_ns, dentry, name, value, size, 0);
pr_debug("setxattr(%pd2, \"%s\", \"%*pE\", %zu, 0) = %i\n",
dentry, name, min((int)size, 48), value, size, err);
return err;
} }
static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry, static inline int ovl_do_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
enum ovl_xattr ox) const char *name)
{ {
const char *name = ovl_xattr(ofs, ox); int err = vfs_removexattr(ovl_upper_mnt_userns(ofs), dentry, name);
int err = vfs_removexattr(&init_user_ns, dentry, name);
pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err); pr_debug("removexattr(%pd2, \"%s\") = %i\n", dentry, name, err);
return err; return err;
} }
static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry, static inline int ovl_removexattr(struct ovl_fs *ofs, struct dentry *dentry,
struct inode *newdir, struct dentry *newdentry, enum ovl_xattr ox)
unsigned int flags) {
return ovl_do_removexattr(ofs, dentry, ovl_xattr(ofs, ox));
}
static inline int ovl_do_rename(struct ovl_fs *ofs, struct inode *olddir,
struct dentry *olddentry, struct inode *newdir,
struct dentry *newdentry, unsigned int flags)
{ {
int err; int err;
struct renamedata rd = { struct renamedata rd = {
.old_mnt_userns = &init_user_ns, .old_mnt_userns = ovl_upper_mnt_userns(ofs),
.old_dir = olddir, .old_dir = olddir,
.old_dentry = olddentry, .old_dentry = olddentry,
.new_mnt_userns = &init_user_ns, .new_mnt_userns = ovl_upper_mnt_userns(ofs),
.new_dir = newdir, .new_dir = newdir,
.new_dentry = newdentry, .new_dentry = newdentry,
.flags = flags, .flags = flags,
...@@ -239,22 +311,31 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry, ...@@ -239,22 +311,31 @@ static inline int ovl_do_rename(struct inode *olddir, struct dentry *olddentry,
return err; return err;
} }
static inline int ovl_do_whiteout(struct inode *dir, struct dentry *dentry) static inline int ovl_do_whiteout(struct ovl_fs *ofs,
struct inode *dir, struct dentry *dentry)
{ {
int err = vfs_whiteout(&init_user_ns, dir, dentry); int err = vfs_whiteout(ovl_upper_mnt_userns(ofs), dir, dentry);
pr_debug("whiteout(%pd2) = %i\n", dentry, err); pr_debug("whiteout(%pd2) = %i\n", dentry, err);
return err; return err;
} }
static inline struct dentry *ovl_do_tmpfile(struct dentry *dentry, umode_t mode) static inline struct dentry *ovl_do_tmpfile(struct ovl_fs *ofs,
struct dentry *dentry, umode_t mode)
{ {
struct dentry *ret = vfs_tmpfile(&init_user_ns, dentry, mode, 0); struct dentry *ret = vfs_tmpfile(ovl_upper_mnt_userns(ofs), dentry, mode, 0);
int err = PTR_ERR_OR_ZERO(ret); int err = PTR_ERR_OR_ZERO(ret);
pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err); pr_debug("tmpfile(%pd2, 0%o) = %i\n", dentry, mode, err);
return ret; return ret;
} }
static inline struct dentry *ovl_lookup_upper(struct ovl_fs *ofs,
const char *name,
struct dentry *base, int len)
{
return lookup_one(ovl_upper_mnt_userns(ofs), name, base, len);
}
static inline bool ovl_open_flags_need_copy_up(int flags) static inline bool ovl_open_flags_need_copy_up(int flags)
{ {
if (!flags) if (!flags)
...@@ -293,10 +374,13 @@ enum ovl_path_type ovl_path_type(struct dentry *dentry); ...@@ -293,10 +374,13 @@ enum ovl_path_type ovl_path_type(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);
void ovl_path_lowerdata(struct dentry *dentry, struct path *path); void ovl_path_lowerdata(struct dentry *dentry, struct path *path);
void ovl_i_path_real(struct inode *inode, 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);
enum ovl_path_type ovl_path_realdata(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_lowerdata(struct dentry *dentry); struct dentry *ovl_dentry_lowerdata(struct dentry *dentry);
const struct ovl_layer *ovl_i_layer_lower(struct inode *inode);
const struct ovl_layer *ovl_layer_lower(struct dentry *dentry); const struct ovl_layer *ovl_layer_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
struct dentry *ovl_i_dentry_upper(struct inode *inode); struct dentry *ovl_i_dentry_upper(struct inode *inode);
...@@ -330,9 +414,20 @@ struct file *ovl_path_open(struct path *path, int flags); ...@@ -330,9 +414,20 @@ struct file *ovl_path_open(struct path *path, int flags);
int ovl_copy_up_start(struct dentry *dentry, int flags); int ovl_copy_up_start(struct dentry *dentry, int flags);
void ovl_copy_up_end(struct dentry *dentry); void ovl_copy_up_end(struct dentry *dentry);
bool ovl_already_copied_up(struct dentry *dentry, int flags); bool ovl_already_copied_up(struct dentry *dentry, int flags);
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry); bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, struct path *path,
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry,
enum ovl_xattr ox); enum ovl_xattr ox);
bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, struct path *path);
static inline bool ovl_check_origin_xattr(struct ovl_fs *ofs,
struct dentry *upperdentry)
{
struct path upperpath = {
.dentry = upperdentry,
.mnt = ovl_upper_mnt(ofs),
};
return ovl_path_check_origin_xattr(ofs, &upperpath);
}
int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry, int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
enum ovl_xattr ox, const void *value, size_t size, enum ovl_xattr ox, const void *value, size_t size,
int xerr); int xerr);
...@@ -344,10 +439,9 @@ bool ovl_need_index(struct dentry *dentry); ...@@ -344,10 +439,9 @@ bool ovl_need_index(struct dentry *dentry);
int ovl_nlink_start(struct dentry *dentry); int ovl_nlink_start(struct dentry *dentry);
void ovl_nlink_end(struct dentry *dentry); void ovl_nlink_end(struct dentry *dentry);
int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir); int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir);
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry); int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct path *path);
bool ovl_is_metacopy_dentry(struct dentry *dentry); bool ovl_is_metacopy_dentry(struct dentry *dentry);
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry, char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct path *path, int padding);
int padding);
int ovl_sync_status(struct ovl_fs *ofs); int ovl_sync_status(struct ovl_fs *ofs);
static inline void ovl_set_flag(unsigned long flag, struct inode *inode) static inline void ovl_set_flag(unsigned long flag, struct inode *inode)
...@@ -366,9 +460,15 @@ static inline bool ovl_test_flag(unsigned long flag, struct inode *inode) ...@@ -366,9 +460,15 @@ static inline bool ovl_test_flag(unsigned long flag, struct inode *inode)
} }
static inline bool ovl_is_impuredir(struct super_block *sb, static inline bool ovl_is_impuredir(struct super_block *sb,
struct dentry *dentry) struct dentry *upperdentry)
{ {
return ovl_check_dir_xattr(sb, dentry, OVL_XATTR_IMPURE); struct ovl_fs *ofs = OVL_FS(sb);
struct path upperpath = {
.dentry = upperdentry,
.mnt = ovl_upper_mnt(ofs),
};
return ovl_path_check_dir_xattr(ofs, &upperpath, OVL_XATTR_IMPURE);
} }
/* /*
...@@ -461,12 +561,13 @@ static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index, ...@@ -461,12 +561,13 @@ static inline int ovl_verify_upper(struct ovl_fs *ofs, struct dentry *index,
extern const struct file_operations ovl_dir_operations; extern const struct file_operations ovl_dir_operations;
struct file *ovl_dir_real_file(const struct file *file, bool want_upper); struct file *ovl_dir_real_file(const struct file *file, bool want_upper);
int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list); int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list);
void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list); void ovl_cleanup_whiteouts(struct ovl_fs *ofs, struct dentry *upper,
struct list_head *list);
void ovl_cache_free(struct list_head *list); void ovl_cache_free(struct list_head *list);
void ovl_dir_cache_free(struct inode *inode); void ovl_dir_cache_free(struct inode *inode);
int ovl_check_d_type_supported(struct path *realpath); int ovl_check_d_type_supported(struct path *realpath);
int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, int ovl_workdir_cleanup(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry, int level); struct vfsmount *mnt, struct dentry *dentry, int level);
int ovl_indexdir_cleanup(struct ovl_fs *ofs); int ovl_indexdir_cleanup(struct ovl_fs *ofs);
/* /*
...@@ -520,16 +621,7 @@ bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir); ...@@ -520,16 +621,7 @@ bool ovl_lookup_trap_inode(struct super_block *sb, struct dentry *dir);
struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir); struct inode *ovl_get_trap_inode(struct super_block *sb, struct dentry *dir);
struct inode *ovl_get_inode(struct super_block *sb, struct inode *ovl_get_inode(struct super_block *sb,
struct ovl_inode_params *oip); struct ovl_inode_params *oip);
static inline void ovl_copyattr(struct inode *from, struct inode *to) void ovl_copyattr(struct inode *to);
{
to->i_uid = from->i_uid;
to->i_gid = from->i_gid;
to->i_mode = from->i_mode;
to->i_atime = from->i_atime;
to->i_mtime = from->i_mtime;
to->i_ctime = from->i_ctime;
i_size_write(to, i_size_read(from));
}
/* vfs inode flags copied from real to ovl inode */ /* vfs inode flags copied from real to ovl inode */
#define OVL_COPY_I_FLAGS_MASK (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE) #define OVL_COPY_I_FLAGS_MASK (S_SYNC | S_NOATIME | S_APPEND | S_IMMUTABLE)
...@@ -570,12 +662,15 @@ struct ovl_cattr { ...@@ -570,12 +662,15 @@ struct ovl_cattr {
#define OVL_CATTR(m) (&(struct ovl_cattr) { .mode = (m) }) #define OVL_CATTR(m) (&(struct ovl_cattr) { .mode = (m) })
int ovl_mkdir_real(struct inode *dir, struct dentry **newdentry, umode_t mode); int ovl_mkdir_real(struct ovl_fs *ofs, struct inode *dir,
struct dentry *ovl_create_real(struct inode *dir, struct dentry *newdentry, struct dentry **newdentry, umode_t mode);
struct dentry *ovl_create_real(struct ovl_fs *ofs,
struct inode *dir, struct dentry *newdentry,
struct ovl_cattr *attr);
int ovl_cleanup(struct ovl_fs *ofs, struct inode *dir, struct dentry *dentry);
struct dentry *ovl_lookup_temp(struct ovl_fs *ofs, struct dentry *workdir);
struct dentry *ovl_create_temp(struct ovl_fs *ofs, struct dentry *workdir,
struct ovl_cattr *attr); struct ovl_cattr *attr);
int ovl_cleanup(struct inode *dir, struct dentry *dentry);
struct dentry *ovl_lookup_temp(struct dentry *workdir);
struct dentry *ovl_create_temp(struct dentry *workdir, struct ovl_cattr *attr);
/* file.c */ /* file.c */
extern const struct file_operations ovl_file_operations; extern const struct file_operations ovl_file_operations;
...@@ -591,9 +686,8 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns, ...@@ -591,9 +686,8 @@ int ovl_fileattr_set(struct user_namespace *mnt_userns,
int ovl_copy_up(struct dentry *dentry); int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_with_data(struct dentry *dentry); int ovl_copy_up_with_data(struct dentry *dentry);
int ovl_maybe_copy_up(struct dentry *dentry, int flags); int ovl_maybe_copy_up(struct dentry *dentry, int flags);
int ovl_copy_xattr(struct super_block *sb, struct dentry *old, int ovl_copy_xattr(struct super_block *sb, struct path *path, struct dentry *new);
struct dentry *new); int ovl_set_attr(struct ovl_fs *ofs, struct dentry *upper, struct kstat *stat);
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real, struct ovl_fh *ovl_encode_real_fh(struct ovl_fs *ofs, struct dentry *real,
bool is_upper); bool is_upper);
int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower, int ovl_set_origin(struct ovl_fs *ofs, struct dentry *lower,
......
...@@ -90,6 +90,11 @@ static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs) ...@@ -90,6 +90,11 @@ static inline struct vfsmount *ovl_upper_mnt(struct ovl_fs *ofs)
return ofs->layers[0].mnt; return ofs->layers[0].mnt;
} }
static inline struct user_namespace *ovl_upper_mnt_userns(struct ovl_fs *ofs)
{
return mnt_user_ns(ovl_upper_mnt(ofs));
}
static inline struct ovl_fs *OVL_FS(struct super_block *sb) static inline struct ovl_fs *OVL_FS(struct super_block *sb)
{ {
return (struct ovl_fs *)sb->s_fs_info; return (struct ovl_fs *)sb->s_fs_info;
...@@ -129,7 +134,7 @@ struct ovl_inode { ...@@ -129,7 +134,7 @@ struct ovl_inode {
unsigned long flags; unsigned long flags;
struct inode vfs_inode; struct inode vfs_inode;
struct dentry *__upperdentry; struct dentry *__upperdentry;
struct inode *lower; struct ovl_path lowerpath;
/* synchronize copy up and more */ /* synchronize copy up and more */
struct mutex lock; struct mutex lock;
......
...@@ -264,11 +264,11 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name, ...@@ -264,11 +264,11 @@ static int ovl_fill_merge(struct dir_context *ctx, const char *name,
return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type); return ovl_fill_lowest(rdd, name, namelen, offset, ino, d_type);
} }
static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) static int ovl_check_whiteouts(struct path *path, struct ovl_readdir_data *rdd)
{ {
int err; int err;
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
struct dentry *dentry; struct dentry *dentry, *dir = path->dentry;
const struct cred *old_cred; const struct cred *old_cred;
old_cred = ovl_override_creds(rdd->dentry->d_sb); old_cred = ovl_override_creds(rdd->dentry->d_sb);
...@@ -278,7 +278,7 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd) ...@@ -278,7 +278,7 @@ static int ovl_check_whiteouts(struct dentry *dir, struct ovl_readdir_data *rdd)
while (rdd->first_maybe_whiteout) { while (rdd->first_maybe_whiteout) {
p = rdd->first_maybe_whiteout; p = rdd->first_maybe_whiteout;
rdd->first_maybe_whiteout = p->next_maybe_whiteout; rdd->first_maybe_whiteout = p->next_maybe_whiteout;
dentry = lookup_one_len(p->name, dir, p->len); dentry = lookup_one(mnt_user_ns(path->mnt), p->name, dir, p->len);
if (!IS_ERR(dentry)) { if (!IS_ERR(dentry)) {
p->is_whiteout = ovl_is_whiteout(dentry); p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry); dput(dentry);
...@@ -312,7 +312,7 @@ static inline int ovl_dir_read(struct path *realpath, ...@@ -312,7 +312,7 @@ static inline int ovl_dir_read(struct path *realpath,
} while (!err && rdd->count); } while (!err && rdd->count);
if (!err && rdd->first_maybe_whiteout && rdd->dentry) if (!err && rdd->first_maybe_whiteout && rdd->dentry)
err = ovl_check_whiteouts(realpath->dentry, rdd); err = ovl_check_whiteouts(realpath, rdd);
fput(realfile); fput(realfile);
...@@ -479,7 +479,7 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p) ...@@ -479,7 +479,7 @@ static int ovl_cache_update_ino(struct path *path, struct ovl_cache_entry *p)
goto get; goto get;
} }
} }
this = lookup_one_len(p->name, dir, p->len); this = lookup_one(mnt_user_ns(path->mnt), p->name, dir, p->len);
if (IS_ERR_OR_NULL(this) || !this->d_inode) { if (IS_ERR_OR_NULL(this) || !this->d_inode) {
/* Mark a stale entry */ /* Mark a stale entry */
p->is_whiteout = true; p->is_whiteout = true;
...@@ -623,7 +623,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path) ...@@ -623,7 +623,7 @@ static struct ovl_dir_cache *ovl_cache_get_impure(struct path *path)
* Removing the "impure" xattr is best effort. * Removing the "impure" xattr is best effort.
*/ */
if (!ovl_want_write(dentry)) { if (!ovl_want_write(dentry)) {
ovl_do_removexattr(ofs, ovl_dentry_upper(dentry), ovl_removexattr(ofs, ovl_dentry_upper(dentry),
OVL_XATTR_IMPURE); OVL_XATTR_IMPURE);
ovl_drop_write(dentry); ovl_drop_write(dentry);
} }
...@@ -1001,7 +1001,8 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list) ...@@ -1001,7 +1001,8 @@ int ovl_check_empty_dir(struct dentry *dentry, struct list_head *list)
return err; return err;
} }
void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) void ovl_cleanup_whiteouts(struct ovl_fs *ofs, struct dentry *upper,
struct list_head *list)
{ {
struct ovl_cache_entry *p; struct ovl_cache_entry *p;
...@@ -1012,7 +1013,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) ...@@ -1012,7 +1013,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
if (WARN_ON(!p->is_whiteout || !p->is_upper)) if (WARN_ON(!p->is_whiteout || !p->is_upper))
continue; continue;
dentry = lookup_one_len(p->name, upper, p->len); dentry = ovl_lookup_upper(ofs, p->name, upper, p->len);
if (IS_ERR(dentry)) { if (IS_ERR(dentry)) {
pr_err("lookup '%s/%.*s' failed (%i)\n", pr_err("lookup '%s/%.*s' failed (%i)\n",
upper->d_name.name, p->len, p->name, upper->d_name.name, p->len, p->name,
...@@ -1020,7 +1021,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list) ...@@ -1020,7 +1021,7 @@ void ovl_cleanup_whiteouts(struct dentry *upper, struct list_head *list)
continue; continue;
} }
if (dentry->d_inode) if (dentry->d_inode)
ovl_cleanup(upper->d_inode, dentry); ovl_cleanup(ofs, upper->d_inode, dentry);
dput(dentry); dput(dentry);
} }
inode_unlock(upper->d_inode); inode_unlock(upper->d_inode);
...@@ -1064,7 +1065,8 @@ int ovl_check_d_type_supported(struct path *realpath) ...@@ -1064,7 +1065,8 @@ int ovl_check_d_type_supported(struct path *realpath)
#define OVL_INCOMPATDIR_NAME "incompat" #define OVL_INCOMPATDIR_NAME "incompat"
static int ovl_workdir_cleanup_recurse(struct path *path, int level) static int ovl_workdir_cleanup_recurse(struct ovl_fs *ofs, struct path *path,
int level)
{ {
int err; int err;
struct inode *dir = path->dentry->d_inode; struct inode *dir = path->dentry->d_inode;
...@@ -1111,11 +1113,11 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level) ...@@ -1111,11 +1113,11 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level)
err = -EINVAL; err = -EINVAL;
break; break;
} }
dentry = lookup_one_len(p->name, path->dentry, p->len); dentry = ovl_lookup_upper(ofs, p->name, path->dentry, p->len);
if (IS_ERR(dentry)) if (IS_ERR(dentry))
continue; continue;
if (dentry->d_inode) if (dentry->d_inode)
err = ovl_workdir_cleanup(dir, path->mnt, dentry, level); err = ovl_workdir_cleanup(ofs, dir, path->mnt, dentry, level);
dput(dentry); dput(dentry);
if (err) if (err)
break; break;
...@@ -1126,24 +1128,24 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level) ...@@ -1126,24 +1128,24 @@ static int ovl_workdir_cleanup_recurse(struct path *path, int level)
return err; return err;
} }
int ovl_workdir_cleanup(struct inode *dir, struct vfsmount *mnt, int ovl_workdir_cleanup(struct ovl_fs *ofs, struct inode *dir,
struct dentry *dentry, int level) struct vfsmount *mnt, struct dentry *dentry, int level)
{ {
int err; int err;
if (!d_is_dir(dentry) || level > 1) { if (!d_is_dir(dentry) || level > 1) {
return ovl_cleanup(dir, dentry); return ovl_cleanup(ofs, dir, dentry);
} }
err = ovl_do_rmdir(dir, dentry); err = ovl_do_rmdir(ofs, dir, dentry);
if (err) { if (err) {
struct path path = { .mnt = mnt, .dentry = dentry }; struct path path = { .mnt = mnt, .dentry = dentry };
inode_unlock(dir); inode_unlock(dir);
err = ovl_workdir_cleanup_recurse(&path, level + 1); err = ovl_workdir_cleanup_recurse(ofs, &path, level + 1);
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
if (!err) if (!err)
err = ovl_cleanup(dir, dentry); err = ovl_cleanup(ofs, dir, dentry);
} }
return err; return err;
...@@ -1179,7 +1181,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1179,7 +1181,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
if (p->len == 2 && p->name[1] == '.') if (p->len == 2 && p->name[1] == '.')
continue; continue;
} }
index = lookup_one_len(p->name, indexdir, p->len); index = ovl_lookup_upper(ofs, p->name, indexdir, p->len);
if (IS_ERR(index)) { if (IS_ERR(index)) {
err = PTR_ERR(index); err = PTR_ERR(index);
index = NULL; index = NULL;
...@@ -1187,7 +1189,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1187,7 +1189,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
} }
/* Cleanup leftover from index create/cleanup attempt */ /* Cleanup leftover from index create/cleanup attempt */
if (index->d_name.name[0] == '#') { if (index->d_name.name[0] == '#') {
err = ovl_workdir_cleanup(dir, path.mnt, index, 1); err = ovl_workdir_cleanup(ofs, dir, path.mnt, index, 1);
if (err) if (err)
break; break;
goto next; goto next;
...@@ -1197,7 +1199,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1197,7 +1199,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
goto next; goto next;
} else if (err == -ESTALE) { } else if (err == -ESTALE) {
/* Cleanup stale index entries */ /* Cleanup stale index entries */
err = ovl_cleanup(dir, index); err = ovl_cleanup(ofs, dir, index);
} else if (err != -ENOENT) { } else if (err != -ENOENT) {
/* /*
* Abort mount to avoid corrupting the index if * Abort mount to avoid corrupting the index if
...@@ -1213,7 +1215,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs) ...@@ -1213,7 +1215,7 @@ int ovl_indexdir_cleanup(struct ovl_fs *ofs)
err = ovl_cleanup_and_whiteout(ofs, dir, index); err = ovl_cleanup_and_whiteout(ofs, dir, index);
} else { } else {
/* Cleanup orphan index entries */ /* Cleanup orphan index entries */
err = ovl_cleanup(dir, index); err = ovl_cleanup(ofs, dir, index);
} }
if (err) if (err)
......
...@@ -184,7 +184,8 @@ static struct inode *ovl_alloc_inode(struct super_block *sb) ...@@ -184,7 +184,8 @@ static struct inode *ovl_alloc_inode(struct super_block *sb)
oi->version = 0; oi->version = 0;
oi->flags = 0; oi->flags = 0;
oi->__upperdentry = NULL; oi->__upperdentry = NULL;
oi->lower = NULL; oi->lowerpath.dentry = NULL;
oi->lowerpath.layer = NULL;
oi->lowerdata = NULL; oi->lowerdata = NULL;
mutex_init(&oi->lock); mutex_init(&oi->lock);
...@@ -205,7 +206,7 @@ static void ovl_destroy_inode(struct inode *inode) ...@@ -205,7 +206,7 @@ static void ovl_destroy_inode(struct inode *inode)
struct ovl_inode *oi = OVL_I(inode); struct ovl_inode *oi = OVL_I(inode);
dput(oi->__upperdentry); dput(oi->__upperdentry);
iput(oi->lower); dput(oi->lowerpath.dentry);
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
ovl_dir_cache_free(inode); ovl_dir_cache_free(inode);
else else
...@@ -761,7 +762,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, ...@@ -761,7 +762,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
retry: retry:
work = lookup_one_len(name, ofs->workbasedir, strlen(name)); work = ovl_lookup_upper(ofs, name, ofs->workbasedir, strlen(name));
if (!IS_ERR(work)) { if (!IS_ERR(work)) {
struct iattr attr = { struct iattr attr = {
...@@ -778,7 +779,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, ...@@ -778,7 +779,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
goto out_unlock; goto out_unlock;
retried = true; retried = true;
err = ovl_workdir_cleanup(dir, mnt, work, 0); err = ovl_workdir_cleanup(ofs, dir, mnt, work, 0);
dput(work); dput(work);
if (err == -EINVAL) { if (err == -EINVAL) {
work = ERR_PTR(err); work = ERR_PTR(err);
...@@ -787,7 +788,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, ...@@ -787,7 +788,7 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
goto retry; goto retry;
} }
err = ovl_mkdir_real(dir, &work, attr.ia_mode); err = ovl_mkdir_real(ofs, dir, &work, attr.ia_mode);
if (err) if (err)
goto out_dput; goto out_dput;
...@@ -809,19 +810,19 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs, ...@@ -809,19 +810,19 @@ static struct dentry *ovl_workdir_create(struct ovl_fs *ofs,
* allowed as upper are limited to "normal" ones, where checking * allowed as upper are limited to "normal" ones, where checking
* for the above two errors is sufficient. * for the above two errors is sufficient.
*/ */
err = vfs_removexattr(&init_user_ns, work, err = ovl_do_removexattr(ofs, work,
XATTR_NAME_POSIX_ACL_DEFAULT); XATTR_NAME_POSIX_ACL_DEFAULT);
if (err && err != -ENODATA && err != -EOPNOTSUPP) if (err && err != -ENODATA && err != -EOPNOTSUPP)
goto out_dput; goto out_dput;
err = vfs_removexattr(&init_user_ns, work, err = ovl_do_removexattr(ofs, work,
XATTR_NAME_POSIX_ACL_ACCESS); XATTR_NAME_POSIX_ACL_ACCESS);
if (err && err != -ENODATA && err != -EOPNOTSUPP) if (err && err != -ENODATA && err != -EOPNOTSUPP)
goto out_dput; goto out_dput;
/* Clear any inherited mode bits */ /* Clear any inherited mode bits */
inode_lock(work->d_inode); inode_lock(work->d_inode);
err = notify_change(&init_user_ns, work, &attr, NULL); err = ovl_do_notify_change(ofs, work, &attr);
inode_unlock(work->d_inode); inode_unlock(work->d_inode);
if (err) if (err)
goto out_dput; goto out_dput;
...@@ -873,10 +874,6 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path) ...@@ -873,10 +874,6 @@ static int ovl_mount_dir_noesc(const char *name, struct path *path)
pr_err("filesystem on '%s' not supported\n", name); pr_err("filesystem on '%s' not supported\n", name);
goto out_put; goto out_put;
} }
if (is_idmapped_mnt(path->mnt)) {
pr_err("idmapped layers are currently not supported\n");
goto out_put;
}
if (!d_is_dir(path->dentry)) { if (!d_is_dir(path->dentry)) {
pr_err("'%s' not a directory\n", name); pr_err("'%s' not a directory\n", name);
goto out_put; goto out_put;
...@@ -1256,8 +1253,9 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1256,8 +1253,9 @@ static int ovl_get_upper(struct super_block *sb, struct ovl_fs *ofs,
* Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and * Returns 1 if RENAME_WHITEOUT is supported, 0 if not supported and
* negative values if error is encountered. * negative values if error is encountered.
*/ */
static int ovl_check_rename_whiteout(struct dentry *workdir) static int ovl_check_rename_whiteout(struct ovl_fs *ofs)
{ {
struct dentry *workdir = ofs->workdir;
struct inode *dir = d_inode(workdir); struct inode *dir = d_inode(workdir);
struct dentry *temp; struct dentry *temp;
struct dentry *dest; struct dentry *dest;
...@@ -1267,12 +1265,12 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) ...@@ -1267,12 +1265,12 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
temp = ovl_create_temp(workdir, OVL_CATTR(S_IFREG | 0)); temp = ovl_create_temp(ofs, workdir, OVL_CATTR(S_IFREG | 0));
err = PTR_ERR(temp); err = PTR_ERR(temp);
if (IS_ERR(temp)) if (IS_ERR(temp))
goto out_unlock; goto out_unlock;
dest = ovl_lookup_temp(workdir); dest = ovl_lookup_temp(ofs, workdir);
err = PTR_ERR(dest); err = PTR_ERR(dest);
if (IS_ERR(dest)) { if (IS_ERR(dest)) {
dput(temp); dput(temp);
...@@ -1281,14 +1279,14 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) ...@@ -1281,14 +1279,14 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
/* Name is inline and stable - using snapshot as a copy helper */ /* Name is inline and stable - using snapshot as a copy helper */
take_dentry_name_snapshot(&name, temp); take_dentry_name_snapshot(&name, temp);
err = ovl_do_rename(dir, temp, dir, dest, RENAME_WHITEOUT); err = ovl_do_rename(ofs, dir, temp, dir, dest, RENAME_WHITEOUT);
if (err) { if (err) {
if (err == -EINVAL) if (err == -EINVAL)
err = 0; err = 0;
goto cleanup_temp; goto cleanup_temp;
} }
whiteout = lookup_one_len(name.name.name, workdir, name.name.len); whiteout = ovl_lookup_upper(ofs, name.name.name, workdir, name.name.len);
err = PTR_ERR(whiteout); err = PTR_ERR(whiteout);
if (IS_ERR(whiteout)) if (IS_ERR(whiteout))
goto cleanup_temp; goto cleanup_temp;
...@@ -1297,11 +1295,11 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) ...@@ -1297,11 +1295,11 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
/* Best effort cleanup of whiteout and temp file */ /* Best effort cleanup of whiteout and temp file */
if (err) if (err)
ovl_cleanup(dir, whiteout); ovl_cleanup(ofs, dir, whiteout);
dput(whiteout); dput(whiteout);
cleanup_temp: cleanup_temp:
ovl_cleanup(dir, temp); ovl_cleanup(ofs, dir, temp);
release_dentry_name_snapshot(&name); release_dentry_name_snapshot(&name);
dput(temp); dput(temp);
dput(dest); dput(dest);
...@@ -1312,16 +1310,17 @@ static int ovl_check_rename_whiteout(struct dentry *workdir) ...@@ -1312,16 +1310,17 @@ static int ovl_check_rename_whiteout(struct dentry *workdir)
return err; return err;
} }
static struct dentry *ovl_lookup_or_create(struct dentry *parent, static struct dentry *ovl_lookup_or_create(struct ovl_fs *ofs,
struct dentry *parent,
const char *name, umode_t mode) const char *name, umode_t mode)
{ {
size_t len = strlen(name); size_t len = strlen(name);
struct dentry *child; struct dentry *child;
inode_lock_nested(parent->d_inode, I_MUTEX_PARENT); inode_lock_nested(parent->d_inode, I_MUTEX_PARENT);
child = lookup_one_len(name, parent, len); child = ovl_lookup_upper(ofs, name, parent, len);
if (!IS_ERR(child) && !child->d_inode) if (!IS_ERR(child) && !child->d_inode)
child = ovl_create_real(parent->d_inode, child, child = ovl_create_real(ofs, parent->d_inode, child,
OVL_CATTR(mode)); OVL_CATTR(mode));
inode_unlock(parent->d_inode); inode_unlock(parent->d_inode);
dput(parent); dput(parent);
...@@ -1343,7 +1342,7 @@ static int ovl_create_volatile_dirty(struct ovl_fs *ofs) ...@@ -1343,7 +1342,7 @@ static int ovl_create_volatile_dirty(struct ovl_fs *ofs)
const char *const *name = volatile_path; const char *const *name = volatile_path;
for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) { for (ctr = ARRAY_SIZE(volatile_path); ctr; ctr--, name++) {
d = ovl_lookup_or_create(d, *name, ctr > 1 ? S_IFDIR : S_IFREG); d = ovl_lookup_or_create(ofs, d, *name, ctr > 1 ? S_IFDIR : S_IFREG);
if (IS_ERR(d)) if (IS_ERR(d))
return PTR_ERR(d); return PTR_ERR(d);
} }
...@@ -1391,7 +1390,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1391,7 +1390,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
pr_warn("upper fs needs to support d_type.\n"); pr_warn("upper fs needs to support d_type.\n");
/* Check if upper/work fs supports O_TMPFILE */ /* Check if upper/work fs supports O_TMPFILE */
temp = ovl_do_tmpfile(ofs->workdir, S_IFREG | 0); temp = ovl_do_tmpfile(ofs, ofs->workdir, S_IFREG | 0);
ofs->tmpfile = !IS_ERR(temp); ofs->tmpfile = !IS_ERR(temp);
if (ofs->tmpfile) if (ofs->tmpfile)
dput(temp); dput(temp);
...@@ -1400,7 +1399,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1400,7 +1399,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
/* Check if upper/work fs supports RENAME_WHITEOUT */ /* Check if upper/work fs supports RENAME_WHITEOUT */
err = ovl_check_rename_whiteout(ofs->workdir); err = ovl_check_rename_whiteout(ofs);
if (err < 0) if (err < 0)
goto out; goto out;
...@@ -1411,7 +1410,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1411,7 +1410,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
/* /*
* Check if upper/work fs supports (trusted|user).overlay.* xattr * Check if upper/work fs supports (trusted|user).overlay.* xattr
*/ */
err = ovl_do_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1); err = ovl_setxattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE, "0", 1);
if (err) { if (err) {
ofs->noxattr = true; ofs->noxattr = true;
if (ofs->config.index || ofs->config.metacopy) { if (ofs->config.index || ofs->config.metacopy) {
...@@ -1429,7 +1428,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs, ...@@ -1429,7 +1428,7 @@ static int ovl_make_workdir(struct super_block *sb, struct ovl_fs *ofs,
} }
err = 0; err = 0;
} else { } else {
ovl_do_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE); ovl_removexattr(ofs, ofs->workdir, OVL_XATTR_OPAQUE);
} }
/* /*
......
...@@ -194,6 +194,20 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) ...@@ -194,6 +194,20 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
return type; return type;
} }
enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path)
{
enum ovl_path_type type = ovl_path_type(dentry);
WARN_ON_ONCE(d_is_dir(dentry));
if (!OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type))
ovl_path_lowerdata(dentry, path);
else
ovl_path_upper(dentry, path);
return type;
}
struct dentry *ovl_dentry_upper(struct dentry *dentry) struct dentry *ovl_dentry_upper(struct dentry *dentry)
{ {
return ovl_upperdentry_dereference(OVL_I(d_inode(dentry))); return ovl_upperdentry_dereference(OVL_I(d_inode(dentry)));
...@@ -236,6 +250,17 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode) ...@@ -236,6 +250,17 @@ struct dentry *ovl_i_dentry_upper(struct inode *inode)
return ovl_upperdentry_dereference(OVL_I(inode)); return ovl_upperdentry_dereference(OVL_I(inode));
} }
void ovl_i_path_real(struct inode *inode, struct path *path)
{
path->dentry = ovl_i_dentry_upper(inode);
if (!path->dentry) {
path->dentry = OVL_I(inode)->lowerpath.dentry;
path->mnt = OVL_I(inode)->lowerpath.layer->mnt;
} else {
path->mnt = ovl_upper_mnt(OVL_FS(inode->i_sb));
}
}
struct inode *ovl_inode_upper(struct inode *inode) struct inode *ovl_inode_upper(struct inode *inode)
{ {
struct dentry *upperdentry = ovl_i_dentry_upper(inode); struct dentry *upperdentry = ovl_i_dentry_upper(inode);
...@@ -245,7 +270,9 @@ struct inode *ovl_inode_upper(struct inode *inode) ...@@ -245,7 +270,9 @@ struct inode *ovl_inode_upper(struct inode *inode)
struct inode *ovl_inode_lower(struct inode *inode) struct inode *ovl_inode_lower(struct inode *inode)
{ {
return OVL_I(inode)->lower; struct dentry *lowerdentry = OVL_I(inode)->lowerpath.dentry;
return lowerdentry ? d_inode(lowerdentry) : NULL;
} }
struct inode *ovl_inode_real(struct inode *inode) struct inode *ovl_inode_real(struct inode *inode)
...@@ -443,7 +470,7 @@ static void ovl_dir_version_inc(struct dentry *dentry, bool impurity) ...@@ -443,7 +470,7 @@ static void ovl_dir_version_inc(struct dentry *dentry, bool impurity)
void ovl_dir_modified(struct dentry *dentry, bool impurity) void ovl_dir_modified(struct dentry *dentry, bool impurity)
{ {
/* Copy mtime/ctime */ /* Copy mtime/ctime */
ovl_copyattr(d_inode(ovl_dentry_upper(dentry)), d_inode(dentry)); ovl_copyattr(d_inode(dentry));
ovl_dir_version_inc(dentry, impurity); ovl_dir_version_inc(dentry, impurity);
} }
...@@ -466,6 +493,7 @@ bool ovl_is_whiteout(struct dentry *dentry) ...@@ -466,6 +493,7 @@ bool ovl_is_whiteout(struct dentry *dentry)
struct file *ovl_path_open(struct path *path, int flags) struct file *ovl_path_open(struct path *path, int flags)
{ {
struct inode *inode = d_inode(path->dentry); struct inode *inode = d_inode(path->dentry);
struct user_namespace *real_mnt_userns = mnt_user_ns(path->mnt);
int err, acc_mode; int err, acc_mode;
if (flags & ~(O_ACCMODE | O_LARGEFILE)) if (flags & ~(O_ACCMODE | O_LARGEFILE))
...@@ -482,12 +510,12 @@ struct file *ovl_path_open(struct path *path, int flags) ...@@ -482,12 +510,12 @@ struct file *ovl_path_open(struct path *path, int flags)
BUG(); BUG();
} }
err = inode_permission(&init_user_ns, inode, acc_mode | MAY_OPEN); err = inode_permission(real_mnt_userns, inode, acc_mode | MAY_OPEN);
if (err) if (err)
return ERR_PTR(err); return ERR_PTR(err);
/* O_NOATIME is an optimization, don't fail if not permitted */ /* O_NOATIME is an optimization, don't fail if not permitted */
if (inode_owner_or_capable(&init_user_ns, inode)) if (inode_owner_or_capable(real_mnt_userns, inode))
flags |= O_NOATIME; flags |= O_NOATIME;
return dentry_open(path, flags, current_cred()); return dentry_open(path, flags, current_cred());
...@@ -550,11 +578,11 @@ void ovl_copy_up_end(struct dentry *dentry) ...@@ -550,11 +578,11 @@ void ovl_copy_up_end(struct dentry *dentry)
ovl_inode_unlock(d_inode(dentry)); ovl_inode_unlock(d_inode(dentry));
} }
bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry) bool ovl_path_check_origin_xattr(struct ovl_fs *ofs, struct path *path)
{ {
int res; int res;
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_ORIGIN, NULL, 0); res = ovl_path_getxattr(ofs, path, OVL_XATTR_ORIGIN, NULL, 0);
/* Zero size value means "copied up but origin unknown" */ /* Zero size value means "copied up but origin unknown" */
if (res >= 0) if (res >= 0)
...@@ -563,16 +591,16 @@ bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry) ...@@ -563,16 +591,16 @@ bool ovl_check_origin_xattr(struct ovl_fs *ofs, struct dentry *dentry)
return false; return false;
} }
bool ovl_check_dir_xattr(struct super_block *sb, struct dentry *dentry, bool ovl_path_check_dir_xattr(struct ovl_fs *ofs, struct path *path,
enum ovl_xattr ox) enum ovl_xattr ox)
{ {
int res; int res;
char val; char val;
if (!d_is_dir(dentry)) if (!d_is_dir(path->dentry))
return false; return false;
res = ovl_do_getxattr(OVL_FS(sb), dentry, ox, &val, 1); res = ovl_path_getxattr(ofs, path, ox, &val, 1);
if (res == 1 && val == 'y') if (res == 1 && val == 'y')
return true; return true;
...@@ -612,7 +640,7 @@ int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry, ...@@ -612,7 +640,7 @@ int ovl_check_setxattr(struct ovl_fs *ofs, struct dentry *upperdentry,
if (ofs->noxattr) if (ofs->noxattr)
return xerr; return xerr;
err = ovl_do_setxattr(ofs, upperdentry, ox, value, size); err = ovl_setxattr(ofs, upperdentry, ox, value, size);
if (err == -EOPNOTSUPP) { if (err == -EOPNOTSUPP) {
pr_warn("cannot set %s xattr on upper\n", ovl_xattr(ofs, ox)); pr_warn("cannot set %s xattr on upper\n", ovl_xattr(ofs, ox));
...@@ -652,7 +680,7 @@ void ovl_check_protattr(struct inode *inode, struct dentry *upper) ...@@ -652,7 +680,7 @@ void ovl_check_protattr(struct inode *inode, struct dentry *upper)
char buf[OVL_PROTATTR_MAX+1]; char buf[OVL_PROTATTR_MAX+1];
int res, n; int res, n;
res = ovl_do_getxattr(ofs, upper, OVL_XATTR_PROTATTR, buf, res = ovl_getxattr_upper(ofs, upper, OVL_XATTR_PROTATTR, buf,
OVL_PROTATTR_MAX); OVL_PROTATTR_MAX);
if (res < 0) if (res < 0)
return; return;
...@@ -708,7 +736,7 @@ int ovl_set_protattr(struct inode *inode, struct dentry *upper, ...@@ -708,7 +736,7 @@ int ovl_set_protattr(struct inode *inode, struct dentry *upper,
err = ovl_check_setxattr(ofs, upper, OVL_XATTR_PROTATTR, err = ovl_check_setxattr(ofs, upper, OVL_XATTR_PROTATTR,
buf, len, -EPERM); buf, len, -EPERM);
} else if (inode->i_flags & OVL_PROT_I_FLAGS_MASK) { } else if (inode->i_flags & OVL_PROT_I_FLAGS_MASK) {
err = ovl_do_removexattr(ofs, upper, OVL_XATTR_PROTATTR); err = ovl_removexattr(ofs, upper, OVL_XATTR_PROTATTR);
if (err == -EOPNOTSUPP || err == -ENODATA) if (err == -EOPNOTSUPP || err == -ENODATA)
err = 0; err = 0;
} }
...@@ -824,7 +852,7 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -824,7 +852,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
} }
inode_lock_nested(dir, I_MUTEX_PARENT); inode_lock_nested(dir, I_MUTEX_PARENT);
index = lookup_one_len(name.name, indexdir, name.len); index = ovl_lookup_upper(ofs, name.name, indexdir, name.len);
err = PTR_ERR(index); err = PTR_ERR(index);
if (IS_ERR(index)) { if (IS_ERR(index)) {
index = NULL; index = NULL;
...@@ -834,7 +862,7 @@ static void ovl_cleanup_index(struct dentry *dentry) ...@@ -834,7 +862,7 @@ static void ovl_cleanup_index(struct dentry *dentry)
dir, index); dir, index);
} else { } else {
/* Cleanup orphan index entries */ /* Cleanup orphan index entries */
err = ovl_cleanup(dir, index); err = ovl_cleanup(ofs, dir, index);
} }
inode_unlock(dir); inode_unlock(dir);
...@@ -943,15 +971,15 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir) ...@@ -943,15 +971,15 @@ int ovl_lock_rename_workdir(struct dentry *workdir, struct dentry *upperdir)
} }
/* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */ /* err < 0, 0 if no metacopy xattr, 1 if metacopy xattr found */
int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct dentry *dentry) int ovl_check_metacopy_xattr(struct ovl_fs *ofs, struct path *path)
{ {
int res; int res;
/* Only regular files can have metacopy xattr */ /* Only regular files can have metacopy xattr */
if (!S_ISREG(d_inode(dentry)->i_mode)) if (!S_ISREG(d_inode(path->dentry)->i_mode))
return 0; return 0;
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_METACOPY, NULL, 0); res = ovl_path_getxattr(ofs, path, OVL_XATTR_METACOPY, NULL, 0);
if (res < 0) { if (res < 0) {
if (res == -ENODATA || res == -EOPNOTSUPP) if (res == -ENODATA || res == -EOPNOTSUPP)
return 0; return 0;
...@@ -987,13 +1015,12 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry) ...@@ -987,13 +1015,12 @@ bool ovl_is_metacopy_dentry(struct dentry *dentry)
return (oe->numlower > 1); return (oe->numlower > 1);
} }
char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry, char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct path *path, int padding)
int padding)
{ {
int res; int res;
char *s, *next, *buf = NULL; char *s, *next, *buf = NULL;
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, NULL, 0); res = ovl_path_getxattr(ofs, path, OVL_XATTR_REDIRECT, NULL, 0);
if (res == -ENODATA || res == -EOPNOTSUPP) if (res == -ENODATA || res == -EOPNOTSUPP)
return NULL; return NULL;
if (res < 0) if (res < 0)
...@@ -1005,7 +1032,7 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry, ...@@ -1005,7 +1032,7 @@ char *ovl_get_redirect_xattr(struct ovl_fs *ofs, struct dentry *dentry,
if (!buf) if (!buf)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
res = ovl_do_getxattr(ofs, dentry, OVL_XATTR_REDIRECT, buf, res); res = ovl_path_getxattr(ofs, path, OVL_XATTR_REDIRECT, buf, res);
if (res < 0) if (res < 0)
goto fail; goto fail;
if (res == 0) if (res == 0)
...@@ -1060,3 +1087,33 @@ int ovl_sync_status(struct ovl_fs *ofs) ...@@ -1060,3 +1087,33 @@ int ovl_sync_status(struct ovl_fs *ofs)
return errseq_check(&mnt->mnt_sb->s_wb_err, ofs->errseq); return errseq_check(&mnt->mnt_sb->s_wb_err, ofs->errseq);
} }
/*
* ovl_copyattr() - copy inode attributes from layer to ovl inode
*
* When overlay copies inode information from an upper or lower layer to the
* relevant overlay inode it will apply the idmapping of the upper or lower
* layer when doing so ensuring that the ovl inode ownership will correctly
* reflect the ownership of the idmapped upper or lower layer. For example, an
* idmapped upper or lower layer mapping id 1001 to id 1000 will take care to
* map any lower or upper inode owned by id 1001 to id 1000. These mapping
* helpers are nops when the relevant layer isn't idmapped.
*/
void ovl_copyattr(struct inode *inode)
{
struct path realpath;
struct inode *realinode;
struct user_namespace *real_mnt_userns;
ovl_i_path_real(inode, &realpath);
realinode = d_inode(realpath.dentry);
real_mnt_userns = mnt_user_ns(realpath.mnt);
inode->i_uid = i_uid_into_mnt(real_mnt_userns, realinode);
inode->i_gid = i_gid_into_mnt(real_mnt_userns, realinode);
inode->i_mode = realinode->i_mode;
inode->i_atime = realinode->i_atime;
inode->i_mtime = realinode->i_mtime;
inode->i_ctime = realinode->i_ctime;
i_size_write(inode, i_size_read(realinode));
}
...@@ -69,6 +69,12 @@ extern struct dentry *lookup_one_len(const char *, struct dentry *, int); ...@@ -69,6 +69,12 @@ extern struct dentry *lookup_one_len(const char *, struct dentry *, int);
extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_one_len_unlocked(const char *, struct dentry *, int);
extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int); extern struct dentry *lookup_positive_unlocked(const char *, struct dentry *, int);
struct dentry *lookup_one(struct user_namespace *, const char *, struct dentry *, int); struct dentry *lookup_one(struct user_namespace *, const char *, struct dentry *, int);
struct dentry *lookup_one_unlocked(struct user_namespace *mnt_userns,
const char *name, struct dentry *base,
int len);
struct dentry *lookup_one_positive_unlocked(struct user_namespace *mnt_userns,
const char *name,
struct dentry *base, int len);
extern int follow_down_one(struct path *); extern int follow_down_one(struct path *);
extern int follow_down(struct path *); extern int follow_down(struct path *);
......
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