Skip to content

Commit

Permalink
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel…
Browse files Browse the repository at this point in the history
…/git/viro/vfs

Pull vfs updates from Al Viro:
 "overlayfs merge + leak fix for d_splice_alias() failure exits"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  overlayfs: embed middle into overlay_readdir_data
  overlayfs: embed root into overlay_readdir_data
  overlayfs: make ovl_cache_entry->name an array instead of pointer
  overlayfs: don't hold ->i_mutex over opening the real directory
  fix inode leaks on d_splice_alias() failure exits
  fs: limit filesystem stacking depth
  overlay: overlay filesystem documentation
  overlayfs: implement show_options
  overlayfs: add statfs support
  overlay filesystem
  shmem: support RENAME_WHITEOUT
  ext4: support RENAME_WHITEOUT
  vfs: add RENAME_WHITEOUT
  vfs: add whiteout support
  vfs: export check_sticky()
  vfs: introduce clone_private_mount()
  vfs: export __inode_permission() to modules
  vfs: export do_splice_direct() to modules
  vfs: add i_op->dentry_open()
  • Loading branch information
torvalds committed Oct 26, 2014
2 parents 2cc9188 + db6ec21 commit d1e14f1
Show file tree
Hide file tree
Showing 27 changed files with 3,814 additions and 58 deletions.
2 changes: 2 additions & 0 deletions Documentation/filesystems/Locking
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ prototypes:
struct file *, unsigned open_flag,
umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*dentry_open)(struct dentry *, struct file *, const struct cred *);

locking rules:
all may block
Expand Down Expand Up @@ -96,6 +97,7 @@ fiemap: no
update_time: no
atomic_open: yes
tmpfile: no
dentry_open: no

Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
victim.
Expand Down
198 changes: 198 additions & 0 deletions Documentation/filesystems/overlayfs.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
Written by: Neil Brown <[email protected]>

Overlay Filesystem
==================

This document describes a prototype for a new approach to providing
overlay-filesystem functionality in Linux (sometimes referred to as
union-filesystems). An overlay-filesystem tries to present a
filesystem which is the result over overlaying one filesystem on top
of the other.

The result will inevitably fail to look exactly like a normal
filesystem for various technical reasons. The expectation is that
many use cases will be able to ignore these differences.

This approach is 'hybrid' because the objects that appear in the
filesystem do not all appear to belong to that filesystem. In many
cases an object accessed in the union will be indistinguishable
from accessing the corresponding object from the original filesystem.
This is most obvious from the 'st_dev' field returned by stat(2).

While directories will report an st_dev from the overlay-filesystem,
all non-directory objects will report an st_dev from the lower or
upper filesystem that is providing the object. Similarly st_ino will
only be unique when combined with st_dev, and both of these can change
over the lifetime of a non-directory object. Many applications and
tools ignore these values and will not be affected.

Upper and Lower
---------------

An overlay filesystem combines two filesystems - an 'upper' filesystem
and a 'lower' filesystem. When a name exists in both filesystems, the
object in the 'upper' filesystem is visible while the object in the
'lower' filesystem is either hidden or, in the case of directories,
merged with the 'upper' object.

It would be more correct to refer to an upper and lower 'directory
tree' rather than 'filesystem' as it is quite possible for both
directory trees to be in the same filesystem and there is no
requirement that the root of a filesystem be given for either upper or
lower.

The lower filesystem can be any filesystem supported by Linux and does
not need to be writable. The lower filesystem can even be another
overlayfs. The upper filesystem will normally be writable and if it
is it must support the creation of trusted.* extended attributes, and
must provide valid d_type in readdir responses, so NFS is not suitable.

A read-only overlay of two read-only filesystems may use any
filesystem type.

Directories
-----------

Overlaying mainly involves directories. If a given name appears in both
upper and lower filesystems and refers to a non-directory in either,
then the lower object is hidden - the name refers only to the upper
object.

Where both upper and lower objects are directories, a merged directory
is formed.

At mount time, the two directories given as mount options "lowerdir" and
"upperdir" are combined into a merged directory:

mount -t overlayfs overlayfs -olowerdir=/lower,upperdir=/upper,\
workdir=/work /merged

The "workdir" needs to be an empty directory on the same filesystem
as upperdir.

Then whenever a lookup is requested in such a merged directory, the
lookup is performed in each actual directory and the combined result
is cached in the dentry belonging to the overlay filesystem. If both
actual lookups find directories, both are stored and a merged
directory is created, otherwise only one is stored: the upper if it
exists, else the lower.

Only the lists of names from directories are merged. Other content
such as metadata and extended attributes are reported for the upper
directory only. These attributes of the lower directory are hidden.

whiteouts and opaque directories
--------------------------------

In order to support rm and rmdir without changing the lower
filesystem, an overlay filesystem needs to record in the upper filesystem
that files have been removed. This is done using whiteouts and opaque
directories (non-directories are always opaque).

A whiteout is created as a character device with 0/0 device number.
When a whiteout is found in the upper level of a merged directory, any
matching name in the lower level is ignored, and the whiteout itself
is also hidden.

A directory is made opaque by setting the xattr "trusted.overlay.opaque"
to "y". Where the upper filesystem contains an opaque directory, any
directory in the lower filesystem with the same name is ignored.

readdir
-------

When a 'readdir' request is made on a merged directory, the upper and
lower directories are each read and the name lists merged in the
obvious way (upper is read first, then lower - entries that already
exist are not re-added). This merged name list is cached in the
'struct file' and so remains as long as the file is kept open. If the
directory is opened and read by two processes at the same time, they
will each have separate caches. A seekdir to the start of the
directory (offset 0) followed by a readdir will cause the cache to be
discarded and rebuilt.

This means that changes to the merged directory do not appear while a
directory is being read. This is unlikely to be noticed by many
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
- seek to the remembered offset

there may be little correlation between the old and new locations in
the list of filenames, particularly if anything has changed in the
directory.

Readdir on directories that are not merged is simply handled by the
underlying directory (upper or lower).


Non-directories
---------------

Objects that are not directories (files, symlinks, device-special
files etc.) are presented either from the upper or lower filesystem as
appropriate. When a file in the lower filesystem is accessed in a way
the requires write-access, such as opening for write access, changing
some metadata etc., the file is first copied from the lower filesystem
to the upper filesystem (copy_up). Note that creating a hard-link
also requires copy_up, though of course creation of a symlink does
not.

The copy_up may turn out to be unnecessary, for example if the file is
opened for read-write but the data is not modified.

The copy_up process first makes sure that the containing directory
exists in the upper filesystem - creating it and any parents as
necessary. It then creates the object with the same metadata (owner,
mode, mtime, symlink-target etc.) and then if the object is a file, the
data is copied from the lower to the upper filesystem. Finally any
extended attributes are copied up.

Once the copy_up is complete, the overlay filesystem simply
provides direct access to the newly created file in the upper
filesystem - future operations on the file are barely noticed by the
overlay filesystem (though an operation on the name of the file such as
rename or unlink will of course be noticed and handled).


Non-standard behavior
---------------------

The copy_up operation essentially creates a new, identical file and
moves it over to the old name. The new file may be on a different
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 and
metadata. Similarly any file locks obtained before copy_up will not
apply to the copied up file.

On a file opened with O_RDONLY fchmod(2), fchown(2), futimesat(2) and
fsetxattr(2) will fail with EROFS.

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.

Symlinks in /proc/PID/ and /proc/PID/fd which point to a non-directory
object in overlayfs will not contain valid absolute paths, only
relative paths leading up to the filesystem's root. This will be
fixed in the future.

Some operations are not atomic, for example a crash during copy_up or
rename will leave the filesystem in an inconsistent state. This will
be addressed in the future.

Changes to underlying filesystems
---------------------------------

Offline changes, when the overlay is not mounted, are allowed to either
the upper or the lower trees.

Changes to the underlying filesystems while part of a mounted overlay
filesystem are not allowed. If the underlying filesystem is changed,
the behavior of the overlay is undefined, though it will not result in
a crash or deadlock.
7 changes: 7 additions & 0 deletions Documentation/filesystems/vfs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ struct inode_operations {
int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*dentry_open)(struct dentry *, struct file *, const struct cred *);
};

Again, all methods are called without any locks being held, unless
Expand Down Expand Up @@ -696,6 +697,12 @@ struct address_space_operations {
but instead uses bmap to find out where the blocks in the file
are and uses those addresses directly.

dentry_open: *WARNING: probably going away soon, do not use!* This is an
alternative to f_op->open(), the difference is that this method may open
a file not necessarily originating from the same filesystem as the one
i_op->open() was called on. It may be useful for stacking filesystems
which want to allow native I/O directly on underlying files.


invalidatepage: If a page has PagePrivate set, then invalidatepage
will be called when part or all of the page is to be removed
Expand Down
7 changes: 7 additions & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -6840,6 +6840,13 @@ F: drivers/scsi/osd/
F: include/scsi/osd_*
F: fs/exofs/

OVERLAYFS FILESYSTEM
M: Miklos Szeredi <[email protected]>
L: [email protected]
S: Supported
F: fs/overlayfs/*
F: Documentation/filesystems/overlayfs.txt

P54 WIRELESS DRIVER
M: Christian Lamparter <[email protected]>
L: [email protected]
Expand Down
1 change: 1 addition & 0 deletions fs/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ source "fs/quota/Kconfig"

source "fs/autofs4/Kconfig"
source "fs/fuse/Kconfig"
source "fs/overlayfs/Kconfig"

menu "Caches"

Expand Down
1 change: 1 addition & 0 deletions fs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ obj-$(CONFIG_QNX6FS_FS) += qnx6/
obj-$(CONFIG_AUTOFS4_FS) += autofs4/
obj-$(CONFIG_ADFS_FS) += adfs/
obj-$(CONFIG_FUSE_FS) += fuse/
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs/
obj-$(CONFIG_UDF_FS) += udf/
obj-$(CONFIG_SUN_OPENPROMFS) += openpromfs/
obj-$(CONFIG_OMFS_FS) += omfs/
Expand Down
20 changes: 1 addition & 19 deletions fs/btrfs/ioctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -765,23 +765,6 @@ static int create_snapshot(struct btrfs_root *root, struct inode *dir,
return ret;
}

/* copy of check_sticky in fs/namei.c()
* It's inline, so penalty for filesystems that don't use sticky bit is
* minimal.
*/
static inline int btrfs_check_sticky(struct inode *dir, struct inode *inode)
{
kuid_t fsuid = current_fsuid();

if (!(dir->i_mode & S_ISVTX))
return 0;
if (uid_eq(inode->i_uid, fsuid))
return 0;
if (uid_eq(dir->i_uid, fsuid))
return 0;
return !capable(CAP_FOWNER);
}

/* copy of may_delete in fs/namei.c()
* Check whether we can remove a link victim from directory dir, check
* whether the type of victim is right.
Expand Down Expand Up @@ -817,8 +800,7 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
return error;
if (IS_APPEND(dir))
return -EPERM;
if (btrfs_check_sticky(dir, victim->d_inode)||
IS_APPEND(victim->d_inode)||
if (check_sticky(dir, victim->d_inode) || IS_APPEND(victim->d_inode) ||
IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
return -EPERM;
if (isdir) {
Expand Down
2 changes: 2 additions & 0 deletions fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -2673,11 +2673,13 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry)
if (!IS_ROOT(new)) {
spin_unlock(&inode->i_lock);
dput(new);
iput(inode);
return ERR_PTR(-EIO);
}
if (d_ancestor(new, dentry)) {
spin_unlock(&inode->i_lock);
dput(new);
iput(inode);
return ERR_PTR(-EIO);
}
write_seqlock(&rename_lock);
Expand Down
7 changes: 7 additions & 0 deletions fs/ecryptfs/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,6 +566,13 @@ static struct dentry *ecryptfs_mount(struct file_system_type *fs_type, int flags
s->s_maxbytes = path.dentry->d_sb->s_maxbytes;
s->s_blocksize = path.dentry->d_sb->s_blocksize;
s->s_magic = ECRYPTFS_SUPER_MAGIC;
s->s_stack_depth = path.dentry->d_sb->s_stack_depth + 1;

rc = -EINVAL;
if (s->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("eCryptfs: maximum fs stacking depth exceeded\n");
goto out_free;
}

inode = ecryptfs_get_inode(path.dentry->d_inode, s);
rc = PTR_ERR(inode);
Expand Down
Loading

0 comments on commit d1e14f1

Please sign in to comment.