You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
Pull overlayfs updates from Miklos Szeredi: "This update contains: - try to clone on copy-up - allow renaming a directory - split source into managable chunks - misc cleanups and fixes It does not contain the read-only fd data inconsistency fix, which Al didn't like. I'll leave that to the next year..." * 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs: (36 commits) ovl: fix reStructuredText syntax errors in documentation ovl: fix return value of ovl_fill_super ovl: clean up kstat usage ovl: fold ovl_copy_up_truncate() into ovl_copy_up() ovl: create directories inside merged parent opaque ovl: opaque cleanup ovl: show redirect_dir mount option ovl: allow setting max size of redirect ovl: allow redirect_dir to default to "on" ovl: check for emptiness of redirect dir ovl: redirect on rename-dir ovl: lookup redirects ovl: consolidate lookup for underlying layers ovl: fix nested overlayfs mount ovl: check namelen ovl: split super.c ovl: use d_is_dir() ovl: simplify lookup ovl: check lower existence of rename target ovl: rename: simplify handling of lower/merged directory ...
This commit is contained in:
+5
-1
@@ -223,7 +223,11 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
|
||||
|
||||
if (!src_file.file)
|
||||
return -EBADF;
|
||||
ret = vfs_clone_file_range(src_file.file, off, dst_file, destoff, olen);
|
||||
ret = -EXDEV;
|
||||
if (src_file.file->f_path.mnt != dst_file->f_path.mnt)
|
||||
goto fdput;
|
||||
ret = do_clone_file_range(src_file.file, off, dst_file, destoff, olen);
|
||||
fdput:
|
||||
fdput(src_file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
+1
-5
@@ -4306,11 +4306,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
bool new_is_dir = false;
|
||||
unsigned max_links = new_dir->i_sb->s_max_links;
|
||||
|
||||
/*
|
||||
* Check source == target.
|
||||
* On overlayfs need to look at underlying inodes.
|
||||
*/
|
||||
if (d_real_inode(old_dentry) == d_real_inode(new_dentry))
|
||||
if (source == target)
|
||||
return 0;
|
||||
|
||||
error = may_delete(old_dir, old_dentry, is_dir);
|
||||
|
||||
+1
-2
@@ -509,8 +509,7 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp,
|
||||
__be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
|
||||
u64 dst_pos, u64 count)
|
||||
{
|
||||
return nfserrno(vfs_clone_file_range(src, src_pos, dst, dst_pos,
|
||||
count));
|
||||
return nfserrno(do_clone_file_range(src, src_pos, dst, dst_pos, count));
|
||||
}
|
||||
|
||||
ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
|
||||
|
||||
@@ -8,3 +8,17 @@ config OVERLAY_FS
|
||||
merged with the 'upper' object.
|
||||
|
||||
For more information see Documentation/filesystems/overlayfs.txt
|
||||
|
||||
config OVERLAY_FS_REDIRECT_DIR
|
||||
bool "Overlayfs: turn on redirect dir feature by default"
|
||||
depends on OVERLAY_FS
|
||||
help
|
||||
If this config option is enabled then overlay filesystems will use
|
||||
redirects when renaming directories by default. In this case it is
|
||||
still possible to turn off redirects globally with the
|
||||
"redirect_dir=off" module option or on a filesystem instance basis
|
||||
with the "redirect_dir=off" mount option.
|
||||
|
||||
Note, that redirects are not backward compatible. That is, mounting
|
||||
an overlay which has redirects on a kernel that doesn't support this
|
||||
feature will have unexpected results.
|
||||
|
||||
@@ -4,4 +4,4 @@
|
||||
|
||||
obj-$(CONFIG_OVERLAY_FS) += overlay.o
|
||||
|
||||
overlay-objs := super.o inode.o dir.o readdir.o copy_up.o
|
||||
overlay-objs := super.o namei.o util.o inode.o dir.o readdir.o copy_up.o
|
||||
|
||||
+32
-29
@@ -153,6 +153,13 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
|
||||
goto out_fput;
|
||||
}
|
||||
|
||||
/* Try to use clone_file_range to clone up within the same fs */
|
||||
error = vfs_clone_file_range(old_file, 0, new_file, 0, len);
|
||||
if (!error)
|
||||
goto out;
|
||||
/* Couldn't clone, so now we try to copy the data */
|
||||
error = 0;
|
||||
|
||||
/* FIXME: copy up sparse files efficiently */
|
||||
while (len) {
|
||||
size_t this_len = OVL_COPY_UP_CHUNK_SIZE;
|
||||
@@ -177,7 +184,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
|
||||
|
||||
len -= bytes;
|
||||
}
|
||||
|
||||
out:
|
||||
if (!error)
|
||||
error = vfs_fsync(new_file, 0);
|
||||
fput(new_file);
|
||||
@@ -231,10 +238,15 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
|
||||
struct inode *udir = upperdir->d_inode;
|
||||
struct dentry *newdentry = NULL;
|
||||
struct dentry *upper = NULL;
|
||||
umode_t mode = stat->mode;
|
||||
int err;
|
||||
const struct cred *old_creds = NULL;
|
||||
struct cred *new_creds = NULL;
|
||||
struct cattr cattr = {
|
||||
/* Can't properly set mode on creation because of the umask */
|
||||
.mode = stat->mode & S_IFMT,
|
||||
.rdev = stat->rdev,
|
||||
.link = link
|
||||
};
|
||||
|
||||
newdentry = ovl_lookup_temp(workdir, dentry);
|
||||
err = PTR_ERR(newdentry);
|
||||
@@ -254,10 +266,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
|
||||
if (new_creds)
|
||||
old_creds = override_creds(new_creds);
|
||||
|
||||
/* Can't properly set mode on creation because of the umask */
|
||||
stat->mode &= S_IFMT;
|
||||
err = ovl_create_real(wdir, newdentry, stat, link, NULL, true);
|
||||
stat->mode = mode;
|
||||
err = ovl_create_real(wdir, newdentry, &cattr, NULL, true);
|
||||
|
||||
if (new_creds) {
|
||||
revert_creds(old_creds);
|
||||
@@ -296,12 +305,6 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
|
||||
ovl_dentry_update(dentry, newdentry);
|
||||
ovl_inode_update(d_inode(dentry), d_inode(newdentry));
|
||||
newdentry = NULL;
|
||||
|
||||
/*
|
||||
* Non-directores become opaque when copied up.
|
||||
*/
|
||||
if (!S_ISDIR(stat->mode))
|
||||
ovl_dentry_set_opaque(dentry, true);
|
||||
out2:
|
||||
dput(upper);
|
||||
out1:
|
||||
@@ -317,20 +320,14 @@ out_cleanup:
|
||||
/*
|
||||
* Copy up a single dentry
|
||||
*
|
||||
* Directory renames only allowed on "pure upper" (already created on
|
||||
* upper filesystem, never copied up). Directories which are on lower or
|
||||
* are merged may not be renamed. For these -EXDEV is returned and
|
||||
* userspace has to deal with it. This means, when copying up a
|
||||
* directory we can rely on it and ancestors being stable.
|
||||
*
|
||||
* Non-directory renames start with copy up of source if necessary. The
|
||||
* actual rename will only proceed once the copy up was successful. Copy
|
||||
* up uses upper parent i_mutex for exclusion. Since rename can change
|
||||
* d_parent it is possible that the copy up will lock the old parent. At
|
||||
* that point the file will have already been copied up anyway.
|
||||
* All renames start with copy up of source if necessary. The actual
|
||||
* rename will only proceed once the copy up was successful. Copy up uses
|
||||
* upper parent i_mutex for exclusion. Since rename can change d_parent it
|
||||
* is possible that the copy up will lock the old parent. At that point
|
||||
* the file will have already been copied up anyway.
|
||||
*/
|
||||
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
struct path *lowerpath, struct kstat *stat)
|
||||
static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
struct path *lowerpath, struct kstat *stat)
|
||||
{
|
||||
DEFINE_DELAYED_CALL(done);
|
||||
struct dentry *workdir = ovl_workdir(dentry);
|
||||
@@ -339,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
struct path parentpath;
|
||||
struct dentry *lowerdentry = lowerpath->dentry;
|
||||
struct dentry *upperdir;
|
||||
struct dentry *upperdentry;
|
||||
const char *link = NULL;
|
||||
|
||||
if (WARN_ON(!workdir))
|
||||
@@ -365,8 +361,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
pr_err("overlayfs: failed to lock workdir+upperdir\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
upperdentry = ovl_dentry_upper(dentry);
|
||||
if (upperdentry) {
|
||||
if (ovl_dentry_upper(dentry)) {
|
||||
/* Raced with another copy-up? Nothing to do, then... */
|
||||
err = 0;
|
||||
goto out_unlock;
|
||||
@@ -385,7 +380,7 @@ out_unlock:
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_copy_up(struct dentry *dentry)
|
||||
int ovl_copy_up_flags(struct dentry *dentry, int flags)
|
||||
{
|
||||
int err = 0;
|
||||
const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
|
||||
@@ -415,6 +410,9 @@ int ovl_copy_up(struct dentry *dentry)
|
||||
|
||||
ovl_path_lower(next, &lowerpath);
|
||||
err = vfs_getattr(&lowerpath, &stat);
|
||||
/* maybe truncate regular file. this has no effect on dirs */
|
||||
if (flags & O_TRUNC)
|
||||
stat.size = 0;
|
||||
if (!err)
|
||||
err = ovl_copy_up_one(parent, next, &lowerpath, &stat);
|
||||
|
||||
@@ -425,3 +423,8 @@ int ovl_copy_up(struct dentry *dentry)
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_copy_up(struct dentry *dentry)
|
||||
{
|
||||
return ovl_copy_up_flags(dentry, 0);
|
||||
}
|
||||
|
||||
+219
-166
File diff suppressed because it is too large
Load Diff
+11
-67
@@ -13,34 +13,6 @@
|
||||
#include <linux/posix_acl.h>
|
||||
#include "overlayfs.h"
|
||||
|
||||
static int ovl_copy_up_truncate(struct dentry *dentry)
|
||||
{
|
||||
int err;
|
||||
struct dentry *parent;
|
||||
struct kstat stat;
|
||||
struct path lowerpath;
|
||||
const struct cred *old_cred;
|
||||
|
||||
parent = dget_parent(dentry);
|
||||
err = ovl_copy_up(parent);
|
||||
if (err)
|
||||
goto out_dput_parent;
|
||||
|
||||
ovl_path_lower(dentry, &lowerpath);
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
err = vfs_getattr(&lowerpath, &stat);
|
||||
if (!err) {
|
||||
stat.size = 0;
|
||||
err = ovl_copy_up_one(parent, dentry, &lowerpath, &stat);
|
||||
}
|
||||
revert_creds(old_cred);
|
||||
|
||||
out_dput_parent:
|
||||
dput(parent);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ovl_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
{
|
||||
int err;
|
||||
@@ -64,27 +36,10 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
struct inode *realinode = d_inode(ovl_dentry_real(dentry));
|
||||
|
||||
err = -ETXTBSY;
|
||||
if (atomic_read(&realinode->i_writecount) < 0)
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
err = ovl_copy_up(dentry);
|
||||
if (!err) {
|
||||
struct inode *winode = NULL;
|
||||
|
||||
upperdentry = ovl_dentry_upper(dentry);
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
winode = d_inode(upperdentry);
|
||||
err = get_write_access(winode);
|
||||
if (err)
|
||||
goto out_drop_write;
|
||||
}
|
||||
|
||||
if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
|
||||
attr->ia_valid &= ~ATTR_MODE;
|
||||
|
||||
@@ -95,11 +50,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
if (!err)
|
||||
ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
|
||||
inode_unlock(upperdentry->d_inode);
|
||||
|
||||
if (winode)
|
||||
put_write_access(winode);
|
||||
}
|
||||
out_drop_write:
|
||||
ovl_drop_write(dentry);
|
||||
out:
|
||||
return err;
|
||||
@@ -302,10 +253,7 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
|
||||
if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
|
||||
err = ovl_want_write(dentry);
|
||||
if (!err) {
|
||||
if (file_flags & O_TRUNC)
|
||||
err = ovl_copy_up_truncate(dentry);
|
||||
else
|
||||
err = ovl_copy_up(dentry);
|
||||
err = ovl_copy_up_flags(dentry, file_flags);
|
||||
ovl_drop_write(dentry);
|
||||
}
|
||||
}
|
||||
@@ -354,7 +302,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
|
||||
.update_time = ovl_update_time,
|
||||
};
|
||||
|
||||
static void ovl_fill_inode(struct inode *inode, umode_t mode)
|
||||
static void ovl_fill_inode(struct inode *inode, umode_t mode, dev_t rdev)
|
||||
{
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_mode = mode;
|
||||
@@ -363,8 +311,11 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode)
|
||||
inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
|
||||
#endif
|
||||
|
||||
mode &= S_IFMT;
|
||||
switch (mode) {
|
||||
switch (mode & S_IFMT) {
|
||||
case S_IFREG:
|
||||
inode->i_op = &ovl_file_inode_operations;
|
||||
break;
|
||||
|
||||
case S_IFDIR:
|
||||
inode->i_op = &ovl_dir_inode_operations;
|
||||
inode->i_fop = &ovl_dir_operations;
|
||||
@@ -375,26 +326,19 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode)
|
||||
break;
|
||||
|
||||
default:
|
||||
WARN(1, "illegal file type: %i\n", mode);
|
||||
/* Fall through */
|
||||
|
||||
case S_IFREG:
|
||||
case S_IFSOCK:
|
||||
case S_IFBLK:
|
||||
case S_IFCHR:
|
||||
case S_IFIFO:
|
||||
inode->i_op = &ovl_file_inode_operations;
|
||||
init_special_inode(inode, mode, rdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode)
|
||||
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev)
|
||||
{
|
||||
struct inode *inode;
|
||||
|
||||
inode = new_inode(sb);
|
||||
if (inode)
|
||||
ovl_fill_inode(inode, mode);
|
||||
ovl_fill_inode(inode, mode, rdev);
|
||||
|
||||
return inode;
|
||||
}
|
||||
@@ -418,7 +362,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode)
|
||||
inode = iget5_locked(sb, (unsigned long) realinode,
|
||||
ovl_inode_test, ovl_inode_set, realinode);
|
||||
if (inode && inode->i_state & I_NEW) {
|
||||
ovl_fill_inode(inode, realinode->i_mode);
|
||||
ovl_fill_inode(inode, realinode->i_mode, realinode->i_rdev);
|
||||
set_nlink(inode, realinode->i_nlink);
|
||||
unlock_new_inode(inode);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Novell Inc.
|
||||
* Copyright (C) 2016 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/ratelimit.h>
|
||||
#include "overlayfs.h"
|
||||
#include "ovl_entry.h"
|
||||
|
||||
struct ovl_lookup_data {
|
||||
struct qstr name;
|
||||
bool is_dir;
|
||||
bool opaque;
|
||||
bool stop;
|
||||
bool last;
|
||||
char *redirect;
|
||||
};
|
||||
|
||||
static int ovl_check_redirect(struct dentry *dentry, struct ovl_lookup_data *d,
|
||||
size_t prelen, const char *post)
|
||||
{
|
||||
int res;
|
||||
char *s, *next, *buf = NULL;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, NULL, 0);
|
||||
if (res < 0) {
|
||||
if (res == -ENODATA || res == -EOPNOTSUPP)
|
||||
return 0;
|
||||
goto fail;
|
||||
}
|
||||
buf = kzalloc(prelen + res + strlen(post) + 1, GFP_TEMPORARY);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (res == 0)
|
||||
goto invalid;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_REDIRECT, buf, res);
|
||||
if (res < 0)
|
||||
goto fail;
|
||||
if (res == 0)
|
||||
goto invalid;
|
||||
if (buf[0] == '/') {
|
||||
for (s = buf; *s++ == '/'; s = next) {
|
||||
next = strchrnul(s, '/');
|
||||
if (s == next)
|
||||
goto invalid;
|
||||
}
|
||||
} else {
|
||||
if (strchr(buf, '/') != NULL)
|
||||
goto invalid;
|
||||
|
||||
memmove(buf + prelen, buf, res);
|
||||
memcpy(buf, d->name.name, prelen);
|
||||
}
|
||||
|
||||
strcat(buf, post);
|
||||
kfree(d->redirect);
|
||||
d->redirect = buf;
|
||||
d->name.name = d->redirect;
|
||||
d->name.len = strlen(d->redirect);
|
||||
|
||||
return 0;
|
||||
|
||||
err_free:
|
||||
kfree(buf);
|
||||
return 0;
|
||||
fail:
|
||||
pr_warn_ratelimited("overlayfs: failed to get redirect (%i)\n", res);
|
||||
goto err_free;
|
||||
invalid:
|
||||
pr_warn_ratelimited("overlayfs: invalid redirect (%s)\n", buf);
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
static bool ovl_is_opaquedir(struct dentry *dentry)
|
||||
{
|
||||
int res;
|
||||
char val;
|
||||
|
||||
if (!d_is_dir(dentry))
|
||||
return false;
|
||||
|
||||
res = vfs_getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
|
||||
if (res == 1 && val == 'y')
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int ovl_lookup_single(struct dentry *base, struct ovl_lookup_data *d,
|
||||
const char *name, unsigned int namelen,
|
||||
size_t prelen, const char *post,
|
||||
struct dentry **ret)
|
||||
{
|
||||
struct dentry *this;
|
||||
int err;
|
||||
|
||||
this = lookup_one_len_unlocked(name, base, namelen);
|
||||
if (IS_ERR(this)) {
|
||||
err = PTR_ERR(this);
|
||||
this = NULL;
|
||||
if (err == -ENOENT || err == -ENAMETOOLONG)
|
||||
goto out;
|
||||
goto out_err;
|
||||
}
|
||||
if (!this->d_inode)
|
||||
goto put_and_out;
|
||||
|
||||
if (ovl_dentry_weird(this)) {
|
||||
/* Don't support traversing automounts and other weirdness */
|
||||
err = -EREMOTE;
|
||||
goto out_err;
|
||||
}
|
||||
if (ovl_is_whiteout(this)) {
|
||||
d->stop = d->opaque = true;
|
||||
goto put_and_out;
|
||||
}
|
||||
if (!d_can_lookup(this)) {
|
||||
d->stop = true;
|
||||
if (d->is_dir)
|
||||
goto put_and_out;
|
||||
goto out;
|
||||
}
|
||||
d->is_dir = true;
|
||||
if (!d->last && ovl_is_opaquedir(this)) {
|
||||
d->stop = d->opaque = true;
|
||||
goto out;
|
||||
}
|
||||
err = ovl_check_redirect(this, d, prelen, post);
|
||||
if (err)
|
||||
goto out_err;
|
||||
out:
|
||||
*ret = this;
|
||||
return 0;
|
||||
|
||||
put_and_out:
|
||||
dput(this);
|
||||
this = NULL;
|
||||
goto out;
|
||||
|
||||
out_err:
|
||||
dput(this);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int ovl_lookup_layer(struct dentry *base, struct ovl_lookup_data *d,
|
||||
struct dentry **ret)
|
||||
{
|
||||
const char *s = d->name.name;
|
||||
struct dentry *dentry = NULL;
|
||||
int err;
|
||||
|
||||
if (*s != '/')
|
||||
return ovl_lookup_single(base, d, d->name.name, d->name.len,
|
||||
0, "", ret);
|
||||
|
||||
while (*s++ == '/' && !IS_ERR_OR_NULL(base) && d_can_lookup(base)) {
|
||||
const char *next = strchrnul(s, '/');
|
||||
size_t slen = strlen(s);
|
||||
|
||||
if (WARN_ON(slen > d->name.len) ||
|
||||
WARN_ON(strcmp(d->name.name + d->name.len - slen, s)))
|
||||
return -EIO;
|
||||
|
||||
err = ovl_lookup_single(base, d, s, next - s,
|
||||
d->name.len - slen, next, &base);
|
||||
dput(dentry);
|
||||
if (err)
|
||||
return err;
|
||||
dentry = base;
|
||||
s = next;
|
||||
}
|
||||
*ret = dentry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns next layer in stack starting from top.
|
||||
* Returns -1 if this is the last layer.
|
||||
*/
|
||||
int ovl_path_next(int idx, struct dentry *dentry, struct path *path)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
BUG_ON(idx < 0);
|
||||
if (idx == 0) {
|
||||
ovl_path_upper(dentry, path);
|
||||
if (path->dentry)
|
||||
return oe->numlower ? 1 : -1;
|
||||
idx++;
|
||||
}
|
||||
BUG_ON(idx > oe->numlower);
|
||||
*path = oe->lowerstack[idx - 1];
|
||||
|
||||
return (idx < oe->numlower) ? idx + 1 : -1;
|
||||
}
|
||||
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct ovl_entry *oe;
|
||||
const struct cred *old_cred;
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
struct ovl_entry *poe = dentry->d_parent->d_fsdata;
|
||||
struct path *stack = NULL;
|
||||
struct dentry *upperdir, *upperdentry = NULL;
|
||||
unsigned int ctr = 0;
|
||||
struct inode *inode = NULL;
|
||||
bool upperopaque = false;
|
||||
char *upperredirect = NULL;
|
||||
struct dentry *this;
|
||||
unsigned int i;
|
||||
int err;
|
||||
struct ovl_lookup_data d = {
|
||||
.name = dentry->d_name,
|
||||
.is_dir = false,
|
||||
.opaque = false,
|
||||
.stop = false,
|
||||
.last = !poe->numlower,
|
||||
.redirect = NULL,
|
||||
};
|
||||
|
||||
if (dentry->d_name.len > ofs->namelen)
|
||||
return ERR_PTR(-ENAMETOOLONG);
|
||||
|
||||
old_cred = ovl_override_creds(dentry->d_sb);
|
||||
upperdir = ovl_upperdentry_dereference(poe);
|
||||
if (upperdir) {
|
||||
err = ovl_lookup_layer(upperdir, &d, &upperdentry);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
if (upperdentry && unlikely(ovl_dentry_remote(upperdentry))) {
|
||||
dput(upperdentry);
|
||||
err = -EREMOTE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (d.redirect) {
|
||||
upperredirect = kstrdup(d.redirect, GFP_KERNEL);
|
||||
if (!upperredirect)
|
||||
goto out_put_upper;
|
||||
if (d.redirect[0] == '/')
|
||||
poe = dentry->d_sb->s_root->d_fsdata;
|
||||
}
|
||||
upperopaque = d.opaque;
|
||||
}
|
||||
|
||||
if (!d.stop && poe->numlower) {
|
||||
err = -ENOMEM;
|
||||
stack = kcalloc(ofs->numlower, sizeof(struct path),
|
||||
GFP_TEMPORARY);
|
||||
if (!stack)
|
||||
goto out_put_upper;
|
||||
}
|
||||
|
||||
for (i = 0; !d.stop && i < poe->numlower; i++) {
|
||||
struct path lowerpath = poe->lowerstack[i];
|
||||
|
||||
d.last = i == poe->numlower - 1;
|
||||
err = ovl_lookup_layer(lowerpath.dentry, &d, &this);
|
||||
if (err)
|
||||
goto out_put;
|
||||
|
||||
if (!this)
|
||||
continue;
|
||||
|
||||
stack[ctr].dentry = this;
|
||||
stack[ctr].mnt = lowerpath.mnt;
|
||||
ctr++;
|
||||
|
||||
if (d.stop)
|
||||
break;
|
||||
|
||||
if (d.redirect &&
|
||||
d.redirect[0] == '/' &&
|
||||
poe != dentry->d_sb->s_root->d_fsdata) {
|
||||
poe = dentry->d_sb->s_root->d_fsdata;
|
||||
|
||||
/* Find the current layer on the root dentry */
|
||||
for (i = 0; i < poe->numlower; i++)
|
||||
if (poe->lowerstack[i].mnt == lowerpath.mnt)
|
||||
break;
|
||||
if (WARN_ON(i == poe->numlower))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
oe = ovl_alloc_entry(ctr);
|
||||
err = -ENOMEM;
|
||||
if (!oe)
|
||||
goto out_put;
|
||||
|
||||
if (upperdentry || ctr) {
|
||||
struct dentry *realdentry;
|
||||
struct inode *realinode;
|
||||
|
||||
realdentry = upperdentry ? upperdentry : stack[0].dentry;
|
||||
realinode = d_inode(realdentry);
|
||||
|
||||
err = -ENOMEM;
|
||||
if (upperdentry && !d_is_dir(upperdentry)) {
|
||||
inode = ovl_get_inode(dentry->d_sb, realinode);
|
||||
} else {
|
||||
inode = ovl_new_inode(dentry->d_sb, realinode->i_mode,
|
||||
realinode->i_rdev);
|
||||
if (inode)
|
||||
ovl_inode_init(inode, realinode, !!upperdentry);
|
||||
}
|
||||
if (!inode)
|
||||
goto out_free_oe;
|
||||
ovl_copyattr(realdentry->d_inode, inode);
|
||||
}
|
||||
|
||||
revert_creds(old_cred);
|
||||
oe->opaque = upperopaque;
|
||||
oe->redirect = upperredirect;
|
||||
oe->__upperdentry = upperdentry;
|
||||
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
|
||||
kfree(stack);
|
||||
kfree(d.redirect);
|
||||
dentry->d_fsdata = oe;
|
||||
d_add(dentry, inode);
|
||||
|
||||
return NULL;
|
||||
|
||||
out_free_oe:
|
||||
kfree(oe);
|
||||
out_put:
|
||||
for (i = 0; i < ctr; i++)
|
||||
dput(stack[i].dentry);
|
||||
kfree(stack);
|
||||
out_put_upper:
|
||||
dput(upperdentry);
|
||||
kfree(upperredirect);
|
||||
out:
|
||||
kfree(d.redirect);
|
||||
revert_creds(old_cred);
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
bool ovl_lower_positive(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
struct ovl_entry *poe = dentry->d_parent->d_fsdata;
|
||||
const struct qstr *name = &dentry->d_name;
|
||||
unsigned int i;
|
||||
bool positive = false;
|
||||
bool done = false;
|
||||
|
||||
/*
|
||||
* If dentry is negative, then lower is positive iff this is a
|
||||
* whiteout.
|
||||
*/
|
||||
if (!dentry->d_inode)
|
||||
return oe->opaque;
|
||||
|
||||
/* Negative upper -> positive lower */
|
||||
if (!oe->__upperdentry)
|
||||
return true;
|
||||
|
||||
/* Positive upper -> have to look up lower to see whether it exists */
|
||||
for (i = 0; !done && !positive && i < poe->numlower; i++) {
|
||||
struct dentry *this;
|
||||
struct dentry *lowerdir = poe->lowerstack[i].dentry;
|
||||
|
||||
this = lookup_one_len_unlocked(name->name, lowerdir,
|
||||
name->len);
|
||||
if (IS_ERR(this)) {
|
||||
switch (PTR_ERR(this)) {
|
||||
case -ENOENT:
|
||||
case -ENAMETOOLONG:
|
||||
break;
|
||||
|
||||
default:
|
||||
/*
|
||||
* Assume something is there, we just couldn't
|
||||
* access it.
|
||||
*/
|
||||
positive = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (this->d_inode) {
|
||||
positive = !ovl_is_whiteout(this);
|
||||
done = true;
|
||||
}
|
||||
dput(this);
|
||||
}
|
||||
}
|
||||
|
||||
return positive;
|
||||
}
|
||||
+34
-28
@@ -9,23 +9,17 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
struct ovl_entry;
|
||||
|
||||
enum ovl_path_type {
|
||||
__OVL_PATH_PURE = (1 << 0),
|
||||
__OVL_PATH_UPPER = (1 << 1),
|
||||
__OVL_PATH_MERGE = (1 << 2),
|
||||
__OVL_PATH_UPPER = (1 << 0),
|
||||
__OVL_PATH_MERGE = (1 << 1),
|
||||
};
|
||||
|
||||
#define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER)
|
||||
#define OVL_TYPE_MERGE(type) ((type) & __OVL_PATH_MERGE)
|
||||
#define OVL_TYPE_PURE_UPPER(type) ((type) & __OVL_PATH_PURE)
|
||||
#define OVL_TYPE_MERGE_OR_LOWER(type) \
|
||||
(OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
|
||||
|
||||
|
||||
#define OVL_XATTR_PREFIX XATTR_TRUSTED_PREFIX "overlay."
|
||||
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
|
||||
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
|
||||
|
||||
#define OVL_ISUPPER_MASK 1UL
|
||||
|
||||
@@ -143,35 +137,43 @@ static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
|
||||
return (struct inode *) (x & ~OVL_ISUPPER_MASK);
|
||||
}
|
||||
|
||||
/* util.c */
|
||||
int ovl_want_write(struct dentry *dentry);
|
||||
void ovl_drop_write(struct dentry *dentry);
|
||||
struct dentry *ovl_workdir(struct dentry *dentry);
|
||||
const struct cred *ovl_override_creds(struct super_block *sb);
|
||||
struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
|
||||
bool ovl_dentry_remote(struct dentry *dentry);
|
||||
bool ovl_dentry_weird(struct dentry *dentry);
|
||||
enum ovl_path_type ovl_path_type(struct dentry *dentry);
|
||||
u64 ovl_dentry_version_get(struct dentry *dentry);
|
||||
void ovl_dentry_version_inc(struct dentry *dentry);
|
||||
void ovl_path_upper(struct dentry *dentry, struct path *path);
|
||||
void ovl_path_lower(struct dentry *dentry, struct path *path);
|
||||
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
|
||||
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
|
||||
struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_real(struct dentry *dentry);
|
||||
struct vfsmount *ovl_entry_mnt_real(struct ovl_entry *oe, struct inode *inode,
|
||||
bool is_upper);
|
||||
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
|
||||
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache);
|
||||
struct dentry *ovl_workdir(struct dentry *dentry);
|
||||
int ovl_want_write(struct dentry *dentry);
|
||||
void ovl_drop_write(struct dentry *dentry);
|
||||
bool ovl_dentry_is_opaque(struct dentry *dentry);
|
||||
void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque);
|
||||
bool ovl_is_whiteout(struct dentry *dentry);
|
||||
const struct cred *ovl_override_creds(struct super_block *sb);
|
||||
bool ovl_dentry_is_whiteout(struct dentry *dentry);
|
||||
void ovl_dentry_set_opaque(struct dentry *dentry);
|
||||
bool ovl_redirect_dir(struct super_block *sb);
|
||||
void ovl_clear_redirect_dir(struct super_block *sb);
|
||||
const char *ovl_dentry_get_redirect(struct dentry *dentry);
|
||||
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect);
|
||||
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry);
|
||||
void ovl_inode_init(struct inode *inode, struct inode *realinode,
|
||||
bool is_upper);
|
||||
void ovl_inode_update(struct inode *inode, struct inode *upperinode);
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry,
|
||||
unsigned int flags);
|
||||
void ovl_dentry_version_inc(struct dentry *dentry);
|
||||
u64 ovl_dentry_version_get(struct dentry *dentry);
|
||||
bool ovl_is_whiteout(struct dentry *dentry);
|
||||
struct file *ovl_path_open(struct path *path, int flags);
|
||||
|
||||
struct dentry *ovl_upper_create(struct dentry *upperdir, struct dentry *dentry,
|
||||
struct kstat *stat, const char *link);
|
||||
/* namei.c */
|
||||
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
|
||||
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags);
|
||||
bool ovl_lower_positive(struct dentry *dentry);
|
||||
|
||||
/* readdir.c */
|
||||
extern const struct file_operations ovl_dir_operations;
|
||||
@@ -195,7 +197,7 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
|
||||
int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
|
||||
bool ovl_is_private_xattr(const char *name);
|
||||
|
||||
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode);
|
||||
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, dev_t rdev);
|
||||
struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode);
|
||||
static inline void ovl_copyattr(struct inode *from, struct inode *to)
|
||||
{
|
||||
@@ -210,14 +212,18 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
|
||||
/* dir.c */
|
||||
extern const struct inode_operations ovl_dir_inode_operations;
|
||||
struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry);
|
||||
struct cattr {
|
||||
dev_t rdev;
|
||||
umode_t mode;
|
||||
const char *link;
|
||||
};
|
||||
int ovl_create_real(struct inode *dir, struct dentry *newdentry,
|
||||
struct kstat *stat, const char *link,
|
||||
struct cattr *attr,
|
||||
struct dentry *hardlink, bool debug);
|
||||
void ovl_cleanup(struct inode *dir, struct dentry *dentry);
|
||||
|
||||
/* copy_up.c */
|
||||
int ovl_copy_up(struct dentry *dentry);
|
||||
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
|
||||
struct path *lowerpath, struct kstat *stat);
|
||||
int ovl_copy_up_flags(struct dentry *dentry, int flags);
|
||||
int ovl_copy_xattr(struct dentry *old, struct dentry *new);
|
||||
int ovl_set_attr(struct dentry *upper, struct kstat *stat);
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2011 Novell Inc.
|
||||
* Copyright (C) 2016 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
struct ovl_config {
|
||||
char *lowerdir;
|
||||
char *upperdir;
|
||||
char *workdir;
|
||||
bool default_permissions;
|
||||
bool redirect_dir;
|
||||
};
|
||||
|
||||
/* private information held for overlayfs's superblock */
|
||||
struct ovl_fs {
|
||||
struct vfsmount *upper_mnt;
|
||||
unsigned numlower;
|
||||
struct vfsmount **lower_mnt;
|
||||
struct dentry *workdir;
|
||||
long namelen;
|
||||
/* pathnames of lower and upper dirs, for show_options */
|
||||
struct ovl_config config;
|
||||
/* creds of process who forced instantiation of super block */
|
||||
const struct cred *creator_cred;
|
||||
};
|
||||
|
||||
/* private information held for every overlayfs dentry */
|
||||
struct ovl_entry {
|
||||
struct dentry *__upperdentry;
|
||||
struct ovl_dir_cache *cache;
|
||||
union {
|
||||
struct {
|
||||
u64 version;
|
||||
const char *redirect;
|
||||
bool opaque;
|
||||
};
|
||||
struct rcu_head rcu;
|
||||
};
|
||||
unsigned numlower;
|
||||
struct path lowerstack[];
|
||||
};
|
||||
|
||||
struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
|
||||
|
||||
static inline struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
|
||||
{
|
||||
return lockless_dereference(oe->__upperdentry);
|
||||
}
|
||||
+59
-498
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Novell Inc.
|
||||
* Copyright (C) 2016 Red Hat, Inc.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 as published by
|
||||
* the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/mount.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include "overlayfs.h"
|
||||
#include "ovl_entry.h"
|
||||
|
||||
int ovl_want_write(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
return mnt_want_write(ofs->upper_mnt);
|
||||
}
|
||||
|
||||
void ovl_drop_write(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
mnt_drop_write(ofs->upper_mnt);
|
||||
}
|
||||
|
||||
struct dentry *ovl_workdir(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
return ofs->workdir;
|
||||
}
|
||||
|
||||
const struct cred *ovl_override_creds(struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
|
||||
return override_creds(ofs->creator_cred);
|
||||
}
|
||||
|
||||
struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
|
||||
{
|
||||
size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
|
||||
struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
|
||||
|
||||
if (oe)
|
||||
oe->numlower = numlower;
|
||||
|
||||
return oe;
|
||||
}
|
||||
|
||||
bool ovl_dentry_remote(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_flags &
|
||||
(DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
|
||||
DCACHE_OP_REAL);
|
||||
}
|
||||
|
||||
bool ovl_dentry_weird(struct dentry *dentry)
|
||||
{
|
||||
return dentry->d_flags & (DCACHE_NEED_AUTOMOUNT |
|
||||
DCACHE_MANAGE_TRANSIT |
|
||||
DCACHE_OP_HASH |
|
||||
DCACHE_OP_COMPARE);
|
||||
}
|
||||
|
||||
enum ovl_path_type ovl_path_type(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
enum ovl_path_type type = 0;
|
||||
|
||||
if (oe->__upperdentry) {
|
||||
type = __OVL_PATH_UPPER;
|
||||
|
||||
/*
|
||||
* Non-dir dentry can hold lower dentry from previous
|
||||
* location.
|
||||
*/
|
||||
if (oe->numlower && d_is_dir(dentry))
|
||||
type |= __OVL_PATH_MERGE;
|
||||
} else {
|
||||
if (oe->numlower > 1)
|
||||
type |= __OVL_PATH_MERGE;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
void ovl_path_upper(struct dentry *dentry, struct path *path)
|
||||
{
|
||||
struct ovl_fs *ofs = dentry->d_sb->s_fs_info;
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
path->mnt = ofs->upper_mnt;
|
||||
path->dentry = ovl_upperdentry_dereference(oe);
|
||||
}
|
||||
|
||||
void ovl_path_lower(struct dentry *dentry, struct path *path)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
*path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL };
|
||||
}
|
||||
|
||||
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
|
||||
{
|
||||
enum ovl_path_type type = ovl_path_type(dentry);
|
||||
|
||||
if (!OVL_TYPE_UPPER(type))
|
||||
ovl_path_lower(dentry, path);
|
||||
else
|
||||
ovl_path_upper(dentry, path);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
struct dentry *ovl_dentry_upper(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
return ovl_upperdentry_dereference(oe);
|
||||
}
|
||||
|
||||
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
|
||||
{
|
||||
return oe->numlower ? oe->lowerstack[0].dentry : NULL;
|
||||
}
|
||||
|
||||
struct dentry *ovl_dentry_lower(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
return __ovl_dentry_lower(oe);
|
||||
}
|
||||
|
||||
struct dentry *ovl_dentry_real(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
struct dentry *realdentry;
|
||||
|
||||
realdentry = ovl_upperdentry_dereference(oe);
|
||||
if (!realdentry)
|
||||
realdentry = __ovl_dentry_lower(oe);
|
||||
|
||||
return realdentry;
|
||||
}
|
||||
|
||||
struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
return oe->cache;
|
||||
}
|
||||
|
||||
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
oe->cache = cache;
|
||||
}
|
||||
|
||||
bool ovl_dentry_is_opaque(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
return oe->opaque;
|
||||
}
|
||||
|
||||
bool ovl_dentry_is_whiteout(struct dentry *dentry)
|
||||
{
|
||||
return !dentry->d_inode && ovl_dentry_is_opaque(dentry);
|
||||
}
|
||||
|
||||
void ovl_dentry_set_opaque(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
oe->opaque = true;
|
||||
}
|
||||
|
||||
bool ovl_redirect_dir(struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
|
||||
return ofs->config.redirect_dir;
|
||||
}
|
||||
|
||||
void ovl_clear_redirect_dir(struct super_block *sb)
|
||||
{
|
||||
struct ovl_fs *ofs = sb->s_fs_info;
|
||||
|
||||
ofs->config.redirect_dir = false;
|
||||
}
|
||||
|
||||
const char *ovl_dentry_get_redirect(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
return oe->redirect;
|
||||
}
|
||||
|
||||
void ovl_dentry_set_redirect(struct dentry *dentry, const char *redirect)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
kfree(oe->redirect);
|
||||
oe->redirect = redirect;
|
||||
}
|
||||
|
||||
void ovl_dentry_update(struct dentry *dentry, struct dentry *upperdentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
WARN_ON(!inode_is_locked(upperdentry->d_parent->d_inode));
|
||||
WARN_ON(oe->__upperdentry);
|
||||
/*
|
||||
* Make sure upperdentry is consistent before making it visible to
|
||||
* ovl_upperdentry_dereference().
|
||||
*/
|
||||
smp_wmb();
|
||||
oe->__upperdentry = upperdentry;
|
||||
}
|
||||
|
||||
void ovl_inode_init(struct inode *inode, struct inode *realinode, bool is_upper)
|
||||
{
|
||||
WRITE_ONCE(inode->i_private, (unsigned long) realinode |
|
||||
(is_upper ? OVL_ISUPPER_MASK : 0));
|
||||
}
|
||||
|
||||
void ovl_inode_update(struct inode *inode, struct inode *upperinode)
|
||||
{
|
||||
WARN_ON(!upperinode);
|
||||
WARN_ON(!inode_unhashed(inode));
|
||||
WRITE_ONCE(inode->i_private,
|
||||
(unsigned long) upperinode | OVL_ISUPPER_MASK);
|
||||
if (!S_ISDIR(upperinode->i_mode))
|
||||
__insert_inode_hash(inode, (unsigned long) upperinode);
|
||||
}
|
||||
|
||||
void ovl_dentry_version_inc(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
WARN_ON(!inode_is_locked(dentry->d_inode));
|
||||
oe->version++;
|
||||
}
|
||||
|
||||
u64 ovl_dentry_version_get(struct dentry *dentry)
|
||||
{
|
||||
struct ovl_entry *oe = dentry->d_fsdata;
|
||||
|
||||
WARN_ON(!inode_is_locked(dentry->d_inode));
|
||||
return oe->version;
|
||||
}
|
||||
|
||||
bool ovl_is_whiteout(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
|
||||
return inode && IS_WHITEOUT(inode);
|
||||
}
|
||||
|
||||
struct file *ovl_path_open(struct path *path, int flags)
|
||||
{
|
||||
return dentry_open(path, flags | O_NOATIME, current_cred());
|
||||
}
|
||||
+10
-13
@@ -1538,9 +1538,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
ret = mnt_want_write_file(file_out);
|
||||
if (ret)
|
||||
return ret;
|
||||
sb_start_write(inode_out->i_sb);
|
||||
|
||||
ret = -EOPNOTSUPP;
|
||||
if (file_out->f_op->copy_file_range)
|
||||
@@ -1559,7 +1557,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
|
||||
inc_syscr(current);
|
||||
inc_syscw(current);
|
||||
|
||||
mnt_drop_write_file(file_out);
|
||||
sb_end_write(inode_out->i_sb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1657,15 +1655,19 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
|
||||
struct inode *inode_out = file_inode(file_out);
|
||||
int ret;
|
||||
|
||||
if (inode_in->i_sb != inode_out->i_sb ||
|
||||
file_in->f_path.mnt != file_out->f_path.mnt)
|
||||
return -EXDEV;
|
||||
|
||||
if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
|
||||
return -EISDIR;
|
||||
if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* FICLONE/FICLONERANGE ioctls enforce that src and dest files are on
|
||||
* the same mount. Practically, they only need to be on the same file
|
||||
* system.
|
||||
*/
|
||||
if (inode_in->i_sb != inode_out->i_sb)
|
||||
return -EXDEV;
|
||||
|
||||
if (!(file_in->f_mode & FMODE_READ) ||
|
||||
!(file_out->f_mode & FMODE_WRITE) ||
|
||||
(file_out->f_flags & O_APPEND))
|
||||
@@ -1685,10 +1687,6 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
|
||||
if (pos_in + len > i_size_read(inode_in))
|
||||
return -EINVAL;
|
||||
|
||||
ret = mnt_want_write_file(file_out);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = file_in->f_op->clone_file_range(file_in, pos_in,
|
||||
file_out, pos_out, len);
|
||||
if (!ret) {
|
||||
@@ -1696,7 +1694,6 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
|
||||
fsnotify_modify(file_out);
|
||||
}
|
||||
|
||||
mnt_drop_write_file(file_out);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(vfs_clone_file_range);
|
||||
|
||||
Reference in New Issue
Block a user