Skip to content

Commit

Permalink
Merge branch 'overlayfs-linus' of git://git.kernel.org/pub/scm/linux/…
Browse files Browse the repository at this point in the history
…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
  ...
  • Loading branch information
Linus Torvalds committed Dec 16, 2016
2 parents 087a76d + c3c8699 commit ff0f962
Show file tree
Hide file tree
Showing 17 changed files with 1,139 additions and 812 deletions.
26 changes: 22 additions & 4 deletions Documentation/filesystems/overlayfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ At mount time, the two directories given as mount options "lowerdir" and
"upperdir" are combined into a merged directory:

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
as upperdir.
Expand Down Expand Up @@ -118,6 +118,7 @@ programs.

seek offsets are assigned sequentially when the directories are read.
Thus if

- read part of a directory
- remember an offset, and close the directory
- re-open the directory some time later
Expand All @@ -130,6 +131,23 @@ directory.
Readdir on directories that are not merged is simply handled by the
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
---------------
Expand Down Expand Up @@ -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 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
"break" the link. Changes will not be propagated to other names
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
---------------------------------

Expand Down
6 changes: 5 additions & 1 deletion fs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
6 changes: 1 addition & 5 deletions fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 1 addition & 2 deletions fs/nfsd/vfs.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions fs/overlayfs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 1 addition & 1 deletion fs/overlayfs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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
61 changes: 32 additions & 29 deletions fs/overlayfs/copy_up.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand All @@ -317,20 +320,14 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
/*
* 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);
Expand All @@ -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))
Expand All @@ -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;
Expand All @@ -385,7 +380,7 @@ int ovl_copy_up_one(struct dentry *parent, struct dentry *dentry,
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);
Expand Down Expand Up @@ -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);

Expand All @@ -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);
}
Loading

0 comments on commit ff0f962

Please sign in to comment.