Merge branch 'for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull more vfs updates from Al Viro:
 "Assorted stuff from this cycle.  The big ones here are multilayer
  overlayfs from Miklos and beginning of sorting ->d_inode accesses out
  from David"

* 'for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: (51 commits)
  autofs4 copy_dev_ioctl(): keep the value of ->size we'd used for allocation
  procfs: fix race between symlink removals and traversals
  debugfs: leave freeing a symlink body until inode eviction
  Documentation/filesystems/Locking: ->get_sb() is long gone
  trylock_super(): replacement for grab_super_passive()
  fanotify: Fix up scripted S_ISDIR/S_ISREG/S_ISLNK conversions
  Cachefiles: Fix up scripted S_ISDIR/S_ISREG/S_ISLNK conversions
  VFS: (Scripted) Convert S_ISLNK/DIR/REG(dentry->d_inode) to d_is_*(dentry)
  SELinux: Use d_is_positive() rather than testing dentry->d_inode
  Smack: Use d_is_positive() rather than testing dentry->d_inode
  TOMOYO: Use d_is_dir() rather than d_inode and S_ISDIR()
  Apparmor: Use d_is_positive/negative() rather than testing dentry->d_inode
  Apparmor: mediated_filesystem() should use dentry->d_sb not inode->i_sb
  VFS: Split DCACHE_FILE_TYPE into regular and special types
  VFS: Add a fallthrough flag for marking virtual dentries
  VFS: Add a whiteout dentry type
  VFS: Introduce inode-getting helpers for layered/unioned fs environments
  Infiniband: Fix potential NULL d_inode dereference
  posix_acl: fix reference leaks in posix_acl_create
  autofs4: Wrong format for printing dentry
  ...
This commit is contained in:
Linus Torvalds 2015-02-22 17:42:14 -08:00
commit be5e6616dd
70 changed files with 927 additions and 778 deletions

View File

@ -164,8 +164,6 @@ the block device inode. See there for more details.
--------------------------- file_system_type ---------------------------
prototypes:
int (*get_sb) (struct file_system_type *, int,
const char *, void *, struct vfsmount *);
struct dentry *(*mount) (struct file_system_type *, int,
const char *, void *);
void (*kill_sb) (struct super_block *);

View File

@ -159,6 +159,22 @@ overlay filesystem (though an operation on the name of the file such as
rename or unlink will of course be noticed and handled).
Multiple lower layers
---------------------
Multiple lower layers can now be given using the the colon (":") as a
separator character between the directory names. For example:
mount -t overlay overlay -olowerdir=/lower1:/lower2:/lower3 /merged
As the example shows, "upperdir=" and "workdir=" may be omitted. In
that case the overlay will be read-only.
The specified lower directories will be stacked beginning from the
rightmost one and going left. In the above example lower1 will be the
top, lower2 the middle and lower3 the bottom layer.
Non-standard behavior
---------------------
@ -196,3 +212,15 @@ 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.
Testsuite
---------
There's testsuite developed by David Howells at:
git://git.infradead.org/users/dhowells/unionmount-testsuite.git
Run as root:
# cd unionmount-testsuite
# ./run --ov

View File

@ -74,7 +74,7 @@ static void hypfs_remove(struct dentry *dentry)
parent = dentry->d_parent;
mutex_lock(&parent->d_inode->i_mutex);
if (hypfs_positive(dentry)) {
if (S_ISDIR(dentry->d_inode->i_mode))
if (d_is_dir(dentry))
simple_rmdir(parent->d_inode, dentry);
else
simple_unlink(parent->d_inode, dentry);
@ -144,36 +144,32 @@ static int hypfs_open(struct inode *inode, struct file *filp)
return nonseekable_open(inode, filp);
}
static ssize_t hypfs_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t offset)
static ssize_t hypfs_read_iter(struct kiocb *iocb, struct iov_iter *to)
{
char *data;
ssize_t ret;
struct file *filp = iocb->ki_filp;
/* XXX: temporary */
char __user *buf = iov[0].iov_base;
size_t count = iov[0].iov_len;
struct file *file = iocb->ki_filp;
char *data = file->private_data;
size_t available = strlen(data);
loff_t pos = iocb->ki_pos;
size_t count;
if (nr_segs != 1)
if (pos < 0)
return -EINVAL;
data = filp->private_data;
ret = simple_read_from_buffer(buf, count, &offset, data, strlen(data));
if (ret <= 0)
return ret;
iocb->ki_pos += ret;
file_accessed(filp);
return ret;
if (pos >= available || !iov_iter_count(to))
return 0;
count = copy_to_iter(data + pos, available - pos, to);
if (!count)
return -EFAULT;
iocb->ki_pos = pos + count;
file_accessed(file);
return count;
}
static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t offset)
static ssize_t hypfs_write_iter(struct kiocb *iocb, struct iov_iter *from)
{
int rc;
struct super_block *sb = file_inode(iocb->ki_filp)->i_sb;
struct hypfs_sb_info *fs_info = sb->s_fs_info;
size_t count = iov_length(iov, nr_segs);
size_t count = iov_iter_count(from);
/*
* Currently we only allow one update per second for two reasons:
@ -202,6 +198,7 @@ static ssize_t hypfs_aio_write(struct kiocb *iocb, const struct iovec *iov,
}
hypfs_update_update(sb);
rc = count;
iov_iter_advance(from, count);
out:
mutex_unlock(&fs_info->lock);
return rc;
@ -440,10 +437,10 @@ struct dentry *hypfs_create_str(struct dentry *dir,
static const struct file_operations hypfs_file_ops = {
.open = hypfs_open,
.release = hypfs_release,
.read = do_sync_read,
.write = do_sync_write,
.aio_read = hypfs_aio_read,
.aio_write = hypfs_aio_write,
.read = new_sync_read,
.write = new_sync_write,
.read_iter = hypfs_read_iter,
.write_iter = hypfs_write_iter,
.llseek = no_llseek,
};

View File

@ -277,7 +277,7 @@ static int remove_file(struct dentry *parent, char *name)
}
spin_lock(&tmp->d_lock);
if (!(d_unhashed(tmp) && tmp->d_inode)) {
if (!d_unhashed(tmp) && tmp->d_inode) {
dget_dlock(tmp);
__d_drop(tmp);
spin_unlock(&tmp->d_lock);

View File

@ -455,7 +455,7 @@ static int remove_file(struct dentry *parent, char *name)
}
spin_lock(&tmp->d_lock);
if (!(d_unhashed(tmp) && tmp->d_inode)) {
if (!d_unhashed(tmp) && tmp->d_inode) {
__d_drop(tmp);
spin_unlock(&tmp->d_lock);
simple_unlink(parent->d_inode, tmp);

View File

@ -270,7 +270,7 @@ void ll_invalidate_aliases(struct inode *inode)
int ll_revalidate_it_finish(struct ptlrpc_request *request,
struct lookup_intent *it,
struct dentry *de)
struct inode *inode)
{
int rc = 0;
@ -280,19 +280,17 @@ int ll_revalidate_it_finish(struct ptlrpc_request *request,
if (it_disposition(it, DISP_LOOKUP_NEG))
return -ENOENT;
rc = ll_prep_inode(&de->d_inode, request, NULL, it);
rc = ll_prep_inode(&inode, request, NULL, it);
return rc;
}
void ll_lookup_finish_locks(struct lookup_intent *it, struct dentry *dentry)
void ll_lookup_finish_locks(struct lookup_intent *it, struct inode *inode)
{
LASSERT(it != NULL);
LASSERT(dentry != NULL);
if (it->d.lustre.it_lock_mode && dentry->d_inode != NULL) {
struct inode *inode = dentry->d_inode;
struct ll_sb_info *sbi = ll_i2sbi(dentry->d_inode);
if (it->d.lustre.it_lock_mode && inode != NULL) {
struct ll_sb_info *sbi = ll_i2sbi(inode);
CDEBUG(D_DLMTRACE, "setting l_data to inode %p (%lu/%u)\n",
inode, inode->i_ino, inode->i_generation);

View File

@ -2912,8 +2912,8 @@ static int __ll_inode_revalidate(struct dentry *dentry, __u64 ibits)
oit.it_op = IT_LOOKUP;
/* Call getattr by fid, so do not provide name at all. */
op_data = ll_prep_md_op_data(NULL, dentry->d_inode,
dentry->d_inode, NULL, 0, 0,
op_data = ll_prep_md_op_data(NULL, inode,
inode, NULL, 0, 0,
LUSTRE_OPC_ANY, NULL);
if (IS_ERR(op_data))
return PTR_ERR(op_data);
@ -2931,7 +2931,7 @@ static int __ll_inode_revalidate(struct dentry *dentry, __u64 ibits)
goto out;
}
rc = ll_revalidate_it_finish(req, &oit, dentry);
rc = ll_revalidate_it_finish(req, &oit, inode);
if (rc != 0) {
ll_intent_release(&oit);
goto out;
@ -2944,7 +2944,7 @@ static int __ll_inode_revalidate(struct dentry *dentry, __u64 ibits)
if (!dentry->d_inode->i_nlink)
d_lustre_invalidate(dentry, 0);
ll_lookup_finish_locks(&oit, dentry);
ll_lookup_finish_locks(&oit, inode);
} else if (!ll_have_md_lock(dentry->d_inode, &ibits, LCK_MINMODE)) {
struct ll_sb_info *sbi = ll_i2sbi(dentry->d_inode);
u64 valid = OBD_MD_FLGETATTR;

View File

@ -786,9 +786,9 @@ extern const struct dentry_operations ll_d_ops;
void ll_intent_drop_lock(struct lookup_intent *);
void ll_intent_release(struct lookup_intent *);
void ll_invalidate_aliases(struct inode *);
void ll_lookup_finish_locks(struct lookup_intent *it, struct dentry *dentry);
void ll_lookup_finish_locks(struct lookup_intent *it, struct inode *inode);
int ll_revalidate_it_finish(struct ptlrpc_request *request,
struct lookup_intent *it, struct dentry *de);
struct lookup_intent *it, struct inode *inode);
/* llite/llite_lib.c */
extern struct super_operations lustre_super_operations;

View File

@ -481,6 +481,7 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
struct lookup_intent lookup_it = { .it_op = IT_LOOKUP };
struct dentry *save = dentry, *retval;
struct ptlrpc_request *req = NULL;
struct inode *inode;
struct md_op_data *op_data;
__u32 opc;
int rc;
@ -539,12 +540,13 @@ static struct dentry *ll_lookup_it(struct inode *parent, struct dentry *dentry,
goto out;
}
if ((it->it_op & IT_OPEN) && dentry->d_inode &&
!S_ISREG(dentry->d_inode->i_mode) &&
!S_ISDIR(dentry->d_inode->i_mode)) {
ll_release_openhandle(dentry->d_inode, it);
inode = dentry->d_inode;
if ((it->it_op & IT_OPEN) && inode &&
!S_ISREG(inode->i_mode) &&
!S_ISDIR(inode->i_mode)) {
ll_release_openhandle(inode, it);
}
ll_lookup_finish_locks(it, dentry);
ll_lookup_finish_locks(it, inode);
if (dentry == save)
retval = NULL;

View File

@ -1127,7 +1127,7 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)
}
/* Write all dirty data */
if (S_ISREG(dentry->d_inode->i_mode))
if (d_is_reg(dentry))
filemap_write_and_wait(dentry->d_inode->i_mapping);
retval = p9_client_wstat(fid, &wstat);

View File

@ -1285,7 +1285,7 @@ SYSCALL_DEFINE2(io_setup, unsigned, nr_events, aio_context_t __user *, ctxp)
ret = -EINVAL;
if (unlikely(ctx || nr_events == 0)) {
pr_debug("EINVAL: io_setup: ctx %lu nr_events %u\n",
pr_debug("EINVAL: ctx %lu nr_events %u\n",
ctx, nr_events);
goto out;
}
@ -1333,7 +1333,7 @@ SYSCALL_DEFINE1(io_destroy, aio_context_t, ctx)
return ret;
}
pr_debug("EINVAL: io_destroy: invalid context id\n");
pr_debug("EINVAL: invalid context id\n");
return -EINVAL;
}
@ -1515,7 +1515,7 @@ static int io_submit_one(struct kioctx *ctx, struct iocb __user *user_iocb,
(iocb->aio_nbytes != (size_t)iocb->aio_nbytes) ||
((ssize_t)iocb->aio_nbytes < 0)
)) {
pr_debug("EINVAL: io_submit: overflow check\n");
pr_debug("EINVAL: overflow check\n");
return -EINVAL;
}

View File

@ -95,7 +95,7 @@ static int check_dev_ioctl_version(int cmd, struct autofs_dev_ioctl *param)
*/
static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *in)
{
struct autofs_dev_ioctl tmp;
struct autofs_dev_ioctl tmp, *res;
if (copy_from_user(&tmp, in, sizeof(tmp)))
return ERR_PTR(-EFAULT);
@ -106,7 +106,11 @@ static struct autofs_dev_ioctl *copy_dev_ioctl(struct autofs_dev_ioctl __user *i
if (tmp.size > (PATH_MAX + sizeof(tmp)))
return ERR_PTR(-ENAMETOOLONG);
return memdup_user(in, tmp.size);
res = memdup_user(in, tmp.size);
if (!IS_ERR(res))
res->size = tmp.size;
return res;
}
static inline void free_dev_ioctl(struct autofs_dev_ioctl *param)

View File

@ -374,7 +374,7 @@ static struct dentry *should_expire(struct dentry *dentry,
return NULL;
}
if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) {
if (dentry->d_inode && d_is_symlink(dentry)) {
DPRINTK("checking symlink %p %pd", dentry, dentry);
/*
* A symlink can't be "busy" in the usual sense so

View File

@ -108,7 +108,7 @@ static int autofs4_dir_open(struct inode *inode, struct file *file)
struct dentry *dentry = file->f_path.dentry;
struct autofs_sb_info *sbi = autofs4_sbi(dentry->d_sb);
DPRINTK("file=%p dentry=%p %pD", file, dentry, dentry);
DPRINTK("file=%p dentry=%p %pd", file, dentry, dentry);
if (autofs4_oz_mode(sbi))
goto out;
@ -371,7 +371,7 @@ static struct vfsmount *autofs4_d_automount(struct path *path)
* having d_mountpoint() true, so there's no need to call back
* to the daemon.
*/
if (dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)) {
if (dentry->d_inode && d_is_symlink(dentry)) {
spin_unlock(&sbi->fs_lock);
goto done;
}
@ -485,7 +485,7 @@ static int autofs4_d_manage(struct dentry *dentry, bool rcu_walk)
* an incorrect ELOOP error return.
*/
if ((!d_mountpoint(dentry) && !simple_empty(dentry)) ||
(dentry->d_inode && S_ISLNK(dentry->d_inode->i_mode)))
(dentry->d_inode && d_is_symlink(dentry)))
status = -EISDIR;
}
spin_unlock(&sbi->fs_lock);

View File

@ -15,161 +15,14 @@
#include <linux/namei.h>
#include <linux/poll.h>
static loff_t bad_file_llseek(struct file *file, loff_t offset, int whence)
{
return -EIO;
}
static ssize_t bad_file_read(struct file *filp, char __user *buf,
size_t size, loff_t *ppos)
{
return -EIO;
}
static ssize_t bad_file_write(struct file *filp, const char __user *buf,
size_t siz, loff_t *ppos)
{
return -EIO;
}
static ssize_t bad_file_aio_read(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
return -EIO;
}
static ssize_t bad_file_aio_write(struct kiocb *iocb, const struct iovec *iov,
unsigned long nr_segs, loff_t pos)
{
return -EIO;
}
static int bad_file_readdir(struct file *file, struct dir_context *ctx)
{
return -EIO;
}
static unsigned int bad_file_poll(struct file *filp, poll_table *wait)
{
return POLLERR;
}
static long bad_file_unlocked_ioctl(struct file *file, unsigned cmd,
unsigned long arg)
{
return -EIO;
}
static long bad_file_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg)
{
return -EIO;
}
static int bad_file_mmap(struct file *file, struct vm_area_struct *vma)
{
return -EIO;
}
static int bad_file_open(struct inode *inode, struct file *filp)
{
return -EIO;
}
static int bad_file_flush(struct file *file, fl_owner_t id)
{
return -EIO;
}
static int bad_file_release(struct inode *inode, struct file *filp)
{
return -EIO;
}
static int bad_file_fsync(struct file *file, loff_t start, loff_t end,
int datasync)
{
return -EIO;
}
static int bad_file_aio_fsync(struct kiocb *iocb, int datasync)
{
return -EIO;
}
static int bad_file_fasync(int fd, struct file *filp, int on)
{
return -EIO;
}
static int bad_file_lock(struct file *file, int cmd, struct file_lock *fl)
{
return -EIO;
}
static ssize_t bad_file_sendpage(struct file *file, struct page *page,
int off, size_t len, loff_t *pos, int more)
{
return -EIO;
}
static unsigned long bad_file_get_unmapped_area(struct file *file,
unsigned long addr, unsigned long len,
unsigned long pgoff, unsigned long flags)
{
return -EIO;
}
static int bad_file_check_flags(int flags)
{
return -EIO;
}
static int bad_file_flock(struct file *filp, int cmd, struct file_lock *fl)
{
return -EIO;
}
static ssize_t bad_file_splice_write(struct pipe_inode_info *pipe,
struct file *out, loff_t *ppos, size_t len,
unsigned int flags)
{
return -EIO;
}
static ssize_t bad_file_splice_read(struct file *in, loff_t *ppos,
struct pipe_inode_info *pipe, size_t len,
unsigned int flags)
{
return -EIO;
}
static const struct file_operations bad_file_ops =
{
.llseek = bad_file_llseek,
.read = bad_file_read,
.write = bad_file_write,
.aio_read = bad_file_aio_read,
.aio_write = bad_file_aio_write,
.iterate = bad_file_readdir,
.poll = bad_file_poll,
.unlocked_ioctl = bad_file_unlocked_ioctl,
.compat_ioctl = bad_file_compat_ioctl,
.mmap = bad_file_mmap,
.open = bad_file_open,
.flush = bad_file_flush,
.release = bad_file_release,
.fsync = bad_file_fsync,
.aio_fsync = bad_file_aio_fsync,
.fasync = bad_file_fasync,
.lock = bad_file_lock,
.sendpage = bad_file_sendpage,
.get_unmapped_area = bad_file_get_unmapped_area,
.check_flags = bad_file_check_flags,
.flock = bad_file_flock,
.splice_write = bad_file_splice_write,
.splice_read = bad_file_splice_read,
};
static int bad_inode_create (struct inode *dir, struct dentry *dentry,

View File

@ -776,11 +776,11 @@ static int btrfs_may_delete(struct inode *dir, struct dentry *victim, int isdir)
IS_IMMUTABLE(victim->d_inode) || IS_SWAPFILE(victim->d_inode))
return -EPERM;
if (isdir) {
if (!S_ISDIR(victim->d_inode->i_mode))
if (!d_is_dir(victim))
return -ENOTDIR;
if (IS_ROOT(victim))
return -EBUSY;
} else if (S_ISDIR(victim->d_inode->i_mode))
} else if (d_is_dir(victim))
return -EISDIR;
if (IS_DEADDIR(dir))
return -ENOENT;

View File

@ -574,7 +574,7 @@ static int cachefiles_daemon_cull(struct cachefiles_cache *cache, char *args)
/* extract the directory dentry from the cwd */
get_fs_pwd(current->fs, &path);
if (!S_ISDIR(path.dentry->d_inode->i_mode))
if (!d_can_lookup(path.dentry))
goto notdir;
cachefiles_begin_secure(cache, &saved_cred);
@ -646,7 +646,7 @@ static int cachefiles_daemon_inuse(struct cachefiles_cache *cache, char *args)
/* extract the directory dentry from the cwd */
get_fs_pwd(current->fs, &path);
if (!S_ISDIR(path.dentry->d_inode->i_mode))
if (!d_can_lookup(path.dentry))
goto notdir;
cachefiles_begin_secure(cache, &saved_cred);

View File

@ -437,7 +437,7 @@ static int cachefiles_attr_changed(struct fscache_object *_object)
if (!object->backer)
return -ENOBUFS;
ASSERT(S_ISREG(object->backer->d_inode->i_mode));
ASSERT(d_is_reg(object->backer));
fscache_set_store_limit(&object->fscache, ni_size);
@ -501,7 +501,7 @@ static void cachefiles_invalidate_object(struct fscache_operation *op)
op->object->debug_id, (unsigned long long)ni_size);
if (object->backer) {
ASSERT(S_ISREG(object->backer->d_inode->i_mode));
ASSERT(d_is_reg(object->backer));
fscache_set_store_limit(&object->fscache, ni_size);

View File

@ -277,7 +277,7 @@ static int cachefiles_bury_object(struct cachefiles_cache *cache,
_debug("remove %p from %p", rep, dir);
/* non-directories can just be unlinked */
if (!S_ISDIR(rep->d_inode->i_mode)) {
if (!d_is_dir(rep)) {
_debug("unlink stale object");
path.mnt = cache->mnt;
@ -323,7 +323,7 @@ try_again:
return 0;
}
if (!S_ISDIR(cache->graveyard->d_inode->i_mode)) {
if (!d_can_lookup(cache->graveyard)) {
unlock_rename(cache->graveyard, dir);
cachefiles_io_error(cache, "Graveyard no longer a directory");
return -EIO;
@ -475,7 +475,7 @@ int cachefiles_walk_to_object(struct cachefiles_object *parent,
ASSERT(parent->dentry);
ASSERT(parent->dentry->d_inode);
if (!(S_ISDIR(parent->dentry->d_inode->i_mode))) {
if (!(d_is_dir(parent->dentry))) {
// TODO: convert file to dir
_leave("looking up in none directory");
return -ENOBUFS;
@ -539,7 +539,7 @@ lookup_again:
_debug("mkdir -> %p{%p{ino=%lu}}",
next, next->d_inode, next->d_inode->i_ino);
} else if (!S_ISDIR(next->d_inode->i_mode)) {
} else if (!d_can_lookup(next)) {
pr_err("inode %lu is not a directory\n",
next->d_inode->i_ino);
ret = -ENOBUFS;
@ -568,8 +568,8 @@ lookup_again:
_debug("create -> %p{%p{ino=%lu}}",
next, next->d_inode, next->d_inode->i_ino);
} else if (!S_ISDIR(next->d_inode->i_mode) &&
!S_ISREG(next->d_inode->i_mode)
} else if (!d_can_lookup(next) &&
!d_is_reg(next)
) {
pr_err("inode %lu is not a file or directory\n",
next->d_inode->i_ino);
@ -642,7 +642,7 @@ lookup_again:
/* open a file interface onto a data file */
if (object->type != FSCACHE_COOKIE_TYPE_INDEX) {
if (S_ISREG(object->dentry->d_inode->i_mode)) {
if (d_is_reg(object->dentry)) {
const struct address_space_operations *aops;
ret = -EPERM;
@ -763,7 +763,7 @@ struct dentry *cachefiles_get_directory(struct cachefiles_cache *cache,
/* we need to make sure the subdir is a directory */
ASSERT(subdir->d_inode);
if (!S_ISDIR(subdir->d_inode->i_mode)) {
if (!d_can_lookup(subdir)) {
pr_err("%s is not a directory\n", dirname);
ret = -EIO;
goto check_error;

View File

@ -900,7 +900,7 @@ int cachefiles_write_page(struct fscache_storage *op, struct page *page)
return -ENOBUFS;
}
ASSERT(S_ISREG(object->backer->d_inode->i_mode));
ASSERT(d_is_reg(object->backer));
cache = container_of(object->fscache.cache,
struct cachefiles_cache, cache);

View File

@ -904,7 +904,7 @@ static int ceph_unlink(struct inode *dir, struct dentry *dentry)
} else if (ceph_snap(dir) == CEPH_NOSNAP) {
dout("unlink/rmdir dir %p dn %p inode %p\n",
dir, dentry, inode);
op = S_ISDIR(dentry->d_inode->i_mode) ?
op = d_is_dir(dentry) ?
CEPH_MDS_OP_RMDIR : CEPH_MDS_OP_UNLINK;
} else
goto out;

View File

@ -292,7 +292,7 @@ int ceph_atomic_open(struct inode *dir, struct dentry *dentry,
}
if (err)
goto out_req;
if (dn || dentry->d_inode == NULL || S_ISLNK(dentry->d_inode->i_mode)) {
if (dn || dentry->d_inode == NULL || d_is_symlink(dentry)) {
/* make vfs retry on splice, ENOENT, or symlink */
dout("atomic_open finish_no_open on dn %p\n", dn);
err = finish_no_open(file, dn);

View File

@ -304,7 +304,7 @@ static int coda_rename(struct inode *old_dir, struct dentry *old_dentry,
(const char *) old_name, (const char *)new_name);
if (!error) {
if (new_dentry->d_inode) {
if (S_ISDIR(new_dentry->d_inode->i_mode)) {
if (d_is_dir(new_dentry)) {
coda_dir_drop_nlink(old_dir);
coda_dir_inc_nlink(new_dir);
}

View File

@ -69,14 +69,13 @@ extern struct kmem_cache *configfs_dir_cachep;
extern int configfs_is_root(struct config_item *item);
extern struct inode * configfs_new_inode(umode_t mode, struct configfs_dirent *, struct super_block *);
extern int configfs_create(struct dentry *, umode_t mode, int (*init)(struct inode *));
extern int configfs_create(struct dentry *, umode_t mode, void (*init)(struct inode *));
extern int configfs_create_file(struct config_item *, const struct configfs_attribute *);
extern int configfs_make_dirent(struct configfs_dirent *,
struct dentry *, void *, umode_t, int);
extern int configfs_dirent_is_ready(struct configfs_dirent *);
extern int configfs_add_file(struct dentry *, const struct configfs_attribute *, int);
extern void configfs_hash_and_remove(struct dentry * dir, const char * name);
extern const unsigned char * configfs_get_name(struct configfs_dirent *sd);

View File

@ -240,60 +240,26 @@ int configfs_make_dirent(struct configfs_dirent * parent_sd,
return 0;
}
static int init_dir(struct inode * inode)
static void init_dir(struct inode * inode)
{
inode->i_op = &configfs_dir_inode_operations;
inode->i_fop = &configfs_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
inc_nlink(inode);
return 0;
}
static int configfs_init_file(struct inode * inode)
static void configfs_init_file(struct inode * inode)
{
inode->i_size = PAGE_SIZE;
inode->i_fop = &configfs_file_operations;
return 0;
}
static int init_symlink(struct inode * inode)
static void init_symlink(struct inode * inode)
{
inode->i_op = &configfs_symlink_inode_operations;
return 0;
}
static int create_dir(struct config_item *k, struct dentry *d)
{
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct dentry *p = d->d_parent;
BUG_ON(!k);
error = configfs_dirent_exists(p->d_fsdata, d->d_name.name);
if (!error)
error = configfs_make_dirent(p->d_fsdata, d, k, mode,
CONFIGFS_DIR | CONFIGFS_USET_CREATING);
if (!error) {
configfs_set_dir_dirent_depth(p->d_fsdata, d->d_fsdata);
error = configfs_create(d, mode, init_dir);
if (!error) {
inc_nlink(p->d_inode);
} else {
struct configfs_dirent *sd = d->d_fsdata;
if (sd) {
spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
}
}
}
return error;
}
/**
* configfs_create_dir - create a directory for an config_item.
* @item: config_itemwe're creating directory for.
@ -303,11 +269,37 @@ static int create_dir(struct config_item *k, struct dentry *d)
* until it is validated by configfs_dir_set_ready()
*/
static int configfs_create_dir(struct config_item * item, struct dentry *dentry)
static int configfs_create_dir(struct config_item *item, struct dentry *dentry)
{
int error = create_dir(item, dentry);
if (!error)
int error;
umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
struct dentry *p = dentry->d_parent;
BUG_ON(!item);
error = configfs_dirent_exists(p->d_fsdata, dentry->d_name.name);
if (unlikely(error))
return error;
error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
CONFIGFS_DIR | CONFIGFS_USET_CREATING);
if (unlikely(error))
return error;
configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
error = configfs_create(dentry, mode, init_dir);
if (!error) {
inc_nlink(p->d_inode);
item->ci_dentry = dentry;
} else {
struct configfs_dirent *sd = dentry->d_fsdata;
if (sd) {
spin_lock(&configfs_dirent_lock);
list_del_init(&sd->s_sibling);
spin_unlock(&configfs_dirent_lock);
configfs_put(sd);
}
}
return error;
}

View File

@ -313,21 +313,6 @@ const struct file_operations configfs_file_operations = {
.release = configfs_release,
};
int configfs_add_file(struct dentry * dir, const struct configfs_attribute * attr, int type)
{
struct configfs_dirent * parent_sd = dir->d_fsdata;
umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
int error = 0;
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, type);
mutex_unlock(&dir->d_inode->i_mutex);
return error;
}
/**
* configfs_create_file - create an attribute file for an item.
* @item: item we're creating for.
@ -336,9 +321,16 @@ int configfs_add_file(struct dentry * dir, const struct configfs_attribute * att
int configfs_create_file(struct config_item * item, const struct configfs_attribute * attr)
{
BUG_ON(!item || !item->ci_dentry || !attr);
struct dentry *dir = item->ci_dentry;
struct configfs_dirent *parent_sd = dir->d_fsdata;
umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
int error = 0;
return configfs_add_file(item->ci_dentry, attr,
CONFIGFS_ITEM_ATTR);
mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_NORMAL);
error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode,
CONFIGFS_ITEM_ATTR);
mutex_unlock(&dir->d_inode->i_mutex);
return error;
}

View File

@ -176,7 +176,7 @@ static void configfs_set_inode_lock_class(struct configfs_dirent *sd,
#endif /* CONFIG_LOCKDEP */
int configfs_create(struct dentry * dentry, umode_t mode, int (*init)(struct inode *))
int configfs_create(struct dentry * dentry, umode_t mode, void (*init)(struct inode *))
{
int error = 0;
struct inode *inode = NULL;
@ -198,13 +198,7 @@ int configfs_create(struct dentry * dentry, umode_t mode, int (*init)(struct ino
p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;
configfs_set_inode_lock_class(sd, inode);
if (init) {
error = init(inode);
if (error) {
iput(inode);
return error;
}
}
init(inode);
d_instantiate(dentry, inode);
if (S_ISDIR(mode) || S_ISLNK(mode))
dget(dentry); /* pin link and directory dentries in core */
@ -242,7 +236,7 @@ void configfs_drop_dentry(struct configfs_dirent * sd, struct dentry * parent)
if (dentry) {
spin_lock(&dentry->d_lock);
if (!(d_unhashed(dentry) && dentry->d_inode)) {
if (!d_unhashed(dentry) && dentry->d_inode) {
dget_dlock(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);

View File

@ -572,7 +572,7 @@ void do_coredump(const siginfo_t *siginfo)
*
* Normally core limits are irrelevant to pipes, since
* we're not writing to the file system, but we use
* cprm.limit of 1 here as a speacial value, this is a
* cprm.limit of 1 here as a special value, this is a
* consistent way to catch recursive crashes.
* We can still crash if the core_pattern binary sets
* RLIM_CORE = !1, but it runs as root, and can do

View File

@ -1659,9 +1659,25 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
}
EXPORT_SYMBOL(d_set_d_op);
/*
* d_set_fallthru - Mark a dentry as falling through to a lower layer
* @dentry - The dentry to mark
*
* Mark a dentry as falling through to the lower layer (as set with
* d_pin_lower()). This flag may be recorded on the medium.
*/
void d_set_fallthru(struct dentry *dentry)
{
spin_lock(&dentry->d_lock);
dentry->d_flags |= DCACHE_FALLTHRU;
spin_unlock(&dentry->d_lock);
}
EXPORT_SYMBOL(d_set_fallthru);
static unsigned d_flags_for_inode(struct inode *inode)
{
unsigned add_flags = DCACHE_FILE_TYPE;
unsigned add_flags = DCACHE_REGULAR_TYPE;
if (!inode)
return DCACHE_MISS_TYPE;
@ -1674,13 +1690,21 @@ static unsigned d_flags_for_inode(struct inode *inode)
else
inode->i_opflags |= IOP_LOOKUP;
}
} else if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
if (unlikely(inode->i_op->follow_link))
add_flags = DCACHE_SYMLINK_TYPE;
else
inode->i_opflags |= IOP_NOFOLLOW;
goto type_determined;
}
if (unlikely(!(inode->i_opflags & IOP_NOFOLLOW))) {
if (unlikely(inode->i_op->follow_link)) {
add_flags = DCACHE_SYMLINK_TYPE;
goto type_determined;
}
inode->i_opflags |= IOP_NOFOLLOW;
}
if (unlikely(!S_ISREG(inode->i_mode)))
add_flags = DCACHE_SPECIAL_TYPE;
type_determined:
if (unlikely(IS_AUTOMOUNT(inode)))
add_flags |= DCACHE_NEED_AUTOMOUNT;
return add_flags;
@ -1691,7 +1715,8 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode)
unsigned add_flags = d_flags_for_inode(inode);
spin_lock(&dentry->d_lock);
__d_set_type(dentry, add_flags);
dentry->d_flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU);
dentry->d_flags |= add_flags;
if (inode)
hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry);
dentry->d_inode = inode;

View File

@ -169,10 +169,19 @@ static int debugfs_show_options(struct seq_file *m, struct dentry *root)
return 0;
}
static void debugfs_evict_inode(struct inode *inode)
{
truncate_inode_pages_final(&inode->i_data);
clear_inode(inode);
if (S_ISLNK(inode->i_mode))
kfree(inode->i_private);
}
static const struct super_operations debugfs_super_operations = {
.statfs = simple_statfs,
.remount_fs = debugfs_remount,
.show_options = debugfs_show_options,
.evict_inode = debugfs_evict_inode,
};
static struct vfsmount *debugfs_automount(struct path *path)
@ -511,23 +520,14 @@ static int __debugfs_remove(struct dentry *dentry, struct dentry *parent)
int ret = 0;
if (debugfs_positive(dentry)) {
if (dentry->d_inode) {
dget(dentry);
switch (dentry->d_inode->i_mode & S_IFMT) {
case S_IFDIR:
ret = simple_rmdir(parent->d_inode, dentry);
break;
case S_IFLNK:
kfree(dentry->d_inode->i_private);
/* fall through */
default:
simple_unlink(parent->d_inode, dentry);
break;
}
if (!ret)
d_delete(dentry);
dput(dentry);
}
dget(dentry);
if (S_ISDIR(dentry->d_inode->i_mode))
ret = simple_rmdir(parent->d_inode, dentry);
else
simple_unlink(parent->d_inode, dentry);
if (!ret)
d_delete(dentry);
dput(dentry);
}
return ret;
}
@ -690,7 +690,7 @@ struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
}
d_move(old_dentry, dentry);
fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name,
S_ISDIR(old_dentry->d_inode->i_mode),
d_is_dir(old_dentry),
NULL, old_dentry);
fsnotify_oldname_free(old_name);
unlock_rename(new_dir, old_dir);

View File

@ -230,7 +230,7 @@ static int ecryptfs_open(struct inode *inode, struct file *file)
}
ecryptfs_set_file_lower(
file, ecryptfs_inode_to_private(inode)->lower_file);
if (S_ISDIR(ecryptfs_dentry->d_inode->i_mode)) {
if (d_is_dir(ecryptfs_dentry)) {
ecryptfs_printk(KERN_DEBUG, "This is a directory\n");
mutex_lock(&crypt_stat->cs_mutex);
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);

View File

@ -907,9 +907,9 @@ static int ecryptfs_setattr(struct dentry *dentry, struct iattr *ia)
lower_inode = ecryptfs_inode_to_lower(inode);
lower_dentry = ecryptfs_dentry_to_lower(dentry);
mutex_lock(&crypt_stat->cs_mutex);
if (S_ISDIR(dentry->d_inode->i_mode))
if (d_is_dir(dentry))
crypt_stat->flags &= ~(ECRYPTFS_ENCRYPTED);
else if (S_ISREG(dentry->d_inode->i_mode)
else if (d_is_reg(dentry)
&& (!(crypt_stat->flags & ECRYPTFS_POLICY_APPLIED)
|| !(crypt_stat->flags & ECRYPTFS_KEY_VALID))) {
struct ecryptfs_mount_crypt_stat *mount_crypt_stat;

View File

@ -429,7 +429,7 @@ struct dentry *exportfs_decode_fh(struct vfsmount *mnt, struct fid *fid,
if (IS_ERR(result))
return result;
if (S_ISDIR(result->d_inode->i_mode)) {
if (d_is_dir(result)) {
/*
* This request is for a directory.
*

View File

@ -769,9 +769,9 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
struct inode *inode = wb_inode(wb->b_io.prev);
struct super_block *sb = inode->i_sb;
if (!grab_super_passive(sb)) {
if (!trylock_super(sb)) {
/*
* grab_super_passive() may fail consistently due to
* trylock_super() may fail consistently due to
* s_umount being grabbed by someone else. Don't use
* requeue_io() to avoid busy retrying the inode/sb.
*/
@ -779,7 +779,7 @@ static long __writeback_inodes_wb(struct bdi_writeback *wb,
continue;
}
wrote += writeback_sb_inodes(sb, wb, work);
drop_super(sb);
up_read(&sb->s_umount);
/* refer to the same tests at the end of writeback_sb_inodes */
if (wrote) {

View File

@ -971,7 +971,7 @@ int fuse_reverse_inval_entry(struct super_block *sb, u64 parent_nodeid,
err = -EBUSY;
goto badentry;
}
if (S_ISDIR(entry->d_inode->i_mode)) {
if (d_is_dir(entry)) {
shrink_dcache_parent(entry);
if (!simple_empty(entry)) {
err = -ENOTEMPTY;

View File

@ -1809,7 +1809,7 @@ int gfs2_dir_del(struct gfs2_inode *dip, const struct dentry *dentry)
gfs2_consist_inode(dip);
dip->i_entries--;
dip->i_inode.i_mtime = dip->i_inode.i_ctime = tv;
if (S_ISDIR(dentry->d_inode->i_mode))
if (d_is_dir(dentry))
drop_nlink(&dip->i_inode);
mark_inode_dirty(&dip->i_inode);

View File

@ -530,7 +530,7 @@ static int hfsplus_rename(struct inode *old_dir, struct dentry *old_dentry,
/* Unlink destination if it already exists */
if (new_dentry->d_inode) {
if (S_ISDIR(new_dentry->d_inode->i_mode))
if (d_is_dir(new_dentry))
res = hfsplus_rmdir(new_dir, new_dentry);
else
res = hfsplus_unlink(new_dir, new_dentry);

View File

@ -678,10 +678,10 @@ static struct inode *get_inode(struct super_block *sb, struct dentry *dentry)
return NULL;
}
if (S_ISDIR(dentry->d_inode->i_mode)) {
if (d_is_dir(dentry)) {
inode->i_op = &hppfs_dir_iops;
inode->i_fop = &hppfs_dir_fops;
} else if (S_ISLNK(dentry->d_inode->i_mode)) {
} else if (d_is_symlink(dentry)) {
inode->i_op = &hppfs_link_iops;
inode->i_fop = &hppfs_file_fops;
} else {

View File

@ -84,7 +84,7 @@ extern struct file *get_empty_filp(void);
* super.c
*/
extern int do_remount_sb(struct super_block *, int, void *, int);
extern bool grab_super_passive(struct super_block *sb);
extern bool trylock_super(struct super_block *sb);
extern struct dentry *mount_fs(struct file_system_type *,
int, const char *, void *);
extern struct super_block *user_get_super(dev_t);

View File

@ -252,7 +252,7 @@ static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct de
if (!f->inocache)
return -EIO;
if (S_ISDIR(old_dentry->d_inode->i_mode))
if (d_is_dir(old_dentry))
return -EPERM;
/* XXX: This is ugly */
@ -772,7 +772,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
*/
if (new_dentry->d_inode) {
victim_f = JFFS2_INODE_INFO(new_dentry->d_inode);
if (S_ISDIR(new_dentry->d_inode->i_mode)) {
if (d_is_dir(new_dentry)) {
struct jffs2_full_dirent *fd;
mutex_lock(&victim_f->sem);
@ -807,7 +807,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
if (victim_f) {
/* There was a victim. Kill it off nicely */
if (S_ISDIR(new_dentry->d_inode->i_mode))
if (d_is_dir(new_dentry))
clear_nlink(new_dentry->d_inode);
else
drop_nlink(new_dentry->d_inode);
@ -815,7 +815,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
inode which didn't exist. */
if (victim_f->inocache) {
mutex_lock(&victim_f->sem);
if (S_ISDIR(new_dentry->d_inode->i_mode))
if (d_is_dir(new_dentry))
victim_f->inocache->pino_nlink = 0;
else
victim_f->inocache->pino_nlink--;
@ -825,7 +825,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
/* If it was a directory we moved, and there was no victim,
increase i_nlink on its new parent */
if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
if (d_is_dir(old_dentry) && !victim_f)
inc_nlink(new_dir_i);
/* Unlink the original */
@ -839,7 +839,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
mutex_lock(&f->sem);
inc_nlink(old_dentry->d_inode);
if (f->inocache && !S_ISDIR(old_dentry->d_inode->i_mode))
if (f->inocache && !d_is_dir(old_dentry))
f->inocache->pino_nlink++;
mutex_unlock(&f->sem);
@ -852,7 +852,7 @@ static int jffs2_rename (struct inode *old_dir_i, struct dentry *old_dentry,
return ret;
}
if (S_ISDIR(old_dentry->d_inode->i_mode))
if (d_is_dir(old_dentry))
drop_nlink(old_dir_i);
new_dir_i->i_mtime = new_dir_i->i_ctime = old_dir_i->i_mtime = old_dir_i->i_ctime = ITIME(now);

View File

@ -138,7 +138,7 @@ static struct dentry *jffs2_get_parent(struct dentry *child)
struct jffs2_inode_info *f;
uint32_t pino;
BUG_ON(!S_ISDIR(child->d_inode->i_mode));
BUG_ON(!d_is_dir(child));
f = JFFS2_INODE_INFO(child->d_inode);

View File

@ -329,7 +329,7 @@ int simple_rename(struct inode *old_dir, struct dentry *old_dentry,
struct inode *new_dir, struct dentry *new_dentry)
{
struct inode *inode = old_dentry->d_inode;
int they_are_dirs = S_ISDIR(old_dentry->d_inode->i_mode);
int they_are_dirs = d_is_dir(old_dentry);
if (!simple_empty(new_dentry))
return -ENOTEMPTY;

View File

@ -2814,7 +2814,7 @@ no_open:
} else if (!dentry->d_inode) {
goto out;
} else if ((open_flag & O_TRUNC) &&
S_ISREG(dentry->d_inode->i_mode)) {
d_is_reg(dentry)) {
goto out;
}
/* will fail later, go on to get the right error */

View File

@ -1907,8 +1907,8 @@ static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER)
return -EINVAL;
if (S_ISDIR(mp->m_dentry->d_inode->i_mode) !=
S_ISDIR(mnt->mnt.mnt_root->d_inode->i_mode))
if (d_is_dir(mp->m_dentry) !=
d_is_dir(mnt->mnt.mnt_root))
return -ENOTDIR;
return attach_recursive_mnt(mnt, p, mp, NULL);
@ -2180,8 +2180,8 @@ static int do_move_mount(struct path *path, const char *old_name)
if (!mnt_has_parent(old))
goto out1;
if (S_ISDIR(path->dentry->d_inode->i_mode) !=
S_ISDIR(old_path.dentry->d_inode->i_mode))
if (d_is_dir(path->dentry) !=
d_is_dir(old_path.dentry))
goto out1;
/*
* Don't move a mount residing in a shared parent.
@ -2271,7 +2271,7 @@ static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
goto unlock;
err = -EINVAL;
if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
if (d_is_symlink(newmnt->mnt.mnt_root))
goto unlock;
newmnt->mnt.mnt_flags = mnt_flags;

View File

@ -583,7 +583,7 @@ nfs4_reset_recoverydir(char *recdir)
if (status)
return status;
status = -ENOTDIR;
if (S_ISDIR(path.dentry->d_inode->i_mode)) {
if (d_is_dir(path.dentry)) {
strcpy(user_recovery_dirname, recdir);
status = 0;
}
@ -1426,7 +1426,7 @@ nfsd4_client_tracking_init(struct net *net)
nn->client_tracking_ops = &nfsd4_legacy_tracking_ops;
status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path);
if (!status) {
status = S_ISDIR(path.dentry->d_inode->i_mode);
status = d_is_dir(path.dentry);
path_put(&path);
if (status)
goto do_init;

View File

@ -114,8 +114,8 @@ static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
* We're exposing only the directories and symlinks that have to be
* traversed on the way to real exports:
*/
if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
!S_ISLNK(dentry->d_inode->i_mode)))
if (unlikely(!d_is_dir(dentry) &&
!d_is_symlink(dentry)))
return nfserr_stale;
/*
* A pseudoroot export gives permission to access only one
@ -259,7 +259,7 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
goto out;
}
if (S_ISDIR(dentry->d_inode->i_mode) &&
if (d_is_dir(dentry) &&
(dentry->d_flags & DCACHE_DISCONNECTED)) {
printk("nfsd: find_fh_dentry returned a DISCONNECTED directory: %pd2\n",
dentry);
@ -414,7 +414,7 @@ static inline void _fh_update_old(struct dentry *dentry,
{
fh->ofh_ino = ino_t_to_u32(dentry->d_inode->i_ino);
fh->ofh_generation = dentry->d_inode->i_generation;
if (S_ISDIR(dentry->d_inode->i_mode) ||
if (d_is_dir(dentry) ||
(exp->ex_flags & NFSEXP_NOSUBTREECHECK))
fh->ofh_dirino = 0;
}

View File

@ -615,9 +615,9 @@ nfsd_access(struct svc_rqst *rqstp, struct svc_fh *fhp, u32 *access, u32 *suppor
export = fhp->fh_export;
dentry = fhp->fh_dentry;
if (S_ISREG(dentry->d_inode->i_mode))
if (d_is_reg(dentry))
map = nfs3_regaccess;
else if (S_ISDIR(dentry->d_inode->i_mode))
else if (d_is_dir(dentry))
map = nfs3_diraccess;
else
map = nfs3_anyaccess;
@ -1402,7 +1402,7 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp,
switch (createmode) {
case NFS3_CREATE_UNCHECKED:
if (! S_ISREG(dchild->d_inode->i_mode))
if (! d_is_reg(dchild))
goto out;
else if (truncp) {
/* in nfsv4, we need to treat this case a little
@ -1615,7 +1615,7 @@ nfsd_link(struct svc_rqst *rqstp, struct svc_fh *ffhp,
if (err)
goto out;
err = nfserr_isdir;
if (S_ISDIR(tfhp->fh_dentry->d_inode->i_mode))
if (d_is_dir(tfhp->fh_dentry))
goto out;
err = nfserr_perm;
if (!len)

View File

@ -115,8 +115,8 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
return false;
/* sorry, fanotify only gives a damn about files and dirs */
if (!S_ISREG(path->dentry->d_inode->i_mode) &&
!S_ISDIR(path->dentry->d_inode->i_mode))
if (!d_is_reg(path->dentry) &&
!d_can_lookup(path->dentry))
return false;
if (inode_mark && vfsmnt_mark) {
@ -139,7 +139,7 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
BUG();
}
if (S_ISDIR(path->dentry->d_inode->i_mode) &&
if (d_is_dir(path->dentry) &&
!(marks_mask & FS_ISDIR & ~marks_ignored_mask))
return false;

View File

@ -191,7 +191,6 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
ovl_set_timestamps(upperdentry, stat);
return err;
}
static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir,
@ -385,7 +384,7 @@ int ovl_copy_up(struct dentry *dentry)
struct kstat stat;
enum ovl_path_type type = ovl_path_type(dentry);
if (type != OVL_PATH_LOWER)
if (OVL_TYPE_UPPER(type))
break;
next = dget(dentry);
@ -394,7 +393,7 @@ int ovl_copy_up(struct dentry *dentry)
parent = dget_parent(next);
type = ovl_path_type(parent);
if (type != OVL_PATH_LOWER)
if (OVL_TYPE_UPPER(type))
break;
dput(next);

View File

@ -19,7 +19,7 @@ void ovl_cleanup(struct inode *wdir, struct dentry *wdentry)
int err;
dget(wdentry);
if (S_ISDIR(wdentry->d_inode->i_mode))
if (d_is_dir(wdentry))
err = ovl_do_rmdir(wdir, wdentry);
else
err = ovl_do_unlink(wdir, wdentry);
@ -118,14 +118,14 @@ int ovl_create_real(struct inode *dir, struct dentry *newdentry,
static int ovl_set_opaque(struct dentry *upperdentry)
{
return ovl_do_setxattr(upperdentry, ovl_opaque_xattr, "y", 1, 0);
return ovl_do_setxattr(upperdentry, OVL_XATTR_OPAQUE, "y", 1, 0);
}
static void ovl_remove_opaque(struct dentry *upperdentry)
{
int err;
err = ovl_do_removexattr(upperdentry, ovl_opaque_xattr);
err = ovl_do_removexattr(upperdentry, OVL_XATTR_OPAQUE);
if (err) {
pr_warn("overlayfs: failed to remove opaque from '%s' (%i)\n",
upperdentry->d_name.name, err);
@ -152,7 +152,7 @@ static int ovl_dir_getattr(struct vfsmount *mnt, struct dentry *dentry,
* correct link count. nlink=1 seems to pacify 'find' and
* other utilities.
*/
if (type == OVL_PATH_MERGE)
if (OVL_TYPE_MERGE(type))
stat->nlink = 1;
return 0;
@ -506,7 +506,7 @@ static int ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir)
struct dentry *opaquedir = NULL;
int err;
if (is_dir) {
if (is_dir && OVL_TYPE_MERGE_OR_LOWER(ovl_path_type(dentry))) {
opaquedir = ovl_check_empty_and_clear(dentry);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir))
@ -630,7 +630,7 @@ static int ovl_do_remove(struct dentry *dentry, bool is_dir)
goto out_drop_write;
type = ovl_path_type(dentry);
if (type == OVL_PATH_PURE_UPPER) {
if (OVL_TYPE_PURE_UPPER(type)) {
err = ovl_remove_upper(dentry, is_dir);
} else {
const struct cred *old_cred;
@ -693,7 +693,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
bool new_create = false;
bool cleanup_whiteout = false;
bool overwrite = !(flags & RENAME_EXCHANGE);
bool is_dir = S_ISDIR(old->d_inode->i_mode);
bool is_dir = d_is_dir(old);
bool new_is_dir = false;
struct dentry *opaquedir = NULL;
const struct cred *old_cred = NULL;
@ -712,7 +712,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
/* Don't copy up directory trees */
old_type = ovl_path_type(old);
err = -EXDEV;
if ((old_type == OVL_PATH_LOWER || old_type == OVL_PATH_MERGE) && is_dir)
if (OVL_TYPE_MERGE_OR_LOWER(old_type) && is_dir)
goto out;
if (new->d_inode) {
@ -720,30 +720,30 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
if (err)
goto out;
if (S_ISDIR(new->d_inode->i_mode))
if (d_is_dir(new))
new_is_dir = true;
new_type = ovl_path_type(new);
err = -EXDEV;
if (!overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir)
if (!overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir)
goto out;
err = 0;
if (new_type == OVL_PATH_LOWER && old_type == OVL_PATH_LOWER) {
if (!OVL_TYPE_UPPER(new_type) && !OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_lower(old)->d_inode ==
ovl_dentry_lower(new)->d_inode)
goto out;
}
if (new_type != OVL_PATH_LOWER && old_type != OVL_PATH_LOWER) {
if (OVL_TYPE_UPPER(new_type) && OVL_TYPE_UPPER(old_type)) {
if (ovl_dentry_upper(old)->d_inode ==
ovl_dentry_upper(new)->d_inode)
goto out;
}
} else {
if (ovl_dentry_is_opaque(new))
new_type = OVL_PATH_UPPER;
new_type = __OVL_PATH_UPPER;
else
new_type = OVL_PATH_PURE_UPPER;
new_type = __OVL_PATH_UPPER | __OVL_PATH_PURE;
}
err = ovl_want_write(old);
@ -763,8 +763,8 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
goto out_drop_write;
}
old_opaque = old_type != OVL_PATH_PURE_UPPER;
new_opaque = new_type != OVL_PATH_PURE_UPPER;
old_opaque = !OVL_TYPE_PURE_UPPER(old_type);
new_opaque = !OVL_TYPE_PURE_UPPER(new_type);
if (old_opaque || new_opaque) {
err = -ENOMEM;
@ -787,7 +787,7 @@ static int ovl_rename2(struct inode *olddir, struct dentry *old,
old_cred = override_creds(override_cred);
}
if (overwrite && (new_type == OVL_PATH_LOWER || new_type == OVL_PATH_MERGE) && new_is_dir) {
if (overwrite && OVL_TYPE_MERGE_OR_LOWER(new_type) && new_is_dir) {
opaquedir = ovl_check_empty_and_clear(new);
err = PTR_ERR(opaquedir);
if (IS_ERR(opaquedir)) {

View File

@ -205,7 +205,7 @@ static int ovl_readlink(struct dentry *dentry, char __user *buf, int bufsiz)
static bool ovl_is_private_xattr(const char *name)
{
return strncmp(name, "trusted.overlay.", 14) == 0;
return strncmp(name, OVL_XATTR_PRE_NAME, OVL_XATTR_PRE_LEN) == 0;
}
int ovl_setxattr(struct dentry *dentry, const char *name,
@ -238,7 +238,10 @@ out:
static bool ovl_need_xattr_filter(struct dentry *dentry,
enum ovl_path_type type)
{
return type == OVL_PATH_UPPER && S_ISDIR(dentry->d_inode->i_mode);
if ((type & (__OVL_PATH_PURE | __OVL_PATH_UPPER)) == __OVL_PATH_UPPER)
return S_ISDIR(dentry->d_inode->i_mode);
else
return false;
}
ssize_t ovl_getxattr(struct dentry *dentry, const char *name,
@ -299,7 +302,7 @@ int ovl_removexattr(struct dentry *dentry, const char *name)
if (ovl_need_xattr_filter(dentry, type) && ovl_is_private_xattr(name))
goto out_drop_write;
if (type == OVL_PATH_LOWER) {
if (!OVL_TYPE_UPPER(type)) {
err = vfs_getxattr(realpath.dentry, name, NULL, 0);
if (err < 0)
goto out_drop_write;
@ -321,7 +324,7 @@ out:
static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
struct dentry *realdentry)
{
if (type != OVL_PATH_LOWER)
if (OVL_TYPE_UPPER(type))
return false;
if (special_file(realdentry->d_inode->i_mode))
@ -430,5 +433,4 @@ struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
}
return inode;
}

View File

@ -12,13 +12,20 @@
struct ovl_entry;
enum ovl_path_type {
OVL_PATH_PURE_UPPER,
OVL_PATH_UPPER,
OVL_PATH_MERGE,
OVL_PATH_LOWER,
__OVL_PATH_PURE = (1 << 0),
__OVL_PATH_UPPER = (1 << 1),
__OVL_PATH_MERGE = (1 << 2),
};
extern const char *ovl_opaque_xattr;
#define OVL_TYPE_UPPER(type) ((type) & __OVL_PATH_UPPER)
#define OVL_TYPE_MERGE(type) ((type) & __OVL_PATH_MERGE)
#define OVL_TYPE_PURE_UPPER(type) ((type) & __OVL_PATH_PURE)
#define OVL_TYPE_MERGE_OR_LOWER(type) \
(OVL_TYPE_MERGE(type) || !OVL_TYPE_UPPER(type))
#define OVL_XATTR_PRE_NAME "trusted.overlay."
#define OVL_XATTR_PRE_LEN 16
#define OVL_XATTR_OPAQUE OVL_XATTR_PRE_NAME"opaque"
static inline int ovl_do_rmdir(struct inode *dir, struct dentry *dentry)
{
@ -130,6 +137,7 @@ void ovl_dentry_version_inc(struct dentry *dentry);
void ovl_path_upper(struct dentry *dentry, struct path *path);
void ovl_path_lower(struct dentry *dentry, struct path *path);
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
int ovl_path_next(int idx, struct dentry *dentry, struct path *path);
struct dentry *ovl_dentry_upper(struct dentry *dentry);
struct dentry *ovl_dentry_lower(struct dentry *dentry);
struct dentry *ovl_dentry_real(struct dentry *dentry);

View File

@ -24,7 +24,6 @@ struct ovl_cache_entry {
struct list_head l_node;
struct rb_node node;
bool is_whiteout;
bool is_cursor;
char name[];
};
@ -40,6 +39,7 @@ struct ovl_readdir_data {
struct rb_root root;
struct list_head *list;
struct list_head middle;
struct dentry *dir;
int count;
int err;
};
@ -48,7 +48,7 @@ struct ovl_dir_file {
bool is_real;
bool is_upper;
struct ovl_dir_cache *cache;
struct ovl_cache_entry cursor;
struct list_head *cursor;
struct file *realfile;
struct file *upperfile;
};
@ -79,23 +79,49 @@ static struct ovl_cache_entry *ovl_cache_entry_find(struct rb_root *root,
return NULL;
}
static struct ovl_cache_entry *ovl_cache_entry_new(const char *name, int len,
static struct ovl_cache_entry *ovl_cache_entry_new(struct dentry *dir,
const char *name, int len,
u64 ino, unsigned int d_type)
{
struct ovl_cache_entry *p;
size_t size = offsetof(struct ovl_cache_entry, name[len + 1]);
p = kmalloc(size, GFP_KERNEL);
if (p) {
memcpy(p->name, name, len);
p->name[len] = '\0';
p->len = len;
p->type = d_type;
p->ino = ino;
p->is_whiteout = false;
p->is_cursor = false;
}
if (!p)
return NULL;
memcpy(p->name, name, len);
p->name[len] = '\0';
p->len = len;
p->type = d_type;
p->ino = ino;
p->is_whiteout = false;
if (d_type == DT_CHR) {
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
kfree(p);
return NULL;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
dentry = lookup_one_len(name, dir, len);
if (!IS_ERR(dentry)) {
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
revert_creds(old_cred);
put_cred(override_cred);
}
return p;
}
@ -122,7 +148,7 @@ static int ovl_cache_entry_add_rb(struct ovl_readdir_data *rdd,
return 0;
}
p = ovl_cache_entry_new(name, len, ino, d_type);
p = ovl_cache_entry_new(rdd->dir, name, len, ino, d_type);
if (p == NULL)
return -ENOMEM;
@ -143,7 +169,7 @@ static int ovl_fill_lower(struct ovl_readdir_data *rdd,
if (p) {
list_move_tail(&p->l_node, &rdd->middle);
} else {
p = ovl_cache_entry_new(name, namelen, ino, d_type);
p = ovl_cache_entry_new(rdd->dir, name, namelen, ino, d_type);
if (p == NULL)
rdd->err = -ENOMEM;
else
@ -168,7 +194,6 @@ static void ovl_cache_put(struct ovl_dir_file *od, struct dentry *dentry)
{
struct ovl_dir_cache *cache = od->cache;
list_del_init(&od->cursor.l_node);
WARN_ON(cache->refcount <= 0);
cache->refcount--;
if (!cache->refcount) {
@ -204,6 +229,7 @@ static inline int ovl_dir_read(struct path *realpath,
if (IS_ERR(realfile))
return PTR_ERR(realfile);
rdd->dir = realpath->dentry;
rdd->ctx.pos = 0;
do {
rdd->count = 0;
@ -227,108 +253,58 @@ static void ovl_dir_reset(struct file *file)
if (cache && ovl_dentry_version_get(dentry) != cache->version) {
ovl_cache_put(od, dentry);
od->cache = NULL;
od->cursor = NULL;
}
WARN_ON(!od->is_real && type != OVL_PATH_MERGE);
if (od->is_real && type == OVL_PATH_MERGE)
WARN_ON(!od->is_real && !OVL_TYPE_MERGE(type));
if (od->is_real && OVL_TYPE_MERGE(type))
od->is_real = false;
}
static int ovl_dir_mark_whiteouts(struct dentry *dir,
struct ovl_readdir_data *rdd)
{
struct ovl_cache_entry *p;
struct dentry *dentry;
const struct cred *old_cred;
struct cred *override_cred;
override_cred = prepare_creds();
if (!override_cred) {
ovl_cache_free(rdd->list);
return -ENOMEM;
}
/*
* CAP_DAC_OVERRIDE for lookup
*/
cap_raise(override_cred->cap_effective, CAP_DAC_OVERRIDE);
old_cred = override_creds(override_cred);
mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry(p, rdd->list, l_node) {
if (p->is_cursor)
continue;
if (p->type != DT_CHR)
continue;
dentry = lookup_one_len(p->name, dir, p->len);
if (IS_ERR(dentry))
continue;
p->is_whiteout = ovl_is_whiteout(dentry);
dput(dentry);
}
mutex_unlock(&dir->d_inode->i_mutex);
revert_creds(old_cred);
put_cred(override_cred);
return 0;
}
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
int err;
struct path lowerpath;
struct path upperpath;
struct path realpath;
struct ovl_readdir_data rdd = {
.ctx.actor = ovl_fill_merge,
.list = list,
.root = RB_ROOT,
.is_merge = false,
};
int idx, next;
ovl_path_lower(dentry, &lowerpath);
ovl_path_upper(dentry, &upperpath);
for (idx = 0; idx != -1; idx = next) {
next = ovl_path_next(idx, dentry, &realpath);
if (upperpath.dentry) {
err = ovl_dir_read(&upperpath, &rdd);
if (err)
goto out;
if (lowerpath.dentry) {
err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
if (next != -1) {
err = ovl_dir_read(&realpath, &rdd);
if (err)
goto out;
break;
} else {
/*
* Insert lowest layer entries before upper ones, this
* allows offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(&realpath, &rdd);
list_del(&rdd.middle);
}
}
if (lowerpath.dentry) {
/*
* Insert lowerpath entries before upperpath ones, this allows
* offsets to be reasonably constant
*/
list_add(&rdd.middle, rdd.list);
rdd.is_merge = true;
err = ovl_dir_read(&lowerpath, &rdd);
list_del(&rdd.middle);
}
out:
return err;
}
static void ovl_seek_cursor(struct ovl_dir_file *od, loff_t pos)
{
struct ovl_cache_entry *p;
struct list_head *p;
loff_t off = 0;
list_for_each_entry(p, &od->cache->entries, l_node) {
if (p->is_cursor)
continue;
list_for_each(p, &od->cache->entries) {
if (off >= pos)
break;
off++;
}
list_move_tail(&od->cursor.l_node, &p->l_node);
/* Cursor is safe since the cache is stable */
od->cursor = p;
}
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
@ -367,6 +343,7 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
{
struct ovl_dir_file *od = file->private_data;
struct dentry *dentry = file->f_path.dentry;
struct ovl_cache_entry *p;
if (!ctx->pos)
ovl_dir_reset(file);
@ -385,19 +362,13 @@ static int ovl_iterate(struct file *file, struct dir_context *ctx)
ovl_seek_cursor(od, ctx->pos);
}
while (od->cursor.l_node.next != &od->cache->entries) {
struct ovl_cache_entry *p;
p = list_entry(od->cursor.l_node.next, struct ovl_cache_entry, l_node);
/* Skip cursors */
if (!p->is_cursor) {
if (!p->is_whiteout) {
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break;
}
ctx->pos++;
}
list_move(&od->cursor.l_node, &p->l_node);
while (od->cursor != &od->cache->entries) {
p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
if (!p->is_whiteout)
if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
break;
od->cursor = p->l_node.next;
ctx->pos++;
}
return 0;
}
@ -452,7 +423,7 @@ static int ovl_dir_fsync(struct file *file, loff_t start, loff_t end,
/*
* Need to check if we started out being a lower dir, but got copied up
*/
if (!od->is_upper && ovl_path_type(dentry) != OVL_PATH_LOWER) {
if (!od->is_upper && OVL_TYPE_UPPER(ovl_path_type(dentry))) {
struct inode *inode = file_inode(file);
realfile = lockless_dereference(od->upperfile);
@ -516,11 +487,9 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
kfree(od);
return PTR_ERR(realfile);
}
INIT_LIST_HEAD(&od->cursor.l_node);
od->realfile = realfile;
od->is_real = (type != OVL_PATH_MERGE);
od->is_upper = (type != OVL_PATH_LOWER);
od->cursor.is_cursor = true;
od->is_real = !OVL_TYPE_MERGE(type);
od->is_upper = OVL_TYPE_UPPER(type);
file->private_data = od;
return 0;

View File

@ -35,7 +35,8 @@ struct ovl_config {
/* private information held for overlayfs's superblock */
struct ovl_fs {
struct vfsmount *upper_mnt;
struct vfsmount *lower_mnt;
unsigned numlower;
struct vfsmount **lower_mnt;
struct dentry *workdir;
long lower_namelen;
/* pathnames of lower and upper dirs, for show_options */
@ -47,7 +48,6 @@ struct ovl_dir_cache;
/* private information held for every overlayfs dentry */
struct ovl_entry {
struct dentry *__upperdentry;
struct dentry *lowerdentry;
struct ovl_dir_cache *cache;
union {
struct {
@ -56,30 +56,36 @@ struct ovl_entry {
};
struct rcu_head rcu;
};
unsigned numlower;
struct path lowerstack[];
};
const char *ovl_opaque_xattr = "trusted.overlay.opaque";
#define OVL_MAX_STACK 500
static struct dentry *__ovl_dentry_lower(struct ovl_entry *oe)
{
return oe->numlower ? oe->lowerstack[0].dentry : NULL;
}
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) {
if (oe->lowerdentry) {
type = __OVL_PATH_UPPER;
if (oe->numlower) {
if (S_ISDIR(dentry->d_inode->i_mode))
return OVL_PATH_MERGE;
else
return OVL_PATH_UPPER;
} else {
if (oe->opaque)
return OVL_PATH_UPPER;
else
return OVL_PATH_PURE_UPPER;
type |= __OVL_PATH_MERGE;
} else if (!oe->opaque) {
type |= __OVL_PATH_PURE;
}
} else {
return OVL_PATH_LOWER;
if (oe->numlower > 1)
type |= __OVL_PATH_MERGE;
}
return type;
}
static struct dentry *ovl_upperdentry_dereference(struct ovl_entry *oe)
@ -98,10 +104,9 @@ void ovl_path_upper(struct dentry *dentry, struct path *path)
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
{
enum ovl_path_type type = ovl_path_type(dentry);
if (type == OVL_PATH_LOWER)
if (!OVL_TYPE_UPPER(type))
ovl_path_lower(dentry, path);
else
ovl_path_upper(dentry, path);
@ -120,7 +125,7 @@ struct dentry *ovl_dentry_lower(struct dentry *dentry)
{
struct ovl_entry *oe = dentry->d_fsdata;
return oe->lowerdentry;
return __ovl_dentry_lower(oe);
}
struct dentry *ovl_dentry_real(struct dentry *dentry)
@ -130,7 +135,7 @@ struct dentry *ovl_dentry_real(struct dentry *dentry)
realdentry = ovl_upperdentry_dereference(oe);
if (!realdentry)
realdentry = oe->lowerdentry;
realdentry = __ovl_dentry_lower(oe);
return realdentry;
}
@ -143,7 +148,7 @@ struct dentry *ovl_entry_real(struct ovl_entry *oe, bool *is_upper)
if (realdentry) {
*is_upper = true;
} else {
realdentry = oe->lowerdentry;
realdentry = __ovl_dentry_lower(oe);
*is_upper = false;
}
return realdentry;
@ -165,11 +170,9 @@ void ovl_set_dir_cache(struct dentry *dentry, struct ovl_dir_cache *cache)
void ovl_path_lower(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->lower_mnt;
path->dentry = oe->lowerdentry;
*path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL };
}
int ovl_want_write(struct dentry *dentry)
@ -249,7 +252,7 @@ static bool ovl_is_opaquedir(struct dentry *dentry)
if (!S_ISDIR(inode->i_mode) || !inode->i_op->getxattr)
return false;
res = inode->i_op->getxattr(dentry, ovl_opaque_xattr, &val, 1);
res = inode->i_op->getxattr(dentry, OVL_XATTR_OPAQUE, &val, 1);
if (res == 1 && val == 'y')
return true;
@ -261,8 +264,11 @@ static void ovl_dentry_release(struct dentry *dentry)
struct ovl_entry *oe = dentry->d_fsdata;
if (oe) {
unsigned int i;
dput(oe->__upperdentry);
dput(oe->lowerdentry);
for (i = 0; i < oe->numlower; i++)
dput(oe->lowerstack[i].dentry);
kfree_rcu(oe, rcu);
}
}
@ -271,9 +277,15 @@ static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release,
};
static struct ovl_entry *ovl_alloc_entry(void)
static struct ovl_entry *ovl_alloc_entry(unsigned int numlower)
{
return kzalloc(sizeof(struct ovl_entry), GFP_KERNEL);
size_t size = offsetof(struct ovl_entry, lowerstack[numlower]);
struct ovl_entry *oe = kzalloc(size, GFP_KERNEL);
if (oe)
oe->numlower = numlower;
return oe;
}
static inline struct dentry *ovl_lookup_real(struct dentry *dir,
@ -295,82 +307,154 @@ static inline struct dentry *ovl_lookup_real(struct dentry *dir,
return dentry;
}
/*
* 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;
struct dentry *upperdir;
struct dentry *lowerdir;
struct dentry *upperdentry = NULL;
struct dentry *lowerdentry = NULL;
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;
struct dentry *this, *prev = NULL;
unsigned int i;
int err;
err = -ENOMEM;
oe = ovl_alloc_entry();
if (!oe)
goto out;
upperdir = ovl_dentry_upper(dentry->d_parent);
lowerdir = ovl_dentry_lower(dentry->d_parent);
upperdir = ovl_upperdentry_dereference(poe);
if (upperdir) {
upperdentry = ovl_lookup_real(upperdir, &dentry->d_name);
err = PTR_ERR(upperdentry);
if (IS_ERR(upperdentry))
goto out_put_dir;
this = ovl_lookup_real(upperdir, &dentry->d_name);
err = PTR_ERR(this);
if (IS_ERR(this))
goto out;
if (lowerdir && upperdentry) {
if (ovl_is_whiteout(upperdentry)) {
dput(upperdentry);
upperdentry = NULL;
oe->opaque = true;
} else if (ovl_is_opaquedir(upperdentry)) {
oe->opaque = true;
if (this) {
if (ovl_is_whiteout(this)) {
dput(this);
this = NULL;
upperopaque = true;
} else if (poe->numlower && ovl_is_opaquedir(this)) {
upperopaque = true;
}
}
}
if (lowerdir && !oe->opaque) {
lowerdentry = ovl_lookup_real(lowerdir, &dentry->d_name);
err = PTR_ERR(lowerdentry);
if (IS_ERR(lowerdentry))
goto out_dput_upper;
upperdentry = prev = this;
}
if (lowerdentry && upperdentry &&
(!S_ISDIR(upperdentry->d_inode->i_mode) ||
!S_ISDIR(lowerdentry->d_inode->i_mode))) {
dput(lowerdentry);
lowerdentry = NULL;
oe->opaque = true;
if (!upperopaque && poe->numlower) {
err = -ENOMEM;
stack = kcalloc(poe->numlower, sizeof(struct path), GFP_KERNEL);
if (!stack)
goto out_put_upper;
}
if (lowerdentry || upperdentry) {
for (i = 0; !upperopaque && i < poe->numlower; i++) {
bool opaque = false;
struct path lowerpath = poe->lowerstack[i];
this = ovl_lookup_real(lowerpath.dentry, &dentry->d_name);
err = PTR_ERR(this);
if (IS_ERR(this)) {
/*
* If it's positive, then treat ENAMETOOLONG as ENOENT.
*/
if (err == -ENAMETOOLONG && (upperdentry || ctr))
continue;
goto out_put;
}
if (!this)
continue;
if (ovl_is_whiteout(this)) {
dput(this);
break;
}
/*
* Only makes sense to check opaque dir if this is not the
* lowermost layer.
*/
if (i < poe->numlower - 1 && ovl_is_opaquedir(this))
opaque = true;
if (prev && (!S_ISDIR(prev->d_inode->i_mode) ||
!S_ISDIR(this->d_inode->i_mode))) {
/*
* FIXME: check for upper-opaqueness maybe better done
* in remove code.
*/
if (prev == upperdentry)
upperopaque = true;
dput(this);
break;
}
/*
* If this is a non-directory then stop here.
*/
if (!S_ISDIR(this->d_inode->i_mode))
opaque = true;
stack[ctr].dentry = this;
stack[ctr].mnt = lowerpath.mnt;
ctr++;
prev = this;
if (opaque)
break;
}
oe = ovl_alloc_entry(ctr);
err = -ENOMEM;
if (!oe)
goto out_put;
if (upperdentry || ctr) {
struct dentry *realdentry;
realdentry = upperdentry ? upperdentry : lowerdentry;
realdentry = upperdentry ? upperdentry : stack[0].dentry;
err = -ENOMEM;
inode = ovl_new_inode(dentry->d_sb, realdentry->d_inode->i_mode,
oe);
if (!inode)
goto out_dput;
goto out_free_oe;
ovl_copyattr(realdentry->d_inode, inode);
}
oe->opaque = upperopaque;
oe->__upperdentry = upperdentry;
oe->lowerdentry = lowerdentry;
memcpy(oe->lowerstack, stack, sizeof(struct path) * ctr);
kfree(stack);
dentry->d_fsdata = oe;
d_add(dentry, inode);
return NULL;
out_dput:
dput(lowerdentry);
out_dput_upper:
dput(upperdentry);
out_put_dir:
out_free_oe:
kfree(oe);
out_put:
for (i = 0; i < ctr; i++)
dput(stack[i].dentry);
kfree(stack);
out_put_upper:
dput(upperdentry);
out:
return ERR_PTR(err);
}
@ -383,10 +467,12 @@ struct file *ovl_path_open(struct path *path, int flags)
static void ovl_put_super(struct super_block *sb)
{
struct ovl_fs *ufs = sb->s_fs_info;
unsigned i;
dput(ufs->workdir);
mntput(ufs->upper_mnt);
mntput(ufs->lower_mnt);
for (i = 0; i < ufs->numlower; i++)
mntput(ufs->lower_mnt[i]);
kfree(ufs->config.lowerdir);
kfree(ufs->config.upperdir);
@ -400,7 +486,7 @@ static void ovl_put_super(struct super_block *sb)
* @buf: The struct kstatfs to fill in with stats
*
* Get the filesystem statistics. As writes always target the upper layer
* filesystem pass the statfs to the same filesystem.
* filesystem pass the statfs to the upper filesystem (if it exists)
*/
static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
{
@ -409,7 +495,7 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
struct path path;
int err;
ovl_path_upper(root_dentry, &path);
ovl_path_real(root_dentry, &path);
err = vfs_statfs(&path, buf);
if (!err) {
@ -432,8 +518,21 @@ static int ovl_show_options(struct seq_file *m, struct dentry *dentry)
struct ovl_fs *ufs = sb->s_fs_info;
seq_printf(m, ",lowerdir=%s", ufs->config.lowerdir);
seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
seq_printf(m, ",workdir=%s", ufs->config.workdir);
if (ufs->config.upperdir) {
seq_printf(m, ",upperdir=%s", ufs->config.upperdir);
seq_printf(m, ",workdir=%s", ufs->config.workdir);
}
return 0;
}
static int ovl_remount(struct super_block *sb, int *flags, char *data)
{
struct ovl_fs *ufs = sb->s_fs_info;
if (!(*flags & MS_RDONLY) &&
(!ufs->upper_mnt || (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)))
return -EROFS;
return 0;
}
@ -441,6 +540,7 @@ static const struct super_operations ovl_super_operations = {
.put_super = ovl_put_super,
.statfs = ovl_statfs,
.show_options = ovl_show_options,
.remount_fs = ovl_remount,
};
enum {
@ -585,24 +685,6 @@ static void ovl_unescape(char *s)
}
}
static int ovl_mount_dir(const char *name, struct path *path)
{
int err;
char *tmp = kstrdup(name, GFP_KERNEL);
if (!tmp)
return -ENOMEM;
ovl_unescape(tmp);
err = kern_path(tmp, LOOKUP_FOLLOW, path);
if (err) {
pr_err("overlayfs: failed to resolve '%s': %i\n", tmp, err);
err = -EINVAL;
}
kfree(tmp);
return err;
}
static bool ovl_is_allowed_fs_type(struct dentry *root)
{
const struct dentry_operations *dop = root->d_op;
@ -622,6 +704,75 @@ static bool ovl_is_allowed_fs_type(struct dentry *root)
return true;
}
static int ovl_mount_dir_noesc(const char *name, struct path *path)
{
int err = -EINVAL;
if (!*name) {
pr_err("overlayfs: empty lowerdir\n");
goto out;
}
err = kern_path(name, LOOKUP_FOLLOW, path);
if (err) {
pr_err("overlayfs: failed to resolve '%s': %i\n", name, err);
goto out;
}
err = -EINVAL;
if (!ovl_is_allowed_fs_type(path->dentry)) {
pr_err("overlayfs: filesystem on '%s' not supported\n", name);
goto out_put;
}
if (!S_ISDIR(path->dentry->d_inode->i_mode)) {
pr_err("overlayfs: '%s' not a directory\n", name);
goto out_put;
}
return 0;
out_put:
path_put(path);
out:
return err;
}
static int ovl_mount_dir(const char *name, struct path *path)
{
int err = -ENOMEM;
char *tmp = kstrdup(name, GFP_KERNEL);
if (tmp) {
ovl_unescape(tmp);
err = ovl_mount_dir_noesc(tmp, path);
kfree(tmp);
}
return err;
}
static int ovl_lower_dir(const char *name, struct path *path, long *namelen,
int *stack_depth)
{
int err;
struct kstatfs statfs;
err = ovl_mount_dir_noesc(name, path);
if (err)
goto out;
err = vfs_statfs(path, &statfs);
if (err) {
pr_err("overlayfs: statfs failed on '%s'\n", name);
goto out_put;
}
*namelen = max(*namelen, statfs.f_namelen);
*stack_depth = max(*stack_depth, path->mnt->mnt_sb->s_stack_depth);
return 0;
out_put:
path_put(path);
out:
return err;
}
/* Workdir should not be subdir of upperdir and vice versa */
static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
{
@ -634,16 +785,39 @@ static bool ovl_workdir_ok(struct dentry *workdir, struct dentry *upperdir)
return ok;
}
static unsigned int ovl_split_lowerdirs(char *str)
{
unsigned int ctr = 1;
char *s, *d;
for (s = d = str;; s++, d++) {
if (*s == '\\') {
s++;
} else if (*s == ':') {
*d = '\0';
ctr++;
continue;
}
*d = *s;
if (!*s)
break;
}
return ctr;
}
static int ovl_fill_super(struct super_block *sb, void *data, int silent)
{
struct path lowerpath;
struct path upperpath;
struct path workpath;
struct inode *root_inode;
struct path upperpath = { NULL, NULL };
struct path workpath = { NULL, NULL };
struct dentry *root_dentry;
struct ovl_entry *oe;
struct ovl_fs *ufs;
struct kstatfs statfs;
struct path *stack = NULL;
char *lowertmp;
char *lower;
unsigned int numlower;
unsigned int stacklen = 0;
unsigned int i;
int err;
err = -ENOMEM;
@ -655,123 +829,135 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
if (err)
goto out_free_config;
/* FIXME: workdir is not needed for a R/O mount */
err = -EINVAL;
if (!ufs->config.upperdir || !ufs->config.lowerdir ||
!ufs->config.workdir) {
pr_err("overlayfs: missing upperdir or lowerdir or workdir\n");
if (!ufs->config.lowerdir) {
pr_err("overlayfs: missing 'lowerdir'\n");
goto out_free_config;
}
sb->s_stack_depth = 0;
if (ufs->config.upperdir) {
/* FIXME: workdir is not needed for a R/O mount */
if (!ufs->config.workdir) {
pr_err("overlayfs: missing 'workdir'\n");
goto out_free_config;
}
err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
if (err)
goto out_free_config;
err = ovl_mount_dir(ufs->config.workdir, &workpath);
if (err)
goto out_put_upperpath;
err = -EINVAL;
if (upperpath.mnt != workpath.mnt) {
pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
goto out_put_workpath;
}
if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
goto out_put_workpath;
}
sb->s_stack_depth = upperpath.mnt->mnt_sb->s_stack_depth;
}
err = -ENOMEM;
lowertmp = kstrdup(ufs->config.lowerdir, GFP_KERNEL);
if (!lowertmp)
goto out_put_workpath;
err = -EINVAL;
stacklen = ovl_split_lowerdirs(lowertmp);
if (stacklen > OVL_MAX_STACK)
goto out_free_lowertmp;
stack = kcalloc(stacklen, sizeof(struct path), GFP_KERNEL);
if (!stack)
goto out_free_lowertmp;
lower = lowertmp;
for (numlower = 0; numlower < stacklen; numlower++) {
err = ovl_lower_dir(lower, &stack[numlower],
&ufs->lower_namelen, &sb->s_stack_depth);
if (err)
goto out_put_lowerpath;
lower = strchr(lower, '\0') + 1;
}
err = -EINVAL;
sb->s_stack_depth++;
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("overlayfs: maximum fs stacking depth exceeded\n");
goto out_put_lowerpath;
}
if (ufs->config.upperdir) {
ufs->upper_mnt = clone_private_mount(&upperpath);
err = PTR_ERR(ufs->upper_mnt);
if (IS_ERR(ufs->upper_mnt)) {
pr_err("overlayfs: failed to clone upperpath\n");
goto out_put_lowerpath;
}
ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
err = PTR_ERR(ufs->workdir);
if (IS_ERR(ufs->workdir)) {
pr_err("overlayfs: failed to create directory %s/%s\n",
ufs->config.workdir, OVL_WORKDIR_NAME);
goto out_put_upper_mnt;
}
}
err = -ENOMEM;
oe = ovl_alloc_entry();
if (oe == NULL)
goto out_free_config;
ufs->lower_mnt = kcalloc(numlower, sizeof(struct vfsmount *), GFP_KERNEL);
if (ufs->lower_mnt == NULL)
goto out_put_workdir;
for (i = 0; i < numlower; i++) {
struct vfsmount *mnt = clone_private_mount(&stack[i]);
err = ovl_mount_dir(ufs->config.upperdir, &upperpath);
if (err)
goto out_free_oe;
err = PTR_ERR(mnt);
if (IS_ERR(mnt)) {
pr_err("overlayfs: failed to clone lowerpath\n");
goto out_put_lower_mnt;
}
/*
* Make lower_mnt R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs.
*/
mnt->mnt_flags |= MNT_READONLY;
err = ovl_mount_dir(ufs->config.lowerdir, &lowerpath);
if (err)
goto out_put_upperpath;
err = ovl_mount_dir(ufs->config.workdir, &workpath);
if (err)
goto out_put_lowerpath;
err = -EINVAL;
if (!S_ISDIR(upperpath.dentry->d_inode->i_mode) ||
!S_ISDIR(lowerpath.dentry->d_inode->i_mode) ||
!S_ISDIR(workpath.dentry->d_inode->i_mode)) {
pr_err("overlayfs: upperdir or lowerdir or workdir not a directory\n");
goto out_put_workpath;
ufs->lower_mnt[ufs->numlower] = mnt;
ufs->numlower++;
}
if (upperpath.mnt != workpath.mnt) {
pr_err("overlayfs: workdir and upperdir must reside under the same mount\n");
goto out_put_workpath;
}
if (!ovl_workdir_ok(workpath.dentry, upperpath.dentry)) {
pr_err("overlayfs: workdir and upperdir must be separate subtrees\n");
goto out_put_workpath;
}
if (!ovl_is_allowed_fs_type(upperpath.dentry)) {
pr_err("overlayfs: filesystem of upperdir is not supported\n");
goto out_put_workpath;
}
if (!ovl_is_allowed_fs_type(lowerpath.dentry)) {
pr_err("overlayfs: filesystem of lowerdir is not supported\n");
goto out_put_workpath;
}
err = vfs_statfs(&lowerpath, &statfs);
if (err) {
pr_err("overlayfs: statfs failed on lowerpath\n");
goto out_put_workpath;
}
ufs->lower_namelen = statfs.f_namelen;
sb->s_stack_depth = max(upperpath.mnt->mnt_sb->s_stack_depth,
lowerpath.mnt->mnt_sb->s_stack_depth) + 1;
err = -EINVAL;
if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) {
pr_err("overlayfs: maximum fs stacking depth exceeded\n");
goto out_put_workpath;
}
ufs->upper_mnt = clone_private_mount(&upperpath);
err = PTR_ERR(ufs->upper_mnt);
if (IS_ERR(ufs->upper_mnt)) {
pr_err("overlayfs: failed to clone upperpath\n");
goto out_put_workpath;
}
ufs->lower_mnt = clone_private_mount(&lowerpath);
err = PTR_ERR(ufs->lower_mnt);
if (IS_ERR(ufs->lower_mnt)) {
pr_err("overlayfs: failed to clone lowerpath\n");
goto out_put_upper_mnt;
}
ufs->workdir = ovl_workdir_create(ufs->upper_mnt, workpath.dentry);
err = PTR_ERR(ufs->workdir);
if (IS_ERR(ufs->workdir)) {
pr_err("overlayfs: failed to create directory %s/%s\n",
ufs->config.workdir, OVL_WORKDIR_NAME);
goto out_put_lower_mnt;
}
/*
* Make lower_mnt R/O. That way fchmod/fchown on lower file
* will fail instead of modifying lower fs.
*/
ufs->lower_mnt->mnt_flags |= MNT_READONLY;
/* If the upper fs is r/o, we mark overlayfs r/o too */
if (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY)
/* If the upper fs is r/o or nonexistent, we mark overlayfs r/o too */
if (!ufs->upper_mnt || (ufs->upper_mnt->mnt_sb->s_flags & MS_RDONLY))
sb->s_flags |= MS_RDONLY;
sb->s_d_op = &ovl_dentry_operations;
err = -ENOMEM;
root_inode = ovl_new_inode(sb, S_IFDIR, oe);
if (!root_inode)
goto out_put_workdir;
oe = ovl_alloc_entry(numlower);
if (!oe)
goto out_put_lower_mnt;
root_dentry = d_make_root(root_inode);
root_dentry = d_make_root(ovl_new_inode(sb, S_IFDIR, oe));
if (!root_dentry)
goto out_put_workdir;
goto out_free_oe;
mntput(upperpath.mnt);
mntput(lowerpath.mnt);
for (i = 0; i < numlower; i++)
mntput(stack[i].mnt);
path_put(&workpath);
kfree(lowertmp);
oe->__upperdentry = upperpath.dentry;
oe->lowerdentry = lowerpath.dentry;
for (i = 0; i < numlower; i++) {
oe->lowerstack[i].dentry = stack[i].dentry;
oe->lowerstack[i].mnt = ufs->lower_mnt[i];
}
root_dentry->d_fsdata = oe;
@ -782,20 +968,26 @@ static int ovl_fill_super(struct super_block *sb, void *data, int silent)
return 0;
out_put_workdir:
dput(ufs->workdir);
out_put_lower_mnt:
mntput(ufs->lower_mnt);
out_put_upper_mnt:
mntput(ufs->upper_mnt);
out_put_workpath:
path_put(&workpath);
out_put_lowerpath:
path_put(&lowerpath);
out_put_upperpath:
path_put(&upperpath);
out_free_oe:
kfree(oe);
out_put_lower_mnt:
for (i = 0; i < ufs->numlower; i++)
mntput(ufs->lower_mnt[i]);
kfree(ufs->lower_mnt);
out_put_workdir:
dput(ufs->workdir);
out_put_upper_mnt:
mntput(ufs->upper_mnt);
out_put_lowerpath:
for (i = 0; i < numlower; i++)
path_put(&stack[i]);
kfree(stack);
out_free_lowertmp:
kfree(lowertmp);
out_put_workpath:
path_put(&workpath);
out_put_upperpath:
path_put(&upperpath);
out_free_config:
kfree(ufs->config.lowerdir);
kfree(ufs->config.upperdir);

View File

@ -564,13 +564,11 @@ posix_acl_create(struct inode *dir, umode_t *mode,
*acl = posix_acl_clone(p, GFP_NOFS);
if (!*acl)
return -ENOMEM;
goto no_mem;
ret = posix_acl_create_masq(*acl, mode);
if (ret < 0) {
posix_acl_release(*acl);
return -ENOMEM;
}
if (ret < 0)
goto no_mem_clone;
if (ret == 0) {
posix_acl_release(*acl);
@ -591,6 +589,12 @@ no_acl:
*default_acl = NULL;
*acl = NULL;
return 0;
no_mem_clone:
posix_acl_release(*acl);
no_mem:
posix_acl_release(p);
return -ENOMEM;
}
EXPORT_SYMBOL_GPL(posix_acl_create);
@ -772,7 +776,7 @@ posix_acl_xattr_get(struct dentry *dentry, const char *name,
if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode))
if (d_is_symlink(dentry))
return -EOPNOTSUPP;
acl = get_acl(dentry->d_inode, type);
@ -832,7 +836,7 @@ posix_acl_xattr_list(struct dentry *dentry, char *list, size_t list_size,
if (!IS_POSIXACL(dentry->d_inode))
return -EOPNOTSUPP;
if (S_ISLNK(dentry->d_inode->i_mode))
if (d_is_symlink(dentry))
return -EOPNOTSUPP;
if (type == ACL_TYPE_ACCESS)

View File

@ -19,7 +19,6 @@
#include <linux/mount.h>
#include <linux/init.h>
#include <linux/idr.h>
#include <linux/namei.h>
#include <linux/bitops.h>
#include <linux/spinlock.h>
#include <linux/completion.h>
@ -223,17 +222,6 @@ void proc_free_inum(unsigned int inum)
spin_unlock_irqrestore(&proc_inum_lock, flags);
}
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
{
nd_set_link(nd, __PDE_DATA(dentry->d_inode));
return NULL;
}
static const struct inode_operations proc_link_inode_operations = {
.readlink = generic_readlink,
.follow_link = proc_follow_link,
};
/*
* Don't create negative dentries here, return -ENOENT by hand
* instead.

View File

@ -23,6 +23,7 @@
#include <linux/slab.h>
#include <linux/mount.h>
#include <linux/magic.h>
#include <linux/namei.h>
#include <asm/uaccess.h>
@ -393,6 +394,26 @@ static const struct file_operations proc_reg_file_ops_no_compat = {
};
#endif
static void *proc_follow_link(struct dentry *dentry, struct nameidata *nd)
{
struct proc_dir_entry *pde = PDE(dentry->d_inode);
if (unlikely(!use_pde(pde)))
return ERR_PTR(-EINVAL);
nd_set_link(nd, pde->data);
return pde;
}
static void proc_put_link(struct dentry *dentry, struct nameidata *nd, void *p)
{
unuse_pde(p);
}
const struct inode_operations proc_link_inode_operations = {
.readlink = generic_readlink,
.follow_link = proc_follow_link,
.put_link = proc_put_link,
};
struct inode *proc_get_inode(struct super_block *sb, struct proc_dir_entry *de)
{
struct inode *inode = new_inode_pseudo(sb);

View File

@ -200,6 +200,7 @@ struct pde_opener {
int closing;
struct completion *c;
};
extern const struct inode_operations proc_link_inode_operations;
extern const struct inode_operations proc_pid_link_inode_operations;

View File

@ -266,7 +266,7 @@ static int reiserfs_for_each_xattr(struct inode *inode,
for (i = 0; !err && i < buf.count && buf.dentries[i]; i++) {
struct dentry *dentry = buf.dentries[i];
if (!S_ISDIR(dentry->d_inode->i_mode))
if (!d_is_dir(dentry))
err = action(dentry, data);
dput(dentry);
@ -322,7 +322,7 @@ static int delete_one_xattr(struct dentry *dentry, void *data)
struct inode *dir = dentry->d_parent->d_inode;
/* This is the xattr dir, handle specially. */
if (S_ISDIR(dentry->d_inode->i_mode))
if (d_is_dir(dentry))
return xattr_rmdir(dir, dentry);
return xattr_unlink(dir, dentry);

View File

@ -71,7 +71,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
if (!(sc->gfp_mask & __GFP_FS))
return SHRINK_STOP;
if (!grab_super_passive(sb))
if (!trylock_super(sb))
return SHRINK_STOP;
if (sb->s_op->nr_cached_objects)
@ -105,7 +105,7 @@ static unsigned long super_cache_scan(struct shrinker *shrink,
freed += sb->s_op->free_cached_objects(sb, sc);
}
drop_super(sb);
up_read(&sb->s_umount);
return freed;
}
@ -118,7 +118,7 @@ static unsigned long super_cache_count(struct shrinker *shrink,
sb = container_of(shrink, struct super_block, s_shrink);
/*
* Don't call grab_super_passive as it is a potential
* Don't call trylock_super as it is a potential
* scalability bottleneck. The counts could get updated
* between super_cache_count and super_cache_scan anyway.
* Call to super_cache_count with shrinker_rwsem held
@ -348,35 +348,31 @@ static int grab_super(struct super_block *s) __releases(sb_lock)
}
/*
* grab_super_passive - acquire a passive reference
* trylock_super - try to grab ->s_umount shared
* @sb: reference we are trying to grab
*
* Tries to acquire a passive reference. This is used in places where we
* Try to prevent fs shutdown. This is used in places where we
* cannot take an active reference but we need to ensure that the
* superblock does not go away while we are working on it. It returns
* false if a reference was not gained, and returns true with the s_umount
* lock held in read mode if a reference is gained. On successful return,
* the caller must drop the s_umount lock and the passive reference when
* done.
* filesystem is not shut down while we are working on it. It returns
* false if we cannot acquire s_umount or if we lose the race and
* filesystem already got into shutdown, and returns true with the s_umount
* lock held in read mode in case of success. On successful return,
* the caller must drop the s_umount lock when done.
*
* Note that unlike get_super() et.al. this one does *not* bump ->s_count.
* The reason why it's safe is that we are OK with doing trylock instead
* of down_read(). There's a couple of places that are OK with that, but
* it's very much not a general-purpose interface.
*/
bool grab_super_passive(struct super_block *sb)
bool trylock_super(struct super_block *sb)
{
spin_lock(&sb_lock);
if (hlist_unhashed(&sb->s_instances)) {
spin_unlock(&sb_lock);
return false;
}
sb->s_count++;
spin_unlock(&sb_lock);
if (down_read_trylock(&sb->s_umount)) {
if (sb->s_root && (sb->s_flags & MS_BORN))
if (!hlist_unhashed(&sb->s_instances) &&
sb->s_root && (sb->s_flags & MS_BORN))
return true;
up_read(&sb->s_umount);
}
put_super(sb);
return false;
}

View File

@ -287,7 +287,7 @@ xfs_readlink_by_handle(
return PTR_ERR(dentry);
/* Restrict this handle operation to symlinks only. */
if (!S_ISLNK(dentry->d_inode->i_mode)) {
if (!d_is_symlink(dentry)) {
error = -EINVAL;
goto out_dput;
}

View File

@ -215,13 +215,16 @@ struct dentry_operations {
#define DCACHE_LRU_LIST 0x00080000
#define DCACHE_ENTRY_TYPE 0x00700000
#define DCACHE_MISS_TYPE 0x00000000 /* Negative dentry */
#define DCACHE_DIRECTORY_TYPE 0x00100000 /* Normal directory */
#define DCACHE_AUTODIR_TYPE 0x00200000 /* Lookupless directory (presumed automount) */
#define DCACHE_SYMLINK_TYPE 0x00300000 /* Symlink */
#define DCACHE_FILE_TYPE 0x00400000 /* Other file type */
#define DCACHE_MISS_TYPE 0x00000000 /* Negative dentry (maybe fallthru to nowhere) */
#define DCACHE_WHITEOUT_TYPE 0x00100000 /* Whiteout dentry (stop pathwalk) */
#define DCACHE_DIRECTORY_TYPE 0x00200000 /* Normal directory */
#define DCACHE_AUTODIR_TYPE 0x00300000 /* Lookupless directory (presumed automount) */
#define DCACHE_REGULAR_TYPE 0x00400000 /* Regular file type (or fallthru to such) */
#define DCACHE_SPECIAL_TYPE 0x00500000 /* Other file type (or fallthru to such) */
#define DCACHE_SYMLINK_TYPE 0x00600000 /* Symlink (or fallthru to such) */
#define DCACHE_MAY_FREE 0x00800000
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
extern seqlock_t rename_lock;
@ -423,6 +426,16 @@ static inline unsigned __d_entry_type(const struct dentry *dentry)
return dentry->d_flags & DCACHE_ENTRY_TYPE;
}
static inline bool d_is_miss(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_MISS_TYPE;
}
static inline bool d_is_whiteout(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_WHITEOUT_TYPE;
}
static inline bool d_can_lookup(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_DIRECTORY_TYPE;
@ -443,14 +456,25 @@ static inline bool d_is_symlink(const struct dentry *dentry)
return __d_entry_type(dentry) == DCACHE_SYMLINK_TYPE;
}
static inline bool d_is_reg(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_REGULAR_TYPE;
}
static inline bool d_is_special(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_SPECIAL_TYPE;
}
static inline bool d_is_file(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_FILE_TYPE;
return d_is_reg(dentry) || d_is_special(dentry);
}
static inline bool d_is_negative(const struct dentry *dentry)
{
return __d_entry_type(dentry) == DCACHE_MISS_TYPE;
// TODO: check d_is_whiteout(dentry) also.
return d_is_miss(dentry);
}
static inline bool d_is_positive(const struct dentry *dentry)
@ -458,10 +482,75 @@ static inline bool d_is_positive(const struct dentry *dentry)
return !d_is_negative(dentry);
}
extern void d_set_fallthru(struct dentry *dentry);
static inline bool d_is_fallthru(const struct dentry *dentry)
{
return dentry->d_flags & DCACHE_FALLTHRU;
}
extern int sysctl_vfs_cache_pressure;
static inline unsigned long vfs_pressure_ratio(unsigned long val)
{
return mult_frac(val, sysctl_vfs_cache_pressure, 100);
}
/**
* d_inode - Get the actual inode of this dentry
* @dentry: The dentry to query
*
* This is the helper normal filesystems should use to get at their own inodes
* in their own dentries and ignore the layering superimposed upon them.
*/
static inline struct inode *d_inode(const struct dentry *dentry)
{
return dentry->d_inode;
}
/**
* d_inode_rcu - Get the actual inode of this dentry with ACCESS_ONCE()
* @dentry: The dentry to query
*
* This is the helper normal filesystems should use to get at their own inodes
* in their own dentries and ignore the layering superimposed upon them.
*/
static inline struct inode *d_inode_rcu(const struct dentry *dentry)
{
return ACCESS_ONCE(dentry->d_inode);
}
/**
* d_backing_inode - Get upper or lower inode we should be using
* @upper: The upper layer
*
* This is the helper that should be used to get at the inode that will be used
* if this dentry were to be opened as a file. The inode may be on the upper
* dentry or it may be on a lower dentry pinned by the upper.
*
* Normal filesystems should not use this to access their own inodes.
*/
static inline struct inode *d_backing_inode(const struct dentry *upper)
{
struct inode *inode = upper->d_inode;
return inode;
}
/**
* d_backing_dentry - Get upper or lower dentry we should be using
* @upper: The upper layer
*
* This is the helper that should be used to get the dentry of the inode that
* will be used if this dentry were opened as a file. It may be the upper
* dentry or it may be a lower dentry pinned by the upper.
*
* Normal filesystems should not use this to access their own dentries.
*/
static inline struct dentry *d_backing_dentry(struct dentry *upper)
{
return upper;
}
#endif /* __LINUX_DCACHE_H */

View File

@ -2319,8 +2319,8 @@ static int shmem_rmdir(struct inode *dir, struct dentry *dentry)
static int shmem_exchange(struct inode *old_dir, struct dentry *old_dentry, struct inode *new_dir, struct dentry *new_dentry)
{
bool old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
bool new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
bool old_is_dir = d_is_dir(old_dentry);
bool new_is_dir = d_is_dir(new_dentry);
if (old_dir != new_dir && old_is_dir != new_is_dir) {
if (old_is_dir) {

View File

@ -112,9 +112,9 @@ static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa,
return aa_dfa_next(dfa, start, 0);
}
static inline bool mediated_filesystem(struct inode *inode)
static inline bool mediated_filesystem(struct dentry *dentry)
{
return !(inode->i_sb->s_flags & MS_NOUSER);
return !(dentry->d_sb->s_flags & MS_NOUSER);
}
#endif /* __APPARMOR_H */

View File

@ -226,7 +226,7 @@ static int common_perm_rm(int op, struct path *dir,
struct inode *inode = dentry->d_inode;
struct path_cond cond = { };
if (!inode || !dir->mnt || !mediated_filesystem(inode))
if (!inode || !dir->mnt || !mediated_filesystem(dentry))
return 0;
cond.uid = inode->i_uid;
@ -250,7 +250,7 @@ static int common_perm_create(int op, struct path *dir, struct dentry *dentry,
{
struct path_cond cond = { current_fsuid(), mode };
if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode))
if (!dir->mnt || !mediated_filesystem(dir->dentry))
return 0;
return common_perm_dir_dentry(op, dir, dentry, mask, &cond);
@ -285,7 +285,7 @@ static int apparmor_path_truncate(struct path *path)
path->dentry->d_inode->i_mode
};
if (!path->mnt || !mediated_filesystem(path->dentry->d_inode))
if (!path->mnt || !mediated_filesystem(path->dentry))
return 0;
return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE,
@ -305,7 +305,7 @@ static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir,
struct aa_profile *profile;
int error = 0;
if (!mediated_filesystem(old_dentry->d_inode))
if (!mediated_filesystem(old_dentry))
return 0;
profile = aa_current_profile();
@ -320,7 +320,7 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
struct aa_profile *profile;
int error = 0;
if (!mediated_filesystem(old_dentry->d_inode))
if (!mediated_filesystem(old_dentry))
return 0;
profile = aa_current_profile();
@ -346,7 +346,7 @@ static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry,
static int apparmor_path_chmod(struct path *path, umode_t mode)
{
if (!mediated_filesystem(path->dentry->d_inode))
if (!mediated_filesystem(path->dentry))
return 0;
return common_perm_mnt_dentry(OP_CHMOD, path->mnt, path->dentry, AA_MAY_CHMOD);
@ -358,7 +358,7 @@ static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
path->dentry->d_inode->i_mode
};
if (!mediated_filesystem(path->dentry->d_inode))
if (!mediated_filesystem(path->dentry))
return 0;
return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond);
@ -366,7 +366,7 @@ static int apparmor_path_chown(struct path *path, kuid_t uid, kgid_t gid)
static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry)
{
if (!mediated_filesystem(dentry->d_inode))
if (!mediated_filesystem(dentry))
return 0;
return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry,
@ -379,7 +379,7 @@ static int apparmor_file_open(struct file *file, const struct cred *cred)
struct aa_profile *profile;
int error = 0;
if (!mediated_filesystem(file_inode(file)))
if (!mediated_filesystem(file->f_path.dentry))
return 0;
/* If in exec, permission is handled by bprm hooks.
@ -432,7 +432,7 @@ static int common_file_perm(int op, struct file *file, u32 mask)
BUG_ON(!fprofile);
if (!file->f_path.mnt ||
!mediated_filesystem(file_inode(file)))
!mediated_filesystem(file->f_path.dentry))
return 0;
profile = __aa_current_profile();

View File

@ -114,7 +114,7 @@ static int d_namespace_path(struct path *path, char *buf, int buflen,
* security_path hooks as a deleted dentry except without an inode
* allocated.
*/
if (d_unlinked(path->dentry) && path->dentry->d_inode &&
if (d_unlinked(path->dentry) && d_is_positive(path->dentry) &&
!(flags & PATH_MEDIATE_DELETED)) {
error = -ENOENT;
goto out;

View File

@ -203,7 +203,7 @@ void securityfs_remove(struct dentry *dentry)
mutex_lock(&parent->d_inode->i_mutex);
if (positive(dentry)) {
if (dentry->d_inode) {
if (S_ISDIR(dentry->d_inode->i_mode))
if (d_is_dir(dentry))
simple_rmdir(parent->d_inode, dentry);
else
simple_unlink(parent->d_inode, dentry);

View File

@ -1799,7 +1799,7 @@ static inline int may_rename(struct inode *old_dir,
old_dsec = old_dir->i_security;
old_isec = old_dentry->d_inode->i_security;
old_is_dir = S_ISDIR(old_dentry->d_inode->i_mode);
old_is_dir = d_is_dir(old_dentry);
new_dsec = new_dir->i_security;
ad.type = LSM_AUDIT_DATA_DENTRY;
@ -1822,14 +1822,14 @@ static inline int may_rename(struct inode *old_dir,
ad.u.dentry = new_dentry;
av = DIR__ADD_NAME | DIR__SEARCH;
if (new_dentry->d_inode)
if (d_is_positive(new_dentry))
av |= DIR__REMOVE_NAME;
rc = avc_has_perm(sid, new_dsec->sid, SECCLASS_DIR, av, &ad);
if (rc)
return rc;
if (new_dentry->d_inode) {
if (d_is_positive(new_dentry)) {
new_isec = new_dentry->d_inode->i_security;
new_is_dir = S_ISDIR(new_dentry->d_inode->i_mode);
new_is_dir = d_is_dir(new_dentry);
rc = avc_has_perm(sid, new_isec->sid,
new_isec->sclass,
(new_is_dir ? DIR__RMDIR : FILE__UNLINK), &ad);

View File

@ -855,7 +855,7 @@ static int smack_inode_link(struct dentry *old_dentry, struct inode *dir,
rc = smk_curacc(isp, MAY_WRITE, &ad);
rc = smk_bu_inode(old_dentry->d_inode, MAY_WRITE, rc);
if (rc == 0 && new_dentry->d_inode != NULL) {
if (rc == 0 && d_is_positive(new_dentry)) {
isp = smk_of_inode(new_dentry->d_inode);
smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry);
rc = smk_curacc(isp, MAY_WRITE, &ad);
@ -961,7 +961,7 @@ static int smack_inode_rename(struct inode *old_inode,
rc = smk_curacc(isp, MAY_READWRITE, &ad);
rc = smk_bu_inode(old_dentry->d_inode, MAY_READWRITE, rc);
if (rc == 0 && new_dentry->d_inode != NULL) {
if (rc == 0 && d_is_positive(new_dentry)) {
isp = smk_of_inode(new_dentry->d_inode);
smk_ad_setfield_u_fs_path_dentry(&ad, new_dentry);
rc = smk_curacc(isp, MAY_READWRITE, &ad);

View File

@ -905,11 +905,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1,
!tomoyo_get_realpath(&buf2, path2))
goto out;
switch (operation) {
struct dentry *dentry;
case TOMOYO_TYPE_RENAME:
case TOMOYO_TYPE_LINK:
dentry = path1->dentry;
if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
if (!d_is_dir(path1->dentry))
break;
/* fall through */
case TOMOYO_TYPE_PIVOT_ROOT: