Commit d1e14f1d authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/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()
parents 2cc91884 db6ec212
......@@ -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
......@@ -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.
......
Written by: Neil Brown <neilb@suse.de>
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.
......@@ -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
......@@ -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
......
......@@ -6840,6 +6840,13 @@ F: drivers/scsi/osd/
F: include/scsi/osd_*
F: fs/exofs/
OVERLAYFS FILESYSTEM
M: Miklos Szeredi <miklos@szeredi.hu>
L: linux-fsdevel@vger.kernel.org
S: Supported
F: fs/overlayfs/*
F: Documentation/filesystems/overlayfs.txt
P54 WIRELESS DRIVER
M: Christian Lamparter <chunkeey@googlemail.com>
L: linux-wireless@vger.kernel.org
......
......@@ -67,6 +67,7 @@ source "fs/quota/Kconfig"
source "fs/autofs4/Kconfig"
source "fs/fuse/Kconfig"
source "fs/overlayfs/Kconfig"
menu "Caches"
......
......@@ -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/
......
......@@ -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.
......@@ -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) {
......
......@@ -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);
......
......@@ -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);
......
......@@ -3148,6 +3148,39 @@ static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
}
}
static struct inode *ext4_whiteout_for_rename(struct ext4_renament *ent,
int credits, handle_t **h)
{
struct inode *wh;
handle_t *handle;
int retries = 0;
/*
* for inode block, sb block, group summaries,
* and inode bitmap
*/
credits += (EXT4_MAXQUOTAS_TRANS_BLOCKS(ent->dir->i_sb) +
EXT4_XATTR_TRANS_BLOCKS + 4);
retry:
wh = ext4_new_inode_start_handle(ent->dir, S_IFCHR | WHITEOUT_MODE,
&ent->dentry->d_name, 0, NULL,
EXT4_HT_DIR, credits);
handle = ext4_journal_current_handle();
if (IS_ERR(wh)) {
if (handle)
ext4_journal_stop(handle);
if (PTR_ERR(wh) == -ENOSPC &&
ext4_should_retry_alloc(ent->dir->i_sb, &retries))
goto retry;
} else {
*h = handle;
init_special_inode(wh, wh->i_mode, WHITEOUT_DEV);
wh->i_op = &ext4_special_inode_operations;
}
return wh;
}
/*
* Anybody can rename anything with this: the permission checks are left to the
* higher-level routines.
......@@ -3157,7 +3190,8 @@ static void ext4_update_dir_count(handle_t *handle, struct ext4_renament *ent)
* This comes from rename(const char *oldpath, const char *newpath)
*/
static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
handle_t *handle = NULL;
struct ext4_renament old = {
......@@ -3172,6 +3206,9 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
};
int force_reread;
int retval;
struct inode *whiteout = NULL;
int credits;
u8 old_file_type;
dquot_initialize(old.dir);
dquot_initialize(new.dir);
......@@ -3210,11 +3247,17 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
if (new.inode && !test_opt(new.dir->i_sb, NO_AUTO_DA_ALLOC))
ext4_alloc_da_blocks(old.inode);
handle = ext4_journal_start(old.dir, EXT4_HT_DIR,
(2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2));
if (IS_ERR(handle))
return PTR_ERR(handle);
credits = (2 * EXT4_DATA_TRANS_BLOCKS(old.dir->i_sb) +
EXT4_INDEX_EXTRA_TRANS_BLOCKS + 2);
if (!(flags & RENAME_WHITEOUT)) {
handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits);
if (IS_ERR(handle))
return PTR_ERR(handle);
} else {
whiteout = ext4_whiteout_for_rename(&old, credits, &handle);
if (IS_ERR(whiteout))
return PTR_ERR(whiteout);
}
if (IS_DIRSYNC(old.dir) || IS_DIRSYNC(new.dir))
ext4_handle_sync(handle);
......@@ -3242,13 +3285,26 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
*/
force_reread = (new.dir->i_ino == old.dir->i_ino &&
ext4_test_inode_flag(new.dir, EXT4_INODE_INLINE_DATA));
old_file_type = old.de->file_type;
if (whiteout) {
/*
* Do this before adding a new entry, so the old entry is sure
* to be still pointing to the valid old entry.
*/
retval = ext4_setent(handle, &old, whiteout->i_ino,
EXT4_FT_CHRDEV);
if (retval)
goto end_rename;
ext4_mark_inode_dirty(handle, whiteout);
}
if (!new.bh) {
retval = ext4_add_entry(handle, new.dentry, old.inode);
if (retval)
goto end_rename;
} else {
retval = ext4_setent(handle, &new,
old.inode->i_ino, old.de->file_type);
old.inode->i_ino, old_file_type);
if (retval)
goto end_rename;
}
......@@ -3263,10 +3319,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
old.inode->i_ctime = ext4_current_time(old.inode);
ext4_mark_inode_dirty(handle, old.inode);
/*
* ok, that's it
*/
ext4_rename_delete(handle, &old, force_reread);
if (!whiteout) {
/*
* ok, that's it
*/
ext4_rename_delete(handle, &old, force_reread);
}
if (new.inode) {
ext4_dec_count(handle, new.inode);
......@@ -3302,6 +3360,12 @@ static int ext4_rename(struct inode *old_dir, struct dentry *old_dentry,
brelse(old.dir_bh);
brelse(old.bh);
brelse(new.bh);
if (whiteout) {
if (retval)
drop_nlink(whiteout);
unlock_new_inode(whiteout);
iput(whiteout);
}
if (handle)
ext4_journal_stop(handle);
return retval;
......@@ -3434,18 +3498,15 @@ static int ext4_rename2(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry,
unsigned int flags)
{
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
return -EINVAL;
if (flags & RENAME_EXCHANGE) {
return ext4_cross_rename(old_dir, old_dentry,
new_dir, new_dentry);
}
/*
* Existence checking was done by the VFS, otherwise "RENAME_NOREPLACE"
* is equivalent to regular rename.
*/
return ext4_rename(old_dir, old_dentry, new_dir, new_dentry);
return ext4_rename(old_dir, old_dentry, new_dir, new_dentry, flags);
}
/*
......
......@@ -47,7 +47,6 @@ extern void __init chrdev_init(void);
/*
* namei.c
*/
extern int __inode_permission(struct inode *, int);
extern int user_path_mountpoint_at(int, const char __user *, unsigned int, struct path *);
extern int vfs_path_lookup(struct dentry *, struct vfsmount *,
const char *, unsigned int, struct path *);
......@@ -138,12 +137,6 @@ extern long prune_dcache_sb(struct super_block *sb, unsigned long nr_to_scan,
*/
extern int rw_verify_area(int, struct file *, const loff_t *, size_t);
/*
* splice.c
*/
extern long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
loff_t *opos, size_t len, unsigned int flags);
/*
* pipe.c
*/
......
......@@ -416,6 +416,7 @@ int __inode_permission(struct inode *inode, int mask)
return security_inode_permission(inode, mask);
}
EXPORT_SYMBOL(__inode_permission);
/**
* sb_permission - Check superblock-level permissions
......@@ -2383,22 +2384,17 @@ kern_path_mountpoint(int dfd, const char *name, struct path *path,
}
EXPORT_SYMBOL(kern_path_mountpoint);
/*
* It's inline, so penalty for filesystems that don't use sticky bit is
* minimal.
*/
static inline int check_sticky(struct inode *dir, struct inode *inode)
int __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_wrt_inode_uidgid(inode, CAP_FOWNER);
}
EXPORT_SYMBOL(__check_sticky);
/*
* Check whether we can remove a link victim from directory dir, check
......@@ -3064,9 +3060,12 @@ static int do_last(struct nameidata *nd, struct path *path,
error = may_open(&nd->path, acc_mode, open_flag);
if (error)
goto out;
file->f_path.mnt = nd->path.mnt;
error = finish_open(file, nd->path.dentry, NULL, opened);
if (error) {
BUG_ON(*opened & FILE_OPENED); /* once it's opened, it's opened */
error = vfs_open(&nd->path, file, current_cred());
if (!error) {
*opened |= FILE_OPENED;
} else {
if (error == -EOPENSTALE)
goto stale_open;
goto out;
......@@ -4210,12 +4209,16 @@ SYSCALL_DEFINE5(renameat2, int, olddfd, const char __user *, oldname,
bool should_retry = false;
int error;
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE | RENAME_WHITEOUT))
return -EINVAL;
if ((flags & RENAME_NOREPLACE) && (flags & RENAME_EXCHANGE))
if ((flags & (RENAME_NOREPLACE | RENAME_WHITEOUT)) &&
(flags & RENAME_EXCHANGE))
return -EINVAL;
if ((flags & RENAME_WHITEOUT) && !capable(CAP_MKNOD))
return -EPERM;
retry:
from = user_path_parent(olddfd, oldname, &oldnd, lookup_flags);
if (IS_ERR(from)) {
......@@ -4347,6 +4350,20 @@ SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newna
return sys_renameat2(AT_FDCWD, oldname, AT_FDCWD, newname, 0);
}
int vfs_whiteout(struct inode *dir, struct dentry *dentry)
{
int error = may_create(dir, dentry);
if (error)
return error;
if (!dir->i_op->mknod)
return -EPERM;
return dir->i_op->mknod(dir, dentry,
S_IFCHR | WHITEOUT_MODE, WHITEOUT_DEV);
}
EXPORT_SYMBOL(vfs_whiteout);
int readlink_copy(char __user *buffer, int buflen, const char *link)
{
int len = PTR_ERR(link);
......
......@@ -1686,6 +1686,33 @@ void drop_collected_mounts(struct vfsmount *mnt)
namespace_unlock();
}
/**
* clone_private_mount - create a private clone of a path
*
* This creates a new vfsmount, which will be the clone of @path. The new will
* not be attached anywhere in the namespace and will be private (i.e. changes
* to the originating mount won't be propagated into this).
*
* Release with mntput().
*/
struct vfsmount *clone_private_mount(struct path *path)
{
struct mount *old_mnt = real_mount(path->mnt);
struct mount *new_mnt;
if (IS_MNT_UNBINDABLE(old_mnt))
return ERR_PTR(-EINVAL);
down_read(&namespace_sem);
new_mnt = clone_mnt(old_mnt, path->dentry, CL_PRIVATE);
up_read(&namespace_sem);
if (IS_ERR(new_mnt))
return ERR_CAST(new_mnt);
return &new_mnt->mnt;
}
EXPORT_SYMBOL_GPL(clone_private_mount);
int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
struct vfsmount *root)
{
......
......@@ -823,8 +823,7 @@ struct file *dentry_open(const struct path *path, int flags,
f = get_empty_filp();
if (!IS_ERR(f)) {
f->f_flags = flags;
f->f_path = *path;
error = do_dentry_open(f, NULL, cred);
error = vfs_open(path, f, cred);
if (!error) {
/* from now on we need fput() to dispose of f */
error = open_check_o_direct(f);
......@@ -841,6 +840,26 @@ struct file *dentry_open(const struct path *path, int flags,
}
EXPORT_SYMBOL(dentry_open);
/**
* vfs_open - open the file at the given path
* @path: path to open
* @filp: newly allocated file with f_flag initialized
* @cred: credentials to use
*/
int vfs_open(const struct path *path, struct file *filp,
const struct cred *cred)
{
struct inode *inode = path->dentry->d_inode;
if (inode->i_op->dentry_open)
return inode->i_op->dentry_open(path->dentry, filp, cred);
else {
filp->f_path = *path;
return do_dentry_open(filp, NULL, cred);
}
}
EXPORT_SYMBOL(vfs_open);
static inline int build_open_flags(int flags, umode_t mode, struct open_flags *op)
{
int lookup_flags = 0;
......
config OVERLAYFS_FS
tristate "Overlay filesystem support"
help
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.
For more information see Documentation/filesystems/overlayfs.txt
#
# Makefile for the overlay filesystem.
#
obj-$(CONFIG_OVERLAYFS_FS) += overlayfs.o
overlayfs-objs := super.o inode.o dir.o readdir.o copy_up.o
/*
*
* Copyright (C) 2011 Novell 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/slab.h>
#include <linux/file.h>
#include <linux/splice.h>
#include <linux/xattr.h>
#include <linux/security.h>
#include <linux/uaccess.h>
#include <linux/sched.h>
#include <linux/namei.h>
#include "overlayfs.h"
#define OVL_COPY_UP_CHUNK_SIZE (1 << 20)
int ovl_copy_xattr(struct dentry *old, struct dentry *new)
{
ssize_t list_size, size;
char *buf, *name, *value;
int error;
if (!old->d_inode->i_op->getxattr ||
!new->d_inode->i_op->getxattr)
return 0;
list_size = vfs_listxattr(old, NULL, 0);
if (list_size <= 0) {
if (list_size == -EOPNOTSUPP)
return 0;
return list_size;
}
buf = kzalloc(list_size, GFP_KERNEL);
if (!buf)
return -ENOMEM;
error = -ENOMEM;
value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL);
if (!value)
goto out;
list_size = vfs_listxattr(old, buf, list_size);
if (list_size <= 0) {
error = list_size;
goto out_free_value;
}