Commit ff0f962c authored by Linus Torvalds's avatar Linus Torvalds

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
  ...
parents 087a76d3 c3c86996
...@@ -66,7 +66,7 @@ At mount time, the two directories given as mount options "lowerdir" and ...@@ -66,7 +66,7 @@ At mount time, the two directories given as mount options "lowerdir" and
"upperdir" are combined into a merged directory: "upperdir" are combined into a merged directory:
mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\ mount -t overlay overlay -olowerdir=/lower,upperdir=/upper,\
workdir=/work /merged workdir=/work /merged
The "workdir" needs to be an empty directory on the same filesystem The "workdir" needs to be an empty directory on the same filesystem
as upperdir. as upperdir.
...@@ -118,6 +118,7 @@ programs. ...@@ -118,6 +118,7 @@ programs.
seek offsets are assigned sequentially when the directories are read. seek offsets are assigned sequentially when the directories are read.
Thus if Thus if
- read part of a directory - read part of a directory
- remember an offset, and close the directory - remember an offset, and close the directory
- re-open the directory some time later - re-open the directory some time later
...@@ -130,6 +131,23 @@ directory. ...@@ -130,6 +131,23 @@ directory.
Readdir on directories that are not merged is simply handled by the Readdir on directories that are not merged is simply handled by the
underlying directory (upper or lower). underlying directory (upper or lower).
renaming directories
--------------------
When renaming a directory that is on the lower layer or merged (i.e. the
directory was not created on the upper layer to start with) overlayfs can
handle it in two different ways:
1. return EXDEV error: this error is returned by rename(2) when trying to
move a file or directory across filesystem boundaries. Hence
applications are usually prepared to hande this error (mv(1) for example
recursively copies the directory tree). This is the default behavior.
2. If the "redirect_dir" feature is enabled, then the directory will be
copied up (but not the contents). Then the "trusted.overlay.redirect"
extended attribute is set to the path of the original location from the
root of the overlay. Finally the directory is moved to the new
location.
Non-directories Non-directories
--------------- ---------------
...@@ -185,13 +203,13 @@ filesystem, so both st_dev and st_ino of the file may change. ...@@ -185,13 +203,13 @@ filesystem, so both st_dev and st_ino of the file may change.
Any open files referring to this inode will access the old data. Any open files referring to this inode will access the old data.
Any file locks (and leases) obtained before copy_up will not apply
to the copied up file.
If a file with multiple hard links is copied up, then this will If a file with multiple hard links is copied up, then this will
"break" the link. Changes will not be propagated to other names "break" the link. Changes will not be propagated to other names
referring to the same inode. referring to the same inode.
Unless "redirect_dir" feature is enabled, rename(2) on a lower or merged
directory will fail with EXDEV.
Changes to underlying filesystems Changes to underlying filesystems
--------------------------------- ---------------------------------
......
...@@ -223,7 +223,11 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd, ...@@ -223,7 +223,11 @@ static long ioctl_file_clone(struct file *dst_file, unsigned long srcfd,
if (!src_file.file) if (!src_file.file)
return -EBADF; 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); fdput(src_file);
return ret; return ret;
} }
......
...@@ -4306,11 +4306,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -4306,11 +4306,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
bool new_is_dir = false; bool new_is_dir = false;
unsigned max_links = new_dir->i_sb->s_max_links; unsigned max_links = new_dir->i_sb->s_max_links;
/* if (source == target)
* Check source == target.
* On overlayfs need to look at underlying inodes.
*/
if (d_real_inode(old_dentry) == d_real_inode(new_dentry))
return 0; return 0;
error = may_delete(old_dir, old_dentry, is_dir); error = may_delete(old_dir, old_dentry, is_dir);
......
...@@ -509,8 +509,7 @@ __be32 nfsd4_set_nfs4_label(struct svc_rqst *rqstp, struct svc_fh *fhp, ...@@ -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, __be32 nfsd4_clone_file_range(struct file *src, u64 src_pos, struct file *dst,
u64 dst_pos, u64 count) u64 dst_pos, u64 count)
{ {
return nfserrno(vfs_clone_file_range(src, src_pos, dst, dst_pos, return nfserrno(do_clone_file_range(src, src_pos, dst, dst_pos, count));
count));
} }
ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst, ssize_t nfsd_copy_file_range(struct file *src, u64 src_pos, struct file *dst,
......
...@@ -8,3 +8,17 @@ config OVERLAY_FS ...@@ -8,3 +8,17 @@ config OVERLAY_FS
merged with the 'upper' object. merged with the 'upper' object.
For more information see Documentation/filesystems/overlayfs.txt 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 @@ ...@@ -4,4 +4,4 @@
obj-$(CONFIG_OVERLAY_FS) += overlay.o 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
...@@ -153,6 +153,13 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len) ...@@ -153,6 +153,13 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
goto out_fput; 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 */ /* FIXME: copy up sparse files efficiently */
while (len) { while (len) {
size_t this_len = OVL_COPY_UP_CHUNK_SIZE; 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) ...@@ -177,7 +184,7 @@ static int ovl_copy_up_data(struct path *old, struct path *new, loff_t len)
len -= bytes; len -= bytes;
} }
out:
if (!error) if (!error)
error = vfs_fsync(new_file, 0); error = vfs_fsync(new_file, 0);
fput(new_file); fput(new_file);
...@@ -231,10 +238,15 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, ...@@ -231,10 +238,15 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
struct inode *udir = upperdir->d_inode; struct inode *udir = upperdir->d_inode;
struct dentry *newdentry = NULL; struct dentry *newdentry = NULL;
struct dentry *upper = NULL; struct dentry *upper = NULL;
umode_t mode = stat->mode;
int err; int err;
const struct cred *old_creds = NULL; const struct cred *old_creds = NULL;
struct cred *new_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); newdentry = ovl_lookup_temp(workdir, dentry);
err = PTR_ERR(newdentry); err = PTR_ERR(newdentry);
...@@ -254,10 +266,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, ...@@ -254,10 +266,7 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
if (new_creds) if (new_creds)
old_creds = override_creds(new_creds); old_creds = override_creds(new_creds);
/* Can't properly set mode on creation because of the umask */ err = ovl_create_real(wdir, newdentry, &cattr, NULL, true);
stat->mode &= S_IFMT;
err = ovl_create_real(wdir, newdentry, stat, link, NULL, true);
stat->mode = mode;
if (new_creds) { if (new_creds) {
revert_creds(old_creds); revert_creds(old_creds);
...@@ -296,12 +305,6 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, ...@@ -296,12 +305,6 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
ovl_dentry_update(dentry, newdentry); ovl_dentry_update(dentry, newdentry);
ovl_inode_update(d_inode(dentry), d_inode(newdentry)); ovl_inode_update(d_inode(dentry), d_inode(newdentry));
newdentry = NULL; newdentry = NULL;
/*
* Non-directores become opaque when copied up.
*/
if (!S_ISDIR(stat->mode))
ovl_dentry_set_opaque(dentry, true);
out2: out2:
dput(upper); dput(upper);
out1: out1:
...@@ -317,19 +320,13 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, ...@@ -317,19 +320,13 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
/* /*
* Copy up a single dentry * Copy up a single dentry
* *
* Directory renames only allowed on "pure upper" (already created on * All renames start with copy up of source if necessary. The actual
* upper filesystem, never copied up). Directories which are on lower or * rename will only proceed once the copy up was successful. Copy up uses
* are merged may not be renamed. For these -EXDEV is returned and * upper parent i_mutex for exclusion. Since rename can change d_parent it
* userspace has to deal with it. This means, when copying up a * is possible that the copy up will lock the old parent. At that point
* directory we can rely on it and ancestors being stable. * the file will have already been copied up anyway.
*
* 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.
*/ */
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, static int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
struct path *lowerpath, struct kstat *stat) struct path *lowerpath, struct kstat *stat)
{ {
DEFINE_DELAYED_CALL(done); DEFINE_DELAYED_CALL(done);
...@@ -339,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -339,7 +336,6 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
struct path parentpath; struct path parentpath;
struct dentry *lowerdentry = lowerpath->dentry; struct dentry *lowerdentry = lowerpath->dentry;
struct dentry *upperdir; struct dentry *upperdir;
struct dentry *upperdentry;
const char *link = NULL; const char *link = NULL;
if (WARN_ON(!workdir)) if (WARN_ON(!workdir))
...@@ -365,8 +361,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -365,8 +361,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
pr_err("overlayfs: failed to lock workdir+upperdir\n"); pr_err("overlayfs: failed to lock workdir+upperdir\n");
goto out_unlock; goto out_unlock;
} }
upperdentry = ovl_dentry_upper(dentry); if (ovl_dentry_upper(dentry)) {
if (upperdentry) {
/* Raced with another copy-up? Nothing to do, then... */ /* Raced with another copy-up? Nothing to do, then... */
err = 0; err = 0;
goto out_unlock; goto out_unlock;
...@@ -385,7 +380,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, ...@@ -385,7 +380,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
return err; return err;
} }
int ovl_copy_up(struct dentry *dentry) int ovl_copy_up_flags(struct dentry *dentry, int flags)
{ {
int err = 0; int err = 0;
const struct cred *old_cred = ovl_override_creds(dentry->d_sb); const struct cred *old_cred = ovl_override_creds(dentry->d_sb);
...@@ -415,6 +410,9 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -415,6 +410,9 @@ int ovl_copy_up(struct dentry *dentry)
ovl_path_lower(next, &lowerpath); ovl_path_lower(next, &lowerpath);
err = vfs_getattr(&lowerpath, &stat); err = vfs_getattr(&lowerpath, &stat);
/* maybe truncate regular file. this has no effect on dirs */
if (flags & O_TRUNC)
stat.size = 0;
if (!err) if (!err)
err = ovl_copy_up_one(parent, next, &lowerpath, &stat); err = ovl_copy_up_one(parent, next, &lowerpath, &stat);
...@@ -425,3 +423,8 @@ int ovl_copy_up(struct dentry *dentry) ...@@ -425,3 +423,8 @@ int ovl_copy_up(struct dentry *dentry)
return err; return err;
} }
int ovl_copy_up(struct dentry *dentry)
{
return ovl_copy_up_flags(dentry, 0);
}
This diff is collapsed.
...@@ -13,34 +13,6 @@ ...@@ -13,34 +13,6 @@
#include <linux/posix_acl.h> #include <linux/posix_acl.h>
#include "overlayfs.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 ovl_setattr(struct dentry *dentry, struct iattr *attr)
{ {
int err; int err;
...@@ -64,27 +36,10 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -64,27 +36,10 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
if (err) if (err)
goto out; 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); err = ovl_copy_up(dentry);
if (!err) { if (!err) {
struct inode *winode = NULL;
upperdentry = ovl_dentry_upper(dentry); 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)) if (attr->ia_valid & (ATTR_KILL_SUID|ATTR_KILL_SGID))
attr->ia_valid &= ~ATTR_MODE; attr->ia_valid &= ~ATTR_MODE;
...@@ -95,11 +50,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr) ...@@ -95,11 +50,7 @@ int ovl_setattr(struct dentry *dentry, struct iattr *attr)
if (!err) if (!err)
ovl_copyattr(upperdentry->d_inode, dentry->d_inode); ovl_copyattr(upperdentry->d_inode, dentry->d_inode);
inode_unlock(upperdentry->d_inode); inode_unlock(upperdentry->d_inode);
if (winode)
put_write_access(winode);
} }
out_drop_write:
ovl_drop_write(dentry); ovl_drop_write(dentry);
out: out:
return err; return err;
...@@ -302,10 +253,7 @@ int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags) ...@@ -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)) { if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (!err) { if (!err) {
if (file_flags & O_TRUNC) err = ovl_copy_up_flags(dentry, file_flags);
err = ovl_copy_up_truncate(dentry);
else
err = ovl_copy_up(dentry);
ovl_drop_write(dentry); ovl_drop_write(dentry);
} }
} }
...@@ -354,7 +302,7 @@ static const struct inode_operations ovl_symlink_inode_operations = { ...@@ -354,7 +302,7 @@ static const struct inode_operations ovl_symlink_inode_operations = {
.update_time = ovl_update_time, .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_ino = get_next_ino();
inode->i_mode = mode; inode->i_mode = mode;
...@@ -363,8 +311,11 @@ static void ovl_fill_inode(struct inode *inode, umode_t 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; inode->i_acl = inode->i_default_acl = ACL_DONT_CACHE;
#endif #endif
mode &= S_IFMT; switch (mode & S_IFMT) {
switch (mode) { case S_IFREG:
inode->i_op = &ovl_file_inode_operations;
break;
case S_IFDIR: case S_IFDIR:
inode->i_op = &ovl_dir_inode_operations; inode->i_op = &ovl_dir_inode_operations;
inode->i_fop = &ovl_dir_operations; inode->i_fop = &ovl_dir_operations;
...@@ -375,26 +326,19 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode) ...@@ -375,26 +326,19 @@ static void ovl_fill_inode(struct inode *inode, umode_t mode)
break; break;
default: 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; inode->i_op = &ovl_file_inode_operations;
init_special_inode(inode, mode, rdev);
break; 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; struct inode *inode;
inode = new_inode(sb); inode = new_inode(sb);
if (inode) if (inode)
ovl_fill_inode(inode, mode); ovl_fill_inode(inode, mode, rdev);
return inode; return inode;
} }
...@@ -418,7 +362,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode) ...@@ -418,7 +362,7 @@ struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode)
inode = iget5_locked(sb, (unsigned long) realinode, inode = iget5_locked(sb, (unsigned long) realinode,
ovl_inode_test, ovl_inode_set, realinode); ovl_inode_test, ovl_inode_set, realinode);
if (inode && inode->i_state & I_NEW) { 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); set_nlink(inode, realinode->i_nlink);
unlock_new_inode(inode); unlock_new_inode(inode);
} }
......
/*
* 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;
}
...@@ -9,23 +9,17 @@ ...@@ -9,23 +9,17 @@
#include <linux/kernel.h> #include <linux/kernel.h>
struct ovl_entry;
enum ovl_path_type { enum ovl_path_type {
__OVL_PATH_PURE = (1 << 0), __OVL_PATH_UPPER = (1 << 0),
__OVL_PATH_UPPER = (1 << 1), __OVL_PATH_MERGE = (1 << 1),
__OVL_PATH_MERGE = (1 << 2),
}; };
#define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER) #define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER)
#define OVL_TYPE_MERGE(type) ((type) & __OVL_PATH_MERGE) #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_PREFIX XATTR_TRUSTED_PREFIX "overlay."
#define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque" #define OVL_XATTR_OPAQUE OVL_XATTR_PREFIX "opaque"
#define OVL_XATTR_REDIRECT OVL_XATTR_PREFIX "redirect"
#define OVL_ISUPPER_MASK 1UL #define OVL_ISUPPER_MASK 1UL
...@@ -143,35 +137,43 @@ static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper) ...@@ -143,35 +137,43 @@ static inline struct inode *ovl_inode_real(struct inode *inode, bool *is_upper)
return (struct inode *) (x & ~OVL_ISUPPER_MASK); 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); 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_upper(struct dentry *dentry, struct path *path);
void ovl_path_lower(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path);
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry); struct dentry *ovl_dentry_real(struct dentry *dentry);
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); struct ovl_dir_cache *ovl_dir_cache(struct dentry *dentry);
void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache); 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); bool ovl_dentry_is_opaque(struct dentry *dentry);
void ovl_dentry_set_opaque(struct dentry *dentry, bool opaque); bool ovl_dentry_is_whiteout(struct dentry *dentry);
bool ovl_is_whiteout(struct dentry *dentry); void ovl_dentry_set_opaque(struct dentry *dentry);
const struct cred *ovl_override_creds(struct super_block *sb); 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_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); void ovl_inode_update(struct inode *inode, struct inode *upperinode);
struct dentry *ovl_lookup(struct inode *dir, struct dentry *dentry, void ovl_dentry_version_inc(struct dentry *dentry);
unsigned int flags); 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 file *ovl_path_open(struct path *path, int flags);
struct dentry *ovl_upper_create(struct dentry *upperdir, struct dentry *dentry, /* namei.c */
struct kstat *stat, const char *link); 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 */ /* readdir.c */
extern const struct file_operations ovl_dir_operations; 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); ...@@ -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); int ovl_update_time(struct inode *inode, struct timespec *ts, int flags);
bool ovl_is_private_xattr(const char *name); 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); struct inode *ovl_get_inode(struct super_block *sb, struct inode *realinode);
static inline void ovl_copyattr(struct inode *from, struct inode *to) 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) ...@@ -210,14 +212,18 @@ static inline void ovl_copyattr(struct inode *from, struct inode *to)
/* dir.c */ /* dir.c */
extern const struct inode_operations ovl_dir_inode_operations; extern const struct inode_operations ovl_dir_inode_operations;
struct dentry *ovl_lookup_temp(struct dentry *workdir, struct dentry *dentry); 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, int ovl_create_real(struct inode *dir, struct dentry *newdentry,
struct kstat *stat, const char *link, struct cattr *attr,
struct dentry *hardlink, bool debug); struct dentry *hardlink, bool debug);
void ovl_cleanup(struct inode *dir, struct dentry *dentry); void ovl_cleanup(struct inode *dir, struct dentry *dentry);
/* copy_up.c */ /* copy_up.c */
int ovl_copy_up(struct dentry *dentry); int ovl_copy_up(struct dentry *dentry);
int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry, int ovl_copy_up_flags(struct dentry *dentry, int flags);
struct path *lowerpath, struct kstat *stat);
int ovl_copy_xattr(struct dentry *old, struct dentry *new); int ovl_copy_xattr(struct dentry *old, struct dentry *new);
int ovl_set_attr(struct dentry *upper, struct kstat *stat); int ovl_set_attr(struct dentry *upper, struct kstat *stat);
/*
*
* 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);
}
This diff is collapsed.
/*
* 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());
}
...@@ -1538,9 +1538,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in, ...@@ -1538,9 +1538,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
if (len == 0) if (len == 0)
return 0; return 0;
ret = mnt_want_write_file(file_out); sb_start_write(inode_out->i_sb);
if (ret)
return ret;
ret = -EOPNOTSUPP; ret = -EOPNOTSUPP;
if (file_out->f_op->copy_file_range) 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, ...@@ -1559,7 +1557,7 @@ ssize_t vfs_copy_file_range(struct file *file_in, loff_t pos_in,
inc_syscr(current); inc_syscr(current);
inc_syscw(current); inc_syscw(current);
mnt_drop_write_file(file_out); sb_end_write(inode_out->i_sb);
return ret; return ret;
} }
...@@ -1657,15 +1655,19 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in, ...@@ -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); struct inode *inode_out = file_inode(file_out);
int ret; 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)) if (S_ISDIR(inode_in->i_mode) || S_ISDIR(inode_out->i_mode))
return -EISDIR; return -EISDIR;
if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode)) if (!S_ISREG(inode_in->i_mode) || !S_ISREG(inode_out->i_mode))
return -EINVAL; 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) || if (!(file_in->f_mode & FMODE_READ) ||
!(file_out->f_mode & FMODE_WRITE) || !(file_out->f_mode & FMODE_WRITE) ||
(file_out->f_flags & O_APPEND)) (file_out->f_flags & O_APPEND))
...@@ -1685,10 +1687,6 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in, ...@@ -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)) if (pos_in + len > i_size_read(inode_in))
return -EINVAL; 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, ret = file_in->f_op->clone_file_range(file_in, pos_in,
file_out, pos_out, len); file_out, pos_out, len);
if (!ret) { if (!ret) {
...@@ -1696,7 +1694,6 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in, ...@@ -1696,7 +1694,6 @@ int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
fsnotify_modify(file_out); fsnotify_modify(file_out);
} }
mnt_drop_write_file(file_out);
return ret; return ret;
} }
EXPORT_SYMBOL(vfs_clone_file_range); EXPORT_SYMBOL(vfs_clone_file_range);
......
...@@ -1731,6 +1731,19 @@ extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in, ...@@ -1731,6 +1731,19 @@ extern int vfs_clone_file_range(struct file *file_in, loff_t pos_in,
extern int vfs_dedupe_file_range(struct file *file, extern int vfs_dedupe_file_range(struct file *file,
struct file_dedupe_range *same); struct file_dedupe_range *same);
static inline int do_clone_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
u64 len)
{
int ret;
sb_start_write(file_inode(file_out)->i_sb);
ret = vfs_clone_file_range(file_in, pos_in, file_out, pos_out, len);
sb_end_write(file_inode(file_out)->i_sb);
return ret;
}
struct super_operations { struct super_operations {
struct inode *(*alloc_inode)(struct super_block *sb); struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *); void (*destroy_inode)(struct inode *);
......
...@@ -315,7 +315,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i) ...@@ -315,7 +315,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i)
&unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) { &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
struct dentry *dentry = unix_sk(s)->path.dentry; struct dentry *dentry = unix_sk(s)->path.dentry;
if (dentry && d_real_inode(dentry) == i) { if (dentry && d_backing_inode(dentry) == i) {
sock_hold(s); sock_hold(s);
goto found; goto found;
} }
...@@ -913,7 +913,7 @@ static struct sock *unix_find_other(struct net *net, ...@@ -913,7 +913,7 @@ static struct sock *unix_find_other(struct net *net,
err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path); err = kern_path(sunname->sun_path, LOOKUP_FOLLOW, &path);
if (err) if (err)
goto fail; goto fail;
inode = d_real_inode(path.dentry); inode = d_backing_inode(path.dentry);
err = inode_permission(inode, MAY_WRITE); err = inode_permission(inode, MAY_WRITE);
if (err) if (err)
goto put_fail; goto put_fail;
...@@ -1040,7 +1040,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) ...@@ -1040,7 +1040,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)
goto out_up; goto out_up;
} }
addr->hash = UNIX_HASH_SIZE; addr->hash = UNIX_HASH_SIZE;
hash = d_real_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1); hash = d_backing_inode(path.dentry)->i_ino & (UNIX_HASH_SIZE - 1);
spin_lock(&unix_table_lock); spin_lock(&unix_table_lock);
u->path = path; u->path = path;
list = &unix_socket_table[hash]; list = &unix_socket_table[hash];
......
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