nfs4_srv_readdir.c revision 710f82c5aa70ff7b45544e6d8e4c603fbc5cbf53
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#include <sys/sysmacros.h>
#include <rpc/rpcsec_gss.h>
/*
* RFS4_MINLEN_ENTRY4: XDR-encoded size of smallest possible dirent.
* This is used to return NFS4ERR_TOOSMALL when clients specify
* maxcount that isn't large enough to hold the smallest possible
* XDR encoded dirent.
*
* sizeof cookie (8 bytes) +
* sizeof name_len (4 bytes) +
* sizeof smallest (padded) name (4 bytes) +
* sizeof bitmap4_len (12 bytes) + NOTE: we always encode len=2 bm4
* sizeof attrlist4_len (4 bytes) +
* sizeof next boolean (4 bytes)
*
* RFS4_MINLEN_RDDIR4: XDR-encoded size of READDIR op reply containing
* the smallest possible entry4 (assumes no attrs requested).
* sizeof nfsstat4 (4 bytes) +
* sizeof verifier4 (8 bytes) +
* sizeof entsecond_to_ry4list bool (4 bytes) +
* sizeof entry4 (36 bytes) +
* sizeof eof bool (4 bytes)
*
* RFS4_MINLEN_RDDIR_BUF: minimum length of buffer server will provide to
* VOP_READDIR. Its value is the size of the maximum possible dirent
* for solaris. The DIRENT64_RECLEN macro returns the size of dirent
* required for a given name length. MAXNAMELEN is the maximum
* filename length allowed in Solaris. The first two DIRENT64_RECLEN()
* macros are to allow for . and .. entries -- just a minor tweak to try
* and guarantee that buffer we give to VOP_READDIR will be large enough
* to hold ., .., and the largest possible solaris dirent64.
*/
#define RFS4_MINLEN_ENTRY4 36
#define RFS4_MINLEN_RDDIR_BUF \
#ifdef nextdp
#endif
static nfs_ftype4 vt_to_nf4[] = {
};
int
{
int error;
int ismntpt;
return (error);
/* Is this object mounted upon? */
/*
* Nothing more to do if object is not a mount point or
* a possible LOFS shadow of an LOFS mount (which won't
* have v_vfsmountedhere set)
*/
return (0);
}
if (ismntpt) {
/*
* Something is mounted here. Traverse and manage the
* namespace
*/
return (error);
}
}
/*
* If VOP_FID not supported by underlying fs (mntfs, procfs,
* etc.), then return attrs for stub instead of VROOT object.
* If it fails for any other reason, then return the error.
*/
if (ismntpt == 0) {
return (error);
}
return (error);
}
/* go back to vnode that is "under" mount */
return (0);
}
if (ismntpt == 0) {
} else {
}
return (0);
}
if (ismntpt)
/* Save the exi and present the new one to checkauth4() */
/* Get the right cred like lookup does */
/* Reset what call_checkauth4() may have set */
if (status == NFS4ERR_DELAY)
return (status);
}
return (0);
}
/* This is the set of pathconf data for vfs */
typedef struct {
static int
{
int error;
pce->maxfilesize = 0;
if (ar & FATTR4_MAXFILESIZE_MASK) {
/* Maximum File Size */
if (error)
return (error);
/*
* If the underlying file system does not support
* _PC_FILESIZEBITS, return a reasonable default. Note that
* error code on VOP_PATHCONF will be 0, even if the underlying
* file system does not support _PC_FILESIZEBITS.
*/
} else {
else
}
}
if (ar & FATTR4_MAXLINK_MASK) {
/* Maximum Link Count */
if (error)
return (error);
}
if (ar & FATTR4_MAXNAME_MASK) {
/* Maximum Name Length */
if (error)
return (error);
}
return (0);
}
/* This is the set of statvfs data that is ready for encoding */
typedef struct {
static int
{
int error;
/* Grab the per filesystem info */
return (error);
}
/* Calculate space available */
psbe->space_avail =
} else {
psbe->space_avail =
}
/* Calculate space free */
psbe->space_free =
} else {
psbe->space_free =
}
/* Calculate space total */
psbe->space_total =
} else {
psbe->space_total =
}
/* For use later on attr encode */
return (0);
}
/*
* Macros to handle if we have don't have enough space for the requested
* attributes and this is the first entry and the
* requested attributes are more than the minimal useful
* set, reset the attributes to the minimal set and
* retry the encoding. If the client has asked for both
* mounted_on_fileid and fileid, prefer mounted_on_fileid.
*/
#define MINIMAL_RD_ATTRS \
#define MINIMIZE_ATTR_MASK(m) { \
if ((m) & FATTR4_MOUNTED_ON_FILEID_MASK) \
else \
(m) &= FATTR4_RDATTR_ERROR_MASK|FATTR4_FILEID_MASK; \
}
#define IS_MIN_ATTR_MASK(m) (((m) & ~MINIMAL_RD_ATTRS) == 0)
/*
* If readdir only needs to return FILEID, we can take it from the
* dirent struct and save doing the lookup.
*/
/* ARGSUSED */
void
{
int error;
int alloc_err = 0;
int vfs_different;
int dircount;
int no_space;
int iseofdir;
int tsize;
int check_visible;
int expseudo = 0;
uint32_t rddirattr_error = 0;
int nents;
int owner_error, group_error;
READDIR4args *, args);
/* Maximum read and write size */
goto out;
}
/*
* If there is an unshared filesystem mounted on this vnode,
* do not allow readdir in this directory.
*/
if (vn_ismntpt(dvp)) {
goto out;
}
goto out;
}
goto out;
}
/*
* If write-only attrs are requested, then fail the readdir op
*/
if (args->attr_request &
goto out;
}
if (error) {
goto out;
}
goto out;
}
/* Is there pseudo-fs work that is needed for this readdir? */
/* Check the requested attributes and only do the work if needed */
if (ar & (FATTR4_MAXFILESIZE_MASK |
goto out;
}
}
/* If there is statvfs data requested, pick it up once */
if (ar &
goto out;
}
}
/*
* Max transfer size of the server is the absolute limite.
* If the client has decided to max out with something really
* tiny, then return toosmall. Otherwise, move forward and
* see if a single entry can be encoded.
*/
goto out;
}
}
/*
* How large should the mblk be for outgoing encoding.
*/
else
/*
* mp will contain the data to be sent out in the readdir reply.
* It will be freed after the reply has been sent.
* Let's roundup the data to a BYTES_PER_XDR_UNIX multiple,
* so that the call to xdrmblk_putmblk() never fails.
*/
/*
* The allocation of the client's requested size has
* failed. It may be that the size is too large for
* current system utilization; step down to a "common"
* size and wait for the allocation to occur.
*/
}
/*
* The "redzone" at the end of the encoding buffer is used
* to deal with xdr encoding length. Instead of checking
* each encoding of an attribute value before it is done,
* make the assumption that it will fit into the buffer and
* check occasionally.
*
* The largest block of attributes that are encoded without
* checking the redzone is 18 * BYTES_PER_XDR_UNIT (72 bytes)
* "round" to 128 as the redzone size.
*/
else
/*
* Set the dircount; this will be used as the size for the
* readdir of the underlying filesystem. First make sure
* that it is large enough to do a reasonable readdir (client
* may have short changed us - it is an advisory number);
* then make sure that it isn't too large.
* After all of that, if maxcount is "small" then just use
* that for the dircount number.
*/
else
}
/* number of entries fully encoded in outgoing buffer */
nents = 0;
/* ENCODE READDIR4res.cookieverf */
if (rddir_data == NULL) {
/* The allocation failed; downsize and wait for it this time */
if (rddir_data_len > MAXBSIZE)
}
/* Move on to reading the directory contents */
if (error) {
goto out;
}
/* No data were read. Check if we reached the end of the directory. */
if (rddir_result_size == 0) {
/* encode the BOOLEAN marking no further entries */
IXDR_PUT_U_INT32(ptr, false);
/* encode the BOOLEAN signifying end of directory */
goto out;
}
lastentry_ptr = ptr;
no_space = 0;
/* reset expseudo */
expseudo = 0;
if (vp) {
}
if (newexi)
/* skip "." and ".." entries */
continue;
}
if (check_visible &&
continue;
}
/*
* Only if the client requested attributes...
* If the VOP_LOOKUP fails ENOENT, then skip this entry
* for the readdir response. If there was another error,
* then set the rddirattr_error and the error will be
* encoded later in the "attributes" section.
*/
if (ar == 0)
goto reencode_attrs;
continue;
}
/*
* The vp obtained from above may be from a
* different filesystem mount and the vfs-like
* attributes should be obtained from that
* different vfs; only do this if appropriate.
*/
if (vp &&
if (ar & (FATTR4_FILES_AVAIL_MASK |
if (error =
&sbe)) {
/* Remove attrs from encode */
ae &= ~(FATTR4_FILES_AVAIL_MASK |
}
}
if (ar & (FATTR4_MAXFILESIZE_MASK |
ar &= ~(FATTR4_MAXFILESIZE_MASK |
}
}
}
/* encode the BOOLEAN for the existence of the next entry */
IXDR_PUT_U_INT32(ptr, true);
/* encode the COOKIE for the entry */
continue;
}
/* Calculate the dirent name length */
/* room for LENGTH + string ? */
continue;
}
/* encode the LENGTH of the name */
/* encode the RNDUP FILL first */
/* encode the NAME of the entry */
/* now bump the ptr after... */
/*
* Keep checking on the dircount to see if we have
* reached the limit; from the RFC, dircount is to be
* the XDR encoded limit of the cookie plus name.
* So the count is the name, XDR_UNIT of length for
* that name and 2 * XDR_UNIT bytes of cookie;
* However, use the regular DIRENT64 to match most
* client's APIs.
*/
continue;
}
/*
* Attributes requested?
* Gather up the attribute info and the previous VOP_LOOKUP()
* succeeded; if an error occurs on the VOP_GETATTR() then
* return just the error (again if it is requested).
* Note that the previous VOP_LOOKUP() could have failed
* itself which leaves this code without anything for
* a VOP_GETATTR().
* Also note that the readdir_attr_error is left in the
* encoding mask if requested and so is the mounted_on_fileid.
*/
if (ae != 0) {
if (!vp) {
} else {
if (rddirattr_error)
}
}
/* START OF ATTRIBUTE ENCODING */
/* encode the LENGTH of the BITMAP4 array */
/* encode the BITMAP4 */
attrmask_ptr = ptr;
/* encode the default LENGTH of the attributes for entry */
IXDR_PUT_U_INT32(ptr, 0);
if (ptr > ptr_redzone) {
continue;
}
/* Check if any of the first 32 attributes are being encoded */
if (ae & 0xffffffff00000000) {
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 18 * BYTES_PER_XDR_UNIT of data
*/
if (ae &
if (ae & FATTR4_SUPPORTED_ATTRS_MASK) {
}
if (ae & FATTR4_TYPE_MASK) {
ftype = NF4ATTRDIR;
else
}
}
if (ae & FATTR4_FH_EXPIRE_TYPE_MASK) {
}
if (ae & FATTR4_CHANGE_MASK) {
}
if (ae & FATTR4_SIZE_MASK) {
}
if (ae & FATTR4_LINK_SUPPORT_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_SYMLINK_SUPPORT_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_NAMED_ATTR_MASK) {
int sattr_error;
VFS_XATTR)) {
} else {
if (sattr_error || pc_val == 0)
(void) VOP_PATHCONF(vp,
&pc_val,
}
}
if (ae & FATTR4_FSID_MASK) {
struct exportinfo *exi;
if (exi->exi_volatile_dev) {
minor = 0;
} else {
}
}
if (ae & FATTR4_UNIQUE_HANDLES_MASK) {
IXDR_PUT_U_INT32(ptr, false);
}
if (ae & FATTR4_LEASE_TIME_MASK) {
}
if (ae & FATTR4_RDATTR_ERROR_MASK) {
(rddirattr_error == 0 ?
0 : puterrno4(rddirattr_error));
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 4 * BYTES_PER_XDR_UNIT of data.
* NOTE: that if ACLs are supported that the
* redzone calculations will need to change.
*/
if (ae &
if (ae & FATTR4_ACL_MASK) {
ASSERT(0);
}
if (ae & FATTR4_ACLSUPPORT_MASK) {
ASSERT(0);
}
if (ae & FATTR4_ARCHIVE_MASK) {
ASSERT(0);
}
if (ae & FATTR4_CANSETTIME_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_CASE_INSENSITIVE_MASK) {
IXDR_PUT_U_INT32(ptr, false);
}
if (ae & FATTR4_CASE_PRESERVING_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_CHOWN_RESTRICTED_MASK) {
(void) VOP_PATHCONF(vp,
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
/*
* Redzone check is done before the filehandle
* is encoded.
*/
if (ae &
if (ae & FATTR4_FILEHANDLE_MASK) {
struct {
char *val;
char fh[NFS_FH4_LEN];
} fh;
&ptr, ptr_redzone,
if (nents ||
IS_MIN_ATTR_MASK(ar)) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
if (ae & FATTR4_FILEID_MASK) {
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 15 * BYTES_PER_XDR_UNIT of data.
*/
if (ae &
if (ae & FATTR4_FILES_AVAIL_MASK) {
}
if (ae & FATTR4_FILES_FREE_MASK) {
}
if (ae & FATTR4_FILES_TOTAL_MASK) {
}
if (ae & FATTR4_FS_LOCATIONS_MASK) {
ASSERT(0);
}
if (ae & FATTR4_HIDDEN_MASK) {
ASSERT(0);
}
if (ae & FATTR4_HOMOGENEOUS_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_MAXFILESIZE_MASK) {
}
if (ae & FATTR4_MAXLINK_MASK) {
}
if (ae & FATTR4_MAXNAME_MASK) {
}
if (ae & FATTR4_MAXREAD_MASK) {
}
if (ae & FATTR4_MAXWRITE_MASK) {
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
}
if (ae & 0x00000000ffffffff) {
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 3 * BYTES_PER_XDR_UNIT of data.
*/
if (ae &
if (ae & FATTR4_MIMETYPE_MASK) {
ASSERT(0);
}
if (ae & FATTR4_MODE_MASK) {
IXDR_PUT_U_INT32(ptr, m);
}
if (ae & FATTR4_NO_TRUNC_MASK) {
IXDR_PUT_U_INT32(ptr, true);
}
if (ae & FATTR4_NUMLINKS_MASK) {
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
/*
* Redzone check is done before the encoding of the
* owner string since the length is indeterminate.
*/
if (ae & FATTR4_OWNER_MASK) {
if (!lu_set) {
if (!owner_error) {
}
if (owner.utf8string_len != 0) {
owner.utf8string_len = 0;
}
if (!owner_error) {
} else {
}
}
if (!owner_error) {
if ((ptr +
+ 2) > ptr_redzone) {
if (nents ||
IS_MIN_ATTR_MASK(ar)) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
/* encode the LENGTH of owner string */
/* encode the RNDUP FILL first */
/* encode the OWNER */
}
}
/*
* Redzone check is done before the encoding of the
* group string since the length is indeterminate.
*/
if (ae & FATTR4_OWNER_GROUP_MASK) {
if (!lg_set) {
if (!group_error) {
}
if (group.utf8string_len != 0) {
group.utf8string_len = 0;
}
if (!group_error)
else
}
if (!group_error) {
if ((ptr +
+ 2) > ptr_redzone) {
if (nents ||
IS_MIN_ATTR_MASK(ar)) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
/* encode the LENGTH of owner string */
/* encode the RNDUP FILL first */
/* encode the OWNER */
}
}
if (ae &
if (ae & FATTR4_QUOTA_AVAIL_HARD_MASK) {
ASSERT(0);
}
if (ae & FATTR4_QUOTA_AVAIL_SOFT_MASK) {
ASSERT(0);
}
if (ae & FATTR4_QUOTA_USED_MASK) {
ASSERT(0);
}
}
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 10 * BYTES_PER_XDR_UNIT of data.
*/
if (ae &
if (ae & FATTR4_RAWDEV_MASK) {
}
if (ae & FATTR4_SPACE_AVAIL_MASK) {
}
if (ae & FATTR4_SPACE_FREE_MASK) {
}
if (ae & FATTR4_SPACE_TOTAL_MASK) {
}
if (ae & FATTR4_SPACE_USED_MASK) {
}
if (ae & FATTR4_SYSTEM_MASK) {
ASSERT(0);
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
/*
* Redzone check is done at the end of this section.
* This particular section will encode a maximum of
* 14 * BYTES_PER_XDR_UNIT of data.
*/
if (ae &
if (ae & FATTR4_TIME_ACCESS_MASK) {
}
if (ae & FATTR4_TIME_ACCESS_SET_MASK) {
ASSERT(0);
}
if (ae & FATTR4_TIME_BACKUP_MASK) {
ASSERT(0);
}
if (ae & FATTR4_TIME_CREATE_MASK) {
ASSERT(0);
}
if (ae & FATTR4_TIME_DELTA_MASK) {
u_longlong_t sec = 0;
}
if (ae & FATTR4_TIME_METADATA_MASK) {
}
if (ae & FATTR4_TIME_MODIFY_MASK) {
}
if (ae & FATTR4_TIME_MODIFY_SET_MASK) {
ASSERT(0);
}
if (ae & FATTR4_MOUNTED_ON_FILEID_MASK) {
}
/* Check the redzone boundary */
if (ptr > ptr_redzone) {
continue;
}
ptr = lastentry_ptr;
goto reencode_attrs;
}
}
}
/* Reset to directory's vfs info when encoding complete */
if (vfs_different) {
vfs_different = 0;
}
/* "go back" and encode the attributes' length */
(char *)ptr -
(char *)attr_offset_ptr -
/*
* If there was trouble obtaining a mapping for either
* the owner or group attributes, then remove them from
* bitmap4 for this entry and reset the bitmap value
* in the data stream.
*/
if (owner_error || group_error) {
if (owner_error)
ae &= ~FATTR4_OWNER_MASK;
if (group_error)
}
/* END OF ATTRIBUTE ENCODING */
lastentry_ptr = ptr;
nents++;
}
/*
* Check for the case that another VOP_READDIR() has to be done.
* - no space encoding error
* - no entry successfully encoded
* - still more directory to read
*/
goto readagain;
/*
* If no_space is set then we terminated prematurely,
* rewind to the last entry and this can never be EOF.
*/
if (no_space) {
ptr = lastentry_ptr;
} else {
}
/*
* If we have entries, always return them, otherwise only error
* if we ran out of space.
*/
/* encode the BOOLEAN marking no further entries */
IXDR_PUT_U_INT32(ptr, false);
/* encode the BOOLEAN signifying end of directory */
} else {
}
if (vp)
if (owner.utf8string_len != 0)
if (group.utf8string_len != 0)
out:
READDIR4res *, resp);
}