Return required size when encode_fh size too small

Quoting <linux/exportfs.h>:

> encode_fh() should return the fileid_type on success and on error
> returns 255 (if the space needed to encode fh is greater than
> @max_len*4 bytes). On error @max_len contains the minimum size (in 4
> byte unit) needed to encode the file handle.

ZFS was not setting max_len in the case where the handle was too
small.  As a result of this, the `t_name_to_handle_at.c' example in
name_to_handle_at(2) did not work on ZFS.

zfsctl_fid() will itself set max_len if called with a fid that is too
small, so if we give zfs_fid() that behavior as well, the fix is quite
easy: if the handle is too small, just use a zero-size fid instead of
the handle.

Tested by running t_name_to_handle_at on a normal file, a directory, a
.zfs directory, and a snapshot.

Thanks-to: Puck Meerburg <puck@puckipedia.com>
Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Tony Nguyen <tony.nguyen@delphix.com>
Signed-off-by: Alyssa Ross <hi@alyssa.is>
Closes #11995
This commit is contained in:
Alyssa Ross 2021-05-07 22:08:16 +00:00 committed by GitHub
parent 4fb9e5638b
commit c074a7de13
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 15 additions and 4 deletions

View File

@ -3950,6 +3950,13 @@ zfs_fid(struct inode *ip, fid_t *fidp)
int size, i, error; int size, i, error;
ZFS_ENTER(zfsvfs); ZFS_ENTER(zfsvfs);
if (fidp->fid_len < SHORT_FID_LEN) {
fidp->fid_len = SHORT_FID_LEN;
ZFS_EXIT(zfsvfs);
return (SET_ERROR(ENOSPC));
}
ZFS_VERIFY_ZP(zp); ZFS_VERIFY_ZP(zp);
if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(zfsvfs), if ((error = sa_lookup(zp->z_sa_hdl, SA_ZPL_GEN(zfsvfs),

View File

@ -41,15 +41,19 @@ zpl_encode_fh(struct dentry *dentry, __u32 *fh, int *max_len, int connectable)
struct inode *ip = dentry->d_inode; struct inode *ip = dentry->d_inode;
#endif /* HAVE_ENCODE_FH_WITH_INODE */ #endif /* HAVE_ENCODE_FH_WITH_INODE */
fstrans_cookie_t cookie; fstrans_cookie_t cookie;
fid_t *fid = (fid_t *)fh; ushort_t empty_fid = 0;
fid_t *fid;
int len_bytes, rc; int len_bytes, rc;
len_bytes = *max_len * sizeof (__u32); len_bytes = *max_len * sizeof (__u32);
if (len_bytes < offsetof(fid_t, fid_data)) if (len_bytes < offsetof(fid_t, fid_data)) {
return (255); fid = (fid_t *)&empty_fid;
} else {
fid = (fid_t *)fh;
fid->fid_len = len_bytes - offsetof(fid_t, fid_data); fid->fid_len = len_bytes - offsetof(fid_t, fid_data);
}
cookie = spl_fstrans_mark(); cookie = spl_fstrans_mark();
if (zfsctl_is_node(ip)) if (zfsctl_is_node(ip))