Exemplo n.º 1
0
int nfs3_readdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res)
{
	struct fsal_obj_handle *dir_obj = NULL;
	struct fsal_obj_handle *parent_dir_obj = NULL;
	unsigned long count = 0;
	uint64_t cookie = 0;
	uint64_t fsal_cookie = 0;
	cookieverf3 cookie_verifier;
	unsigned int num_entries = 0;
	unsigned long estimated_num_entries = 0;
	object_file_type_t dir_filetype = 0;
	bool eod_met = false;
	fsal_status_t fsal_status = {0, 0};
	fsal_status_t fsal_status_gethandle = {0, 0};
	int rc = NFS_REQ_OK;
	struct nfs3_readdir_cb_data tracker = { NULL };
	bool use_cookie_verifier = op_ctx_export_has_option(
					EXPORT_OPTION_USE_COOKIE_VERIFIER);

	if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) {
		char str[LEN_FH_STR];
		log_components_t component;

		nfs_FhandleToStr(req->rq_vers,
				 &(arg->arg_readdir3.dir),
				 NULL,
				 str);

		if (isDebug(COMPONENT_NFSPROTO))
			component = COMPONENT_NFSPROTO;
		else
			component = COMPONENT_NFS_READDIR;

		LogDebug(component,
			 "REQUEST PROCESSING: Calling nfs_Readdir handle: %s",
			 str);
	}

	READDIR3resok * const RES_READDIR3_OK =
	    &res->res_readdir3.READDIR3res_u.resok;

	/* to avoid setting it on each error case */
	res->res_readdir3.READDIR3res_u.resfail.dir_attributes.
	    attributes_follow = FALSE;

	/* Look up object for filehandle */
	dir_obj = nfs3_FhandleToCache(&(arg->arg_readdir3.dir),
					&(res->res_readdir3.status),
					&rc);
	if (dir_obj == NULL) {
		/* Status and rc have been set by nfs3_FhandleToCache */
		goto out;
	}

	/* Extract the filetype */
	dir_filetype = dir_obj->type;
	/* Sanity checks -- must be a directory */
	if (dir_filetype != DIRECTORY) {
		res->res_readdir3.status = NFS3ERR_NOTDIR;

		rc = NFS_REQ_OK;
		goto out;
	}

	/* Parse out request arguments and decide how many entries we
	 * want.  For NFSv3, deal with the cookie verifier.
	 */

	count = arg->arg_readdir3.count;
	cookie = arg->arg_readdir3.cookie;
	estimated_num_entries =
	    MIN(count / (sizeof(entry3) - sizeof(char *)), 120);
	LogFullDebug(COMPONENT_NFS_READDIR,
		     "---> nfs3_readdir: count=%lu  cookie=%" PRIu64
		     " estimated_num_entries=%lu",
		     count, cookie, estimated_num_entries);
	if (estimated_num_entries == 0) {
		res->res_readdir3.status = NFS3ERR_TOOSMALL;
		rc = NFS_REQ_OK;
		goto out;
	}

	/* To make or check the cookie verifier */
	memset(cookie_verifier, 0, sizeof(cookieverf3));

	/* If cookie verifier is used, then a
	 * non-trivial value is returned to the
	 * client.
	 *
	 * This value is the ctime of the directory. If verifier is
	 * unused (as in many NFS Servers) then only a set of zeros
	 * is returned (trivial value).
	 */
	if (use_cookie_verifier) {
		struct attrlist attrs;

		fsal_prepare_attrs(&attrs, ATTR_CTIME);

		fsal_status = dir_obj->obj_ops.getattrs(dir_obj, &attrs);

		if (FSAL_IS_ERROR(fsal_status)) {
			res->res_readdir3.status =
						nfs3_Errno_status(fsal_status);
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "getattrs returned %s",
				     msg_fsal_err(fsal_status.major));
			goto out;
		}

		memcpy(cookie_verifier,
		       &attrs.ctime.tv_sec,
		       sizeof(attrs.ctime.tv_sec));

		/* Done with the attrs */
		fsal_release_attrs(&attrs);
	}

	if (cookie != 0 && use_cookie_verifier) {
		/* Not the first call, so we have to check the cookie
		 * verifier
		 */
		if (memcmp(cookie_verifier,
			   arg->arg_readdir3.cookieverf,
			   NFS3_COOKIEVERFSIZE) != 0) {
			res->res_readdir3.status = NFS3ERR_BAD_COOKIE;
			rc = NFS_REQ_OK;
			goto out;
		}
	}

	tracker.entries = gsh_calloc(estimated_num_entries, sizeof(entry3));
	tracker.total_entries = estimated_num_entries;
	tracker.mem_left = count - sizeof(READDIR3resok);
	tracker.count = 0;
	tracker.error = NFS3_OK;

	/* Adjust the cookie we supply to fsal */
	if (cookie > 2) {	/* it is not the cookie for "." nor ".." */
		fsal_cookie = cookie;
	} else {
		fsal_cookie = 0;
	}

	/* Fills "."  */
	if (cookie == 0) {
		res->res_readdir3.status = nfs_readdir_dot_entry(dir_obj, ".",
					 1, nfs3_readdir_callback, &tracker);

		if (res->res_readdir3.status != NFS3_OK) {
			rc = NFS_REQ_OK;
			goto out;
		}
	}

	/* Fills ".." */
	if ((cookie <= 1) && (estimated_num_entries > 1)) {
		/* Get parent pentry */
		fsal_status_gethandle = fsal_lookupp(dir_obj,
						     &parent_dir_obj,
						     NULL);

		if (parent_dir_obj == NULL) {
			res->res_readdir3.status =
			    nfs3_Errno_status(fsal_status_gethandle);
			rc = NFS_REQ_OK;
			goto out;
		}

		res->res_readdir3.status = nfs_readdir_dot_entry(parent_dir_obj,
				"..", 2, nfs3_readdir_callback, &tracker);

		if (res->res_readdir3.status != NFS3_OK) {
			rc = NFS_REQ_OK;
			goto out;
		}

		parent_dir_obj->obj_ops.put_ref(parent_dir_obj);
		parent_dir_obj = NULL;
	}

	/* Call readdir */
	fsal_status = fsal_readdir(dir_obj, fsal_cookie, &num_entries, &eod_met,
				   0,	/* no attr */
				   nfs3_readdir_callback, &tracker);

	if (FSAL_IS_ERROR(fsal_status)) {
		if (nfs_RetryableError(fsal_status.major)) {
			rc = NFS_REQ_DROP;
			goto out;
		}

		res->res_readdir3.status = nfs3_Errno_status(fsal_status);
		nfs_SetPostOpAttr(dir_obj,
				  &res->res_readdir3.READDIR3res_u.resfail.
					dir_attributes,
				  NULL);
		goto out;
	}

	if (tracker.error != NFS3_OK) {
		res->res_readdir3.status = tracker.error;
		nfs_SetPostOpAttr(dir_obj,
				  &res->res_readdir3.READDIR3res_u.resfail.
					dir_attributes,
				  NULL);
		goto out;
	}

	LogFullDebug(COMPONENT_NFS_READDIR,
		     "-- Readdir -> Call to fsal_readdir(cookie=%"
		     PRIu64 ")",
		     fsal_cookie);

	if ((num_entries == 0) && (cookie > 1)) {
		RES_READDIR3_OK->reply.entries = NULL;
		RES_READDIR3_OK->reply.eof = TRUE;
	} else {
		RES_READDIR3_OK->reply.entries = tracker.entries;
		RES_READDIR3_OK->reply.eof = eod_met;
	}
	nfs_SetPostOpAttr(dir_obj, &RES_READDIR3_OK->dir_attributes, NULL);
	memcpy(RES_READDIR3_OK->cookieverf, cookie_verifier,
	       sizeof(cookieverf3));
	res->res_readdir3.status = NFS3_OK;

	rc = NFS_REQ_OK;

 out:
	/* return references */
	if (dir_obj)
		dir_obj->obj_ops.put_ref(dir_obj);

	if (parent_dir_obj)
		parent_dir_obj->obj_ops.put_ref(parent_dir_obj);

	/* Deallocate memory in the event of an error */
	if (((res->res_readdir3.status != NFS3_OK) || (rc != NFS_REQ_OK) ||
	    ((num_entries == 0) && (cookie > 1))) &&
	    (tracker.entries != NULL)) {
		free_entry3s(tracker.entries);
		RES_READDIR3_OK->reply.entries = NULL;
	}

	return rc;
}				/* nfs3_readdir */
Exemplo n.º 2
0
/**
 * @brief The NFS4_OP_LISTXATTR operation.
 *
 * This functions handles the NFS4_OP_LISTXATTR operation in NFSv4. This
 * function can be called only from nfs4_Compound
 *
 * @param[in]     op   Arguments for nfs4_op
 * @param[in,out] data Compound request's data
 * @param[out]    resp Results for nfs4_op
 *
 */
int nfs4_op_listxattr(struct nfs_argop4 *op, compound_data_t *data,
		      struct nfs_resop4 *resp)
{
	LISTXATTR4args * const arg_LISTXATTR4 = &op->nfs_argop4_u.oplistxattr;
	LISTXATTR4res * const res_LISTXATTR4 = &resp->nfs_resop4_u.oplistxattr;
	fsal_status_t fsal_status;
	struct fsal_obj_handle *obj_handle = data->current_obj;
	xattrlist4 list;
	nfs_cookie4 la_cookie;
	verifier4 la_cookieverf;
	bool_t lr_eof;
	component4 *entry;
	int i;
	bool use_cookie_verifier = op_ctx_export_has_option(
					EXPORT_OPTION_USE_COOKIE_VERIFIER);

	resp->resop = NFS4_OP_LISTXATTR;
	res_LISTXATTR4->status = NFS4_OK;

	LogDebug(COMPONENT_NFS_V4,
		 "SetXattr max count %d cookie %" PRIu64,
		 arg_LISTXATTR4->la_maxcount, arg_LISTXATTR4->la_cookie);

	/* Do basic checks on a filehandle */
	res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE,
						      false);
	if (res_LISTXATTR4->status != NFS4_OK)
		return res_LISTXATTR4->status;

	/* Do basic checks on a filehandle */
	res_LISTXATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE,
									false);
	if (res_LISTXATTR4->status != NFS4_OK)
		return res_LISTXATTR4->status;

	/* Double buf size, one half for compound and on half for names. */
	list.entries = (component4 *)gsh_malloc(2*arg_LISTXATTR4->la_maxcount);
	la_cookie = arg_LISTXATTR4->la_cookie;
	memset(la_cookieverf, 0, NFS4_VERIFIER_SIZE);

	if (la_cookie == 0 && use_cookie_verifier) {
		if (memcmp(la_cookieverf, arg_LISTXATTR4->la_cookieverf,
			   NFS4_VERIFIER_SIZE) != 0) {
			res_LISTXATTR4->status = NFS4ERR_BAD_COOKIE;
			LogFullDebug(COMPONENT_NFS_V4,
				     "Bad cookie");
			return res_LISTXATTR4->status;
		}
	}
	fsal_status = obj_handle->obj_ops->listxattrs(obj_handle,
						arg_LISTXATTR4->la_maxcount,
						&la_cookie,
						&la_cookieverf,
						&lr_eof,
						&list);
	if (FSAL_IS_ERROR(fsal_status)) {
		res_LISTXATTR4->status =
			nfs4_Errno_state(state_error_convert(fsal_status));
		gsh_free(list.entries);
		res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names.entries = NULL;
		return res_LISTXATTR4->status;
	}
	res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookie = la_cookie;
	res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_eof = lr_eof;
	memcpy(res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_cookieverf,
		la_cookieverf, NFS4_VERIFIER_SIZE);
	res_LISTXATTR4->LISTXATTR4res_u.resok4.lr_names = list;
	entry = list.entries;
	for (i = 0; i < list.entryCount; i++) {
		LogFullDebug(COMPONENT_FSAL,
			"entry %d at %p len %d at %p name %s",
			i, entry, entry->utf8string_len,
			entry->utf8string_val, entry->utf8string_val);
		entry += 1;
	}
	return res_LISTXATTR4->status;
}