@@ -37,8 +37,8 @@ zpl_encode_fh(struct inode *ip, __u32 *fh, int *max_len, struct inode *parent)
3737{
3838 fstrans_cookie_t cookie ;
3939 ushort_t empty_fid = 0 ;
40- fid_t * fid ;
41- int len_bytes , rc ;
40+ fid_t * fid , * pfid ;
41+ int len_bytes , required_len , parent_len , rc , prc , fh_type ;
4242
4343 len_bytes = * max_len * sizeof (__u32 );
4444
@@ -56,11 +56,44 @@ zpl_encode_fh(struct inode *ip, __u32 *fh, int *max_len, struct inode *parent)
5656 else
5757 rc = zfs_fid (ip , fid );
5858
59+ required_len = offsetof(fid_t , fid_data ) + fid -> fid_len ;
60+
61+ /*
62+ * Kernel has requested that the resulting file handle contain
63+ * a reference to the provided parent. This typically would happen
64+ * if the NFS export has subtree checking enabled.
65+ */
66+ if (parent != NULL ) {
67+ if ((rc == 0 ) && (len_bytes >
68+ required_len + offsetof(fid_t , fid_data ))) {
69+ parent_len = len_bytes - required_len ;
70+ pfid = (fid_t * )((char * )fh + required_len );
71+ pfid -> fid_len = parent_len - offsetof(fid_t , fid_data );
72+ } else {
73+ empty_fid = 0 ;
74+ pfid = (fid_t * )& empty_fid ;
75+ }
76+
77+ if (zfsctl_is_node (parent ))
78+ prc = zfsctl_fid (parent , pfid );
79+ else
80+ prc = zfs_fid (parent , pfid );
81+
82+ if (rc == 0 && prc != 0 )
83+ rc = prc ;
84+
85+ required_len += offsetof(fid_t , fid_data ) +
86+ pfid -> fid_len ;
87+ fh_type = FILEID_INO32_GEN_PARENT ;
88+ } else {
89+ fh_type = FILEID_INO32_GEN ;
90+ }
91+
5992 spl_fstrans_unmark (cookie );
60- len_bytes = offsetof(fid_t , fid_data ) + fid -> fid_len ;
61- * max_len = roundup (len_bytes , sizeof (__u32 )) / sizeof (__u32 );
6293
63- return (rc == 0 ? FILEID_INO32_GEN : 255 );
94+ * max_len = roundup (required_len , sizeof (__u32 )) / sizeof (__u32 );
95+
96+ return (rc == 0 ? fh_type : FILEID_INVALID );
6497}
6598
6699static struct dentry *
@@ -74,7 +107,8 @@ zpl_fh_to_dentry(struct super_block *sb, struct fid *fh,
74107
75108 len_bytes = fh_len * sizeof (__u32 );
76109
77- if (fh_type != FILEID_INO32_GEN ||
110+ if ((fh_type != FILEID_INO32_GEN &&
111+ fh_type != FILEID_INO32_GEN_PARENT ) ||
78112 len_bytes < offsetof(fid_t , fid_data ) ||
79113 len_bytes < offsetof(fid_t , fid_data ) + fid -> fid_len )
80114 return (ERR_PTR (- EINVAL ));
@@ -104,6 +138,46 @@ zpl_fh_to_dentry(struct super_block *sb, struct fid *fh,
104138 return (d_obtain_alias (ip ));
105139}
106140
141+ static struct dentry *
142+ zpl_fh_to_parent (struct super_block * sb , struct fid * fh ,
143+ int fh_len , int fh_type )
144+ {
145+ /*
146+ * Convert the provided struct fid to a dentry for the parent
147+ * This is possible only if it was created with the parent,
148+ * e.g. type is FILEID_INO32_GEN_PARENT. When this type of
149+ * filehandle is created we simply pack the parent fid_t
150+ * after the entry's fid_t. So this function will adjust
151+ * offset in the provided buffer to the begining of the
152+ * parent fid_t and call zpl_fh_to_dentry() on it.
153+ */
154+ fid_t * fid = (fid_t * )fh ;
155+ fid_t * pfid ;
156+ int len_bytes , parent_len_bytes , child_fid_bytes , parent_fh_len ;
157+
158+ len_bytes = fh_len * sizeof (__u32 );
159+
160+ if ((fh_type != FILEID_INO32_GEN_PARENT ) ||
161+ len_bytes < offsetof(fid_t , fid_data ) ||
162+ len_bytes < offsetof(fid_t , fid_data ) + fid -> fid_len )
163+ return (ERR_PTR (- EINVAL ));
164+
165+ child_fid_bytes = offsetof(fid_t , fid_data ) + fid -> fid_len ;
166+ parent_len_bytes = len_bytes - child_fid_bytes ;
167+
168+ if (parent_len_bytes < offsetof(fid_t , fid_data ))
169+ return (ERR_PTR (- EINVAL ));
170+
171+ pfid = (fid_t * )((char * )fh + child_fid_bytes );
172+
173+ if (parent_len_bytes < offsetof(fid_t , fid_data ) + pfid -> fid_len )
174+ return (ERR_PTR (- EINVAL ));
175+
176+ parent_fh_len = parent_len_bytes / sizeof (__u32 );
177+ return (zpl_fh_to_dentry (sb , (struct fid * )pfid , parent_fh_len ,
178+ FILEID_INO32_GEN ));
179+ }
180+
107181/*
108182 * In case the filesystem contains name longer than 255, we need to override
109183 * the default get_name so we don't get buffer overflow. Unfortunately, since
@@ -177,6 +251,7 @@ zpl_commit_metadata(struct inode *inode)
177251const struct export_operations zpl_export_operations = {
178252 .encode_fh = zpl_encode_fh ,
179253 .fh_to_dentry = zpl_fh_to_dentry ,
254+ .fh_to_parent = zpl_fh_to_parent ,
180255 .get_name = zpl_get_name ,
181256 .get_parent = zpl_get_parent ,
182257 .commit_metadata = zpl_commit_metadata ,
0 commit comments