Skip to content

Commit aca58db

Browse files
authored
Add fh_to_parent export definition
This commit adds support for converting a file handle to its parent dentry. This is called in exportfs_decode_fh_raw() when subtree checking is enabled in NFS. Defining this and handling the expanded filehandles allows the knfsd to succeed in handling the file handle where it might otherwise fail with ESTALE when trying to open by filehandle. A side effect of this change is that name_to_handle_at(2) and open_by_handle_at(2) now support AT_HANDLE_CONNECTABLE. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <alexander.motin@TrueNAS.com> Reviewed-by: Ameer Hamza <ahamza@ixsystems.com> Signed-off-by: Andrew Walker <andrew.walker@truenas.com> Closes #18099
1 parent f2b4ed3 commit aca58db

1 file changed

Lines changed: 81 additions & 6 deletions

File tree

module/os/linux/zfs/zpl_export.c

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -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

6699
static 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)
177251
const 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

Comments
 (0)