Commit 1b91dbdd authored by Miklos Szeredi's avatar Miklos Szeredi

Merge branch 'd_real' into overlayfs-next

parents 523d939e 0cac643c
...@@ -20,6 +20,8 @@ prototypes: ...@@ -20,6 +20,8 @@ prototypes:
char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen); char *(*d_dname)((struct dentry *dentry, char *buffer, int buflen);
struct vfsmount *(*d_automount)(struct path *path); struct vfsmount *(*d_automount)(struct path *path);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
locking rules: locking rules:
rename_lock ->d_lock may block rcu-walk rename_lock ->d_lock may block rcu-walk
...@@ -34,6 +36,7 @@ d_iput: no no yes no ...@@ -34,6 +36,7 @@ d_iput: no no yes no
d_dname: no no no no d_dname: no no no no
d_automount: no no yes no d_automount: no no yes no
d_manage: no no yes (ref-walk) maybe d_manage: no no yes (ref-walk) maybe
d_real no no yes no
--------------------------- inode_operations --------------------------- --------------------------- inode_operations ---------------------------
prototypes: prototypes:
...@@ -66,7 +69,6 @@ prototypes: ...@@ -66,7 +69,6 @@ prototypes:
struct file *, unsigned open_flag, struct file *, unsigned open_flag,
umode_t create_mode, int *opened); umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*dentry_open)(struct dentry *, struct file *, const struct cred *);
locking rules: locking rules:
all may block all may block
...@@ -95,7 +97,6 @@ fiemap: no ...@@ -95,7 +97,6 @@ fiemap: no
update_time: no update_time: no
atomic_open: yes atomic_open: yes
tmpfile: no tmpfile: no
dentry_open: no
Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on Additionally, ->rmdir(), ->unlink() and ->rename() have ->i_mutex on
victim. victim.
......
...@@ -364,7 +364,6 @@ struct inode_operations { ...@@ -364,7 +364,6 @@ struct inode_operations {
int (*atomic_open)(struct inode *, struct dentry *, struct file *, int (*atomic_open)(struct inode *, struct dentry *, struct file *,
unsigned open_flag, umode_t create_mode, int *opened); unsigned open_flag, umode_t create_mode, int *opened);
int (*tmpfile) (struct inode *, struct dentry *, umode_t); int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*dentry_open)(struct dentry *, struct file *, const struct cred *);
}; };
Again, all methods are called without any locks being held, unless Again, all methods are called without any locks being held, unless
...@@ -696,13 +695,6 @@ struct address_space_operations { ...@@ -696,13 +695,6 @@ struct address_space_operations {
but instead uses bmap to find out where the blocks in the file but instead uses bmap to find out where the blocks in the file
are and uses those addresses directly. are and uses those addresses directly.
dentry_open: *WARNING: probably going away soon, do not use!* This is an
alternative to f_op->open(), the difference is that this method may open
a file not necessarily originating from the same filesystem as the one
i_op->open() was called on. It may be useful for stacking filesystems
which want to allow native I/O directly on underlying files.
invalidatepage: If a page has PagePrivate set, then invalidatepage invalidatepage: If a page has PagePrivate set, then invalidatepage
will be called when part or all of the page is to be removed will be called when part or all of the page is to be removed
from the address space. This generally corresponds to either a from the address space. This generally corresponds to either a
...@@ -938,6 +930,8 @@ struct dentry_operations { ...@@ -938,6 +930,8 @@ struct dentry_operations {
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool);
struct dentry *(*d_real)(struct dentry *, const struct inode *,
unsigned int);
}; };
d_revalidate: called when the VFS needs to revalidate a dentry. This d_revalidate: called when the VFS needs to revalidate a dentry. This
...@@ -1022,6 +1016,14 @@ struct dentry_operations { ...@@ -1022,6 +1016,14 @@ struct dentry_operations {
at the end of the buffer, and returns a pointer to the first char. at the end of the buffer, and returns a pointer to the first char.
dynamic_dname() helper function is provided to take care of this. dynamic_dname() helper function is provided to take care of this.
Example :
static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen)
{
return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]",
dentry->d_inode->i_ino);
}
d_automount: called when an automount dentry is to be traversed (optional). d_automount: called when an automount dentry is to be traversed (optional).
This should create a new VFS mount record and return the record to the This should create a new VFS mount record and return the record to the
caller. The caller is supplied with a path parameter giving the caller. The caller is supplied with a path parameter giving the
...@@ -1060,13 +1062,23 @@ struct dentry_operations { ...@@ -1060,13 +1062,23 @@ struct dentry_operations {
This function is only used if DCACHE_MANAGE_TRANSIT is set on the This function is only used if DCACHE_MANAGE_TRANSIT is set on the
dentry being transited from. dentry being transited from.
Example : d_real: overlay/union type filesystems implement this method to return one of
the underlying dentries hidden by the overlay. It is used in three
different modes:
static char *pipefs_dname(struct dentry *dent, char *buffer, int buflen) Called from open it may need to copy-up the file depending on the
{ supplied open flags. This mode is selected with a non-zero flags
return dynamic_dname(dentry, buffer, buflen, "pipe:[%lu]", argument. In this mode the d_real method can return an error.
dentry->d_inode->i_ino);
} Called from file_dentry() it returns the real dentry matching the inode
argument. The real dentry may be from a lower layer already copied up,
but still referenced from the file. This mode is selected with a
non-NULL inode argument. This will always succeed.
With NULL inode and zero flags the topmost real underlying dentry is
returned. This will always succeed.
This method is never called with both non-NULL inode and non-zero flags.
Each dentry has a pointer to its parent dentry, as well as a hash list Each dentry has a pointer to its parent dentry, as well as a hash list
of child dentries. Child dentries are basically like files in a of child dentries. Child dentries are basically like files in a
......
...@@ -1729,7 +1729,6 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) ...@@ -1729,7 +1729,6 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
DCACHE_OP_REVALIDATE | DCACHE_OP_REVALIDATE |
DCACHE_OP_WEAK_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE |
DCACHE_OP_DELETE | DCACHE_OP_DELETE |
DCACHE_OP_SELECT_INODE |
DCACHE_OP_REAL)); DCACHE_OP_REAL));
dentry->d_op = op; dentry->d_op = op;
if (!op) if (!op)
...@@ -1746,8 +1745,6 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op) ...@@ -1746,8 +1745,6 @@ void d_set_d_op(struct dentry *dentry, const struct dentry_operations *op)
dentry->d_flags |= DCACHE_OP_DELETE; dentry->d_flags |= DCACHE_OP_DELETE;
if (op->d_prune) if (op->d_prune)
dentry->d_flags |= DCACHE_OP_PRUNE; dentry->d_flags |= DCACHE_OP_PRUNE;
if (op->d_select_inode)
dentry->d_flags |= DCACHE_OP_SELECT_INODE;
if (op->d_real) if (op->d_real)
dentry->d_flags |= DCACHE_OP_REAL; dentry->d_flags |= DCACHE_OP_REAL;
......
...@@ -4328,7 +4328,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, ...@@ -4328,7 +4328,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
* Check source == target. * Check source == target.
* On overlayfs need to look at underlying inodes. * On overlayfs need to look at underlying inodes.
*/ */
if (vfs_select_inode(old_dentry, 0) == vfs_select_inode(new_dentry, 0)) if (d_real_inode(old_dentry) == d_real_inode(new_dentry))
return 0; return 0;
error = may_delete(old_dir, old_dentry, is_dir); error = may_delete(old_dir, old_dentry, is_dir);
......
...@@ -840,13 +840,13 @@ EXPORT_SYMBOL(file_path); ...@@ -840,13 +840,13 @@ EXPORT_SYMBOL(file_path);
int vfs_open(const struct path *path, struct file *file, int vfs_open(const struct path *path, struct file *file,
const struct cred *cred) const struct cred *cred)
{ {
struct inode *inode = vfs_select_inode(path->dentry, file->f_flags); struct dentry *dentry = d_real(path->dentry, NULL, file->f_flags);
if (IS_ERR(inode)) if (IS_ERR(dentry))
return PTR_ERR(inode); return PTR_ERR(dentry);
file->f_path = *path; file->f_path = *path;
return do_dentry_open(file, inode, NULL, cred); return do_dentry_open(file, d_backing_inode(dentry), NULL, cred);
} }
struct file *dentry_open(const struct path *path, int flags, struct file *dentry_open(const struct path *path, int flags,
......
...@@ -351,36 +351,25 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type, ...@@ -351,36 +351,25 @@ static bool ovl_open_need_copy_up(int flags, enum ovl_path_type type,
return true; return true;
} }
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags) int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags)
{ {
int err; int err = 0;
struct path realpath; struct path realpath;
enum ovl_path_type type; enum ovl_path_type type;
if (d_is_dir(dentry))
return d_backing_inode(dentry);
type = ovl_path_real(dentry, &realpath); type = ovl_path_real(dentry, &realpath);
if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) { if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
err = ovl_want_write(dentry); err = ovl_want_write(dentry);
if (err) if (!err) {
return ERR_PTR(err); if (file_flags & O_TRUNC)
err = ovl_copy_up_truncate(dentry);
if (file_flags & O_TRUNC) else
err = ovl_copy_up_truncate(dentry); err = ovl_copy_up(dentry);
else ovl_drop_write(dentry);
err = ovl_copy_up(dentry); }
ovl_drop_write(dentry);
if (err)
return ERR_PTR(err);
ovl_path_upper(dentry, &realpath);
} }
if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE) return err;
return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags);
return d_backing_inode(realpath.dentry);
} }
static const struct inode_operations ovl_file_inode_operations = { static const struct inode_operations ovl_file_inode_operations = {
......
...@@ -179,7 +179,7 @@ ssize_t ovl_getxattr(struct dentry *dentry, struct inode *inode, ...@@ -179,7 +179,7 @@ ssize_t ovl_getxattr(struct dentry *dentry, struct inode *inode,
const char *name, void *value, size_t size); const char *name, void *value, size_t size);
ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size); ssize_t ovl_listxattr(struct dentry *dentry, char *list, size_t size);
int ovl_removexattr(struct dentry *dentry, const char *name); int ovl_removexattr(struct dentry *dentry, const char *name);
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags); int ovl_open_maybe_copy_up(struct dentry *dentry, unsigned int file_flags);
struct inode *ovl_new_inode(struct super_block *sb, umode_t mode, struct inode *ovl_new_inode(struct super_block *sb, umode_t mode,
struct ovl_entry *oe); struct ovl_entry *oe);
......
...@@ -304,7 +304,9 @@ static void ovl_dentry_release(struct dentry *dentry) ...@@ -304,7 +304,9 @@ static void ovl_dentry_release(struct dentry *dentry)
} }
} }
static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) static struct dentry *ovl_d_real(struct dentry *dentry,
const struct inode *inode,
unsigned int open_flags)
{ {
struct dentry *real; struct dentry *real;
...@@ -314,6 +316,16 @@ static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) ...@@ -314,6 +316,16 @@ static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode)
goto bug; goto bug;
} }
if (d_is_negative(dentry))
return dentry;
if (open_flags) {
int err = ovl_open_maybe_copy_up(dentry, open_flags);
if (err)
return ERR_PTR(err);
}
real = ovl_dentry_upper(dentry); real = ovl_dentry_upper(dentry);
if (real && (!inode || inode == d_inode(real))) if (real && (!inode || inode == d_inode(real)))
return real; return real;
...@@ -326,9 +338,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode) ...@@ -326,9 +338,7 @@ static struct dentry *ovl_d_real(struct dentry *dentry, struct inode *inode)
return real; return real;
/* Handle recursion */ /* Handle recursion */
if (real->d_flags & DCACHE_OP_REAL) return d_real(real, inode, open_flags);
return real->d_op->d_real(real, inode);
bug: bug:
WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry, WARN(1, "ovl_d_real(%pd4, %s:%lu\n): real dentry not found\n", dentry,
inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0); inode ? inode->i_sb->s_id : "NULL", inode ? inode->i_ino : 0);
...@@ -378,13 +388,11 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags) ...@@ -378,13 +388,11 @@ static int ovl_dentry_weak_revalidate(struct dentry *dentry, unsigned int flags)
static const struct dentry_operations ovl_dentry_operations = { static const struct dentry_operations ovl_dentry_operations = {
.d_release = ovl_dentry_release, .d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
.d_real = ovl_d_real, .d_real = ovl_d_real,
}; };
static const struct dentry_operations ovl_reval_dentry_operations = { static const struct dentry_operations ovl_reval_dentry_operations = {
.d_release = ovl_dentry_release, .d_release = ovl_dentry_release,
.d_select_inode = ovl_d_select_inode,
.d_real = ovl_d_real, .d_real = ovl_d_real,
.d_revalidate = ovl_dentry_revalidate, .d_revalidate = ovl_dentry_revalidate,
.d_weak_revalidate = ovl_dentry_weak_revalidate, .d_weak_revalidate = ovl_dentry_weak_revalidate,
......
...@@ -139,8 +139,8 @@ struct dentry_operations { ...@@ -139,8 +139,8 @@ struct dentry_operations {
char *(*d_dname)(struct dentry *, char *, int); char *(*d_dname)(struct dentry *, char *, int);
struct vfsmount *(*d_automount)(struct path *); struct vfsmount *(*d_automount)(struct path *);
int (*d_manage)(struct dentry *, bool); int (*d_manage)(struct dentry *, bool);
struct inode *(*d_select_inode)(struct dentry *, unsigned); struct dentry *(*d_real)(struct dentry *, const struct inode *,
struct dentry *(*d_real)(struct dentry *, struct inode *); unsigned int);
} ____cacheline_aligned; } ____cacheline_aligned;
/* /*
...@@ -206,10 +206,8 @@ struct dentry_operations { ...@@ -206,10 +206,8 @@ struct dentry_operations {
#define DCACHE_MAY_FREE 0x00800000 #define DCACHE_MAY_FREE 0x00800000
#define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */ #define DCACHE_FALLTHRU 0x01000000 /* Fall through to lower layer */
#define DCACHE_OP_SELECT_INODE 0x02000000 /* Unioned entry: dcache op selects inode */ #define DCACHE_ENCRYPTED_WITH_KEY 0x02000000 /* dir is encrypted with a valid key */
#define DCACHE_OP_REAL 0x04000000
#define DCACHE_ENCRYPTED_WITH_KEY 0x04000000 /* dir is encrypted with a valid key */
#define DCACHE_OP_REAL 0x08000000
#define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */ #define DCACHE_PAR_LOOKUP 0x10000000 /* being looked up (with parent locked shared) */
#define DCACHE_DENTRY_CURSOR 0x20000000 #define DCACHE_DENTRY_CURSOR 0x20000000
...@@ -557,25 +555,27 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper) ...@@ -557,25 +555,27 @@ static inline struct dentry *d_backing_dentry(struct dentry *upper)
return upper; return upper;
} }
static inline struct dentry *d_real(struct dentry *dentry) /**
* d_real - Return the real dentry
* @dentry: the dentry to query
* @inode: inode to select the dentry from multiple layers (can be NULL)
* @flags: open flags to control copy-up behavior
*
* If dentry is on an union/overlay, then return the underlying, real dentry.
* Otherwise return the dentry itself.
*
* See also: Documentation/filesystems/vfs.txt
*/
static inline struct dentry *d_real(struct dentry *dentry,
const struct inode *inode,
unsigned int flags)
{ {
if (unlikely(dentry->d_flags & DCACHE_OP_REAL)) if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
return dentry->d_op->d_real(dentry, NULL); return dentry->d_op->d_real(dentry, inode, flags);
else else
return dentry; return dentry;
} }
static inline struct inode *vfs_select_inode(struct dentry *dentry,
unsigned open_flags)
{
struct inode *inode = d_inode(dentry);
if (inode && unlikely(dentry->d_flags & DCACHE_OP_SELECT_INODE))
inode = dentry->d_op->d_select_inode(dentry, open_flags);
return inode;
}
/** /**
* d_real_inode - Return the real inode * d_real_inode - Return the real inode
* @dentry: The dentry to query * @dentry: The dentry to query
...@@ -585,7 +585,7 @@ static inline struct inode *vfs_select_inode(struct dentry *dentry, ...@@ -585,7 +585,7 @@ static inline struct inode *vfs_select_inode(struct dentry *dentry,
*/ */
static inline struct inode *d_real_inode(struct dentry *dentry) static inline struct inode *d_real_inode(struct dentry *dentry)
{ {
return d_backing_inode(d_real(dentry)); return d_backing_inode(d_real(dentry, NULL, 0));
} }
......
...@@ -1272,12 +1272,7 @@ static inline struct inode *file_inode(const struct file *f) ...@@ -1272,12 +1272,7 @@ static inline struct inode *file_inode(const struct file *f)
static inline struct dentry *file_dentry(const struct file *file) static inline struct dentry *file_dentry(const struct file *file)
{ {
struct dentry *dentry = file->f_path.dentry; return d_real(file->f_path.dentry, file_inode(file), 0);
if (unlikely(dentry->d_flags & DCACHE_OP_REAL))
return dentry->d_op->d_real(dentry, file_inode(file));
else
return dentry;
} }
static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl) static inline int locks_lock_file_wait(struct file *filp, struct file_lock *fl)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment