Exemplo n.º 1
0
/**
 * @brief Get supplementary groups given uid
 *
 * @param[in]  uid  The uid of the user
 * @param[out]  group_data
 *
 * @return true if successful, false otherwise
 */
bool uid2grp(uid_t uid, struct group_data **gdata)
{
	bool success = false;

	PTHREAD_RWLOCK_rdlock(&uid2grp_user_lock);
	success = uid2grp_lookup_by_uid(uid, gdata);

	/* Handle common case first */
	if (success && !uid2grp_expired(*gdata)) {
		uid2grp_hold_group_data(*gdata);
		PTHREAD_RWLOCK_unlock(&uid2grp_user_lock);
		return success;
	}
	PTHREAD_RWLOCK_unlock(&uid2grp_user_lock);

	if (success) {
		/* Cache entry is expired */
		PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock);
		uid2grp_remove_by_uid(uid);
		PTHREAD_RWLOCK_unlock(&uid2grp_user_lock);
	}

	*gdata = uid2grp_allocate_by_uid(uid);
	PTHREAD_RWLOCK_wrlock(&uid2grp_user_lock);
	if (*gdata)
		uid2grp_add_user(*gdata);
	success = uid2grp_lookup_by_uid(uid, gdata);
	if (success)
		uid2grp_hold_group_data(*gdata);
	PTHREAD_RWLOCK_unlock(&uid2grp_user_lock);

	return success;
}
Exemplo n.º 2
0
static void nfs4_acls_test(void)
{
    int i = 0;
    fsal_acl_data_t acldata, acldata2;
    fsal_ace_t *ace = NULL;
    fsal_acl_t *acl = NULL;
    fsal_acl_status_t status;

    acldata.naces = 3;
    acldata.aces = nfs4_ace_alloc(3);
    LogDebug(COMPONENT_NFS_V4_ACL, "acldata.aces = %p", acldata.aces);

    ace = acldata.aces;

    for (i = 0; i < 3; i++) {
        ace->type = i;
        ace->perm = i;
        ace->flag = i;
        ace->who.uid = i;
        ace++;
    }

    acl = nfs4_acl_new_entry(&acldata, &status);
    PTHREAD_RWLOCK_rdlock(&acl->lock);
    LogDebug(COMPONENT_NFS_V4_ACL, "acl = %p, ref = %u, status = %u", acl,
             acl->ref, status);
    PTHREAD_RWLOCK_unlock(&acl->lock);

    acldata2.naces = 3;
    acldata2.aces = nfs4_ace_alloc(3);

    LogDebug(COMPONENT_NFS_V4_ACL, "acldata2.aces = %p", acldata2.aces);

    ace = acldata2.aces;

    for (i = 0; i < 3; i++) {
        ace->type = i;
        ace->perm = i;
        ace->flag = i;
        ace->who.uid = i;
        ace++;
    }

    acl = nfs4_acl_new_entry(&acldata2, &status);
    PTHREAD_RWLOCK_rdlock(&acl->lock);
    LogDebug(COMPONENT_NFS_V4_ACL,
             "re-access: acl = %p, ref = %u, status = %u", acl, acl->ref,
             status);
    PTHREAD_RWLOCK_unlock(&acl->lock);

    nfs4_acl_release_entry(acl, &status);
    PTHREAD_RWLOCK_rdlock(&acl->lock);
    LogDebug(COMPONENT_NFS_V4_ACL,
             "release: acl = %p, ref = %u, status = %u", acl, acl->ref,
             status);
    PTHREAD_RWLOCK_unlock(&acl->lock);

    nfs4_acl_release_entry(acl, &status);
}
Exemplo n.º 3
0
/**
 * @brief Remove and free all (key,val) couples from the hash store
 *
 * This function removes all (key,val) couples from the hashtable and
 * frees the stored data using the supplied function
 *
 * @param[in,out] ht        The hashtable to be cleared of all entries
 * @param[in]     free_func The function with which to free the contents
 *                          of each entry
 *
 * @return HASHTABLE_SUCCESS or errors
 */
hash_error_t
hashtable_delall(struct hash_table *ht,
		 int (*free_func)(struct gsh_buffdesc,
				  struct gsh_buffdesc))
{
	/* Successive partition numbers */
	uint32_t index = 0;

	for (index = 0; index < ht->parameter.index_size; index++) {
		/* The root of each successive partition */
		struct rbt_head *root = &ht->partitions[index].rbt;
		/* Pointer to node in tree for removal */
		struct rbt_node *cursor = NULL;

		PTHREAD_RWLOCK_wrlock(&ht->partitions[index].lock);

		/* Continue until there are no more entries in the red-black
		   tree */
		while ((cursor = RBT_LEFTMOST(root)) != NULL) {
			/* Pointer to the key and value descriptors
			   for each successive entry */
			struct hash_data *data = NULL;
			/* Aliased poitner to node, for freeing
			   buffers after removal from tree */
			struct rbt_node *holder = cursor;
			/* Buffer descriptor for key, as stored */
			struct gsh_buffdesc key;
			/* Buffer descriptor for value, as stored */
			struct gsh_buffdesc val;
			/* Return code from the free function.  Zero
			   on failure */
			int rc = 0;

			RBT_UNLINK(root, cursor);
			data = RBT_OPAQ(holder);

			key = data->key;
			val = data->val;

			pool_free(ht->data_pool, data);
			pool_free(ht->node_pool, holder);
			--ht->partitions[index].count;
			rc = free_func(key, val);

			if (rc == 0) {
				PTHREAD_RWLOCK_unlock(&ht->partitions[index].
						      lock);
				return HASHTABLE_ERROR_DELALL_FAIL;
			}
		}
		PTHREAD_RWLOCK_unlock(&ht->partitions[index].lock);
	}

	return HASHTABLE_SUCCESS;
}
Exemplo n.º 4
0
static inline void
src_dest_unlock(cache_entry_t *src, cache_entry_t *dest)
{
	if (src == dest)
		PTHREAD_RWLOCK_unlock(&src->content_lock);
	else {
		if (src < dest) {
			PTHREAD_RWLOCK_unlock(&dest->content_lock);
			PTHREAD_RWLOCK_unlock(&src->content_lock);
		} else {
			PTHREAD_RWLOCK_unlock(&src->content_lock);
			PTHREAD_RWLOCK_unlock(&dest->content_lock);
		}
	}
}
Exemplo n.º 5
0
fsal_status_t vfs_close2(struct fsal_obj_handle *obj_hdl,
			 struct state_t *state)
{
	struct vfs_fd *my_fd = (struct vfs_fd *)(state + 1);
	struct vfs_fsal_obj_handle *myself = NULL;

	myself = container_of(obj_hdl,
			      struct vfs_fsal_obj_handle,
			      obj_handle);

	if (state->state_type == STATE_TYPE_SHARE ||
	    state->state_type == STATE_TYPE_NLM_SHARE ||
	    state->state_type == STATE_TYPE_9P_FID) {
		/* This is a share state, we must update the share counters */

		/* This can block over an I/O operation. */
		PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

		update_share_counters(&myself->u.file.share,
				      my_fd->openflags,
				      FSAL_O_CLOSED);

		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
	}

	return vfs_close_my_fd(my_fd);
}
Exemplo n.º 6
0
fsal_status_t vfs_merge(struct fsal_obj_handle *orig_hdl,
			struct fsal_obj_handle *dupe_hdl)
{
	fsal_status_t status = {ERR_FSAL_NO_ERROR, 0};

	if (orig_hdl->type == REGULAR_FILE &&
	    dupe_hdl->type == REGULAR_FILE) {
		/* We need to merge the share reservations on this file.
		 * This could result in ERR_FSAL_SHARE_DENIED.
		 */
		struct vfs_fsal_obj_handle *orig, *dupe;

		orig = container_of(orig_hdl,
				    struct vfs_fsal_obj_handle,
				    obj_handle);
		dupe = container_of(dupe_hdl,
				    struct vfs_fsal_obj_handle,
				    obj_handle);

		/* This can block over an I/O operation. */
		PTHREAD_RWLOCK_wrlock(&orig_hdl->lock);

		status = merge_share(&orig->u.file.share, &dupe->u.file.share);

		PTHREAD_RWLOCK_unlock(&orig_hdl->lock);
	}

	return status;
}
Exemplo n.º 7
0
/**
 * @brief Adds a new state to a file
 *
 * @param[in,out] obj         File to operate on
 * @param[in]     state_type  State to be defined
 * @param[in]     state_data  Data related to this state
 * @param[in]     owner_input Related open_owner
 * @param[out]    state       The new state
 * @param[in]     refer       Reference to compound creating state
 *
 * @return Operation status
 */
state_status_t state_add(struct fsal_obj_handle *obj,
			 enum state_type state_type,
			 union state_data *state_data,
			 state_owner_t *owner_input,
			 state_t **state, struct state_refer *refer)
{
	state_status_t status = 0;

	/* Ensure that states are are associated only with the appropriate
	   owners */

	if (((state_type == STATE_TYPE_SHARE)
	     && (owner_input->so_type != STATE_OPEN_OWNER_NFSV4))
	    || ((state_type == STATE_TYPE_LOCK)
		&& (owner_input->so_type != STATE_LOCK_OWNER_NFSV4))
	    ||
	    (((state_type == STATE_TYPE_DELEG)
	      || (state_type == STATE_TYPE_LAYOUT))
	     && (owner_input->so_type != STATE_CLIENTID_OWNER_NFSV4))) {
		return STATE_BAD_TYPE;
	}

	PTHREAD_RWLOCK_wrlock(&obj->state_hdl->state_lock);
	status =
	    state_add_impl(obj, state_type, state_data, owner_input, state,
			   refer);
	PTHREAD_RWLOCK_unlock(&obj->state_hdl->state_lock);

	return status;
}
/**
 * @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.º 9
0
fsal_status_t vfs_lru_cleanup(struct fsal_obj_handle *obj_hdl,
			      lru_actions_t requests)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR;
	int retval = 0;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		retval = EXDEV;
		fsal_error = posix2fsal_error(retval);
		return fsalstat(fsal_error, retval);
	}

	/* Take read lock on object to protect file descriptor. */
	PTHREAD_RWLOCK_rdlock(&obj_hdl->lock);

	if (obj_hdl->type == REGULAR_FILE && myself->u.file.fd >= 0) {
		retval = close(myself->u.file.fd);
		myself->u.file.fd = -1;
		myself->u.file.openflags = FSAL_O_CLOSED;
	}
	if (retval == -1) {
		retval = errno;
		fsal_error = posix2fsal_error(retval);
	}

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return fsalstat(fsal_error, retval);
}
Exemplo n.º 10
0
fsal_status_t vfs_commit(struct fsal_obj_handle *obj_hdl,	/* sync */
			 off_t offset, size_t len)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR;
	int retval = 0;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		retval = EXDEV;
		fsal_error = posix2fsal_error(retval);
		return fsalstat(fsal_error, retval);
	}

	/* Take read lock on object to protect file descriptor. */
	PTHREAD_RWLOCK_rdlock(&obj_hdl->lock);

	assert(myself->u.file.fd >= 0
	       && myself->u.file.openflags != FSAL_O_CLOSED);

	retval = fsync(myself->u.file.fd);
	if (retval == -1) {
		retval = errno;
		fsal_error = posix2fsal_error(retval);
	}

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return fsalstat(fsal_error, retval);
}
Exemplo n.º 11
0
fsal_status_t vfs_close(struct fsal_obj_handle *obj_hdl)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status;

	assert(obj_hdl->type == REGULAR_FILE);
	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		return fsalstat(posix2fsal_error(EXDEV), EXDEV);
	}

	/* Take write lock on object to protect file descriptor.
	 * This can block over an I/O operation.
	 */
	PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

	status = vfs_close_my_fd(&myself->u.file.fd);

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}
Exemplo n.º 12
0
void nfs4_acl_entry_inc_ref(fsal_acl_t *acl)
{
    /* Increase ref counter */
    PTHREAD_RWLOCK_wrlock(&acl->lock);
    acl->ref++;
    LogDebug(COMPONENT_NFS_V4_ACL, "(acl, ref) = (%p, %u)", acl, acl->ref);
    PTHREAD_RWLOCK_unlock(&acl->lock);
}
Exemplo n.º 13
0
void nfs4_fs_locations_get_ref(fsal_fs_locations_t *fs_locations)
{
	PTHREAD_RWLOCK_wrlock(&fs_locations->lock);
	fs_locations->ref++;
	LogFullDebug(COMPONENT_NFS_V4, "(fs_locations, ref) = (%p, %u)",
		     fs_locations, fs_locations->ref);
	PTHREAD_RWLOCK_unlock(&fs_locations->lock);
}
Exemplo n.º 14
0
void
hashtable_releaselatched(struct hash_table *ht, struct hash_latch *latch)
{
	if (latch) {
		PTHREAD_RWLOCK_unlock(&ht->partitions[latch->index].lock);
		memset(latch, 0, sizeof(struct hash_latch));
	}
}
Exemplo n.º 15
0
fsal_status_t vfs_getattr2(struct fsal_obj_handle *obj_hdl,
			   struct attrlist *attrs)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status = {0, 0};
	bool has_lock = false;
	bool need_fsync = false;
	bool closefd = false;
	int my_fd;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s getattr for handle belonging to FSAL %s, ignoring",
			 obj_hdl->fsal->name,
			 obj_hdl->fs->fsal != NULL
				? obj_hdl->fs->fsal->name
				: "(none)");
		goto out;
	}

	/* Get a usable file descriptor (don't need to bypass - FSAL_O_ANY
	 * won't conflict with any share reservation).
	 */
	status = find_fd(&my_fd, obj_hdl, false, NULL, FSAL_O_ANY,
			 &has_lock, &need_fsync, &closefd, false);

	if (FSAL_IS_ERROR(status)) {
		if (obj_hdl->type == SYMBOLIC_LINK &&
		    status.major == ERR_FSAL_PERM) {
			/* You cannot open_by_handle (XFS on linux) a symlink
			 * and it throws an EPERM error for it.
			 * open_by_handle_at does not throw that error for
			 * symlinks so we play a game here.  Since there is
			 * not much we can do with symlinks anyway,
			 * say that we did it but don't actually
			 * do anything.  In this case, return the stat we got
			 * at lookup time.  If you *really* want to tweek things
			 * like owners, get a modern linux kernel...
			 */
			status = fsalstat(ERR_FSAL_NO_ERROR, 0);
		}
		goto out;
	}

	status = fetch_attrs(myself, my_fd, attrs);

 out:

	if (closefd)
		close(my_fd);

	if (has_lock)
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}
Exemplo n.º 16
0
cache_inode_status_t
cache_inode_readlink(cache_entry_t *entry,
		     struct gsh_buffdesc *link_content)
{
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	fsal_status_t fsal_status = { ERR_FSAL_NO_ERROR, 0 };
	bool refresh = false;

	if (entry->type != SYMBOLIC_LINK) {
		status = CACHE_INODE_BAD_TYPE;
		return status;
	}

	PTHREAD_RWLOCK_rdlock(&entry->content_lock);
	if (!(entry->flags & CACHE_INODE_TRUST_CONTENT)) {
		/* Our data are stale.  Drop the lock, get a
		   write-lock, load in new data, and copy it out to
		   the caller. */
		PTHREAD_RWLOCK_unlock(&entry->content_lock);
		PTHREAD_RWLOCK_wrlock(&entry->content_lock);
		/* Make sure nobody updated the content while we were
		   waiting. */
		refresh = !(entry->flags & CACHE_INODE_TRUST_CONTENT);
	}
	fsal_status =
	    entry->obj_handle->ops->readlink(entry->obj_handle,
					     link_content, refresh);
	if (refresh && !(FSAL_IS_ERROR(fsal_status)))
		atomic_set_uint32_t_bits(&entry->flags,
					 CACHE_INODE_TRUST_CONTENT);

	PTHREAD_RWLOCK_unlock(&entry->content_lock);

	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 readlink");
			cache_inode_kill_entry(entry);
		}
		return status;
	}

	return status;
}
Exemplo n.º 17
0
void
hashtable_log(log_components_t component, struct hash_table *ht)
{
	/* The current position in the hash table */
	struct rbt_node *it = NULL;
	/* The root of the tree currently being inspected */
	struct rbt_head *root;
	/* Buffer descriptors for the key and value */
	struct hash_data *data = NULL;
	/* String representation of the key */
	char dispkey[HASHTABLE_DISPLAY_STRLEN];
	/* String representation of the stored value */
	char dispval[HASHTABLE_DISPLAY_STRLEN];
	/* Index for traversing the partitions */
	uint32_t i = 0;
	/* Running count of entries  */
	size_t nb_entries = 0;
	/* Recomputed partitionindex */
	uint32_t index = 0;
	/* Recomputed hash for Red-Black tree */
	uint64_t rbt_hash = 0;

	LogFullDebug(component, "The hash is partitioned into %d trees",
		     ht->parameter.index_size);

	for (i = 0; i < ht->parameter.index_size; i++)
		nb_entries += ht->partitions[i].count;

	LogFullDebug(component, "The hash contains %zd entries", nb_entries);

	for (i = 0; i < ht->parameter.index_size; i++) {
		root = &ht->partitions[i].rbt;
		LogFullDebug(component,
			     "The partition in position %" PRIu32
			     "contains: %u entries", i, root->rbt_num_node);
		PTHREAD_RWLOCK_rdlock(&ht->partitions[i].lock);
		RBT_LOOP(root, it) {
			data = it->rbt_opaq;

			ht->parameter.key_to_str(&(data->key), dispkey);
			ht->parameter.val_to_str(&(data->val), dispval);

			if (compute(ht, &data->key, &index, &rbt_hash)
			    != HASHTABLE_SUCCESS) {
				LogCrit(component,
					"Possible implementation error in hash_func_both");
				index = 0;
				rbt_hash = 0;
			}

			LogFullDebug(component,
				     "%s => %s; index=%" PRIu32 " rbt_hash=%"
				     PRIu64, dispkey, dispval, index, rbt_hash);
			RBT_INCREMENT(it);
		}
		PTHREAD_RWLOCK_unlock(&ht->partitions[i].lock);
	}
Exemplo n.º 18
0
fsal_status_t vfs_write(struct fsal_obj_handle *obj_hdl,
			uint64_t offset,
			size_t buffer_size, void *buffer, size_t *write_amount,
			bool *fsal_stable)
{
	struct vfs_fsal_obj_handle *myself;
	ssize_t nb_written;
	fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR;
	int retval = 0;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		retval = EXDEV;
		fsal_error = posix2fsal_error(retval);
		return fsalstat(fsal_error, retval);
	}

	/* Take read lock on object to protect file descriptor. */
	PTHREAD_RWLOCK_rdlock(&obj_hdl->lock);

	assert(myself->u.file.fd >= 0
	       && myself->u.file.openflags != FSAL_O_CLOSED);

	fsal_set_credentials(op_ctx->creds);
	nb_written = pwrite(myself->u.file.fd, buffer, buffer_size, offset);

	if (offset == -1 || nb_written == -1) {
		retval = errno;
		fsal_error = posix2fsal_error(retval);
		goto out;
	}

	*write_amount = nb_written;

	/* attempt stability */
	if (fsal_stable != NULL && *fsal_stable) {
		retval = fsync(myself->u.file.fd);
		if (retval == -1) {
			retval = errno;
			fsal_error = posix2fsal_error(retval);
		}
		*fsal_stable = true;
	}

 out:

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	fsal_restore_ganesha_credentials();
	return fsalstat(fsal_error, retval);
}
Exemplo n.º 19
0
void nfs4_fs_locations_release(fsal_fs_locations_t *fs_locations)
{
	if (fs_locations == NULL)
		return;

	PTHREAD_RWLOCK_wrlock(&fs_locations->lock);
	if (fs_locations->ref > 1) {
		nfs4_fs_locations_put_ref(fs_locations);
		PTHREAD_RWLOCK_unlock(&fs_locations->lock);
		return;
	} else {
		LogFullDebug(COMPONENT_NFS_V4, "Free fs_locations: %p",
			     fs_locations);
	}

	PTHREAD_RWLOCK_unlock(&fs_locations->lock);

	// Releasing fs_locations
	nfs4_fs_locations_free(fs_locations);
}
Exemplo n.º 20
0
fsal_status_t vfs_read(struct fsal_obj_handle *obj_hdl,
		       uint64_t offset,
		       size_t buffer_size, void *buffer, size_t *read_amount,
		       bool *end_of_file)
{
	struct vfs_fsal_obj_handle *myself;
	ssize_t nb_read;
	fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR;
	int retval = 0;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		retval = EXDEV;
		fsal_error = posix2fsal_error(retval);
		return fsalstat(fsal_error, retval);
	}

	/* Take read lock on object to protect file descriptor. */
	PTHREAD_RWLOCK_rdlock(&obj_hdl->lock);

	assert(myself->u.file.fd >= 0
	       && myself->u.file.openflags != FSAL_O_CLOSED);

	nb_read = pread(myself->u.file.fd, buffer, buffer_size, offset);

	if (offset == -1 || nb_read == -1) {
		retval = errno;
		fsal_error = posix2fsal_error(retval);
		goto out;
	}

	*read_amount = nb_read;

	/* dual eof condition */
	*end_of_file = ((nb_read == 0) /* most clients */ ||	/* ESXi */
			(((offset + nb_read) >= myself->attributes.filesize)))
	    ? true : false;

 out:

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return fsalstat(fsal_error, retval);
}
Exemplo n.º 21
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.º 22
0
fsal_status_t vfs_commit2(struct fsal_obj_handle *obj_hdl,
			  off_t offset,
			  size_t len)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status;
	int retval;
	struct vfs_fd temp_fd = {0, -1}, *out_fd = &temp_fd;
	bool has_lock = false;
	bool closefd = false;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	/* Make sure file is open in appropriate mode.
	 * Do not check share reservation.
	 */
	status = fsal_reopen_obj(obj_hdl, false, false, FSAL_O_WRITE,
				 (struct fsal_fd *)&myself->u.file.fd,
				 &myself->u.file.share,
				 vfs_open_func, vfs_close_func,
				 (struct fsal_fd **)&out_fd, &has_lock,
				 &closefd);

	if (!FSAL_IS_ERROR(status)) {

		fsal_set_credentials(op_ctx->creds);

		retval = fsync(out_fd->fd);

		if (retval == -1) {
			retval = errno;
			status = fsalstat(posix2fsal_error(retval), retval);
		}

		fsal_restore_ganesha_credentials();
	}

	if (closefd)
		close(out_fd->fd);

	if (has_lock)
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}
Exemplo n.º 23
0
fsal_status_t vfs_open(struct fsal_obj_handle *obj_hdl,
		       fsal_openflags_t openflags)
{
	struct vfs_fsal_obj_handle *myself;
	int fd;
	fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR;
	int posix_flags = 0;
	int retval = 0;

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		retval = EXDEV;
		fsal_error = posix2fsal_error(retval);
		return fsalstat(fsal_error, retval);
	}

	/* Take write lock on object to protect file descriptor. */
	PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

	assert(myself->u.file.fd == -1
	       && myself->u.file.openflags == FSAL_O_CLOSED && openflags != 0);

	fsal2posix_openflags(openflags, &posix_flags);
	LogFullDebug(COMPONENT_FSAL, "open_by_handle_at flags from %x to %x",
		     openflags, posix_flags);
	fd = vfs_fsal_open(myself, posix_flags, &fsal_error);
	if (fd < 0) {
		retval = -fd;
	} else {
		myself->u.file.fd = fd;
		myself->u.file.openflags = openflags;
	}

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return fsalstat(fsal_error, retval);
}
Exemplo n.º 24
0
static bool nfs_rpc_cbsim_get_v40_client_ids(DBusMessageIter *args,
					     DBusMessage *reply)
{
	uint32_t i;
	hash_table_t *ht = ht_confirmed_client_id;
	struct rbt_head *head_rbt;
	struct hash_data *pdata = NULL;
	struct rbt_node *pn;
	nfs_client_id_t *pclientid;
	uint64_t clientid;
	DBusMessageIter iter, sub_iter;
	struct timespec ts;

	/* create a reply from the message */
	now(&ts);
	dbus_message_iter_init_append(reply, &iter);
	dbus_append_timestamp(&iter, &ts);

	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
					 DBUS_TYPE_UINT64_AS_STRING, &sub_iter);
	/* For each bucket of the hashtable */
	for (i = 0; i < ht->parameter.index_size; i++) {
		head_rbt = &(ht->partitions[i].rbt);

		/* acquire mutex */
		PTHREAD_RWLOCK_wrlock(&(ht->partitions[i].lock));

		/* go through all entries in the red-black-tree */
		RBT_LOOP(head_rbt, pn) {
			pdata = RBT_OPAQ(pn);
			pclientid = pdata->val.addr;
			clientid = pclientid->cid_clientid;
			dbus_message_iter_append_basic(&sub_iter,
						       DBUS_TYPE_UINT64,
						       &clientid);
			RBT_INCREMENT(pn);
		}
		PTHREAD_RWLOCK_unlock(&(ht->partitions[i].lock));
	}
Exemplo n.º 25
0
/**
 * @brief Look up an entry, latching the table
 *
 * This function looks up an entry in the hash table and latches the
 * partition in which that entry would belong in preparation for other
 * activities.  This function is a primitive and is intended more for
 * use building other access functions than for client code itself.
 *
 * @brief[in]  ht        The hash table to search
 * @brief[in]  key       The key for which to search
 * @brief[out] val       The value found
 * @brief[in]  may_write This must be true if the followup call might
 *                       mutate the hash table (set or delete)
 * @brief[out] latch     Opaque structure holding information on the
 *                       table.
 *
 * @retval HASHTABLE_SUCCESS The entry was found, the table is
 *         latched.
 * @retval HASHTABLE_ERROR_NOT_FOUND The entry was not found, the
 *         table is latched.
 * @retval Others, failure, the table is not latched.
 */
hash_error_t
hashtable_getlatch(struct hash_table *ht,
		   const struct gsh_buffdesc *key,
		   struct gsh_buffdesc *val, bool may_write,
		   struct hash_latch *latch)
{
	/* The index specifying the partition to search */
	uint32_t index = 0;
	/* The node found for the key */
	struct rbt_node *locator = NULL;
	/* The buffer descritpros for the key and value for the found entry */
	struct hash_data *data = NULL;
	/* The hash value to be searched for within the Red-Black tree */
	uint64_t rbt_hash = 0;
	/* Stored error return */
	hash_error_t rc = HASHTABLE_SUCCESS;

	/* This combination of options makes no sense ever */
	assert(!(may_write && !latch));

	rc = compute(ht, key, &index, &rbt_hash);
	if (rc != HASHTABLE_SUCCESS)
		return rc;

	/* Acquire mutex */
	if (may_write)
		PTHREAD_RWLOCK_wrlock(&(ht->partitions[index].lock));
	else
		PTHREAD_RWLOCK_rdlock(&(ht->partitions[index].lock));

	rc = key_locate(ht, key, index, rbt_hash, &locator);

	if (rc == HASHTABLE_SUCCESS) {
		/* Key was found */
		data = RBT_OPAQ(locator);
		if (val) {
			val->addr = data->val.addr;
			val->len = data->val.len;
		}

		if (isDebug(COMPONENT_HASHTABLE)
		    && isFullDebug(ht->parameter.ht_log_component)) {
			char dispval[HASHTABLE_DISPLAY_STRLEN];

			if (ht->parameter.val_to_str != NULL)
				ht->parameter.val_to_str(&data->val, dispval);
			else
				dispval[0] = '\0';

			LogFullDebug(ht->parameter.ht_log_component,
				     "Get %s returning Value=%p {%s}",
				     ht->parameter.ht_name, data->val.addr,
				     dispval);
		}
	}

	if (((rc == HASHTABLE_SUCCESS) || (rc == HASHTABLE_ERROR_NO_SUCH_KEY))
	    && (latch != NULL)) {
		latch->index = index;
		latch->rbt_hash = rbt_hash;
		latch->locator = locator;
	} else {
		PTHREAD_RWLOCK_unlock(&ht->partitions[index].lock);
	}

	if (rc != HASHTABLE_SUCCESS && isDebug(COMPONENT_HASHTABLE)
	    && isFullDebug(ht->parameter.ht_log_component))
		LogFullDebug(ht->parameter.ht_log_component,
			     "Get %s returning failure %s",
			     ht->parameter.ht_name, hash_table_err_to_str(rc));

	return rc;
}
Exemplo n.º 26
0
/**
 * @brief Renames an entry
 *
 * This function calls the FSAL to rename a file, then mirrors the
 * operation in the cache.
 *
 * @param[in] dir_src  The source directory
 * @param[in] oldname  The current name of the file
 * @param[in] dir_dest The destination directory
 * @param[in] newname  The name to be assigned to the object
 *
 * @retval CACHE_INODE_SUCCESS if operation is a success.
 * @retval CACHE_INODE_NOT_FOUND if source object does not exist
 * @retval CACHE_INODE_ENTRY_EXISTS on collision.
 * @retval CACHE_INODE_NOT_A_DIRECTORY if dir_src or dir_dest is not a
 *                              directory.
 * @retval CACHE_INODE_BADNAME if either name is "." or ".."
 */
cache_inode_status_t
cache_inode_rename(cache_entry_t *dir_src,
		   const char *oldname,
		   cache_entry_t *dir_dest,
		   const char *newname)
{
	fsal_status_t fsal_status = { 0, 0 };
	cache_entry_t *lookup_src = NULL;
	cache_entry_t *lookup_dst = NULL;
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	cache_inode_status_t status_ref_dir_src = CACHE_INODE_SUCCESS;
	cache_inode_status_t status_ref_dir_dst = CACHE_INODE_SUCCESS;
	cache_inode_status_t status_ref_dst = CACHE_INODE_SUCCESS;

	if ((dir_src->type != DIRECTORY) || (dir_dest->type != DIRECTORY)) {
		status = CACHE_INODE_NOT_A_DIRECTORY;
		goto out;
	}

	/* Check for . and .. on oldname and newname. */
	if (!strcmp(oldname, ".") || !strcmp(oldname, "..")
	    || !strcmp(newname, ".") || !strcmp(newname, "..")) {
		status = CACHE_INODE_BADNAME;
		goto out;
	}

	/* Check for object existence in source directory */
	status =
	    cache_inode_lookup_impl(dir_src, oldname, &lookup_src);

	if (lookup_src == NULL) {
		/* If FSAL FH is stale, then this was managed in
		 * cache_inode_lookup */
		if (status != CACHE_INODE_FSAL_ESTALE)
			status = CACHE_INODE_NOT_FOUND;

		LogEvent(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : source doesn't exist",
			 dir_src, oldname, dir_dest, newname);
		goto out;
	}

	/* Do not rename a junction node or an export root. */
	if (lookup_src->type == DIRECTORY) {
		/* Get attr_lock for looking at junction_export */
		PTHREAD_RWLOCK_rdlock(&lookup_src->attr_lock);

		if (lookup_src->object.dir.junction_export != NULL ||
		    atomic_fetch_int32_t(&lookup_src->exp_root_refcount)
		    != 0) {
			/* Trying to rename an export mount point */
			LogCrit(COMPONENT_CACHE_INODE,
				 "Attempt to rename export %s",
				 oldname);

			/* Release attr_lock */
			PTHREAD_RWLOCK_unlock(&lookup_src->attr_lock);

			status = CACHE_INODE_DIR_NOT_EMPTY;
			goto out;
		}

		/* Release attr_lock */
		PTHREAD_RWLOCK_unlock(&lookup_src->attr_lock);
	}


	/* Check if an object with the new name exists in the destination
	   directory */
	status =
	    cache_inode_lookup_impl(dir_dest, newname, &lookup_dst);

	if (status == CACHE_INODE_SUCCESS) {
		LogDebug(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : destination already exists",
			 dir_src, oldname, dir_dest, newname);
	}
	if (status == CACHE_INODE_NOT_FOUND)
		status = CACHE_INODE_SUCCESS;
	if (status == CACHE_INODE_FSAL_ESTALE) {
		LogDebug(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : stale destination", dir_src,
			 oldname, dir_dest, newname);
	}

	if (lookup_src == lookup_dst) {
		/* Nothing to do according to POSIX and NFS3/4
		 * If from and to both refer to the same file (they might be
		 * hard links of each other), then RENAME should perform no
		 * action and return success */
		LogDebug(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : same file so skipping out",
			 dir_src, oldname, dir_dest, newname);
		goto out;
	}

	/* Perform the rename operation in FSAL before doing anything in the
	 * cache. Indeed, if the FSAL_rename fails unexpectly, the cache would
	 * be inconsistent!
	 *
	 * We do almost no checking before making call because we want to return
	 * error based on the files actually present in the directories, not
	 * what we have in our cache.
	 */
	LogFullDebug(COMPONENT_CACHE_INODE, "about to call FSAL rename");

	fsal_status =
	    dir_src->obj_handle->ops->rename(dir_src->obj_handle,
					     oldname, dir_dest->obj_handle,
					     newname);

	LogFullDebug(COMPONENT_CACHE_INODE, "returned from FSAL rename");

	status_ref_dir_src = cache_inode_refresh_attrs_locked(dir_src);

	if (dir_src != dir_dest)
		status_ref_dir_dst =
			cache_inode_refresh_attrs_locked(dir_dest);

	LogFullDebug(COMPONENT_CACHE_INODE, "done refreshing attributes");

	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);

		LogFullDebug(COMPONENT_CACHE_INODE,
			     "FSAL rename failed with %s",
			     cache_inode_err_str(status));

		goto out;
	}

	if (lookup_dst) {
		/* Force a refresh of the overwritten inode */
		status_ref_dst =
		    cache_inode_refresh_attrs_locked(lookup_dst);
		if (status_ref_dst == CACHE_INODE_FSAL_ESTALE)
			status_ref_dst = CACHE_INODE_SUCCESS;
	}

	status = status_ref_dir_src;

	if (status == CACHE_INODE_SUCCESS)
		status = status_ref_dir_dst;

	if (status == CACHE_INODE_SUCCESS)
		status = status_ref_dst;

	if (status != CACHE_INODE_SUCCESS)
		goto out;

	/* Must take locks on directories now,
	 * because if another thread checks source and destination existence
	 * in the same time, it will try to do the same checks...
	 * and it will have the same conclusion !!!
	 */
	src_dest_lock(dir_src, dir_dest);

	if (lookup_dst) {
		/* Remove the entry from parent dir_entries avl */
		status_ref_dir_dst =
		    cache_inode_remove_cached_dirent(dir_dest, newname);

		if (status_ref_dir_dst != CACHE_INODE_SUCCESS) {
			LogDebug(COMPONENT_CACHE_INODE,
				 "remove entry failed with status %s",
				 cache_inode_err_str(status_ref_dir_dst));
			cache_inode_invalidate_all_cached_dirent(dir_dest);
		}
	}

	if (dir_src == dir_dest) {
		/* if the rename operation is made within the same dir, then we
		 * use an optimization: cache_inode_rename_dirent is used
		 * instead of adding/removing dirent. This limits the use of
		 * resource in this case */

		LogDebug(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : source and target "
			 "directory  the same", dir_src, oldname, dir_dest,
			 newname);

		cache_inode_status_t tmp_status =
		    cache_inode_rename_cached_dirent(dir_dest,
						     oldname, newname);
		if (tmp_status != CACHE_INODE_SUCCESS) {
			/* We're obviously out of date.  Throw out the cached
			   directory */
			cache_inode_invalidate_all_cached_dirent(dir_dest);
		}
	} else {
		cache_inode_status_t tmp_status = CACHE_INODE_SUCCESS;

		LogDebug(COMPONENT_CACHE_INODE,
			 "Rename (%p,%s)->(%p,%s) : moving entry", dir_src,
			 oldname, dir_dest, newname);

		/* We may have a cache entry for the destination
		 * filename.  If we do, we must delete it : it is stale. */
		tmp_status =
		    cache_inode_remove_cached_dirent(dir_dest, newname);

		if (tmp_status != CACHE_INODE_SUCCESS
		    && tmp_status != CACHE_INODE_NOT_FOUND) {
			LogDebug(COMPONENT_CACHE_INODE,
				 "Remove stale dirent returned %s",
				 cache_inode_err_str(tmp_status));
			cache_inode_invalidate_all_cached_dirent(dir_dest);
		}

		tmp_status =
		    cache_inode_add_cached_dirent(dir_dest, newname, lookup_src,
						  NULL);
		if (tmp_status != CACHE_INODE_SUCCESS) {
			/* We're obviously out of date.  Throw out the cached
			   directory */
			LogCrit(COMPONENT_CACHE_INODE, "Add dirent returned %s",
				cache_inode_err_str(tmp_status));
			cache_inode_invalidate_all_cached_dirent(dir_dest);
		}

		/* Remove the old entry */
		tmp_status =
		    cache_inode_remove_cached_dirent(dir_src, oldname);
		if (tmp_status != CACHE_INODE_SUCCESS
		    && tmp_status != CACHE_INODE_NOT_FOUND) {
			LogDebug(COMPONENT_CACHE_INODE,
				 "Remove old dirent returned %s",
				 cache_inode_err_str(tmp_status));
			cache_inode_invalidate_all_cached_dirent(dir_src);
		}
	}

	/* unlock entries */
	src_dest_unlock(dir_src, dir_dest);

out:
	if (lookup_src)
		cache_inode_put(lookup_src);

	if (lookup_dst)
		cache_inode_put(lookup_dst);

	return status;
}
Exemplo n.º 27
0
fsal_status_t vfs_read2(struct fsal_obj_handle *obj_hdl,
			bool bypass,
			struct state_t *state,
			uint64_t offset,
			size_t buffer_size,
			void *buffer,
			size_t *read_amount,
			bool *end_of_file,
			struct io_info *info)
{
	int my_fd = -1;
	ssize_t nb_read;
	fsal_status_t status;
	int retval = 0;
	bool has_lock = false;
	bool need_fsync = false;
	bool closefd = false;

	if (info != NULL) {
		/* Currently we don't support READ_PLUS */
		return fsalstat(ERR_FSAL_NOTSUPP, 0);
	}

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		return fsalstat(posix2fsal_error(EXDEV), EXDEV);
	}

	/* Get a usable file descriptor */
	status = find_fd(&my_fd, obj_hdl, bypass, state, FSAL_O_READ,
			 &has_lock, &need_fsync, &closefd, false);

	if (FSAL_IS_ERROR(status))
		goto out;

	nb_read = pread(my_fd, buffer, buffer_size, offset);

	if (offset == -1 || nb_read == -1) {
		retval = errno;
		status = fsalstat(posix2fsal_error(retval), retval);
		goto out;
	}

	*read_amount = nb_read;

	*end_of_file = (nb_read == 0);

#if 0
	/** @todo
	 *
	 * Is this all we really need to do to support READ_PLUS? Will anyone
	 * ever get upset that we don't return holes, even for blocks of all
	 * zeroes?
	 *
	 */
	if (info != NULL) {
		info->io_content.what = NFS4_CONTENT_DATA;
		info->io_content.data.d_offset = offset + nb_read;
		info->io_content.data.d_data.data_len = nb_read;
		info->io_content.data.d_data.data_val = buffer;
	}
#endif

 out:

	if (closefd)
		close(my_fd);

	if (has_lock)
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}
Exemplo n.º 28
0
fsal_status_t vfs_reopen2(struct fsal_obj_handle *obj_hdl,
			  struct state_t *state,
			  fsal_openflags_t openflags)
{
	struct vfs_fd fd, *my_fd = &fd, *my_share_fd;
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status = {0, 0};
	int posix_flags = 0;
	fsal_openflags_t old_openflags;

	my_share_fd = (struct vfs_fd *)(state + 1);

	fsal2posix_openflags(openflags, &posix_flags);

	LogFullDebug(COMPONENT_FSAL,
		     posix_flags & O_TRUNC ? "Truncate" : "No truncate");

	memset(my_fd, 0, sizeof(*my_fd));
	fd.fd = -1;

	myself  = container_of(obj_hdl,
			       struct vfs_fsal_obj_handle,
			       obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
		return fsalstat(posix2fsal_error(EXDEV), EXDEV);
	}

	/* This can block over an I/O operation. */
	PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

	old_openflags = my_share_fd->openflags;

	/* We can conflict with old share, so go ahead and check now. */
	status = check_share_conflict(&myself->u.file.share, openflags, false);

	if (FSAL_IS_ERROR(status)) {
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

		return status;
	}

	/* Set up the new share so we can drop the lock and not have a
	 * conflicting share be asserted, updating the share counters.
	 */
	update_share_counters(&myself->u.file.share, old_openflags, openflags);

	PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd);

	if (!FSAL_IS_ERROR(status)) {
		/* Close the existing file descriptor and copy the new
		 * one over.
		 */
		vfs_close_my_fd(my_share_fd);
		*my_share_fd = fd;
	} else {
		/* We had a failure on open - we need to revert the share.
		 * This can block over an I/O operation.
		 */
		PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

		update_share_counters(&myself->u.file.share,
				      openflags,
				      old_openflags);

		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
	}

	return status;
}
Exemplo n.º 29
0
fsal_status_t vfs_open2(struct fsal_obj_handle *obj_hdl,
			struct state_t *state,
			fsal_openflags_t openflags,
			enum fsal_create_mode createmode,
			const char *name,
			struct attrlist *attrib_set,
			fsal_verifier_t verifier,
			struct fsal_obj_handle **new_obj,
			struct attrlist *attrs_out,
			bool *caller_perm_check)
{
	int posix_flags = 0;
	int fd, dir_fd;
	int retval = 0;
	mode_t unix_mode;
	fsal_status_t status = {0, 0};
	struct vfs_fd *my_fd = NULL;
	struct vfs_fsal_obj_handle *myself, *hdl = NULL;
	struct stat stat;
	vfs_file_handle_t *fh = NULL;
	bool truncated;
	bool created = false;

	if (state != NULL)
		my_fd = (struct vfs_fd *)(state + 1);

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG,
		    "attrib_set ", attrib_set, false);

	fsal2posix_openflags(openflags, &posix_flags);

	truncated = (posix_flags & O_TRUNC) != 0;

	LogFullDebug(COMPONENT_FSAL,
		     truncated ? "Truncate" : "No truncate");

	if (createmode >= FSAL_EXCLUSIVE) {
		/* Now fixup attrs for verifier if exclusive create */
		set_common_verifier(attrib_set, verifier);
	}

	if (name == NULL) {
		/* This is an open by handle */
		struct vfs_fsal_obj_handle *myself;

		myself = container_of(obj_hdl,
				      struct vfs_fsal_obj_handle,
				      obj_handle);

		if (obj_hdl->fsal != obj_hdl->fs->fsal) {
			LogDebug(COMPONENT_FSAL,
				 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
				 obj_hdl->fsal->name, obj_hdl->fs->fsal->name);
			return fsalstat(posix2fsal_error(EXDEV), EXDEV);
		}

		if (state != NULL) {
			/* Prepare to take the share reservation, but only if we
			 * are called with a valid state (if state is NULL the
			 * caller is a stateless create such as NFS v3 CREATE).
			 */

			/* This can block over an I/O operation. */
			PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

			/* Check share reservation conflicts. */
			status = check_share_conflict(&myself->u.file.share,
						      openflags,
						      false);

			if (FSAL_IS_ERROR(status)) {
				PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
				return status;
			}

			/* Take the share reservation now by updating the
			 * counters.
			 */
			update_share_counters(&myself->u.file.share,
					      FSAL_O_CLOSED,
					      openflags);

			PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
		} else {
			/* We need to use the global fd to continue, and take
			 * the lock to protect it.
			 */
			my_fd = &hdl->u.file.fd;
			PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);
		}

		status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd);

		if (FSAL_IS_ERROR(status)) {
			if (state == NULL) {
				/* Release the lock taken above, and return
				 * since there is nothing to undo.
				 */
				PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
				return status;
			} else {
				/* Error - need to release the share */
				goto undo_share;
			}
		}

		if (createmode >= FSAL_EXCLUSIVE || truncated) {
			/* Refresh the attributes */
			struct stat stat;

			retval = fstat(my_fd->fd, &stat);

			if (retval == 0) {
				LogFullDebug(COMPONENT_FSAL,
					     "New size = %" PRIx64,
					     stat.st_size);
			} else {
				if (errno == EBADF)
					errno = ESTALE;
				status = fsalstat(posix2fsal_error(errno),
						  errno);
			}

			/* Now check verifier for exclusive, but not for
			 * FSAL_EXCLUSIVE_9P.
			 */
			if (!FSAL_IS_ERROR(status) &&
			    createmode >= FSAL_EXCLUSIVE &&
			    createmode != FSAL_EXCLUSIVE_9P &&
			    !check_verifier_stat(&stat, verifier)) {
				/* Verifier didn't match, return EEXIST */
				status =
				    fsalstat(posix2fsal_error(EEXIST), EEXIST);
			}
		}

		if (state == NULL) {
			/* If no state, release the lock taken above and return
			 * status.
			 */
			PTHREAD_RWLOCK_unlock(&obj_hdl->lock);
			return status;
		}

		if (!FSAL_IS_ERROR(status)) {
			/* Return success. */
			return status;
		}

		(void) vfs_close_my_fd(my_fd);

 undo_share:

		/* Can only get here with state not NULL and an error */

		/* On error we need to release our share reservation
		 * and undo the update of the share counters.
		 * This can block over an I/O operation.
		 */
		PTHREAD_RWLOCK_wrlock(&obj_hdl->lock);

		update_share_counters(&myself->u.file.share,
				      openflags,
				      FSAL_O_CLOSED);

		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

		return status;
	}

	/* In this path where we are opening by name, we can't check share
	 * reservation yet since we don't have an object_handle yet. If we
	 * indeed create the object handle (there is no race with another
	 * open by name), then there CAN NOT be a share conflict, otherwise
	 * the share conflict will be resolved when the object handles are
	 * merged.
	 */

#ifdef ENABLE_VFS_DEBUG_ACL
	if (createmode != FSAL_NO_CREATE) {
		/* Need to ammend attributes for inherited ACL, these will be
		 * set later. We also need to test for permission to create
		 * since there might be an ACL.
		 */
		struct attrlist attrs;
		fsal_accessflags_t access_type;

		access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) |
			FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE);
		status = obj_hdl->obj_ops.test_access(obj_hdl, access_type,
						      NULL, NULL, false);

		if (FSAL_IS_ERROR(status))
			return status;

		fsal_prepare_attrs(&attrs, ATTR_ACL);

		status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs);

		if (FSAL_IS_ERROR(status))
			return status;

		status.major = fsal_inherit_acls(attrib_set, attrs.acl,
						 FSAL_ACE_FLAG_FILE_INHERIT);

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

		if (FSAL_IS_ERROR(status))
			return status;
	}
#endif /* ENABLE_VFS_DEBUG_ACL */

	if (createmode != FSAL_NO_CREATE) {
		/* Now add in O_CREAT and O_EXCL. */
		posix_flags |= O_CREAT;

		/* And if we are at least FSAL_GUARDED, do an O_EXCL create. */
		if (createmode >= FSAL_GUARDED)
			posix_flags |= O_EXCL;

		/* Fetch the mode attribute to use in the openat system call. */
		unix_mode = fsal2unix_mode(attrib_set->mode) &
		    ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export);

		/* Don't set the mode if we later set the attributes */
		FSAL_UNSET_MASK(attrib_set->mask, ATTR_MODE);
	}

	if (createmode == FSAL_UNCHECKED && (attrib_set->mask != 0)) {
		/* If we have FSAL_UNCHECKED and want to set more attributes
		 * than the mode, we attempt an O_EXCL create first, if that
		 * succeeds, then we will be allowed to set the additional
		 * attributes, otherwise, we don't know we created the file
		 * and this can NOT set the attributes.
		 */
		posix_flags |= O_EXCL;
	}

	dir_fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &status.major);

	if (dir_fd < 0)
		return fsalstat(status.major, -dir_fd);

	/** @todo: not sure what this accomplishes... */
	retval = vfs_stat_by_handle(dir_fd, &stat);

	if (retval < 0) {
		retval = errno;
		status = fsalstat(posix2fsal_error(retval), retval);
		goto direrr;
	}

	/* Become the user because we are creating an object in this dir.
	 */
	if (createmode != FSAL_NO_CREATE)
		fsal_set_credentials(op_ctx->creds);

	if ((posix_flags & O_CREAT) != 0)
		fd = openat(dir_fd, name, posix_flags, unix_mode);
	else
		fd = openat(dir_fd, name, posix_flags);

	if (fd == -1 && errno == EEXIST && createmode == FSAL_UNCHECKED) {
		/* We tried to create O_EXCL to set attributes and failed.
		 * Remove O_EXCL and retry. We still try O_CREAT again just in
		 * case file disappears out from under us.
		 *
		 * Note that because we have dropped O_EXCL, later on we will
		 * not assume we created the file, and thus will not set
		 * additional attributes. We don't need to separately track
		 * the condition of not wanting to set attributes.
		 */
		posix_flags &= ~O_EXCL;
		fd = openat(dir_fd, name, posix_flags, unix_mode);
	}

	/* Preserve errno */
	retval = errno;

	/* If we were creating, restore credentials now. */
	if (createmode != FSAL_NO_CREATE)
		fsal_restore_ganesha_credentials();

	if (fd < 0) {
		status = fsalstat(posix2fsal_error(retval), retval);
		goto direrr;
	}

	/* Remember if we were responsible for creating the file.
	 * Note that in an UNCHECKED retry we MIGHT have re-created the
	 * file and won't remember that. Oh well, so in that rare case we
	 * leak a partially created file if we have a subsequent error in here.
	 * Also notify caller to do permission check if we DID NOT create the
	 * file. Note it IS possible in the case of a race between an UNCHECKED
	 * open and an external unlink, we did create the file, but we will
	 * still force a permission check. Of course that permission check
	 * SHOULD succeed since we also won't set the mode the caller requested
	 * and the default file create permissions SHOULD allow the owner
	 * read/write.
	 */
	created = (posix_flags & O_EXCL) != 0;
	*caller_perm_check = !created;

	vfs_alloc_handle(fh);

	retval = vfs_name_to_handle(dir_fd, obj_hdl->fs, name, fh);

	if (retval < 0) {
		retval = errno;
		status = fsalstat(posix2fsal_error(retval), retval);
		goto fileerr;
	}

	retval = fstat(fd, &stat);

	if (retval < 0) {
		retval = errno;
		status = fsalstat(posix2fsal_error(retval), retval);
		goto fileerr;
	}

	/* allocate an obj_handle and fill it up */
	hdl = alloc_handle(dir_fd, fh, obj_hdl->fs, &stat, myself->handle, name,
			   op_ctx->fsal_export);

	if (hdl == NULL) {
		status = fsalstat(posix2fsal_error(ENOMEM), ENOMEM);
		goto fileerr;
	}

	/* If we didn't have a state above, use the global fd. At this point,
	 * since we just created the global fd, no one else can have a
	 * reference to it, and thus we can mamnipulate unlocked which is
	 * handy since we can then call setattr2 which WILL take the lock
	 * without a double locking deadlock.
	 */
	if (my_fd == NULL)
		my_fd = &hdl->u.file.fd;

	my_fd->fd = fd;
	my_fd->openflags = openflags;

	*new_obj = &hdl->obj_handle;

	if (created && attrib_set->mask != 0) {
		/* Set attributes using our newly opened file descriptor as the
		 * share_fd if there are any left to set (mode and truncate
		 * have already been handled).
		 *
		 * Note that we only set the attributes if we were responsible
		 * for creating the file and we have attributes to set.
		 *
		 * Note if we have ENABLE_VFS_DEBUG_ACL an inherited ACL might
		 * be part of the attributes we are setting here.
		 */
		status = (*new_obj)->obj_ops.setattr2(*new_obj,
						      false,
						      state,
						      attrib_set);

		if (FSAL_IS_ERROR(status)) {
			/* Release the handle we just allocated. */
			(*new_obj)->obj_ops.release(*new_obj);
			*new_obj = NULL;
			goto fileerr;
		}

		if (attrs_out != NULL) {
			status = (*new_obj)->obj_ops.getattrs(*new_obj,
							      attrs_out);
			if (FSAL_IS_ERROR(status) &&
			    (attrs_out->mask & ATTR_RDATTR_ERR) == 0) {
				/* Get attributes failed and caller expected
				 * to get the attributes. Otherwise continue
				 * with attrs_out indicating ATTR_RDATTR_ERR.
				 */
				goto fileerr;
			}
		}
	} else if (attrs_out != NULL) {
		/* Since we haven't set any attributes other than what was set
		 * on create (if we even created), just use the stat results
		 * we used to create the fsal_obj_handle.
		 */
		posix2fsal_attributes(&stat, attrs_out);

		/* Make sure ATTR_RDATTR_ERR is cleared on success. */
		attrs_out->mask &= ~ATTR_RDATTR_ERR;
	}

	close(dir_fd);

	if (state != NULL) {
		/* Prepare to take the share reservation, but only if we are
		 * called with a valid state (if state is NULL the caller is
		 * a stateless create such as NFS v3 CREATE).
		 */

		/* This can block over an I/O operation. */
		PTHREAD_RWLOCK_wrlock(&(*new_obj)->lock);

		/* Take the share reservation now by updating the counters. */
		update_share_counters(&hdl->u.file.share,
				      FSAL_O_CLOSED,
				      openflags);

		PTHREAD_RWLOCK_unlock(&(*new_obj)->lock);
	}

	return fsalstat(ERR_FSAL_NO_ERROR, 0);

 fileerr:

	close(fd);

	/* Delete the file if we actually created it. */
	if (created)
		unlinkat(dir_fd, name, 0);

 direrr:

	close(dir_fd);
	return fsalstat(posix2fsal_error(retval), retval);
}
Exemplo n.º 30
0
/**
 * @brief Set attributes on an object
 *
 * This function sets attributes on an object.  Which attributes are
 * set is determined by attrib_set->mask. The FSAL must manage bypass
 * or not of share reservations, and a state may be passed.
 *
 * @param[in] obj_hdl    File on which to operate
 * @param[in] state      state_t to use for this operation
 * @param[in] attrib_set Attributes to set
 *
 * @return FSAL status.
 */
fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl,
			   bool bypass,
			   struct state_t *state,
			   struct attrlist *attrib_set)
{
	struct vfs_fsal_obj_handle *myself;
	fsal_status_t status = {0, 0};
	int retval = 0;
	fsal_openflags_t openflags = FSAL_O_ANY;
	bool has_lock = false;
	bool need_fsync = false;
	bool closefd = false;
	int my_fd;
	const char *func;

	/* apply umask, if mode attribute is to be changed */
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE))
		attrib_set->mode &=
		    ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export);

	myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle);

	if (obj_hdl->fsal != obj_hdl->fs->fsal) {
		LogDebug(COMPONENT_FSAL,
			 "FSAL %s operation for handle belonging to FSAL %s, return EXDEV",
			 obj_hdl->fsal->name,
			 obj_hdl->fs->fsal != NULL
				? obj_hdl->fs->fsal->name
				: "(none)");
		return fsalstat(posix2fsal_error(EXDEV), EXDEV);
	}

#ifdef ENABLE_VFS_DEBUG_ACL
#ifdef ENABLE_RFC_ACL
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE) &&
	    !FSAL_TEST_MASK(attrib_set->mask, ATTR_ACL)) {
		/* Set ACL from MODE */
		struct attrlist attrs;

		fsal_prepare_attrs(&attrs, ATTR_ACL);

		status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs);

		if (FSAL_IS_ERROR(status))
			return status;

		status = fsal_mode_to_acl(attrib_set, attrs.acl);

		/* Done with the attrs */
		fsal_release_attrs(&attrs);
	} else {
		/* If ATTR_ACL is set, mode needs to be adjusted no matter what.
		 * See 7530 s 6.4.1.3 */
		if (!FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE))
			attrib_set->mode = myself->mode;
		status = fsal_acl_to_mode(attrib_set);
	}

	if (FSAL_IS_ERROR(status))
		return status;
#endif /* ENABLE_RFC_ACL */
#endif

	/* This is yet another "you can't get there from here".  If this object
	 * is a socket (AF_UNIX), an fd on the socket s useless _period_.
	 * If it is for a symlink, without O_PATH, you will get an ELOOP error
	 * and (f)chmod doesn't work for a symlink anyway - not that it matters
	 * because access checking is not done on the symlink but the final
	 * target.
	 * AF_UNIX sockets are also ozone material.  If the socket is already
	 * active listeners et al, you can manipulate the mode etc.  If it is
	 * just sitting there as in you made it with a mknod.
	 * (one of those leaky abstractions...)
	 * or the listener forgot to unlink it, it is lame duck.
	 */

	/* Test if size is being set, make sure file is regular and if so,
	 * require a read/write file descriptor.
	 */
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) {
		if (obj_hdl->type != REGULAR_FILE) {
			LogFullDebug(COMPONENT_FSAL,
				     "Setting size on non-regular file");
			return fsalstat(ERR_FSAL_INVAL, EINVAL);
		}
		openflags = FSAL_O_RDWR;
	}

	/* Get a usable file descriptor. Share conflict is only possible if
	 * size is being set.
	 */
	status = find_fd(&my_fd, obj_hdl, bypass, state, openflags,
			 &has_lock, &need_fsync, &closefd, false);

	if (FSAL_IS_ERROR(status)) {
		if (obj_hdl->type == SYMBOLIC_LINK &&
		    status.major == ERR_FSAL_PERM) {
			/* You cannot open_by_handle (XFS) a symlink and it
			 * throws an EPERM error for it.  open_by_handle_at
			 * does not throw that error for symlinks so we play a
			 * game here.  Since there is not much we can do with
			 * symlinks anyway, say that we did it
			 * but don't actually do anything.
			 * If you *really* want to tweek things
			 * like owners, get a modern linux kernel...
			 */
			status = fsalstat(ERR_FSAL_NO_ERROR, 0);
		}
		LogFullDebug(COMPONENT_FSAL,
			     "find_fd status=%s",
			     fsal_err_txt(status));
		goto out;
	}

	/** TRUNCATE **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) {
		retval = ftruncate(my_fd, attrib_set->filesize);
		if (retval != 0) {
			/** @todo FSF: is this still necessary?
			 *
			 * XXX ESXi volume creation pattern reliably
			 * reached this point in the past, however now that we
			 * only use the already open file descriptor if it is
			 * open read/write, this may no longer fail.
			 * If there is some other error from ftruncate, then
			 * we will needlessly retry, but without more detail
			 * of the original failure, we can't be sure.
			 * Fortunately permission checking is done by
			 * Ganesha before calling here, so we won't get an
			 * EACCES since this call is done as root. We could
			 * get EFBIG, EPERM, or EINVAL.
			 */
			/** @todo FSF: re-open if we really still need this
			 */

			retval = ftruncate(my_fd, attrib_set->filesize);
			if (retval != 0) {
				func = "truncate";
				goto fileerr;
			}
		}
	}

	/** CHMOD **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) {
		/* The POSIX chmod call doesn't affect the symlink object, but
		 * the entry it points to. So we must ignore it.
		 */
		if (obj_hdl->type != SYMBOLIC_LINK) {
			if (vfs_unopenable_type(obj_hdl->type))
				retval = fchmodat(
					my_fd,
					myself->u.unopenable.name,
					fsal2unix_mode(attrib_set->mode),
					0);
			else
				retval = fchmod(
					my_fd,
					fsal2unix_mode(attrib_set->mode));

			if (retval != 0) {
				func = "chmod";
				goto fileerr;
			}
		}
	}

	/**  CHOWN  **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER | ATTR_GROUP)) {
		uid_t user = FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER)
		    ? (int)attrib_set->owner : -1;
		gid_t group = FSAL_TEST_MASK(attrib_set->mask, ATTR_GROUP)
		    ? (int)attrib_set->group : -1;

		if (vfs_unopenable_type(obj_hdl->type))
			retval = fchownat(my_fd, myself->u.unopenable.name,
					  user, group, AT_SYMLINK_NOFOLLOW);
		else if (obj_hdl->type == SYMBOLIC_LINK)
			retval = fchownat(my_fd, "", user, group,
					  AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH);
		else
			retval = fchown(my_fd, user, group);

		if (retval) {
			func = "chown";
			goto fileerr;
		}
	}

	/**  UTIME  **/
	if (FSAL_TEST_MASK(attrib_set->mask, ATTRS_SET_TIME)) {
		struct timespec timebuf[2];

		if (obj_hdl->type == SYMBOLIC_LINK)
			goto out; /* Setting time on symlinks is illegal */
		/* Atime */
		if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME_SERVER)) {
			timebuf[0].tv_sec = 0;
			timebuf[0].tv_nsec = UTIME_NOW;
		} else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME)) {
			timebuf[0] = attrib_set->atime;
		} else {
			timebuf[0].tv_sec = 0;
			timebuf[0].tv_nsec = UTIME_OMIT;
		}

		/* Mtime */
		if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME_SERVER)) {
			timebuf[1].tv_sec = 0;
			timebuf[1].tv_nsec = UTIME_NOW;
		} else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME)) {
			timebuf[1] = attrib_set->mtime;
		} else {
			timebuf[1].tv_sec = 0;
			timebuf[1].tv_nsec = UTIME_OMIT;
		}
		if (vfs_unopenable_type(obj_hdl->type))
			retval = vfs_utimesat(my_fd, myself->u.unopenable.name,
					      timebuf, AT_SYMLINK_NOFOLLOW);
		else
			retval = vfs_utimes(my_fd, timebuf);
		if (retval != 0) {
			func = "utimes";
			goto fileerr;
		}
	}

	/** SUBFSAL **/
	if (myself->sub_ops && myself->sub_ops->setattrs) {
		status = myself->sub_ops->setattrs(
					myself,
					my_fd,
					attrib_set->mask, attrib_set);
		if (FSAL_IS_ERROR(status))
			goto out;
	}

	errno = 0;

 fileerr:

	retval = errno;

	if (retval != 0) {
		LogDebug(COMPONENT_FSAL,
			 "%s returned %s",
			 func, strerror(retval));
	}

	status = fsalstat(posix2fsal_error(retval), retval);

 out:

	if (closefd)
		close(my_fd);

	if (has_lock)
		PTHREAD_RWLOCK_unlock(&obj_hdl->lock);

	return status;
}