/**
 * @brief Return true if create verifier matches
 *
 * This function returns true if the create verifier matches
 *
 * @param[in] entry   Entry to be managed.
 * @param[in] req_ctx Request context(user creds, client address etc)
 * @param[in] verf_hi High long of verifier
 * @param[in] verf_lo Low long of verifier
 *
 * @return Errors from cache_inode_lock_trust_attributes.
 *
 */
bool
cache_inode_create_verify(cache_entry_t *entry,
                          const struct req_op_context *req_ctx,
                          uint32_t verf_hi,
                          uint32_t verf_lo)
{
        /* True if the verifier matches */
        bool verified = false;

        /* Lock (and refresh if necessary) the attributes, copy them
           out, and unlock. */

        if (cache_inode_lock_trust_attrs(entry, req_ctx, false)
                == CACHE_INODE_SUCCESS) {
                if (FSAL_TEST_MASK(entry->obj_handle->attributes.mask,
                                   ATTR_ATIME) &&
                    FSAL_TEST_MASK(entry->obj_handle->attributes.mask,
                                   ATTR_MTIME) &&
                    entry->obj_handle->attributes.atime.tv_sec == verf_hi &&
                    entry->obj_handle->attributes.mtime.tv_sec == verf_lo) {
                        verified = true;
                }
                PTHREAD_RWLOCK_unlock(&entry->attr_lock);
        }

        return verified;
}
Exemplo n.º 2
0
/**
 *
 * @brief Checks the permissions on an object
 *
 * This function returns success if the supplied credentials possess
 * permission required to meet the specified access.
 *
 * @param[in]  entry       The object to be checked
 * @param[in]  access_type The kind of access to be checked
 * @param[in]  use_mutex   Whether to acquire a read lock
 *
 * @return CACHE_INODE_SUCCESS if operation is a success
 *
 */
cache_inode_status_t
cache_inode_access_sw(cache_entry_t *entry,
		      fsal_accessflags_t access_type,
		      fsal_accessflags_t *allowed,
		      fsal_accessflags_t *denied,
		      bool use_mutex)
{
	fsal_status_t fsal_status;
	struct fsal_obj_handle *pfsal_handle = entry->obj_handle;
	cache_inode_status_t status = CACHE_INODE_SUCCESS;

	LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
		    "access_type=0X%x", access_type);

	/* Set the return default to CACHE_INODE_SUCCESS */
	status = CACHE_INODE_SUCCESS;

	/* We actually need the lock here since we're using
	   the attribute cache, so get it if the caller didn't
	   acquire it.  */
	if (use_mutex) {
		status = cache_inode_lock_trust_attrs(entry, false);
		if (status != CACHE_INODE_SUCCESS)
			goto out;
	}
	fsal_status =
	    pfsal_handle->ops->test_access(pfsal_handle, access_type,
					   allowed, denied);
	if (use_mutex)
		PTHREAD_RWLOCK_unlock(&entry->attr_lock);
	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "status=%s", cache_inode_err_str(status));
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_CACHE_INODE,
				 "STALE returned by FSAL, calling kill_entry");
			cache_inode_kill_entry(entry);
		}
	} else {
		status = CACHE_INODE_SUCCESS;
	}

 out:
	return status;
}
Exemplo n.º 3
0
/**
 * @brief Set the attributes for a file.
 *
 * This function sets the attributes of a file, both in the cache and
 * in the underlying filesystem.
 *
 * @param[in]     entry   Entry whose attributes are to be set
 * @param[in,out] attr    Attributes to set/result of set
 *
 * @retval CACHE_INODE_SUCCESS if operation is a success
 */
cache_inode_status_t
cache_inode_setattr(cache_entry_t *entry,
		    struct attrlist *attr,
		    bool is_open_write)
{
	struct fsal_obj_handle *obj_handle = entry->obj_handle;
	fsal_status_t fsal_status = { 0, 0 };
	fsal_acl_t *saved_acl = NULL;
	fsal_acl_status_t acl_status = 0;
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	uint64_t before;

	/* True if we have taken the content lock on 'entry' */
	bool content_locked = false;

	if ((attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED))
	     && (entry->type != REGULAR_FILE)) {
		LogWarn(COMPONENT_CACHE_INODE,
			"Attempt to truncate non-regular file: type=%d",
			entry->type);
		status = CACHE_INODE_BAD_TYPE;
	}

	/* Is it allowed to change times ? */
	if (!op_ctx->fsal_export->ops->fs_supports(op_ctx->fsal_export,
						    fso_cansettime)
	    &&
	    (FSAL_TEST_MASK
	     (attr->mask,
	      (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME)))) {
		status = CACHE_INODE_INVALID_ARGUMENT;
		goto out;
	}

	/* Get wrlock on attr_lock and verify attrs */
	status = cache_inode_lock_trust_attrs(entry, true);
	if (status != CACHE_INODE_SUCCESS)
		return status;

	/* Do permission checks */
	status = cache_inode_check_setattr_perms(entry, attr, is_open_write);
	if (status != CACHE_INODE_SUCCESS)
		goto unlock;

	if (attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) {
		PTHREAD_RWLOCK_wrlock(&entry->content_lock);
		content_locked = true;
	}

	saved_acl = obj_handle->attributes.acl;
	before = obj_handle->attributes.change;
	fsal_status = obj_handle->ops->setattrs(obj_handle, attr);
	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_CACHE_INODE,
				 "FSAL returned STALE from truncate");
			cache_inode_kill_entry(entry);
		}
		goto unlock;
	}
	fsal_status = obj_handle->ops->getattrs(obj_handle);
	*attr = obj_handle->attributes;
	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_CACHE_INODE,
				 "FSAL returned STALE from setattrs");
			cache_inode_kill_entry(entry);
		}
		goto unlock;
	}
	if (before == obj_handle->attributes.change)
		obj_handle->attributes.change++;
	/* Decrement refcount on saved ACL */
	nfs4_acl_release_entry(saved_acl, &acl_status);
	if (acl_status != NFS_V4_ACL_SUCCESS)
		LogCrit(COMPONENT_CACHE_INODE,
			"Failed to release old acl, status=%d", acl_status);

	cache_inode_fixup_md(entry);

	/* Copy the complete set of new attributes out. */

	*attr = entry->obj_handle->attributes;

	status = CACHE_INODE_SUCCESS;

unlock:
	if (content_locked)
		PTHREAD_RWLOCK_unlock(&entry->content_lock);
	PTHREAD_RWLOCK_unlock(&entry->attr_lock);

out:
	return status;
}
/**
 * @brief Set the attributes for a file.
 *
 * This function sets the attributes of a file, both in the cache and
 * in the underlying filesystem.
 *
 * @param[in]     entry   Entry whose attributes are to be set
 * @param[in,out] attr    Attributes to set/result of set
 *
 * @retval CACHE_INODE_SUCCESS if operation is a success
 */
cache_inode_status_t
cache_inode_setattr(cache_entry_t *entry,
		    struct attrlist *attr,
		    bool is_open_write)
{
	struct fsal_obj_handle *obj_handle = entry->obj_handle;
	fsal_status_t fsal_status = { 0, 0 };
	fsal_acl_t *saved_acl = NULL;
	fsal_acl_status_t acl_status = 0;
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	uint64_t before;
	const struct user_cred *creds = op_ctx->creds;

	/* True if we have taken the content lock on 'entry' */
	bool content_locked = false;

	if ((attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED))
	     && (entry->type != REGULAR_FILE)) {
		LogWarn(COMPONENT_CACHE_INODE,
			"Attempt to truncate non-regular file: type=%d",
			entry->type);
		status = CACHE_INODE_BAD_TYPE;
		goto out;
	}

	/* Is it allowed to change times ? */
	if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export,
						    fso_cansettime)
	    &&
	    (FSAL_TEST_MASK
	     (attr->mask,
	      (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME)))) {
		status = CACHE_INODE_INVALID_ARGUMENT;
		goto out;
	}

	/* Get wrlock on attr_lock and verify attrs */
	status = cache_inode_lock_trust_attrs(entry, true);
	if (status != CACHE_INODE_SUCCESS)
		return status;

	/* Do permission checks */
	status = cache_inode_check_setattr_perms(entry, attr, is_open_write);
	if (status != CACHE_INODE_SUCCESS)
		goto unlock;

	if (attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) {
		PTHREAD_RWLOCK_wrlock(&entry->content_lock);
		content_locked = true;
	}

	/* Test for the following condition from chown(2):
	 *
	 *     When the owner or group of an executable file are changed by an
	 *     unprivileged user the S_ISUID and S_ISGID mode bits are cleared.
	 *     POSIX does not specify whether this also should happen when
	 *     root does the chown(); the Linux behavior depends on the kernel
	 *     version.  In case of a non-group-executable file (i.e., one for
	 *     which the S_IXGRP bit is not set) the S_ISGID bit indicates
	 *     mandatory locking, and is not cleared by a chown().
	 *
	 */
	if (creds->caller_uid != 0 &&
	    (FSAL_TEST_MASK(attr->mask, ATTR_OWNER) ||
	     FSAL_TEST_MASK(attr->mask, ATTR_GROUP)) &&
	    ((entry->obj_handle->attrs->mode &
	      (S_IXOTH | S_IXUSR | S_IXGRP)) != 0) &&
	    ((entry->obj_handle->attrs->mode &
	      (S_ISUID | S_ISGID)) != 0)) {
		/* Non-priviledged user changing ownership on an executable
		 * file with S_ISUID or S_ISGID bit set, need to be cleared.
		 */
		if (!FSAL_TEST_MASK(attr->mask, ATTR_MODE)) {
			/* Mode wasn't being set, so set it now, start with
			 * the current attributes.
			 */
			attr->mode = entry->obj_handle->attrs->mode;
			FSAL_SET_MASK(attr->mask, ATTR_MODE);
		}

		/* Don't clear S_ISGID if the file isn't group executable.
		 * In that case, S_ISGID indicates mandatory locking and
		 * is not cleared by chown.
		 */
		if ((entry->obj_handle->attrs->mode & S_IXGRP) != 0)
			attr->mode &= ~S_ISGID;

		/* Clear S_ISUID. */
		attr->mode &= ~S_ISUID;
	}

	/* Test for the following condition from chmod(2):
	 *
	 *     If the calling process is not privileged (Linux: does not have
	 *     the CAP_FSETID capability), and the group of the file does not
	 *     match the effective group ID of the process or one of its
	 *     supplementary group IDs, the S_ISGID bit will be turned off,
	 *     but this will not cause an error to be returned.
	 *
	 * We test the actual mode being set before testing for group
	 * membership since that is a bit more expensive.
	 */
	if (creds->caller_uid != 0 &&
	    FSAL_TEST_MASK(attr->mask, ATTR_MODE) &&
	    (attr->mode & S_ISGID) != 0 &&
	    not_in_group_list(entry->obj_handle->attrs->group)) {
		/* Clear S_ISGID */
		attr->mode &= ~S_ISGID;
	}

	saved_acl = entry->obj_handle->attrs->acl;
	before = entry->obj_handle->attrs->change;
	fsal_status = obj_handle->obj_ops.setattrs(obj_handle, attr);
	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_CACHE_INODE,
				 "FSAL returned STALE from setattrs");
			cache_inode_kill_entry(entry);
		}
		goto unlock;
	}
	fsal_status = obj_handle->obj_ops.getattrs(obj_handle);
	*attr = *entry->obj_handle->attrs;
	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_CACHE_INODE,
				 "FSAL returned STALE from getattrs");
			cache_inode_kill_entry(entry);
		}
		goto unlock;
	}
	if (before == entry->obj_handle->attrs->change)
		entry->obj_handle->attrs->change++;
	/* Decrement refcount on saved ACL */
	nfs4_acl_release_entry(saved_acl, &acl_status);
	if (acl_status != NFS_V4_ACL_SUCCESS)
		LogCrit(COMPONENT_CACHE_INODE,
			"Failed to release old acl, status=%d", acl_status);

	cache_inode_fixup_md(entry);

	/* Copy the complete set of new attributes out. */

	*attr = *entry->obj_handle->attrs;

	status = CACHE_INODE_SUCCESS;

unlock:
	if (content_locked)
		PTHREAD_RWLOCK_unlock(&entry->content_lock);
	PTHREAD_RWLOCK_unlock(&entry->attr_lock);

out:
	return status;
}
Exemplo n.º 5
0
cache_inode_status_t
cache_inode_setattr(cache_entry_t *entry,
                    fsal_attrib_list_t *attr,
                    fsal_op_context_t *context,
                    int is_open_write,
                    cache_inode_status_t *status)
{
     fsal_status_t fsal_status = {0, 0};
     int           got_content_lock = FALSE;
#ifdef _USE_NFS4_ACL
     fsal_acl_t *saved_acl = NULL;
     fsal_acl_status_t acl_status = 0;
#endif /* _USE_NFS4_ACL */

     if ((entry->type == UNASSIGNED) ||
         (entry->type == RECYCLED)) {
          LogWarn(COMPONENT_CACHE_INODE,
                  "WARNING: unknown source entry type: type=%d, "
                  "line %d in file %s", entry->type, __LINE__, __FILE__);
          *status = CACHE_INODE_BAD_TYPE;
          goto out;
     }

     if ((attr->asked_attributes & FSAL_ATTR_SIZE) &&
         (entry->type != REGULAR_FILE)) {
          LogWarn(COMPONENT_CACHE_INODE,
                   "Attempt to truncate non-regular file: type=%d",
                   entry->type);
          *status = CACHE_INODE_BAD_TYPE;
          goto out;
     }

     /* Get wrlock on attr_lock and verify attrs */
     *status = cache_inode_lock_trust_attrs(entry, context, TRUE);
     if(*status != CACHE_INODE_SUCCESS)
       return *status;

     /* Do permission checks */
     if(cache_inode_check_setattr_perms(entry,
                                        attr,
                                        context,
                                        is_open_write,
                                        status) != CACHE_INODE_SUCCESS)
       {
         goto unlock;
       }

     if (attr->asked_attributes & FSAL_ATTR_SIZE) {
          PTHREAD_RWLOCK_WRLOCK(&entry->content_lock);
          got_content_lock = TRUE;
     }

#ifdef _USE_NFS4_ACL
     saved_acl = entry->attributes.acl;
#endif /* _USE_NFS4_ACL */
     fsal_status = FSAL_setattrs(&entry->handle, context, attr,
                                 &entry->attributes);
     if (FSAL_IS_ERROR(fsal_status)) {
          *status = cache_inode_error_convert(fsal_status);
          if (fsal_status.major == ERR_FSAL_STALE) {
               LogEvent(COMPONENT_CACHE_INODE,
                       "FSAL returned STALE from setattrs");
               cache_inode_kill_entry(entry);
          }
          goto unlock;
     } else {
#ifdef _USE_NFS4_ACL
          /* Decrement refcount on saved ACL */
         nfs4_acl_release_entry(saved_acl, &acl_status);
         if (acl_status != NFS_V4_ACL_SUCCESS) {
              LogCrit(COMPONENT_CACHE_INODE,
                      "Failed to release old acl, status=%d",
                      acl_status);
         }
#endif /* _USE_NFS4_ACL */
     }

     cache_inode_fixup_md(entry);
     /* Copy the complete set of new attributes out. */

     *attr = entry->attributes;
     set_mounted_on_fileid(entry, attr, context->export_context->fe_export);

     *status = CACHE_INODE_SUCCESS;

unlock:
     if(got_content_lock) {
          PTHREAD_RWLOCK_UNLOCK(&entry->content_lock);
     }
     PTHREAD_RWLOCK_UNLOCK(&entry->attr_lock);

out:

     return *status;
} /* cache_inode_setattr */
Exemplo n.º 6
0
cache_inode_status_t
cache_inode_readdir(cache_entry_t *directory,
		    uint64_t cookie, unsigned int *nbfound,
		    bool *eod_met,
		    attrmask_t attrmask,
		    cache_inode_getattr_cb_t cb,
		    void *opaque)
{
	/* The entry being examined */
	cache_inode_dir_entry_t *dirent = NULL;
	/* The node in the tree being traversed */
	struct avltree_node *dirent_node;
	/* The access mask corresponding to permission to list directory
	   entries */
	fsal_accessflags_t access_mask =
	    (FSAL_MODE_MASK_SET(FSAL_R_OK) |
	     FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR));
	fsal_accessflags_t access_mask_attr =
	    (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_MODE_MASK_SET(FSAL_X_OK) |
	     FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR) |
	     FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE));
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	cache_inode_status_t attr_status;
	struct cache_inode_readdir_cb_parms cb_parms = { opaque, NULL,
							 true, 0, true };
	bool retry_stale = true;

	LogFullDebug(COMPONENT_NFS_READDIR,
		     "Enter....");

	/* readdir can be done only with a directory */
	if (directory->type != DIRECTORY) {
		status = CACHE_INODE_NOT_A_DIRECTORY;
		/* no lock acquired so far, just return status */
		LogFullDebug(COMPONENT_NFS_READDIR,
			     "Not a directory");
		return status;
	}

	/* cache_inode_lock_trust_attrs can return an error, and no lock will
	 * be acquired */
	status = cache_inode_lock_trust_attrs(directory, false);
	if (status != CACHE_INODE_SUCCESS) {
		LogDebug(COMPONENT_NFS_READDIR,
			 "cache_inode_lock_trust_attrs status=%s",
			 cache_inode_err_str(status));
		return status;
	}

	/* Adjust access mask if ACL is asked for.
	 * NOTE: We intentionally do NOT check ACE4_READ_ATTR.
	 */
	if ((attrmask & ATTR_ACL) != 0) {
		access_mask |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL);
		access_mask_attr |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL);
	}

	/* Check if user (as specified by the credentials) is authorized to read
	 * the directory or not */
	status = cache_inode_access_no_mutex(directory, access_mask);
	if (status != CACHE_INODE_SUCCESS) {
		LogFullDebug(COMPONENT_NFS_READDIR,
			     "permission check for directory status=%s",
			     cache_inode_err_str(status));
		PTHREAD_RWLOCK_unlock(&directory->attr_lock);
		return status;
	}

	if (attrmask != 0) {
		/* Check for access permission to get attributes */
		attr_status = cache_inode_access_no_mutex(directory,
							  access_mask_attr);
		if (attr_status != CACHE_INODE_SUCCESS) {
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "permission check for attributes "
				     "status=%s",
				     cache_inode_err_str(attr_status));
		}
	} else
		/* No attributes requested, we don't need permission */
		attr_status = CACHE_INODE_SUCCESS;

	PTHREAD_RWLOCK_rdlock(&directory->content_lock);
	PTHREAD_RWLOCK_unlock(&directory->attr_lock);
	if (!
	    ((directory->flags & CACHE_INODE_TRUST_CONTENT)
	     && (directory->flags & CACHE_INODE_DIR_POPULATED))) {
		PTHREAD_RWLOCK_unlock(&directory->content_lock);
		PTHREAD_RWLOCK_wrlock(&directory->content_lock);
		status = cache_inode_readdir_populate(directory);
		if (status != CACHE_INODE_SUCCESS) {
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "cache_inode_readdir_populate status=%s",
				     cache_inode_err_str(status));
			goto unlock_dir;
		}
	}

	/* deal with initial cookie value:
	 * 1. cookie is invalid (-should- be checked by caller)
	 * 2. cookie is 0 (first cookie) -- ok
	 * 3. cookie is > than highest dirent position (error)
	 * 4. cookie <= highest dirent position but > highest cached cookie
	 *    (currently equivalent to #2, because we pre-populate the cookie
	 *    avl)
	 * 5. cookie is in cached range -- ok */

	if (cookie > 0) {
		/* N.B., cache_inode_avl_qp_insert_s ensures k > 2 */
		if (cookie < 3) {
			status = CACHE_INODE_BAD_COOKIE;
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "Bad cookie");
			goto unlock_dir;
		}

		/* we assert this can now succeed */
		dirent =
		    cache_inode_avl_lookup_k(directory, cookie,
					     CACHE_INODE_FLAG_NEXT_ACTIVE);
		if (!dirent) {
			/* Linux (3.4, etc) has been observed to send readdir
			 * at the offset of the last entry's cookie, and
			 * returns no dirents to userland if that readdir
			 * notfound or badcookie. */
			if (cache_inode_avl_lookup_k
			    (directory, cookie, CACHE_INODE_FLAG_NONE)) {
				/* yup, it was the last entry */
				LogFullDebug(COMPONENT_NFS_READDIR,
					     "EOD because empty result");
				*eod_met = true;
				goto unlock_dir;
			}
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "seek to cookie=%" PRIu64 " fail",
				     cookie);
			status = CACHE_INODE_BAD_COOKIE;
			goto unlock_dir;
		}

		/* dirent is the NEXT entry to return, since we sent
		 * CACHE_INODE_FLAG_NEXT_ACTIVE */
		dirent_node = &dirent->node_hk;

	} else {
		/* initial readdir */
		dirent_node = avltree_first(&directory->object.dir.avl.t);
	}

	LogFullDebug(COMPONENT_NFS_READDIR,
		     "About to readdir in cache_inode_readdir: directory=%p "
		     "cookie=%" PRIu64 " collisions %d", directory, cookie,
		     directory->object.dir.avl.collisions);

	/* Now satisfy the request from the cached readdir--stop when either
	 * the requested sequence or dirent sequence is exhausted */
	*nbfound = 0;
	*eod_met = false;

	for (; cb_parms.in_result && dirent_node;
	     dirent_node = avltree_next(dirent_node)) {

		cache_entry_t *entry = NULL;
		cache_inode_status_t tmp_status = 0;

		dirent =
		    avltree_container_of(dirent_node, cache_inode_dir_entry_t,
					 node_hk);

 estale_retry:
		LogFullDebug(COMPONENT_NFS_READDIR,
			     "Lookup direct %s",
			     dirent->name);

		entry =
		    cache_inode_get_keyed(&dirent->ckey,
					  CIG_KEYED_FLAG_NONE, &tmp_status);
		if (!entry) {
			LogFullDebug(COMPONENT_NFS_READDIR,
				     "Lookup returned %s",
				     cache_inode_err_str(tmp_status));

			if (retry_stale
			    && tmp_status == CACHE_INODE_ESTALE) {
				LogDebug(COMPONENT_NFS_READDIR,
					 "cache_inode_get_keyed returned %s "
					 "for %s - retrying entry",
					 cache_inode_err_str(tmp_status),
					 dirent->name);
				retry_stale = false; /* only one retry per
						      * dirent */
				goto estale_retry;
			}

			if (tmp_status == CACHE_INODE_NOT_FOUND
			    || tmp_status == CACHE_INODE_ESTALE) {
				/* Directory changed out from under us.
				   Invalidate it, skip the name, and keep
				   going. */
				atomic_clear_uint32_t_bits(
					&directory->flags,
					CACHE_INODE_TRUST_CONTENT);
				LogDebug(COMPONENT_NFS_READDIR,
					 "cache_inode_get_keyed returned %s "
					 "for %s - skipping entry",
					 cache_inode_err_str(tmp_status),
					 dirent->name);
				continue;
			} else {
				/* Something is more seriously wrong,
				   probably an inconsistency. */
				status = tmp_status;
				LogCrit(COMPONENT_NFS_READDIR,
					"cache_inode_get_keyed returned %s "
					"for %s - bailing out",
					cache_inode_err_str(status),
					dirent->name);
				goto unlock_dir;
			}
		}

		LogFullDebug(COMPONENT_NFS_READDIR,
			     "cache_inode_readdir: dirent=%p name=%s "
			     "cookie=%" PRIu64 " (probes %d)", dirent,
			     dirent->name, dirent->hk.k, dirent->hk.p);

		cb_parms.name = dirent->name;
		cb_parms.attr_allowed = attr_status == CACHE_INODE_SUCCESS;
		cb_parms.cookie = dirent->hk.k;

		tmp_status =
		    cache_inode_getattr(entry, &cb_parms, cb, CB_ORIGINAL);

		if (tmp_status != CACHE_INODE_SUCCESS) {
			cache_inode_lru_unref(entry, LRU_FLAG_NONE);
			if (tmp_status == CACHE_INODE_ESTALE) {
				if (retry_stale) {
					LogDebug(COMPONENT_NFS_READDIR,
						 "cache_inode_getattr returned "
						 "%s for %s - retrying entry",
						 cache_inode_err_str
						 (tmp_status), dirent->name);
					retry_stale = false; /* only one retry
							      * per dirent */
					goto estale_retry;
				}

				/* Directory changed out from under us.
				   Invalidate it, skip the name, and keep
				   going. */
				atomic_clear_uint32_t_bits(
					&directory->flags,
					CACHE_INODE_TRUST_CONTENT);

				LogDebug(COMPONENT_NFS_READDIR,
					 "cache_inode_lock_trust_attrs "
					 "returned %s for %s - skipping entry",
					 cache_inode_err_str(tmp_status),
					 dirent->name);
				continue;
			}

			status = tmp_status;

			LogCrit(COMPONENT_NFS_READDIR,
				"cache_inode_lock_trust_attrs returned %s for "
				"%s - bailing out",
				cache_inode_err_str(status), dirent->name);

			goto unlock_dir;
		}

		(*nbfound)++;

		cache_inode_lru_unref(entry, LRU_FLAG_NONE);

		if (!cb_parms.in_result) {
			LogDebug(COMPONENT_NFS_READDIR,
				 "bailing out due to entry not in result");
			break;
		}
	}

	/* We have reached the last node and every node traversed was
	   added to the result */

	LogDebug(COMPONENT_NFS_READDIR,
		 "dirent_node = %p, nbfound = %u, in_result = %s", dirent_node,
		 *nbfound, cb_parms.in_result ? "TRUE" : "FALSE");

	if (!dirent_node && cb_parms.in_result)
		*eod_met = true;
	else
		*eod_met = false;

unlock_dir:
	PTHREAD_RWLOCK_unlock(&directory->content_lock);
	return status;

}				/* cache_inode_readdir */
Exemplo n.º 7
0
/**
 *
 * @brief Gets an entry by using its fsdata as a key and caches it if needed.
 *
 * Gets an entry by using its fsdata as a key and caches it if needed.
 *
 * If a cache entry is returned, its refcount is incremented by one.
 *
 * It turns out we do need cache_inode_get_located functionality for
 * cases like lookupp on an entry returning itself when it isn't a
 * root.  Therefore, if the 'associated' parameter is equal to the got
 * cache entry, a reference count is incremented but the structure
 * pointed to by attr is NOT filled in.
 *
 * @param[in]     fsdata     File system data
 * @param[out]    attr       The attributes of the got entry
 * @param[in]     context    FSAL credentials
 * @param[in]     associated Entry that may be equal to the got entry
 * @param[out]    status     Returned status
 *
 * @return If successful, the pointer to the entry; NULL otherwise
 *
 */
cache_entry_t *
cache_inode_get(cache_inode_fsal_data_t *fsdata,
                fsal_attrib_list_t *attr,
                fsal_op_context_t *context,
                cache_entry_t *associated,
                cache_inode_status_t *status)
{
    hash_buffer_t key, value;
    cache_entry_t *entry = NULL;
    fsal_status_t fsal_status = {0, 0};
    cache_inode_create_arg_t create_arg = {
        .newly_created_dir = FALSE
    };
    cache_inode_file_type_t type = UNASSIGNED;
    hash_error_t hrc = 0;
    fsal_attrib_list_t fsal_attributes;
    fsal_handle_t *file_handle;
    struct hash_latch latch;

    /* Set the return default to CACHE_INODE_SUCCESS */
    *status = CACHE_INODE_SUCCESS;

    /* Turn the input to a hash key on our own.
     */
    key.pdata = fsdata->fh_desc.start;
    key.len = fsdata->fh_desc.len;

    hrc = HashTable_GetLatch(fh_to_cache_entry_ht, &key, &value,
                             FALSE,
                             &latch);

    if ((hrc != HASHTABLE_SUCCESS) &&
            (hrc != HASHTABLE_ERROR_NO_SUCH_KEY)) {
        /* This should not happened */
        *status = CACHE_INODE_HASH_TABLE_ERROR;
        LogCrit(COMPONENT_CACHE_INODE,
                "Hash access failed with code %d"
                " - this should not have happened",
                hrc);
        return NULL;
    }

    if (hrc == HASHTABLE_SUCCESS) {
        /* Entry exists in the cache and was found */
        entry = value.pdata;
        /* take an extra reference within the critical section */
        if (cache_inode_lru_ref(entry, LRU_REQ_INITIAL) !=
                CACHE_INODE_SUCCESS) {
            /* Dead entry.  Treat like a lookup failure. */
            entry = NULL;
        } else {
            if (entry == associated) {
                /* Take a quick exit so we don't invert lock
                   ordering. */
                HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);
                return entry;
            }
        }
    }
    HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch);

    if (!context) {
        /* Upcalls have no access to fsal_op_context_t,
           so just return the entry without revalidating it or
           creating a new one. */
        if (entry == NULL) {
            *status = CACHE_INODE_NOT_FOUND;
        }
        return entry;
    }

    if (!entry) {
        /* Cache miss, allocate a new entry */
        file_handle = (fsal_handle_t *) fsdata->fh_desc.start;
        /* First, call FSAL to know what the object is */
        fsal_attributes.asked_attributes = cache_inode_params.attrmask;
        fsal_status
            = FSAL_getattrs(file_handle, context, &fsal_attributes);
        if (FSAL_IS_ERROR(fsal_status)) {
            *status = cache_inode_error_convert(fsal_status);
            LogDebug(COMPONENT_CACHE_INODE,
                     "cache_inode_get: cache_inode_status=%u "
                     "fsal_status=%u,%u ", *status,
                     fsal_status.major,
                     fsal_status.minor);
            return NULL;
        }

        /* The type has to be set in the attributes */
        if (!FSAL_TEST_MASK(fsal_attributes.supported_attributes,
                            FSAL_ATTR_TYPE)) {
            *status = CACHE_INODE_FSAL_ERROR;
            return NULL;
        }

        /* Get the cache_inode file type */
        type = cache_inode_fsal_type_convert(fsal_attributes.type);
        if (type == SYMBOLIC_LINK) {
            fsal_attributes.asked_attributes = cache_inode_params.attrmask;
            fsal_status =
                FSAL_readlink(file_handle, context,
                              &create_arg.link_content,
                              &fsal_attributes);

            if (FSAL_IS_ERROR(fsal_status)) {
                *status = cache_inode_error_convert(fsal_status);
                return NULL;
            }
        }
        if ((entry
                = cache_inode_new_entry(fsdata,
                                        &fsal_attributes,
                                        type,
                                        &create_arg,
                                        status)) == NULL) {
            return NULL;
        }

    }

    *status = CACHE_INODE_SUCCESS;

    /* This is the replacement for cache_inode_renew_entry.  Rather
       than calling that function at the start of every cache_inode
       call with the inode locked, we call cache_inode_check trust to
       perform 'heavyweight' (timed expiration of cached attributes,
       getattr-based directory trust) checks the first time after
       getting an inode.  It does all of the checks read-locked and
       only acquires a write lock if there's something requiring a
       change.

       There is a second light-weight check done before use of cached
       data that checks whether the bits saying that inode attributes
       or inode content are trustworthy have been cleared by, for
       example, FSAL_CB.

       To summarize, the current implementation is that policy-based
       trust of validity is checked once per logical series of
       operations at cache_inode_get, and asynchronous trust is
       checked with use (when the attributes are locked for reading,
       for example.) */

    if ((*status = cache_inode_check_trust(entry,
                                           context))
            != CACHE_INODE_SUCCESS) {
        goto out_put;
    }

    /* Set the returned attributes */
    *status = cache_inode_lock_trust_attrs(entry, context);

    /* cache_inode_lock_trust_attrs may fail, in that case, the
       attributes are wrong and pthread_rwlock_unlock can't be called
       again */
    if(*status != CACHE_INODE_SUCCESS)
    {
        goto out_put;
    }
    *attr = entry->attributes;
    pthread_rwlock_unlock(&entry->attr_lock);

    return entry;

out_put:
    cache_inode_put(entry);
    entry = NULL;
    return entry;
} /* cache_inode_get */