blob: 3678be2bcc6c38ce6b8c9ce7dc3724c566a81b6b [file] [log] [blame]
/*
* vim:noexpandtab:shiftwidth=8:tabstop=8:
*
* Copyright CEA/DAM/DIF (2008)
* contributeur : Philippe DENIEL philippe.deniel@cea.fr
* Thomas LEIBOVICI thomas.leibovici@cea.fr
*
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* ---------------------------------------
*/
/**
* @addtogroup cache_inode
* @{
*/
/**
* @file cache_inode_create.c
* @brief Creation of a file through the cache layer
*/
#include "config.h"
#include "log.h"
#include "hashtable.h"
#include "fsal.h"
#include "cache_inode.h"
#include "cache_inode_lru.h"
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <time.h>
#include <pthread.h>
#include <assert.h>
/**
* @brief Creates an object in a directory
*
* This function creates an entry in the cache and underlying
* filesystem. If an entry is returned, its refcount charged to the
* call path is +1. An entry is returned for both CACHE_INODE_SUCCESS
* and CACHE_INODE_ENTRY_EXISTS.
*
* @param[in] parent Parent directory
* @param[in] name Name of the object to create
* @param[in] type Type of the object to create
* @param[in] mode Mode to be used at file creation
* @param[in] create_arg Additional argument for object creation
* @param[out] entry Cache entry for the created file
*
* @return CACHE_INODE_SUCCESS or errors.
*/
cache_inode_status_t
cache_inode_create(cache_entry_t *parent,
const char *name,
object_file_type_t type, uint32_t mode,
cache_inode_create_arg_t *create_arg,
cache_entry_t **entry)
{
cache_inode_status_t status = CACHE_INODE_SUCCESS;
fsal_status_t fsal_status = { 0, 0 };
struct fsal_obj_handle *object_handle;
struct attrlist object_attributes;
struct fsal_obj_handle *dir_handle;
cache_inode_create_arg_t zero_create_arg;
bool needdec = false;
memset(&zero_create_arg, 0, sizeof(zero_create_arg));
memset(&object_attributes, 0, sizeof(object_attributes));
if (create_arg == NULL)
create_arg = &zero_create_arg;
if ((type != REGULAR_FILE) && (type != DIRECTORY)
&& (type != SYMBOLIC_LINK) && (type != SOCKET_FILE)
&& (type != FIFO_FILE) && (type != CHARACTER_FILE)
&& (type != BLOCK_FILE)) {
status = CACHE_INODE_BAD_TYPE;
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because of bad type");
*entry = NULL;
goto out;
}
/* Permission checking will be done by the FSAL operation. */
/* Try to create it first */
dir_handle = parent->obj_handle;
/* we pass in attributes to the create. We will get them back below */
FSAL_SET_MASK(object_attributes.mask,
ATTR_MODE | ATTR_OWNER | ATTR_GROUP);
object_attributes.owner = op_ctx->creds->caller_uid;
object_attributes.group = op_ctx->creds->caller_gid; /* be more
* selective? */
object_attributes.mode = mode;
/* increase the refcount to ensure forced lookup in FSAL */
atomic_inc_uint32_t(&parent->icreate_refcnt);
needdec = true;
switch (type) {
case REGULAR_FILE:
fsal_status =
dir_handle->obj_ops.create(dir_handle, name,
&object_attributes, &object_handle);
break;
case DIRECTORY:
fsal_status =
dir_handle->obj_ops.mkdir(dir_handle, name,
&object_attributes, &object_handle);
break;
case SYMBOLIC_LINK:
fsal_status =
dir_handle->obj_ops.symlink(dir_handle, name,
create_arg->link_content,
&object_attributes,
&object_handle);
break;
case SOCKET_FILE:
case FIFO_FILE:
fsal_status = dir_handle->obj_ops.mknode(dir_handle,
name, type,
NULL, /* dev_t !needed */
&object_attributes,
&object_handle);
break;
case BLOCK_FILE:
case CHARACTER_FILE:
fsal_status =
dir_handle->obj_ops.mknode(dir_handle, name, type,
&create_arg->dev_spec,
&object_attributes, &object_handle);
break;
case NO_FILE_TYPE:
case EXTENDED_ATTR:
/* we should never go there */
status = CACHE_INODE_INCONSISTENT_ENTRY;
*entry = NULL;
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because inconsistent entry");
goto out;
}
/* Refresh the parent's attributes */
cache_inode_refresh_attrs_locked(parent);
/* Check for the result */
if (FSAL_IS_ERROR(fsal_status)) {
if (fsal_status.major == ERR_FSAL_STALE) {
LogEvent(COMPONENT_CACHE_INODE,
"FSAL returned STALE on create type %d", type);
cache_inode_kill_entry(parent);
} else if (fsal_status.major == ERR_FSAL_EXIST) {
/* Already exists. Check if type if correct */
status =
cache_inode_lookup(parent, name, entry);
if (*entry != NULL) {
status = CACHE_INODE_ENTRY_EXISTS;
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because it already exists");
if ((*entry)->type != type) {
/* Incompatible types, returns NULL */
cache_inode_put(*entry);
*entry = NULL;
}
goto out;
}
if (status == CACHE_INODE_NOT_FOUND) {
/* Too bad, FSAL insist the file exists when
* we try to create it, but lookup couldn't
* find it, retry. */
status = CACHE_INODE_INCONSISTENT_ENTRY;
goto out;
}
}
status = cache_inode_error_convert(fsal_status);
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because FSAL failed");
*entry = NULL;
goto out;
}
status =
cache_inode_new_entry(object_handle, CACHE_INODE_FLAG_CREATE,
entry);
if (*entry == NULL) {
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because insert new entry failed");
goto out;
}
PTHREAD_RWLOCK_wrlock(&parent->content_lock);
/* Add this entry to the directory (also takes an internal ref) */
status = cache_inode_add_cached_dirent(parent, name, *entry, NULL);
PTHREAD_RWLOCK_unlock(&parent->content_lock);
if (status != CACHE_INODE_SUCCESS) {
cache_inode_put(*entry);
*entry = NULL;
LogFullDebug(COMPONENT_CACHE_INODE,
"create failed because add dirent failed");
goto out;
}
if (type == DIRECTORY) {
/* Insert Parent's key */
cache_inode_key_dup(&(*entry)->object.dir.parent,
&parent->fh_hk.key);
}
out:
if (needdec == true) {
/* decrease refcnt to allow negative cache lookup */
atomic_dec_uint32_t(&parent->icreate_refcnt);
}
LogFullDebug(COMPONENT_CACHE_INODE,
"Returning entry=%p status=%s for %s FSAL=%s", *entry,
cache_inode_err_str(status), name,
parent->obj_handle->fsal->name);
return status;
}
/**
* @brief Set the create verifier
*
* This function sets the mtime/atime attributes according to the create
* verifier
*
* @param[in] sattr attrlist to be managed.
* @param[in] verf_hi High long of verifier
* @param[in] verf_lo Low long of verifier
*
*/
void
cache_inode_create_set_verifier(struct attrlist *sattr, uint32_t verf_hi,
uint32_t verf_lo)
{
sattr->atime.tv_sec = verf_hi;
sattr->atime.tv_nsec = 0;
FSAL_SET_MASK(sattr->mask, ATTR_ATIME);
sattr->mtime.tv_sec = verf_lo;
sattr->mtime.tv_nsec = 0;
FSAL_SET_MASK(sattr->mask, ATTR_MTIME);
}
/**
* @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] 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,
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, 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;
}
/** @} */