[PATCH 08/14] ovl: encode/decode struct ovl_fh format file handles

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Encode an overlay inode as struct ovl_fh encoding of the real inode.
Pure upper file handles are explicitly marked with a flag in ovl_fh
header.

Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxx>
---
 fs/overlayfs/copy_up.c   | 18 +++++-----
 fs/overlayfs/export.c    | 86 +++++++++++++++++++++++++++++++++---------------
 fs/overlayfs/namei.c     | 12 ++++---
 fs/overlayfs/overlayfs.h | 10 +++++-
 4 files changed, 85 insertions(+), 41 deletions(-)

diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c
index 0a2c2c3743b1..9af3a6b62038 100644
--- a/fs/overlayfs/copy_up.c
+++ b/fs/overlayfs/copy_up.c
@@ -233,25 +233,21 @@ int ovl_set_attr(struct dentry *upperdentry, struct kstat *stat)
 	return err;
 }
 
-struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper)
+struct ovl_fh *ovl_encode_fh(struct dentry *dentry, bool is_upper,
+			     bool connectable)
 {
 	struct ovl_fh *fh;
 	int fh_type, fh_len, dwords;
 	void *buf;
 	int buflen = MAX_HANDLE_SZ;
-	uuid_t *uuid = &lower->d_sb->s_uuid;
+	uuid_t *uuid = &dentry->d_sb->s_uuid;
 
 	buf = kmalloc(buflen, GFP_KERNEL);
 	if (!buf)
 		return ERR_PTR(-ENOMEM);
 
-	/*
-	 * We encode a non-connectable file handle for non-dir, because we
-	 * only need to find the lower inode number and we don't want to pay
-	 * the price or reconnecting the dentry.
-	 */
 	dwords = buflen >> 2;
-	fh_type = exportfs_encode_fh(lower, buf, &dwords, 0);
+	fh_type = exportfs_encode_fh(dentry, buf, &dwords, connectable);
 	buflen = (dwords << 2);
 
 	fh = ERR_PTR(-EIO);
@@ -296,12 +292,16 @@ static int ovl_set_origin(struct dentry *dentry, struct dentry *origin,
 	int err;
 
 	/*
+	 * We encode a non-connectable file handle for non-dir, because we
+	 * only need to find the lower inode number and we don't want to pay
+	 * the price of reconnecting the dentry on lookup.
+	 *
 	 * When lower layer doesn't support export operations store a 'null' fh,
 	 * so we can use the overlay.origin xattr to distignuish between a copy
 	 * up and a pure upper inode.
 	 */
 	if (ovl_can_decode_fh(origin->d_sb)) {
-		fh = ovl_encode_fh(origin, is_upper);
+		fh = ovl_encode_fh(origin, is_upper, false);
 		if (IS_ERR(fh))
 			return PTR_ERR(fh);
 	}
diff --git a/fs/overlayfs/export.c b/fs/overlayfs/export.c
index 9ec2fe659e38..47ef0302dbd3 100644
--- a/fs/overlayfs/export.c
+++ b/fs/overlayfs/export.c
@@ -41,7 +41,8 @@ static bool ovl_is_pure_upper_or_root(struct dentry *dentry, int connectable)
 static int ovl_dentry_to_fh(struct dentry *dentry, struct fid *fid,
 			    int *max_len, int connectable)
 {
-	int type;
+	const struct ovl_fh *fh;
+	int len = *max_len << 2;
 
 	/*
 	 * Overlay root dir inode is hashed and encoded as pure upper, because
@@ -49,20 +50,27 @@ static int ovl_dentry_to_fh(struct dentry *dentry, struct fid *fid,
 	 * that root dir is not indexed, because root dentry is pinned to cache.
 	 *
 	 * TODO: handle encoding of non pure upper.
+	 *       Parent and child may not be on the same layer, so encode
+	 *       connectable file handle as an array of self ovl_fh and
+	 *       parent ovl_fh (type OVL_FILEID_WITH_PARENT).
 	 */
 	if (!ovl_is_pure_upper_or_root(dentry, connectable))
 		return FILEID_INVALID;
 
-	/*
-	 * Ask real fs to encode the inode of the real upper dentry.
-	 * When decoding we ask real fs for the upper dentry and use
-	 * the real inode to get the overlay inode.
-	 */
-	type = exportfs_encode_fh(ovl_dentry_upper(dentry), fid, max_len,
-				  connectable);
+	fh = ovl_encode_fh(ovl_dentry_upper(dentry), true, connectable);
+	if (IS_ERR(fh))
+		return FILEID_INVALID;
 
-	/* TODO: encode an ovl_fh struct and return OVL file handle type */
-	return type;
+	if (fh->len > len) {
+		kfree(fh);
+		return FILEID_INVALID;
+	}
+
+	memcpy((char *)fid, (char *)fh, len);
+	*max_len = len >> 2;
+	kfree(fh);
+
+	return OVL_FILEID_WITHOUT_PARENT;
 }
 
 /* Find an alias of inode. If @dir is non NULL, find a child alias */
@@ -171,24 +179,44 @@ static struct dentry *ovl_fh_to_d(struct super_block *sb, struct fid *fid,
 {
 	struct ovl_fs *ofs = sb->s_fs_info;
 	struct vfsmount *mnt = ofs->upper_mnt;
-	const struct export_operations *real_op;
-	struct dentry *(*fh_to_d)(struct super_block *, struct fid *, int, int);
 	struct dentry *upper;
+	struct ovl_fh *fh = (struct ovl_fh *) fid;
+	int err;
 
-	/* TODO: handle decoding of non pure upper */
-	if (!mnt || !mnt->mnt_sb->s_export_op)
-		return NULL;
+	/* TODO: handle file handle with parent from different layer */
+	if (fh_type != OVL_FILEID_WITHOUT_PARENT)
+		return ERR_PTR(-EINVAL);
+
+	err = ovl_check_fh_len(fh, fh_len << 2);
+	if (err)
+		return ERR_PTR(err);
 
-	real_op = mnt->mnt_sb->s_export_op;
-	fh_to_d = to_parent ? real_op->fh_to_parent : real_op->fh_to_dentry;
-	if (!fh_to_d)
+	/* TODO: handle decoding of non pure upper */
+	if (!mnt || !(fh->flags & OVL_FH_FLAG_PATH_UPPER))
 		return NULL;
 
-	/* TODO: decode ovl_fh format file handle */
-	upper = fh_to_d(mnt->mnt_sb, fid, fh_len, fh_type);
+	upper = ovl_decode_fh(fh, mnt);
 	if (IS_ERR_OR_NULL(upper))
 		return upper;
 
+	/*
+	 * ovl_decode_fh() will return a connected dentry if the encoded real
+	 * file handle was connectable (the case of pure upper ancestry).
+	 * fh_to_parent() needs to instantiate an overlay dentry from real
+	 * upper parent in that case.
+	 */
+	if (to_parent) {
+		struct dentry *parent;
+
+		if (upper->d_flags & DCACHE_DISCONNECTED) {
+			dput(upper);
+			return NULL;
+		}
+		parent = dget_parent(upper);
+		dput(upper);
+		upper = parent;
+	}
+
 	/* Find or instantiate a pure upper dentry */
 	return ovl_obtain_alias(sb, upper, NULL);
 }
@@ -207,21 +235,25 @@ static struct dentry *ovl_fh_to_parent(struct super_block *sb, struct fid *fid,
 
 static struct dentry *ovl_get_parent(struct dentry *dentry)
 {
-	const struct export_operations *real_op;
 	struct dentry *upper;
 
 	/* TODO: handle connecting of non pure upper */
 	if (ovl_dentry_lower(dentry))
 		return ERR_PTR(-EACCES);
 
+	/*
+	 * When ovl_fh_to_d() returns an overlay dentry, its real upper
+	 * dentry should be positive and connected. The reconnecting of
+	 * the upper dentry is done by ovl_decode_fh() when decoding the
+	 * real upper file handle, so here we have the upper dentry parent
+	 * and we need to instantiate an overlay dentry with upper dentry
+	 * parent.
+	 */
 	upper = ovl_dentry_upper(dentry);
-	real_op = upper->d_sb->s_export_op;
-	if (!real_op || !real_op->get_parent)
-		return ERR_PTR(-EACCES);
+	if (!upper || (upper->d_flags & DCACHE_DISCONNECTED))
+		return ERR_PTR(-ESTALE);
 
-	upper = real_op->get_parent(upper);
-	if (IS_ERR(upper))
-		return upper;
+	upper = dget_parent(upper);
 
 	/* Find or instantiate a pure upper dentry */
 	return ovl_obtain_alias(dentry->d_sb, upper, NULL);
diff --git a/fs/overlayfs/namei.c b/fs/overlayfs/namei.c
index d5313fb02e73..4f20af22bd0b 100644
--- a/fs/overlayfs/namei.c
+++ b/fs/overlayfs/namei.c
@@ -108,7 +108,7 @@ static int ovl_acceptable(void *mnt, struct dentry *dentry)
  * Return -ENODATA for "origin unknown".
  * Return <0 for an invalid file handle.
  */
-static int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
+int ovl_check_fh_len(struct ovl_fh *fh, int fh_len)
 {
 	if (fh_len < sizeof(struct ovl_fh) || fh_len < fh->len)
 		return -EINVAL;
@@ -172,7 +172,7 @@ static struct ovl_fh *ovl_get_origin_fh(struct dentry *dentry)
 	goto out;
 }
 
-static struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt)
 {
 	struct dentry *origin;
 	int bytes;
@@ -412,7 +412,7 @@ int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
 	struct ovl_fh *fh;
 	int err;
 
-	fh = ovl_encode_fh(origin, is_upper);
+	fh = ovl_encode_fh(origin, is_upper, false);
 	err = PTR_ERR(fh);
 	if (IS_ERR(fh))
 		goto fail;
@@ -574,7 +574,11 @@ int ovl_get_index_name(struct dentry *origin, struct qstr *name)
 	struct ovl_fh *fh;
 	char *n, *s;
 
-	fh = ovl_encode_fh(origin, false);
+	/*
+	 * We encode a non-connectable file handle for index, because the index
+	 * must be unqiue and invariant of lower hardlink aliases.
+	 */
+	fh = ovl_encode_fh(origin, false, false);
 	if (IS_ERR(fh))
 		return PTR_ERR(fh);
 
diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h
index 66a6447a0c2a..75a8b10d4e10 100644
--- a/fs/overlayfs/overlayfs.h
+++ b/fs/overlayfs/overlayfs.h
@@ -72,6 +72,11 @@ enum ovl_index {
 #error Endianness not defined
 #endif
 
+/* The type returned by overlay encode_fh when encoding an ovl_fh handle */
+#define OVL_FILEID_WITHOUT_PARENT	0xf1
+/* The type returned by overlay encode_fh when encoding two ovl_fh handles */
+#define OVL_FILEID_WITH_PARENT		0xf2
+
 /* On-disk and in-memeory format for redirect by file handle */
 struct ovl_fh {
 	u8 version;	/* 0 */
@@ -266,6 +271,8 @@ static inline bool ovl_is_opaquedir(struct dentry *dentry)
 
 
 /* namei.c */
+int ovl_check_fh_len(struct ovl_fh *fh, int fh_len);
+struct dentry *ovl_decode_fh(struct ovl_fh *fh, struct vfsmount *mnt);
 int ovl_verify_origin(struct dentry *dentry, struct dentry *origin,
 		      bool is_upper, bool set);
 int ovl_verify_index(struct dentry *index, struct vfsmount *mnt,
@@ -341,7 +348,8 @@ int ovl_copy_up(struct dentry *dentry);
 int ovl_copy_up_flags(struct dentry *dentry, int flags);
 int ovl_copy_xattr(struct dentry *old, struct dentry *new);
 int ovl_set_attr(struct dentry *upper, struct kstat *stat);
-struct ovl_fh *ovl_encode_fh(struct dentry *lower, bool is_upper);
+struct ovl_fh *ovl_encode_fh(struct dentry *dentry, bool is_upper,
+			     bool connectable);
 
 /* export.c */
 extern const struct export_operations ovl_export_operations;
-- 
2.7.4




[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux