Ejemplo n.º 1
0
cache_inode_status_t
cache_inode_readlink(cache_entry_t *entry,
                     fsal_path_t *link_content,
                     fsal_op_context_t *context,
                     cache_inode_status_t *status)
{
    fsal_status_t fsal_status = {0, 0};

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

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

    assert(entry->object.symlink);
    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. */
        if (!(entry->flags & CACHE_INODE_TRUST_CONTENT)) {
            fsal_status
                = FSAL_readlink(&entry->handle,
                                context,
                                &entry->object.symlink->content,
                                NULL);
            if (!(FSAL_IS_ERROR(fsal_status))) {
                atomic_set_uint32_t_bits(&entry->flags,
                                         CACHE_INODE_TRUST_CONTENT);
            }
        }
    }
    if (!(FSAL_IS_ERROR(fsal_status))) {
        FSAL_pathcpy(link_content,
                     &(entry->object.symlink->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;
} /* cache_inode_readlink */
Ejemplo 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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
cache_inode_status_t cache_inode_statfs(cache_entry_t *entry,
                                        fsal_dynamicfsinfo_t *dynamicinfo,
                                        fsal_op_context_t *context,
                                        cache_inode_status_t *status)
{
    fsal_status_t fsal_status = {0, 0};

    /* Sanity check */
    if(!entry || !context || !dynamicinfo || !status)
    {
        *status = CACHE_INODE_INVALID_ARGUMENT;
        return *status;
    }

    /* Default return value */
    *status = CACHE_INODE_SUCCESS;

    /* Get FSAL to get dynamic info */
    if(FSAL_IS_ERROR
            ((fsal_status = FSAL_dynamic_fsinfo(&entry->handle,
                            context,
                            dynamicinfo))))
    {
        *status = cache_inode_error_convert(fsal_status);
        if (fsal_status.major == ERR_FSAL_STALE) {
            cache_inode_kill_entry(entry);
        }
        return *status;
    }
    LogFullDebug(COMPONENT_CACHE_INODE,
                 "cache_inode_statfs: dynamicinfo: {total_bytes = %zu, "
                 "free_bytes = %zu, avail_bytes = %zu, total_files = %llu, "
                 "free_files = %llu, avail_files = %llu}",
                 dynamicinfo->total_bytes, dynamicinfo->free_bytes,
                 dynamicinfo->avail_bytes, dynamicinfo->total_files,
                 dynamicinfo->free_files, dynamicinfo->avail_files);
    return CACHE_INODE_SUCCESS;
} /* cache_inode_statfs */
Ejemplo n.º 5
0
cache_entry_t *
cache_inode_create(cache_entry_t *parent,
                   fsal_name_t *name,
                   cache_inode_file_type_t type,
                   fsal_accessmode_t mode,
                   cache_inode_create_arg_t *create_arg,
                   fsal_attrib_list_t *attr,
                   fsal_op_context_t *context,
                   cache_inode_status_t *status)
{
     cache_entry_t *entry = NULL;
     fsal_status_t fsal_status = {0, 0};
     fsal_handle_t object_handle;
     fsal_attrib_list_t object_attributes;
     cache_inode_fsal_data_t fsal_data;
     cache_inode_create_arg_t zero_create_arg;

     memset(&zero_create_arg, 0, sizeof(zero_create_arg));
     memset(&fsal_data, 0, sizeof(fsal_data));
     memset(&object_handle, 0, sizeof(object_handle));

     if (create_arg == NULL) {
          create_arg = &zero_create_arg;
     }

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

     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;

          entry = NULL;
          goto out;
        }

     /* Check if an entry of the same name exists */
     entry = cache_inode_lookup(parent,
                                name,
                                attr,
                                context,
                                status);
     if (entry != NULL) {
          *status = CACHE_INODE_ENTRY_EXISTS;
          if (entry->type != type) {
               /* Incompatible types, returns NULL */
               cache_inode_lru_unref(entry, LRU_FLAG_NONE);
               entry = NULL;
               goto out;
          } else {
               goto out;
          }
     }

     /* The entry doesn't exist, so we can create it. */

     object_attributes.asked_attributes = cache_inode_params.attrmask;
     switch (type) {
     case REGULAR_FILE:
          fsal_status = FSAL_create(&parent->handle,
                                    name, context, mode,
                                    &object_handle, &object_attributes);
          break;

     case DIRECTORY:
          fsal_status = FSAL_mkdir(&parent->handle,
                                   name, context, mode,
                                   &object_handle, &object_attributes);
          break;

     case SYMBOLIC_LINK:
          fsal_status = FSAL_symlink(&parent->handle,
                                     name, &create_arg->link_content,
                                     context, mode, &object_handle,
                                     &object_attributes);
          break;

     case SOCKET_FILE:
          fsal_status = FSAL_mknode(&parent->handle, name, context,
                                    mode, FSAL_TYPE_SOCK, NULL,
                                    &object_handle, &object_attributes);
          break;

     case FIFO_FILE:
          fsal_status = FSAL_mknode(&parent->handle, name, context,
                                    mode, FSAL_TYPE_FIFO, NULL,
                                    &object_handle, &object_attributes);
          break;

     case BLOCK_FILE:
          fsal_status = FSAL_mknode(&parent->handle,
                                    name, context,
                                    mode, FSAL_TYPE_BLK,
                                    &create_arg->dev_spec,
                                    &object_handle, &object_attributes);
             break;

     case CHARACTER_FILE:
          fsal_status = FSAL_mknode(&parent->handle,
                                    name, context,
                                    mode, FSAL_TYPE_CHR,
                                    &create_arg->dev_spec,
                                    &object_handle,
                                    &object_attributes);
          break;

     default:
          /* we should never go there */
          *status = CACHE_INODE_INCONSISTENT_ENTRY;
          entry = NULL;
          goto out;
          break;
        }

     /* 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);
          }
          *status = cache_inode_error_convert(fsal_status);
          entry = NULL;
          goto out;
     }
     fsal_data.fh_desc.start = (caddr_t) &object_handle;
     fsal_data.fh_desc.len = 0;
     FSAL_ExpandHandle(context->export_context,
                       FSAL_DIGEST_SIZEOF,
                       &fsal_data.fh_desc);

     entry = cache_inode_new_entry(&fsal_data,
                                   &object_attributes,
                                   type,
                                   create_arg,
                                   status);
     if (entry == NULL) {
          *status = CACHE_INODE_INSERT_ERROR;

          return NULL;
     }

     PTHREAD_RWLOCK_WRLOCK(&parent->content_lock);
     /* Add this entry to the directory (also takes an internal ref) */
     cache_inode_add_cached_dirent(parent,
                                   name, entry,
                                   NULL,
                                   status);
     PTHREAD_RWLOCK_UNLOCK(&parent->content_lock);
     if (*status != CACHE_INODE_SUCCESS) {
          cache_inode_lru_unref(entry, LRU_FLAG_NONE);
          entry = NULL;
          goto out;
     }

     PTHREAD_RWLOCK_WRLOCK(&parent->attr_lock);
     /* Update the parent cached attributes */
     cache_inode_set_time_current(&parent->attributes.mtime);
     parent->attributes.ctime = parent->attributes.mtime;
     /* if the created object is a directory, it contains a link
        to its parent : '..'. Thus the numlink attr must be increased. */
     if (type == DIRECTORY) {
          ++(parent->attributes.numlinks);
     }
     PTHREAD_RWLOCK_UNLOCK(&parent->attr_lock);

     /* Copy up the child attributes */
     *attr = object_attributes;

     *status = CACHE_INODE_SUCCESS;

out:

     return entry;
}
Ejemplo n.º 6
0
/**
 *
 * cache_inode_readdir_populate: fully reads a directory in FSAL and caches
 * the related entries.
 *
 * fully reads a directory in FSAL and caches the related entries. No MT
 * safety managed here !!
 *
 * @param pentry [IN]  entry for the parent directory to be read. This must be
 * a DIRECTORY
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs
 * management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 */
cache_inode_status_t cache_inode_readdir_populate(
    cache_entry_t * pentry_dir,
    cache_inode_policy_t policy,
    hash_table_t * ht,
    cache_inode_client_t * pclient,
    fsal_op_context_t * pcontext,
    cache_inode_status_t * pstatus)
{
  fsal_dir_t fsal_dirhandle;
  fsal_status_t fsal_status;
  fsal_attrib_list_t dir_attributes;

  fsal_cookie_t begin_cookie;
  fsal_cookie_t end_cookie;
  fsal_count_t nbfound;
  fsal_count_t iter;
  fsal_boolean_t fsal_eod;

  cache_entry_t *pentry = NULL;
  cache_entry_t *pentry_parent = pentry_dir;
  fsal_attrib_list_t object_attributes;

  cache_inode_create_arg_t create_arg;
  cache_inode_file_type_t type;
  cache_inode_status_t cache_status;
  fsal_dirent_t array_dirent[FSAL_READDIR_SIZE + 20];
  cache_inode_fsal_data_t new_entry_fsdata;
  cache_inode_dir_entry_t *new_dir_entry = NULL;
  uint64_t i = 0;

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

  /* Only DIRECTORY entries are concerned */
  if(pentry_dir->internal_md.type != DIRECTORY)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }
#ifdef _USE_MFSL_ASYNC
  /* If entry is asynchronous (via MFSL), it should not be repopulated until
     it is synced */
  if(MFSL_ASYNC_is_synced(&pentry_dir->mobject) == FALSE)
    {
      /* Directory is asynchronous, do not repopulate it and let it
       * in the state 'has_been_readdir == FALSE' */
      *pstatus = CACHE_INODE_SUCCESS;
      return *pstatus;
    }
#endif

  /* If directory is already populated , there is no job to do */
  if(pentry_dir->object.dir.has_been_readdir == CACHE_INODE_YES)
    {
      *pstatus = CACHE_INODE_SUCCESS;
      return *pstatus;
    }

  /* Invalidate all the dirents */
  if(cache_inode_invalidate_all_cached_dirent(pentry_dir,
                                              ht,
                                              pclient,
					      pstatus) != CACHE_INODE_SUCCESS)
    return *pstatus;

  /* Open the directory */
  dir_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
  fsal_status = MFSL_opendir(&pentry_dir->mobject,
                             pcontext,
                             &pclient->mfsl_context, &fsal_dirhandle, &dir_attributes, NULL);
#else
  fsal_status = FSAL_opendir(&pentry_dir->object.dir.handle,
                             pcontext, &fsal_dirhandle, &dir_attributes);
#endif
  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);

      if(fsal_status.major == ERR_FSAL_STALE)
        {
          cache_inode_status_t kill_status;

          LogEvent(COMPONENT_CACHE_INODE,
                   "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                   pentry_dir, fsal_status.major, fsal_status.minor);

          if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) !=
             CACHE_INODE_SUCCESS)
            LogCrit(COMPONENT_CACHE_INODE,
                    "cache_inode_readdir: Could not kill entry %p, status = %u",
                    pentry_dir, kill_status);

          *pstatus = CACHE_INODE_FSAL_ESTALE;
        }
      return *pstatus;
    }

  /* Loop for readding the directory */
  FSAL_SET_COOKIE_BEGINNING(begin_cookie);
  FSAL_SET_COOKIE_BEGINNING(end_cookie);
  fsal_eod = FALSE;

  do
    {
#ifdef _USE_MFSL
      fsal_status = MFSL_readdir(&fsal_dirhandle,
                                 begin_cookie,
                                 pclient->attrmask,
                                 FSAL_READDIR_SIZE * sizeof(fsal_dirent_t),
                                 array_dirent,
                                 &end_cookie,
                                 &nbfound, &fsal_eod, &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_readdir(&fsal_dirhandle,
                                 begin_cookie,
                                 pclient->attrmask,
                                 FSAL_READDIR_SIZE * sizeof(fsal_dirent_t),
                                 array_dirent, &end_cookie, &nbfound, &fsal_eod);
#endif

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);
          return *pstatus;
        }

      for(iter = 0; iter < nbfound; iter++)
        {
          LogFullDebug(COMPONENT_NFS_READDIR,
                       "cache readdir populate found entry %s",
                       array_dirent[iter].name.name);

          /* It is not needed to cache '.' and '..' */
          if(!FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT) ||
             !FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT_DOT))
            {
              LogFullDebug(COMPONENT_NFS_READDIR,
                           "cache readdir populate : do not cache . and ..");
              continue;
            }

          /* If dir entry is a symbolic link, its content has to be read */
          if((type =
              cache_inode_fsal_type_convert(array_dirent[iter].attributes.type)) ==
             SYMBOLIC_LINK)
            {
#ifdef _USE_MFSL
              mfsl_object_t tmp_mfsl;
#endif
              /* Let's read the link for caching its value */
              object_attributes.asked_attributes = pclient->attrmask;
              if( CACHE_INODE_KEEP_CONTENT( pentry_dir->policy ) )
                {
#ifdef _USE_MFSL
                  tmp_mfsl.handle = array_dirent[iter].handle;
                  fsal_status = MFSL_readlink(&tmp_mfsl,
                                              pcontext,
                                              &pclient->mfsl_context,
                                              &create_arg.link_content, &object_attributes, NULL);
#else
                  fsal_status = FSAL_readlink(&array_dirent[iter].handle,
                                              pcontext,
                                              &create_arg.link_content, &object_attributes);
#endif
                }
              else
                {
                   fsal_status.major = ERR_FSAL_NO_ERROR ;
                   fsal_status.minor = 0 ;
                }

              if(FSAL_IS_ERROR(fsal_status))
                {
                  *pstatus = cache_inode_error_convert(fsal_status);

                  if(fsal_status.major == ERR_FSAL_STALE)
                    {
                      cache_inode_status_t kill_status;

                      LogEvent(COMPONENT_CACHE_INODE,
                               "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                               pentry_dir, fsal_status.major, fsal_status.minor );

                      if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) !=
                         CACHE_INODE_SUCCESS)
                        LogCrit(COMPONENT_CACHE_INODE,
                                "cache_inode_readdir: Could not kill entry %p, status = %u",
                                pentry_dir, kill_status);

                      *pstatus = CACHE_INODE_FSAL_ESTALE;
                    }

                  return *pstatus;
                }
            }

          /* Try adding the entry, if it exists then this existing entry is
             returned */
          new_entry_fsdata.handle = array_dirent[iter].handle;
	  new_entry_fsdata.cookie = 0; /* XXX needed? */

          if((pentry = cache_inode_new_entry( &new_entry_fsdata,
		                              &array_dirent[iter].attributes,
		                              type, 
                                              policy,
		                              &create_arg,
		                              NULL, 
		                              ht, 
		                              pclient, 
		                              pcontext, 
		                              FALSE,  /* This is population and no creation */
		                              pstatus)) == NULL)
            return *pstatus;

          cache_status = cache_inode_add_cached_dirent(
	      pentry_parent,
	      &(array_dirent[iter].name),
	      pentry,
	      ht,
	      &new_dir_entry,
	      pclient,
	      pcontext,
	      pstatus);

          if(cache_status != CACHE_INODE_SUCCESS
             && cache_status != CACHE_INODE_ENTRY_EXISTS)
            return *pstatus;

          /*
           * Remember the FSAL readdir cookie associated with this dirent.  This
           * is needed for partial directory reads.
           * 
           * to_uint64 should be a lightweight operation--it is in the current
           * default implementation.  We think the right thing -should- happen
           * therefore with if _USE_MFSL. 
           *
           * I'm ignoring the status because the default operation is a memcmp--
           * we lready -have- the cookie. */

          if (cache_status != CACHE_INODE_ENTRY_EXISTS) {

              (void) FSAL_cookie_to_uint64(&array_dirent[iter].handle,
                                           pcontext, &array_dirent[iter].cookie,
                                           &new_dir_entry->fsal_cookie);

              /* we are filling in all entries, and the cookie avl was
               * cleared before adding dirents */
              new_dir_entry->cookie = i; /* still an offset */
              (void) avltree_insert(&new_dir_entry->node_c,
                                    &pentry_parent->object.dir.cookies);
          } /* !exist */

        } /* iter */
      
      /* Get prepared for next step */
      begin_cookie = end_cookie;

      /* next offset */
      i++;
    }
  while(fsal_eod != TRUE);

  /* Close the directory */
#ifdef _USE_MFSL
  fsal_status = MFSL_closedir(&fsal_dirhandle, &pclient->mfsl_context, NULL);
#else
  fsal_status = FSAL_closedir(&fsal_dirhandle);
#endif
  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);
      return *pstatus;
    }

  /* End of work */
  pentry_dir->object.dir.has_been_readdir = CACHE_INODE_YES;
  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;
}                               /* cache_inode_readdir_populate */
Ejemplo n.º 7
0
/**
 *
 * cache_inode_readdir_nonamecache: Reads a directory without populating the name cache (no dirents created).
 *
 * Reads a directory without populating the name cache (no dirents created).
 *
 * @param pentry [IN] entry for the parent directory to be read.
 * @param cookie [IN] cookie for the readdir operation (basically the offset).
 * @param nbwanted [IN] Maximum number of directory entries wanted.
 * @param peod_met [OUT] A flag to know if end of directory was met during this call.
 * @param dirent_array [OUT] the resulting array of found directory entries.
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param unlock [OUT] the caller shall release read-lock on pentry when done
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 *
 */
static cache_inode_status_t cache_inode_readdir_nonamecache( cache_entry_t * pentry_dir,
                                                             cache_inode_policy_t policy,
                                                             uint64_t cookie,
                                                             unsigned int nbwanted,
                                                             unsigned int *pnbfound,
                                                             uint64_t *pend_cookie,
                                                             cache_inode_endofdir_t *peod_met,
                                                             cache_inode_dir_entry_t **dirent_array,
                                                             hash_table_t *ht,
                                                             int *unlock,
                                                             cache_inode_client_t *pclient,
                                                             fsal_op_context_t *pcontext,
                                                             cache_inode_status_t *pstatus)
{
  fsal_dir_t fsal_dirhandle;
  fsal_status_t fsal_status;
  fsal_attrib_list_t dir_attributes;

  fsal_cookie_t begin_cookie;
  fsal_cookie_t end_cookie;
  fsal_count_t iter;
  fsal_boolean_t fsal_eod;
  fsal_dirent_t fsal_dirent_array[FSAL_READDIR_SIZE + 20];

  cache_inode_fsal_data_t entry_fsdata;

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

  /* Only DIRECTORY entries are concerned */
  if(pentry_dir->internal_md.type != DIRECTORY)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  LogFullDebug(COMPONENT_NFS_READDIR,
               "About to readdir in  cache_inode_readdir_nonamecache: pentry=%p "
	       "cookie=%"PRIu64, pentry_dir, cookie ) ;

  /* Open the directory */
  dir_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
  fsal_status = MFSL_opendir(&pentry_dir->mobject,
                             pcontext,
                             &pclient->mfsl_context, &fsal_dirhandle, &dir_attributes, NULL);
#else
  fsal_status = FSAL_opendir(&pentry_dir->object.dir.handle,
                             pcontext, &fsal_dirhandle, &dir_attributes);
#endif

  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);

      if(fsal_status.major == ERR_FSAL_STALE)
        {
          cache_inode_status_t kill_status;

          LogEvent(COMPONENT_CACHE_INODE,
                   "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                   pentry_dir, fsal_status.major, fsal_status.minor);

          if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) !=
             CACHE_INODE_SUCCESS)
            LogCrit(COMPONENT_CACHE_INODE,
                    "cache_inode_readdir: Could not kill entry %p, status = %u",
                    pentry_dir, kill_status);

          *pstatus = CACHE_INODE_FSAL_ESTALE;
        }
      return *pstatus;
    }

  /* Loop for readding the directory */
  // memcpy( &(begin_cookie.data), &cookie, sizeof( uint64_t ) ) ;
  FSAL_SET_COOKIE_BY_OFFSET( begin_cookie, cookie ) ;
  fsal_eod = FALSE;

#ifdef _USE_MFSL
  fsal_status = MFSL_readdir( &fsal_dirhandle,
                              begin_cookie,
                              pclient->attrmask,
                              nbwanted * sizeof(fsal_dirent_t),
                              fsal_dirent_array,
                              &end_cookie,
                              (fsal_count_t *)pnbfound, 
                              &fsal_eod, 
                              &pclient->mfsl_context, 
                              NULL);
#else
  fsal_status = FSAL_readdir( &fsal_dirhandle,
                              begin_cookie,
                              pclient->attrmask,
                              nbwanted * sizeof(fsal_dirent_t),
                              fsal_dirent_array,
                              &end_cookie, 
                              (fsal_count_t *)pnbfound, 
                              &fsal_eod);
#endif
  if(FSAL_IS_ERROR(fsal_status))
   {
      *pstatus = cache_inode_error_convert(fsal_status);
      return *pstatus;
   }

  for( iter = 0 ; iter < *pnbfound ; iter ++ )
   {
      /* cache_inode_readdir does not return . or .. */
      if(!FSAL_namecmp(&(fsal_dirent_array[iter].name), (fsal_name_t *) & FSAL_DOT) ||
         !FSAL_namecmp(&(fsal_dirent_array[iter].name), (fsal_name_t *) & FSAL_DOT_DOT))
              continue;

      /* Get the related pentry without populating the name cache (but eventually populating the attrs cache */
      entry_fsdata.handle = fsal_dirent_array[iter].handle;
      entry_fsdata.cookie = 0; /* XXX needed? */

      /* Allocate a dirent to be returned to the client */
      /** @todo Make sure this piece of memory once the data are used */
      GetFromPool( dirent_array[iter], &pclient->pool_dir_entry, cache_inode_dir_entry_t);
      if( dirent_array[iter] == NULL ) 
       {
         *pstatus = CACHE_INODE_MALLOC_ERROR;
         return *pstatus;
       }


      /* fills in the dirent_array */
      if( ( dirent_array[iter]->pentry= cache_inode_get( &entry_fsdata,
                                                         policy,
                                                         &fsal_dirent_array[iter].attributes,
                                                         ht,
                                                         pclient,
                                                         pcontext,
                                                         pstatus ) ) == NULL )
          return *pstatus ;

      fsal_status = FSAL_namecpy( &dirent_array[iter]->name, &fsal_dirent_array[iter].name ) ;
      if(FSAL_IS_ERROR(fsal_status))
       {
         *pstatus = cache_inode_error_convert(fsal_status);
         return *pstatus;
       }
  
      (void) FSAL_cookie_to_uint64( &fsal_dirent_array[iter].handle,
                                    pcontext, 
                                    &fsal_dirent_array[iter].cookie,
                                    &dirent_array[iter]->fsal_cookie);

       dirent_array[iter]->cookie = dirent_array[iter]->fsal_cookie ;

   } /* for( iter = 0 ; iter < nbfound ; iter ++ ) */

  if( fsal_eod == TRUE )
    *peod_met = END_OF_DIR ;
  else
    *peod_met = TO_BE_CONTINUED ;

  /* Do not forget to set returned end cookie */
  //memcpy( pend_cookie, &(end_cookie.data), sizeof( uint64_t ) ) ; 
  FSAL_SET_POFFSET_BY_COOKIE( end_cookie, pend_cookie ) ;

  LogFullDebug(COMPONENT_NFS_READDIR,
               "End of readdir in  cache_inode_readdir_nonamecache: pentry=%p "
	       "cookie=%"PRIu64, pentry_dir, *pend_cookie ) ;


  /* Close the directory */
#ifdef _USE_MFSL
  fsal_status = MFSL_closedir(&fsal_dirhandle, &pclient->mfsl_context, NULL);
#else
  fsal_status = FSAL_closedir(&fsal_dirhandle);
#endif
  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);
      return *pstatus;
    }

  return CACHE_INODE_SUCCESS ;
} /* cache_inode_readdir_nomanecache */
Ejemplo n.º 8
0
/**
 *
 * cache_inode_remove_sw: removes a pentry addressed by its parent pentry and
 * its FSAL name.  Mutex management is switched.
 *
 * Removes a pentry addressed by its parent pentry and its FSAL name.  Mutex
 * management is switched.
 *
 * @param pentry  [IN]     entry for the parent directory to be managed.
 * @param name    [IN]     name of the entry that we are looking for in the cache.
 * @param pattr   [OUT]    attributes for the entry that we have found.
 * @param ht      [IN]     hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext   [IN]    FSAL credentials
 * @param pstatus [OUT]   returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry
 *
 */
cache_inode_status_t cache_inode_remove_sw(cache_entry_t * pentry,             /**< Parent entry */
                                           fsal_name_t * pnode_name,
                                           fsal_attrib_list_t * pattr,
                                           hash_table_t * ht,
                                           cache_inode_client_t * pclient,
                                           fsal_op_context_t * pcontext,
                                           cache_inode_status_t * pstatus, int use_mutex)
{
  fsal_status_t fsal_status;
  cache_entry_t *to_remove_entry;
  fsal_handle_t fsal_handle_parent;
  fsal_attrib_list_t remove_attr;
  fsal_attrib_list_t after_attr;
  cache_inode_status_t status;
  cache_content_status_t cache_content_status;
  int to_remove_numlinks = 0;
  fsal_accessflags_t access_mask = 0;

  /* stats */
  (pclient->stat.nb_call_total)++;
  (pclient->stat.func_stats.nb_call[CACHE_INODE_REMOVE])++;

  /* pentry is a directory */
  if(use_mutex)
    P_w(&pentry->lock);

  /* Check if caller is allowed to perform the operation */
  access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) |
                FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_DELETE_CHILD);
  if((status = cache_inode_access_sw(pentry,
                                     access_mask,
                                     ht,
                                     pclient,
                                     pcontext, &status, FALSE)) != CACHE_INODE_SUCCESS)
    {
      *pstatus = status;

      /* pentry is a directory */
      if(use_mutex)
        V_w(&pentry->lock);

      return *pstatus;
    }

  /* Looks up for the entry to remove */
  if((to_remove_entry = cache_inode_lookup_sw( pentry,
                                               pnode_name,
                                               CACHE_INODE_JOKER_POLICY,
                                               &remove_attr,
                                               ht,
                                               pclient, 
                                               pcontext, 
                                               &status, 
                                               FALSE)) == NULL)
    {
      *pstatus = status;

      /* pentry is a directory */
      if(use_mutex)
        V_w(&pentry->lock);

      return *pstatus;
    }

  /* lock it */
  if(use_mutex)
    P_w(&to_remove_entry->lock);

  if(pentry->internal_md.type != DIRECTORY)
    {
      if(use_mutex)
        {
          V_w(&to_remove_entry->lock);
          V_w(&pentry->lock);
        }

      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }
  
  LogDebug(COMPONENT_CACHE_INODE,
           "---> Cache_inode_remove : %s", pnode_name->name);

  /* Non-empty directories should not be removed. */
  if(to_remove_entry->internal_md.type == DIRECTORY &&
     to_remove_entry->object.dir.has_been_readdir == CACHE_INODE_YES)
    {
      if(cache_inode_is_dir_empty(to_remove_entry) != CACHE_INODE_SUCCESS)
        {
          if(use_mutex)
            {
              V_w(&to_remove_entry->lock);
              V_w(&pentry->lock);
            }

          *pstatus = CACHE_INODE_DIR_NOT_EMPTY;
          return *pstatus;
        }
    }
    
  /* pentry->internal_md.type == DIRECTORY */
  fsal_handle_parent = pentry->object.dir.handle;

  if(status == CACHE_INODE_SUCCESS)
    {
      /* Remove the file from FSAL */
      after_attr.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
      cache_inode_get_attributes(pentry, &after_attr);
#ifdef _USE_PNFS
      after_attr.numlinks = remove_attr.numlinks ; /* Hook used to pass nlinks to MFSL_unlink */
      if( to_remove_entry->internal_md.type == REGULAR_FILE )
        fsal_status = MFSL_unlink(&pentry->mobject,
                                  pnode_name,
                                  &to_remove_entry->mobject,
                                  pcontext, &pclient->mfsl_context, &after_attr,
                                  &to_remove_entry->object.file.pnfs_file );
      else
#endif /* _USE_PNFS */
      fsal_status = MFSL_unlink(&pentry->mobject,
                                pnode_name,
                                &to_remove_entry->mobject,
                                pcontext, &pclient->mfsl_context, &after_attr,
                                NULL);

#else
      fsal_status = FSAL_unlink(&fsal_handle_parent, pnode_name, pcontext, &after_attr);
#endif

      /* Set the 'after' attr */
      if(pattr != NULL)
        *pattr = after_attr;

      if(FSAL_IS_ERROR(fsal_status))
        {
          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_remove: Stale FSAL FH detected for pentry %p",
                       pentry);

              if(cache_inode_kill_entry(pentry, WT_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_remove: Could not kill entry %p, status = %u",
                        pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          *pstatus = cache_inode_error_convert(fsal_status);
          if(use_mutex)
            {
              V_w(&to_remove_entry->lock);
              V_w(&pentry->lock);
            }
          return *pstatus;
        }
    } /* CACHE_INODE_SUCCESS */
  else
    {
      if(use_mutex)
        {
          V_w(&to_remove_entry->lock);
          V_w(&pentry->lock);
        }
      (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_REMOVE])++;
      return status;
    }

  /* Remove the entry from parent dir_entries avl */
  cache_inode_remove_cached_dirent(pentry, pnode_name, ht, pclient, &status);

  LogFullDebug(COMPONENT_CACHE_INODE,
               "cache_inode_remove_cached_dirent: status=%d", status);

  /* Update the cached attributes */
  pentry->object.dir.attributes = after_attr;

  /* Update the attributes for the removed entry */

  if(remove_attr.type != FSAL_TYPE_DIR)
    {
      if(remove_attr.numlinks > 1)
        {
          switch (to_remove_entry->internal_md.type)
            {
            case SYMBOLIC_LINK:
              assert(to_remove_entry->object.symlink);
              to_remove_entry->object.symlink->attributes.numlinks -= 1;
              cache_inode_set_time_current( &to_remove_entry->object.symlink->attributes.ctime ) ;
              to_remove_numlinks = to_remove_entry->object.symlink->attributes.numlinks;
              break;

            case REGULAR_FILE:
              to_remove_entry->object.file.attributes.numlinks -= 1;
              cache_inode_set_time_current( &to_remove_entry->object.file.attributes.ctime ) ;
              to_remove_numlinks = to_remove_entry->object.file.attributes.numlinks;
              break;

            case CHARACTER_FILE:
            case BLOCK_FILE:
            case SOCKET_FILE:
            case FIFO_FILE:
              to_remove_entry->object.special_obj.attributes.numlinks -= 1;
              cache_inode_set_time_current( &to_remove_entry->object.special_obj.attributes.ctime ) ;
              to_remove_numlinks =
                  to_remove_entry->object.special_obj.attributes.numlinks;
              break;

            default:
              /* Other objects should not be hard linked */
              if(use_mutex)
                {
                  V_w(&to_remove_entry->lock);
                  V_w(&pentry->lock);
                }
              *pstatus = CACHE_INODE_BAD_TYPE;
              return *pstatus;
              break;
            }
        }
    }
  else
    {
      /* No hardlink counter to be decremented for a directory: hardlink are not allowed for them */
    }

  /* Now, delete "to_remove_entry" from the cache inode and free its associated resources, but only if
   * numlinks == 0 */
  if(to_remove_numlinks == 0)
    {

      /* If pentry is a regular file, data cached, the related data cache entry should be removed as well */
      if(to_remove_entry->internal_md.type == REGULAR_FILE)
        {
          if(to_remove_entry->object.file.pentry_content != NULL)
            {
              /* Something is to be deleted, release the cache data entry */
              if(cache_content_release_entry
                 ((cache_content_entry_t *) to_remove_entry->object.file.pentry_content,
                  (cache_content_client_t *) pclient->pcontent_client,
                  &cache_content_status) != CACHE_CONTENT_SUCCESS)
                {
                  LogEvent(COMPONENT_CACHE_INODE,
                           "pentry %p, named %s could not be released from data cache, status=%d",
                           to_remove_entry, pnode_name->name,
                           cache_content_status);
                }
            }
        }
	
      if((*pstatus =
      	cache_inode_clean_internal(to_remove_entry, ht,
					 pclient)) != CACHE_INODE_SUCCESS)
    	{
      	  if(use_mutex)
	  {
	    V_w(&pentry->lock);
	    V_w(&to_remove_entry->lock);
	  }

      	  LogCrit(COMPONENT_CACHE_INODE,
	   	   "cache_inode_clean_internal ERROR %d", *pstatus);
      	return *pstatus;
      }

      /* Finally put the main pentry back to pool */
      if(use_mutex)
        V_w(&to_remove_entry->lock);

      /* Destroy the mutex associated with the pentry */
      cache_inode_mutex_destroy(to_remove_entry);

      ReleaseToPool(to_remove_entry, &pclient->pool_entry);

    } /* to_remove->numlinks == 0 */

  /* Validate the entries */
  *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_SET, pclient);

  /* Regular exit */
  if(use_mutex)
    {
      if(to_remove_numlinks != 0)
        V_w(&to_remove_entry->lock);    /* This was not release yet, it should be done here */

      V_w(&pentry->lock);
    }

  if(status == CACHE_INODE_SUCCESS)
      (pclient->stat.func_stats.nb_success[CACHE_INODE_REMOVE])++;
  else
      (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_REMOVE])++;

  return status;
}                               /* cache_inode_remove */
Ejemplo n.º 9
0
/**
 *
 * cache_inode_getattr: Gets the attributes for a cached entry.
 *
 * Gets the attributes for a cached entry. The FSAL attributes are kept in a structure when the entry
 * is added to the cache.
 *
 * @param pentry [IN] entry to be managed.
 * @param pattr [OUT] pointer to the results
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry
 *
 */
cache_inode_status_t
cache_inode_getattr(cache_entry_t * pentry,
                    fsal_attrib_list_t * pattr,
                    hash_table_t * ht, /* Unused, kept for protototype's homogeneity */
                    cache_inode_client_t * pclient,
                    fsal_op_context_t * pcontext,
                    cache_inode_status_t * pstatus)
{
    cache_inode_status_t status;
    fsal_handle_t *pfsal_handle;
    fsal_status_t fsal_status;

    /* sanity check */
    if(pentry == NULL || pattr == NULL ||
       ht == NULL || pclient == NULL || pcontext == NULL)
        {
            *pstatus = CACHE_INODE_INVALID_ARGUMENT;
            return *pstatus;
        }

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

    /* stats */
    pclient->stat.nb_call_total += 1;
    inc_func_call(pclient, CACHE_INODE_GETATTR);

    /* Lock the entry */
    P_w(&pentry->lock);
    status = cache_inode_renew_entry(pentry, pattr, ht,
                                     pclient, pcontext, pstatus);
    if(status != CACHE_INODE_SUCCESS)
        {
            V_w(&pentry->lock);
            inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
            return *pstatus;
        }

    /* RW Lock goes for writer to reader */
    rw_lock_downgrade(&pentry->lock);

    cache_inode_get_attributes(pentry, pattr);

    if(FSAL_TEST_MASK(pattr->asked_attributes,
                      FSAL_ATTR_RDATTR_ERR))
        {
            switch (pentry->internal_md.type)
                {
                case REGULAR_FILE:
                    pfsal_handle = &pentry->object.file.handle;
                    break;

                case SYMBOLIC_LINK:
                    pfsal_handle = &pentry->object.symlink.handle;
                    break;

                case DIR_BEGINNING:
                    pfsal_handle = &pentry->object.dir_begin.handle;
                    break;

                case DIR_CONTINUE:
                    /*
                     * lock the related dir_begin (dir begin are garbagge
                     * collected AFTER their related dir_cont)
                     * this means that if a DIR_CONTINUE exists,
                     * its pdir pointer is not endless
                     */
                    P_r(&pentry->object.dir_cont.pdir_begin->lock);
                    pfsal_handle = &pentry->object.dir_cont.pdir_begin->object.dir_begin.handle;
                    V_r(&pentry->object.dir_cont.pdir_begin->lock);
                    break;

                case SOCKET_FILE:
                case FIFO_FILE:
                case BLOCK_FILE:
                case CHARACTER_FILE:
                    pfsal_handle = &pentry->object.special_obj.handle;
                    break;
                }

            /*
             * An error occured when trying to get
             * the attributes, they have to be renewed
             */
            fsal_status = FSAL_getattrs(pfsal_handle, pcontext, pattr);
            if(FSAL_IS_ERROR(fsal_status))
                {
                    *pstatus = cache_inode_error_convert(fsal_status);
                    V_r(&pentry->lock);

                    if(fsal_status.major == ERR_FSAL_STALE)
                        {
                            cache_inode_status_t kill_status;

                            LogDebug(COMPONENT_CACHE_INODE_GC,
                                     "cache_inode_getattr: Stale FSAL File "
                                     "Handle detected for pentry = %p",
                                     pentry);

                            cache_inode_kill_entry(pentry, ht,
                                                   pclient, &kill_status);
                            if(kill_status != CACHE_INODE_SUCCESS)
                                LogCrit(COMPONENT_CACHE_INODE_GC,
                                        "cache_inode_getattr: Could not kill "
                                        "entry %p, status = %u",
                                        pentry, kill_status);

                            *pstatus = CACHE_INODE_FSAL_ESTALE;
                        }

                    /* stat */
                    inc_func_err_unrecover(pclient, CACHE_INODE_GETATTR);
                    return *pstatus;
                }

            /* Set the new attributes */
            cache_inode_set_attributes(pentry, pattr);
        }
    *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient);

    V_r(&pentry->lock);

    /* stat */
    if(*pstatus != CACHE_INODE_SUCCESS)
        inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
    else
        inc_func_success(pclient, CACHE_INODE_GETATTR);

    return *pstatus;
}
Ejemplo n.º 10
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;
}
Ejemplo n.º 11
0
/**
 * @brief Implement actual work of removing file
 *
 * Actually remove an entry from the directory.  Assume that the
 * directory contents and attributes are locked for writes.  The
 * attribute lock is released unless keep_md_lock is TRUE.
 *
 * @param[in] entry   Entry for the parent directory to be managed.
 * @param[in] name    Name of the entry that we are looking for in the cache.
 * @param[in] context FSAL credentials
 * @param[in] status  Returned status
 * @param[in] flags   Flags to control lock retention
 *
 * @return CACHE_INODE_SUCCESS if operation is a success
 *
 */
cache_inode_status_t
cache_inode_remove_impl(cache_entry_t *entry,
                        fsal_name_t *name,
                        fsal_op_context_t *context,
                        cache_inode_status_t *status,
                        uint32_t flags)
{
     cache_entry_t *to_remove_entry = NULL;
     fsal_status_t fsal_status = {0, 0};
#ifdef _USE_NFS4_ACL
     fsal_acl_t *saved_acl = NULL;
     fsal_acl_status_t acl_status = 0;
#endif /* _USE_NFS4_ACL */

     if(entry->type != DIRECTORY) {
          *status = CACHE_INODE_BAD_TYPE;
          goto out;
     }

     if (!(flags & CACHE_INODE_FLAG_CONTENT_HAVE)) {
          pthread_rwlock_rdlock(&entry->content_lock);
          flags |= CACHE_INODE_FLAG_CONTENT_HAVE;
     }

     /* Factor this somewhat.  In the case where the directory hasn't
        been populated, the entry may not exist in the cache and we'd
        be bringing it in just to dispose of it. */

     /* Looks up for the entry to remove */
     if ((to_remove_entry
          = cache_inode_lookup_impl(entry,
                                    name,
                                    context,
                                    status)) == NULL) {
          goto out;
     }

     /* Lock the attributes (so we can decrement the link count) */
     pthread_rwlock_wrlock(&to_remove_entry->attr_lock);

     LogDebug(COMPONENT_CACHE_INODE,
              "---> Cache_inode_remove : %s", name->name);


#ifdef _USE_NFS4_ACL
     saved_acl = entry->attributes.acl;
#endif /* _USE_NFS4_ACL */
     fsal_status = FSAL_unlink(&entry->handle,
                               name,
                               context,
                               &entry->attributes);

     if (FSAL_IS_ERROR(fsal_status)) {
          *status = cache_inode_error_convert(fsal_status);
          if (fsal_status.major == ERR_FSAL_STALE) {
               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);

     if ((flags & CACHE_INODE_FLAG_ATTR_HAVE) &&
         !(flags & CACHE_INODE_FLAG_ATTR_HOLD)) {
          pthread_rwlock_unlock(&entry->attr_lock);
     }

     /* Remove the entry from parent dir_entries avl */
     cache_inode_remove_cached_dirent(entry, name, status);

     LogFullDebug(COMPONENT_CACHE_INODE,
                  "cache_inode_remove_cached_dirent: status=%d", *status);

     /* Update the attributes for the removed entry */

     if ((to_remove_entry->type != DIRECTORY) &&
         (to_remove_entry->attributes.numlinks > 1)) {
          if ((*status = cache_inode_refresh_attrs(to_remove_entry,
                                                   context))
              != CACHE_INODE_SUCCESS) {
               goto unlock;
          }
     } else {
          /* Otherwise our count is zero, or it was an empty
             directory. */
          to_remove_entry->attributes.numlinks = 0;
     }

     /* Now, delete "to_remove_entry" from the cache inode and free
        its associated resources, but only if numlinks == 0 */
     if (to_remove_entry->attributes.numlinks == 0) {
          /* Destroy the entry when everyone's references to it have
             been relinquished.  Most likely now. */
          pthread_rwlock_unlock(&to_remove_entry->attr_lock);
          /* Kill off the sentinel reference (and mark the entry so
             it doesn't get recycled while a reference exists.) */
          cache_inode_lru_kill(to_remove_entry);
     } else {
     unlock:

          pthread_rwlock_unlock(&to_remove_entry->attr_lock);
     }

out:
     if ((flags & CACHE_INODE_FLAG_CONTENT_HAVE) &&
         !(flags & CACHE_INODE_FLAG_CONTENT_HOLD)) {
          pthread_rwlock_unlock(&entry->content_lock);
     }

     /* This is for the reference taken by lookup */
     if (to_remove_entry)
       {
         cache_inode_put(to_remove_entry);
       }

     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;
}
Ejemplo n.º 13
0
/**
 *
 * cache_inode_getattr: Gets the attributes for a cached entry.
 *
 * Gets the attributes for a cached entry. The FSAL attributes are kept in a structure when the entry
 * is added to the cache.
 *
 * @param pentry [IN] entry to be managed.
 * @param pattr [OUT] pointer to the results
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry
 *
 */
cache_inode_status_t
cache_inode_getattr(cache_entry_t * pentry,
                    fsal_attrib_list_t * pattr,
                    hash_table_t * ht, /* Unused, kept for protototype's homogeneity */
                    cache_inode_client_t * pclient,
                    fsal_op_context_t * pcontext,
                    cache_inode_status_t * pstatus)
{
    cache_inode_status_t status;
    fsal_handle_t *pfsal_handle = NULL;
    fsal_status_t fsal_status;

    /* sanity check */
    if(pentry == NULL || pattr == NULL ||
       ht == NULL || pclient == NULL || pcontext == NULL)
        {
            *pstatus = CACHE_INODE_INVALID_ARGUMENT;
            LogDebug(COMPONENT_CACHE_INODE,
                     "cache_inode_getattr: returning CACHE_INODE_INVALID_ARGUMENT because of bad arg");
            return *pstatus;
        }

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

    /* stats */
    pclient->stat.nb_call_total += 1;
    inc_func_call(pclient, CACHE_INODE_GETATTR);

    /* Lock the entry */
    P_w(&pentry->lock);
    status = cache_inode_renew_entry(pentry, pattr, ht,
                                     pclient, pcontext, pstatus);
    if(status != CACHE_INODE_SUCCESS)
        {
            V_w(&pentry->lock);
            inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
            LogFullDebug(COMPONENT_CACHE_INODE,
                         "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry",
                         *pstatus, cache_inode_err_str(*pstatus));
            return *pstatus;
        }

    /* RW Lock goes for writer to reader */
    rw_lock_downgrade(&pentry->lock);

    cache_inode_get_attributes(pentry, pattr);

    if(FSAL_TEST_MASK(pattr->asked_attributes,
                      FSAL_ATTR_RDATTR_ERR))
        {
            switch (pentry->internal_md.type)
                {
                case REGULAR_FILE:
                    pfsal_handle = &pentry->object.file.handle;
                    break;

                case SYMBOLIC_LINK:
                    assert(pentry->object.symlink);
                    pfsal_handle = &pentry->object.symlink->handle;
                    break;

                case DIRECTORY:
                    pfsal_handle = &pentry->object.dir.handle;
                    break;
                case SOCKET_FILE:
                case FIFO_FILE:
                case BLOCK_FILE:
                case CHARACTER_FILE:
                    pfsal_handle = &pentry->object.special_obj.handle;
                    break;
                case FS_JUNCTION:
                case UNASSIGNED:
                case RECYCLED:
                    *pstatus = CACHE_INODE_INVALID_ARGUMENT;
                    LogFullDebug(COMPONENT_CACHE_INODE,
                                 "cache_inode_getattr: returning %d(%s) from cache_inode_renew_entry - unexpected md_type",
                                 *pstatus, cache_inode_err_str(*pstatus));
                    return *pstatus;
                }

            /*
             * An error occured when trying to get
             * the attributes, they have to be renewed
             */
#ifdef _USE_MFSL
            fsal_status = FSAL_getattrs_descriptor(&(cache_inode_fd(pentry)->fsal_file), pfsal_handle, pcontext, pattr);
#else
            fsal_status = FSAL_getattrs_descriptor(cache_inode_fd(pentry), pfsal_handle, pcontext, pattr);
#endif
            if(FSAL_IS_ERROR(fsal_status))
                {
                    *pstatus = cache_inode_error_convert(fsal_status);
                    
                    V_r(&pentry->lock);

                    if(fsal_status.major == ERR_FSAL_STALE)
                        {
                            cache_inode_status_t kill_status;

                            LogEvent(COMPONENT_CACHE_INODE,
                                     "cache_inode_getattr: Stale FSAL File Handle detected for pentry = %p",
                                     pentry);

                            /* Locked flag is set to true to show entry has a read lock */
                            cache_inode_kill_entry( pentry, WT_LOCK, ht,
                                                    pclient, &kill_status);
                            if(kill_status != CACHE_INODE_SUCCESS)
                                LogCrit(COMPONENT_CACHE_INODE,
                                        "cache_inode_getattr: Could not kill entry %p, status = %u",
                                        pentry, kill_status);

                            *pstatus = CACHE_INODE_FSAL_ESTALE;
                        }

                    /* stat */
                    inc_func_err_unrecover(pclient, CACHE_INODE_GETATTR);
                    LogDebug(COMPONENT_CACHE_INODE,
                             "cache_inode_getattr: returning %d(%s) from FSAL_getattrs_descriptor",
                             *pstatus, cache_inode_err_str(*pstatus));
                    return *pstatus;
                }

            /* Set the new attributes */
            cache_inode_set_attributes(pentry, pattr);
        }
    *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient);

    V_r(&pentry->lock);

    /* stat */
    if(*pstatus != CACHE_INODE_SUCCESS)
        inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
    else
        inc_func_success(pclient, CACHE_INODE_GETATTR);

#ifdef _USE_NFS4_ACL
    if(isDebug(COMPONENT_NFS_V4_ACL))
      {
        LogDebug(COMPONENT_CACHE_INODE,
                 "cache_inode_getattr: pentry = %p, acl = %p",
                 pentry, pattr->acl);

        if(pattr->acl)
          {
            fsal_ace_t *pace;
            for(pace = pattr->acl->aces; pace < pattr->acl->aces + pattr->acl->naces; pace++)
              {
                LogDebug(COMPONENT_CACHE_INODE,
                         "cache_inode_getattr: ace type = 0x%x, flag = 0x%x, perm = 0x%x, special = %d, %s = 0x%x",
                         pace->type, pace->flag, pace->perm, IS_FSAL_ACE_SPECIAL_ID(*pace),
                         GET_FSAL_ACE_WHO_TYPE(*pace), GET_FSAL_ACE_WHO(*pace));
              }
          }
      }
#endif                          /* _USE_NFS4_ACL */

    LogFullDebug(COMPONENT_CACHE_INODE,
                 "cache_inode_getattr: returning %d(%s) from cache_inode_valid",
                 *pstatus, cache_inode_err_str(*pstatus));
    return *pstatus;
}
Ejemplo n.º 14
0
cache_entry_t *cache_inode_get_located(cache_inode_fsal_data_t * pfsdata,
                                       cache_entry_t * plocation, 
                                       cache_inode_policy_t policy,
                                       fsal_attrib_list_t * pattr,
                                       hash_table_t * ht,
                                       cache_inode_client_t * pclient,
                                       fsal_op_context_t * pcontext,
                                       cache_inode_status_t * pstatus)
{
  hash_buffer_t key, value;
  cache_entry_t *pentry = NULL;
  fsal_status_t fsal_status;
  cache_inode_create_arg_t create_arg;
  cache_inode_file_type_t type;
  int hrc = 0;
  fsal_attrib_list_t fsal_attributes;
  cache_inode_fsal_data_t *ppoolfsdata = NULL;

  memset(&create_arg, 0, sizeof(create_arg));

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

  /* stats */
  /* cache_invalidate calls this with no context or client */
  if (pclient) {
    pclient->stat.nb_call_total += 1;
    pclient->stat.func_stats.nb_call[CACHE_INODE_GET] += 1;
  }

  /* Turn the input to a hash key */
  if(cache_inode_fsaldata_2_key(&key, pfsdata, pclient))
    {
      *pstatus = CACHE_INODE_UNAPPROPRIATED_KEY;

      /* stats */
      /* cache_invalidate calls this with no context or client */
      if (pclient) {
	pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;
	ppoolfsdata = (cache_inode_fsal_data_t *) key.pdata;
	ReleaseToPool(ppoolfsdata, &pclient->pool_key);
      }
      return NULL;
    }

  switch (hrc = HashTable_Get(ht, &key, &value))
    {
    case HASHTABLE_SUCCESS:
      /* Entry exists in the cache and was found */
      pentry = (cache_entry_t *) value.pdata;

      /* return attributes additionally */
      *pattr = pentry->attributes;

      if ( !pclient ) {
	/* invalidate. Just return it to mark it stale and go on. */
	return( pentry );
      }

      break;

    case HASHTABLE_ERROR_NO_SUCH_KEY:
      if ( !pclient ) {
	/* invalidate. Just return */
	return( NULL );
      }
      /* Cache miss, allocate a new entry */

      /* XXX I do not think this can happen with avl dirent cache */
      if(pfsdata->cookie != DIR_START)
        {
          /* added for sanity check */
          LogDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_get: pfsdata->cookie != DIR_START (=%"PRIu64") on object whose type is %u",
                       pfsdata->cookie,
                       cache_inode_fsal_type_convert(fsal_attributes.type));

          pfsdata->cookie = DIR_START;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          /* redo the call */
          return cache_inode_get(pfsdata, policy, pattr, ht, pclient, pcontext, pstatus);
        }

      /* First, call FSAL to know what the object is */
      fsal_attributes.asked_attributes = pclient->attrmask;
      fsal_status = FSAL_getattrs(&pfsdata->handle, pcontext, &fsal_attributes);
      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_get: cache_inode_status=%u fsal_status=%u,%u ",
                   *pstatus, fsal_status.major, fsal_status.minor);

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              char handle_str[256];

              snprintHandle(handle_str, 256, &pfsdata->handle);
              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_get: Stale FSAL File Handle %s, fsal_status=(%u,%u)",
                       handle_str, fsal_status.major, fsal_status.minor);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

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

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

      /* Get the cache_inode file type */
      type = cache_inode_fsal_type_convert(fsal_attributes.type);

      if(type == SYMBOLIC_LINK)
        {
          if( CACHE_INODE_KEEP_CONTENT( policy ) )
           {
             FSAL_CLEAR_MASK(fsal_attributes.asked_attributes);
             FSAL_SET_MASK(fsal_attributes.asked_attributes, pclient->attrmask);
             fsal_status =
                FSAL_readlink(&pfsdata->handle, pcontext, &create_arg.link_content,
                              &fsal_attributes);
            }
          else
            { 
               fsal_status.major = ERR_FSAL_NO_ERROR ;
               fsal_status.minor = 0 ;
            }

          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);

              /* stats */
              pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

              /* Free this key */
              cache_inode_release_fsaldata_key(&key, pclient);

              if(fsal_status.major == ERR_FSAL_STALE)
                {
                  cache_inode_status_t kill_status;

                  LogEvent(COMPONENT_CACHE_INODE,
                           "cache_inode_get: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                           pentry, fsal_status.major, fsal_status.minor);

                  if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                     CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_get: Could not kill entry %p, status = %u, fsal_status=(%u,%u)",
                            pentry, kill_status, fsal_status.major, fsal_status.minor);

                  *pstatus = CACHE_INODE_FSAL_ESTALE;

                }

              return NULL;
            }
        }

      /* Add the entry to the cache */
      if ( type == 1)
	LogCrit(COMPONENT_CACHE_INODE,"inode get");

      if((pentry = cache_inode_new_entry( pfsdata,
                                          &fsal_attributes, 
                                          type,
                                          policy, 
                                          &create_arg, 
                                          NULL,    /* never used to add a new DIR_CONTINUE within this function */
                                          ht, 
                                          pclient, 
                                          pcontext, 
                                          FALSE,  /* This is a population, not a creation */
                                          pstatus ) ) == NULL )
        {
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

      /* Set the returned attributes */
      *pattr = fsal_attributes;

      /* Now, exit the switch/case and returns */
      break;

    default:
      /* This should not happened */
      *pstatus = CACHE_INODE_INVALID_ARGUMENT;
      LogCrit(COMPONENT_CACHE_INODE,
              "cache_inode_get returning CACHE_INODE_INVALID_ARGUMENT - this should not have happened");

      if ( !pclient ) {
        /* invalidate. Just return */
        return( NULL );
      }

      /* stats */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

      /* Free this key */
      cache_inode_release_fsaldata_key(&key, pclient);

      return NULL;
      break;
    }

  /* Want to ASSERT pclient at this point */
  *pstatus = CACHE_INODE_SUCCESS;
  
  if (pentry->object.symlink != NULL) {
  	int stop_here;
	stop_here = 1;
	if (stop_here) {
		stop_here = 2;
	}
  }

  /* valid the found entry, if this is not feasable, returns nothing to the client */
  if( plocation != NULL )
   {
     if( plocation != pentry )
      {
        P_w(&pentry->lock);
        if((*pstatus =
           cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient)) != CACHE_INODE_SUCCESS)
          {
            V_w(&pentry->lock);
            pentry = NULL;
          }
        V_w(&pentry->lock);
      }
   }

  /* stats */
  pclient->stat.func_stats.nb_success[CACHE_INODE_GET] += 1;

  /* Free this key */
  cache_inode_release_fsaldata_key(&key, pclient);

  return pentry;
}  /* cache_inode_get_located */
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,
                   struct req_op_context *req_ctx,
                   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;
     fsal_accessflags_t access_mask = 0;

     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;

          *entry = NULL;
          goto out;
        }

    /*
     * Check if caller is allowed to perform the operation
     */
    access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) |
                  FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE |
                                     FSAL_ACE_PERM_ADD_SUBDIRECTORY);
    status = cache_inode_access(parent,
                                access_mask,
                                req_ctx);
    if (status != CACHE_INODE_SUCCESS)
        {
          *entry = NULL;
          goto out;
        }

    /* 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 = req_ctx->creds->caller_uid;
    object_attributes.group = req_ctx->creds->caller_gid; /* be more selective? */
    object_attributes.mode = mode;

    switch (type) {
    case REGULAR_FILE:
            fsal_status = dir_handle->ops->create(dir_handle, req_ctx,
                                                  name,
                                                  &object_attributes,
                                                  &object_handle);
            break;

    case DIRECTORY:
            fsal_status = dir_handle->ops->mkdir(dir_handle, req_ctx,
                                                 name,
                                                 &object_attributes,
                                                 &object_handle);
            break;

    case SYMBOLIC_LINK:
            fsal_status = dir_handle->ops->symlink(dir_handle, req_ctx,
                                                   name,
                                                   create_arg->link_content,
                                                   &object_attributes,
                                                   &object_handle);
            break;

        case SOCKET_FILE:
        case FIFO_FILE:
            fsal_status = dir_handle->ops->mknode(dir_handle, req_ctx,
                                                  name,
                                                  type,
                                                  NULL, /* no dev_t needed */
                                                  &object_attributes,
                                                  &object_handle);
            break;

        case BLOCK_FILE:
        case CHARACTER_FILE:
            fsal_status = dir_handle->ops->mknode(dir_handle, req_ctx,
                                                  name,
                                                  type,
                                                  &create_arg->dev_spec,
                                                  &object_attributes,
                                                  &object_handle);
            break;

    default:
            /* we should never go there */
            status = CACHE_INODE_INCONSISTENT_ENTRY;
            *entry = NULL;
            goto out;
            break;
    }

     cache_inode_refresh_attrs_locked(parent, req_ctx);

     /* 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,
                                           req_ctx,
                                           entry);
               if (*entry != NULL) {
                    status = CACHE_INODE_ENTRY_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);
          *entry = NULL;
          goto out;
     }
     status = cache_inode_new_entry(object_handle,
				    CACHE_INODE_FLAG_CREATE,
				    entry);
     if (*entry == NULL) {
          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;
          goto out;
     }

out:
     return status;
}
Ejemplo n.º 16
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 */
Ejemplo n.º 17
0
static cache_inode_status_t
cache_inode_readdir_populate(cache_entry_t *directory)
{
	fsal_status_t fsal_status;
	bool eod = false;
	cache_inode_status_t status = CACHE_INODE_SUCCESS;

	struct cache_inode_populate_cb_state state;

	/* Only DIRECTORY entries are concerned */
	if (directory->type != DIRECTORY) {
		status = CACHE_INODE_NOT_A_DIRECTORY;
		LogDebug(COMPONENT_NFS_READDIR,
			 "CACHE_INODE_NOT_A_DIRECTORY");
		return status;
	}

	if ((directory->flags & CACHE_INODE_DIR_POPULATED)
	    && (directory->flags & CACHE_INODE_TRUST_CONTENT)) {
		LogFullDebug(COMPONENT_NFS_READDIR,
			     "CACHE_INODE_DIR_POPULATED and CACHE_INODE_TRUST_CONTENT"
			     );
		status = CACHE_INODE_SUCCESS;
		return status;
	}

	/* Invalidate all the dirents */
	status = cache_inode_invalidate_all_cached_dirent(directory);
	if (status != CACHE_INODE_SUCCESS) {
		LogDebug(COMPONENT_NFS_READDIR,
			 "cache_inode_invalidate_all_cached_dirent status=%s",
			 cache_inode_err_str(status));
		return status;
	}

	state.directory = directory;
	state.status = &status;
	state.offset_cookie = 0;

	fsal_status =
		directory->obj_handle->obj_ops.readdir(directory->obj_handle,
						    NULL,
						    (void *)&state,
						    populate_dirent,
						    &eod);
	if (FSAL_IS_ERROR(fsal_status)) {
		if (fsal_status.major == ERR_FSAL_STALE) {
			LogEvent(COMPONENT_NFS_READDIR,
				 "FSAL returned STALE from readdir.");
			cache_inode_kill_entry(directory);
		}

		status = cache_inode_error_convert(fsal_status);
		LogDebug(COMPONENT_NFS_READDIR,
			 "FSAL readdir status=%s",
			 cache_inode_err_str(status));
		return status;
	}

	/* we were supposed to read to the end.... */
	if (!eod && cache_param.retry_readdir) {
		LogInfo(COMPONENT_NFS_READDIR,
			"Readdir didn't reach eod on dir %p (status %s)",
			directory->obj_handle, cache_inode_err_str(status));
		status = CACHE_INODE_DELAY;
	} else if (eod) {
		/* End of work */
		atomic_set_uint32_t_bits(&directory->flags,
					 CACHE_INODE_DIR_POPULATED);

		/* override status in case it was set to a
		 * minor error in the callback */
		status = CACHE_INODE_SUCCESS;
	}

	/* If !eod (and fsal_status isn't an error), then the only error path
	 * is through a callback failure and status has been set the
	 * populate_dirent callback */

	return status;
}				/* cache_inode_readdir_populate */
Ejemplo n.º 18
0
/**
 *
 * cache_inode_get: 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.
 * ASSUMPTION: DIR_CONT entries are always garbabbaged before their related DIR_BEGINNG 
 *
 * @param fsdata [IN] file system data
 * @param pattr [OUT] pointer to the attributes for the result. 
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials 
 * @param pstatus [OUT] returned status.
 * 
 * @return the pointer to the entry is successfull, NULL otherwise.
 *
 */
cache_entry_t *cache_inode_get(cache_inode_fsal_data_t * pfsdata,
                               fsal_attrib_list_t * pattr,
                               hash_table_t * ht,
                               cache_inode_client_t * pclient,
                               fsal_op_context_t * pcontext,
                               cache_inode_status_t * pstatus)
{
  hash_buffer_t key, value;
  cache_entry_t *pentry = NULL;
  fsal_status_t fsal_status;
  cache_inode_create_arg_t create_arg;
  cache_inode_file_type_t type;
  int hrc = 0;
  fsal_attrib_list_t fsal_attributes;
  cache_inode_fsal_data_t *ppoolfsdata = NULL;

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

  /* stats */
  pclient->stat.nb_call_total += 1;
  pclient->stat.func_stats.nb_call[CACHE_INODE_GET] += 1;

  /* Turn the input to a hash key */
  if(cache_inode_fsaldata_2_key(&key, pfsdata, pclient))
    {
      *pstatus = CACHE_INODE_UNAPPROPRIATED_KEY;

      /* stats */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

      ppoolfsdata = (cache_inode_fsal_data_t *) key.pdata;
      RELEASE_PREALLOC(ppoolfsdata, pclient->pool_key, next_alloc);

      return NULL;
    }

  switch (hrc = HashTable_Get(ht, &key, &value))
    {
    case HASHTABLE_SUCCESS:
      /* Entry exists in the cache and was found */
      pentry = (cache_entry_t *) value.pdata;

      /* return attributes additionally */
      cache_inode_get_attributes(pentry, pattr);

      break;

    case HASHTABLE_ERROR_NO_SUCH_KEY:
      /* Cache miss, allocate a new entry */

      /* If we ask for a dir cont (in this case pfsdata.cookie != FSAL_DIR_BEGINNING, we have 
       * a client who performs a readdir in the middle of a directory, when the direcctories
       * have been garbbage. we must search for the DIR_BEGIN related to this DIR_CONT */
      if(pfsdata->cookie != DIR_START)
        {
          /* added for sanity check */
          LogDebug(COMPONENT_CACHE_INODE_GC,
              "=======> Pb cache_inode_get: line %u pfsdata->cookie != DIR_START (=%u) on object whose type is %u",
               __LINE__, pfsdata->cookie,
               cache_inode_fsal_type_convert(fsal_attributes.type));

          pfsdata->cookie = DIR_START;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          /* redo the call */
          return cache_inode_get(pfsdata, pattr, ht, pclient, pcontext, pstatus);
        }

      /* First, call FSAL to know what the object is */
      fsal_attributes.asked_attributes = pclient->attrmask;
      fsal_status = FSAL_getattrs(&pfsdata->handle, pcontext, &fsal_attributes);
      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE_GC, "cache_inode_get: line %u cache_inode_status=%u fsal_status=%u,%u ",
                     __LINE__, *pstatus, fsal_status.major, fsal_status.minor);

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              char handle_str[256];

              snprintHandle(handle_str, 256, &pfsdata->handle);
              LogEvent(COMPONENT_CACHE_INODE_GC,"cache_inode_get: Stale FSAL File Handle %s", handle_str);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

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

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

      /* Get the cache_inode file type */
      type = cache_inode_fsal_type_convert(fsal_attributes.type);

      if(type == SYMBOLIC_LINK)
        {
          FSAL_CLEAR_MASK(fsal_attributes.asked_attributes);
          FSAL_SET_MASK(fsal_attributes.asked_attributes, pclient->attrmask);
          fsal_status =
              FSAL_readlink(&pfsdata->handle, pcontext, &create_arg.link_content,
                            &fsal_attributes);
          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);

              /* stats */
              pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

              /* Free this key */
              cache_inode_release_fsaldata_key(&key, pclient);

              if(fsal_status.major == ERR_FSAL_STALE)
                {
                  cache_inode_status_t kill_status;

                  LogDebug(COMPONENT_CACHE_INODE_GC,
                      "cache_inode_get: Stale FSAL File Handle detected for pentry = %p",
                       pentry);

                  if(cache_inode_kill_entry(pentry, ht, pclient, &kill_status) !=
                     CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE_GC,"cache_inode_get: Could not kill entry %p, status = %u",
                               pentry, kill_status);

                  *pstatus = CACHE_INODE_FSAL_ESTALE;

                }

              return NULL;
            }
        }

      /* Add the entry to the cache */
      if((pentry = cache_inode_new_entry(pfsdata, &fsal_attributes, type, &create_arg, NULL,    /* never used to add a new DIR_CONTINUE within the scope of this function */
                                         ht, pclient, pcontext, FALSE,  /* This is a population, not a creation */
                                         pstatus)) == NULL)
        {
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

          /* Free this key */
          cache_inode_release_fsaldata_key(&key, pclient);

          return NULL;
        }

      /* Set the returned attributes */
      *pattr = fsal_attributes;

      /* Now, exit the switch/case and returns */
      break;

    default:
      /* This should not happened */
      *pstatus = CACHE_INODE_INVALID_ARGUMENT;

      /* stats */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1;

      /* Free this key */
      cache_inode_release_fsaldata_key(&key, pclient);

      return NULL;
      break;
    }

  *pstatus = CACHE_INODE_SUCCESS;

  /* valid the found entry, if this is not feasable, returns nothing to the client */
  P_w(&pentry->lock);
  if((*pstatus =
      cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient)) != CACHE_INODE_SUCCESS)
    {
      V_w(&pentry->lock);
      pentry = NULL;
    }
  V_w(&pentry->lock);

  /* stats */
  pclient->stat.func_stats.nb_success[CACHE_INODE_GET] += 1;

  /* Free this key */
  cache_inode_release_fsaldata_key(&key, pclient);

  return pentry;
}                               /* cache_inode_get */
Ejemplo n.º 19
0
/**
 *
 * cache_inode_setattrs: set the attributes for an entry located in the cache by its address. 
 * 
 * Sets the attributes for an entry located in the cache by its address. Attributes are provided 
 * with compliance to the underlying FSAL semantics. Attributes that are set are returned in "*pattr".
 *
 * @param pentry_parent [IN] entry for the parent directory to be managed.
 * @param pattr [INOUT] attributes for the entry that we have found. Out: attributes set.
 * @param ht [INOUT] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials 
 * @param pstatus [OUT] returned status.
 * 
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry
 *
 */
cache_inode_status_t cache_inode_setattr(cache_entry_t * pentry, fsal_attrib_list_t * pattr, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */
                                         cache_inode_client_t * pclient,
                                         fsal_op_context_t * pcontext,
                                         cache_inode_status_t * pstatus)
{
  fsal_handle_t *pfsal_handle = NULL;
  fsal_status_t fsal_status;
  fsal_attrib_list_t *p_object_attributes = NULL;
  fsal_attrib_list_t result_attributes;
  fsal_attrib_list_t truncate_attributes;

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

  /* stat */
  pclient->stat.nb_call_total += 1;
  pclient->stat.func_stats.nb_call[CACHE_INODE_SETATTR] += 1;

  /* Lock the entry */
  P_w(&pentry->lock);

  switch (pentry->internal_md.type)
    {
    case REGULAR_FILE:
      pfsal_handle = &pentry->object.file.handle;
      break;

    case SYMBOLIC_LINK:
      assert(pentry->object.symlink);
      pfsal_handle = &pentry->object.symlink->handle;
      break;

    case FS_JUNCTION:
    case DIRECTORY:
      pfsal_handle = &pentry->object.dir.handle;
      break;

    case CHARACTER_FILE:
    case BLOCK_FILE:
    case SOCKET_FILE:
    case FIFO_FILE:
      pfsal_handle = &pentry->object.special_obj.handle;
      break;

    case UNASSIGNED:
    case RECYCLED:
      LogCrit(COMPONENT_CACHE_INODE,
              "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s",
              pentry->internal_md.type, __LINE__, __FILE__);
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* Call FSAL to set the attributes */
  /* result_attributes.asked_attributes = pattr->asked_attributes ; */

  /* mod Th.Leibovici on 2006/02/13
   * We ask back all standard attributes, in case they have been modified
   * by another program (pftp, rcpd...)
   */

  memset(&result_attributes, 0, sizeof(fsal_attrib_list_t));
  result_attributes.asked_attributes = pclient->attrmask;
  /* end of mod */

#ifdef _USE_MFSL
  fsal_status =
      MFSL_setattrs(&pentry->mobject, pcontext, &pclient->mfsl_context, pattr,
                    &result_attributes, NULL);
#else
  fsal_status = FSAL_setattrs(pfsal_handle, pcontext, pattr, &result_attributes);
#endif
  if(FSAL_IS_ERROR(fsal_status))
    {
      *pstatus = cache_inode_error_convert(fsal_status);
      V_w(&pentry->lock);

      /* stat */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_SETATTR] += 1;

      if(fsal_status.major == ERR_FSAL_STALE)
        {
          cache_inode_status_t kill_status;

          LogEvent(COMPONENT_CACHE_INODE,
                   "cache_inode_setattr: Stale FSAL File Handle detected for pentry = %p",
                   pentry);

          if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
             CACHE_INODE_SUCCESS)
            LogCrit(COMPONENT_CACHE_INODE,
                    "cache_inode_setattr: Could not kill entry %p, status = %u",
                    pentry, kill_status);

          *pstatus = CACHE_INODE_FSAL_ESTALE;
        }

      return *pstatus;
    }

  if(pattr->asked_attributes & FSAL_ATTR_SIZE)
    {
      truncate_attributes.asked_attributes = pclient->attrmask;

      fsal_status = FSAL_truncate(pfsal_handle,
                                  pcontext, pattr->filesize, NULL, &truncate_attributes);
      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);
          V_w(&pentry->lock);

          /* stat */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_SETATTR] += 1;

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_setattr: Stale FSAL File Handle detected for pentry = %p",
                       pentry);

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_setattr: Could not kill entry %p, status = %u",
                        pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          return *pstatus;
        }

    }

  /* Keep the new attribute in cache */
  switch (pentry->internal_md.type)
    {
    case REGULAR_FILE:
      p_object_attributes = &(pentry->object.file.attributes);
      break;

    case SYMBOLIC_LINK:
      assert(pentry->object.symlink);
      p_object_attributes = &(pentry->object.symlink->attributes);
      break;

    case FS_JUNCTION:
    case DIRECTORY:
      p_object_attributes = &(pentry->object.dir.attributes);
      break;

    case CHARACTER_FILE:
    case BLOCK_FILE:
    case SOCKET_FILE:
    case FIFO_FILE:
      p_object_attributes = &(pentry->object.special_obj.attributes);
      break;

    case UNASSIGNED:
    case RECYCLED:
      LogCrit(COMPONENT_CACHE_INODE,
              "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s",
              pentry->internal_md.type, __LINE__, __FILE__);
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* Update the cached attributes */
  if((result_attributes.asked_attributes & FSAL_ATTR_SIZE) ||
     (result_attributes.asked_attributes & FSAL_ATTR_SPACEUSED))
    {

      if(pentry->internal_md.type == REGULAR_FILE)
        {
          if(pentry->object.file.pentry_content == NULL)
            {
              /* Operation on a non data cached file */
              p_object_attributes->filesize = result_attributes.filesize;
              p_object_attributes->spaceused = result_attributes.filesize;      /* Unclear hook here. BUGAZOMEU */
            }
          else
            {
              /* Data cached file */
              /* Do not set the p_object_attributes->filesize and p_object_attributes->spaceused  in this case 
               * This will lead to a situation where (for example) untar-ing a file will produced invalid files 
               * with a size of 0 despite the fact that they are not empty */

              LogFullDebug(COMPONENT_CACHE_INODE,
                           "cache_inode_setattr with FSAL_ATTR_SIZE on data cached entry");
            }
        }
      else if(pattr->asked_attributes & FSAL_ATTR_SIZE)
        LogCrit(COMPONENT_CACHE_INODE,
                "WARNING !!! cache_inode_setattr tried to set size on a non REGULAR_FILE type=%d",
                pentry->internal_md.type);
    }

  if(result_attributes.asked_attributes &
     (FSAL_ATTR_MODE | FSAL_ATTR_OWNER | FSAL_ATTR_GROUP))
    {
      if(result_attributes.asked_attributes & FSAL_ATTR_MODE)
        p_object_attributes->mode = result_attributes.mode;

      if(result_attributes.asked_attributes & FSAL_ATTR_OWNER)
        p_object_attributes->owner = result_attributes.owner;

      if(result_attributes.asked_attributes & FSAL_ATTR_GROUP)
        p_object_attributes->group = result_attributes.group;
    }

  if(result_attributes.asked_attributes &
     (FSAL_ATTR_ATIME | FSAL_ATTR_CTIME | FSAL_ATTR_MTIME))
    {
      if(result_attributes.asked_attributes & FSAL_ATTR_ATIME)
        p_object_attributes->atime = result_attributes.atime;

      if(result_attributes.asked_attributes & FSAL_ATTR_CTIME)
        p_object_attributes->ctime = result_attributes.ctime;

      if(result_attributes.asked_attributes & FSAL_ATTR_MTIME)
        p_object_attributes->mtime = result_attributes.mtime;
    }
  
#ifdef _USE_NFS4_ACL
  if(result_attributes.asked_attributes & FSAL_ATTR_ACL)
    {
      LogDebug(COMPONENT_CACHE_INODE, "cache_inode_setattr: old acl = %p, new acl = %p",
               p_object_attributes->acl, result_attributes.acl);

      /* Release previous acl entry. */
      if(p_object_attributes->acl)
        {
          fsal_acl_status_t status;
          nfs4_acl_release_entry(p_object_attributes->acl, &status);
          if(status != NFS_V4_ACL_SUCCESS)
            LogEvent(COMPONENT_CACHE_INODE, "cache_inode_setattr: Failed to release old acl:"
                     " status = %d", status);
        }

      /* Update with new acl entry. */
      p_object_attributes->acl = result_attributes.acl;
    }
#endif                          /* _USE_NFS4_ACL */

  /* Return the attributes as set */
  *pattr = *p_object_attributes;

  /* validate the entry */
  *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_SET, pclient);

  /* Release the entry */
  V_w(&pentry->lock);

  /* stat */
  if(*pstatus != CACHE_INODE_SUCCESS)
    pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_SETATTR] += 1;
  else
    pclient->stat.func_stats.nb_success[CACHE_INODE_SETATTR] += 1;

  return *pstatus;
}                               /* cache_inode_setattr */
Ejemplo n.º 20
0
/**
 *
 * cache_inode_create: creates an entry through the cache.
 *
 * Creates an entry through the cache.
 *
 * @param pentry_parent [IN] pointer to the pentry parent
 * @param pname         [IN] pointer to the name of the object in the destination directory.
 * @param type          [IN] type of the object to be created.
 * @param mode          [IN] mode to be used at file creation
 * @param pcreate_arg   [IN] additional argument for object creation
 * @param pattr         [OUT] attributes for the new object.
 * @param ht            [INOUT] hash table used for the cache.
 * @param pclient       [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext      [IN] FSAL credentials
 * @param pstatus       [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry\n
 * @return CACHE_INODE_BAD_TYPE either source or destination have incorrect type\n
 * @return CACHE_INODE_ENTRY_EXISTS entry of that name already exists in destination.
 *
 */
cache_entry_t *
cache_inode_create(cache_entry_t * pentry_parent,
                   fsal_name_t * pname,
                   cache_inode_file_type_t type,
                   fsal_accessmode_t mode,
                   cache_inode_create_arg_t * pcreate_arg,
                   fsal_attrib_list_t * pattr,
                   hash_table_t * ht,
                   cache_inode_client_t * pclient,
                   fsal_op_context_t * pcontext,
                   cache_inode_status_t * pstatus)
{
    cache_entry_t *pentry = NULL;
    fsal_status_t fsal_status;
#ifdef _USE_MFSL
    mfsl_object_t object_handle;
#else
    fsal_handle_t object_handle;
#endif
    fsal_attrib_list_t parent_attributes;
    fsal_attrib_list_t object_attributes;
    fsal_handle_t dir_handle;
    cache_inode_fsal_data_t fsal_data;
    cache_inode_status_t status;
    struct cache_inode_dir_begin__ *dir_begin;
    int pnfs_status;

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

    /* stats */
    pclient->stat.nb_call_total += 1;
    inc_func_call(pclient, CACHE_INODE_CREATE);
    /*
     * Check if the required type is correct, with this
     * function, we manage file, dir and symlink
     */
    if(type != REGULAR_FILE && type != DIR_BEGINNING && type != SYMBOLIC_LINK &&
       type != SOCKET_FILE && type != FIFO_FILE && type != CHARACTER_FILE &&
       type != BLOCK_FILE)
        {
            *pstatus = CACHE_INODE_BAD_TYPE;

            /* stats */
            inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);
            return NULL;
        }
    /*
     * Check if caller is allowed to perform the operation
     */
    status = cache_inode_access(pentry_parent,
                                FSAL_W_OK, ht,
                                pclient, pcontext, &status);
    if (status != CACHE_INODE_SUCCESS)
        {
            *pstatus = status;

            /* stats */
            inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);

            /* pentry is a directory */
            return NULL;
        }
    /*
     * Check if an entry of the same name exists
     */
    pentry = cache_inode_lookup(pentry_parent,
                                pname, &object_attributes,
                                ht, pclient, pcontext, pstatus);
    if (pentry != NULL)
        {
            *pstatus = CACHE_INODE_ENTRY_EXISTS;

            if(pentry->internal_md.type != type)
                {
                    /*
                     * Incompatible types, returns NULL
                     */
                    /* stats */
                    inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);
                    return NULL;
                }
            else
                {
                    /* stats */
                    inc_func_success(pclient, CACHE_INODE_CREATE);
                    /*
                     * redondant creation, returned the
                     * previously created entry
                     */
                    return pentry;
                }
        }
    /*
     * At this point, the entry was not found, this means
     * that is doesn't exist is FSAL, we can create it
     */
    /* Get the lock for the parent */
    P_w(&pentry_parent->lock);

    if(pentry_parent->internal_md.type == DIR_BEGINNING)
        dir_handle = pentry_parent->object.dir_begin.handle;

    if(pentry_parent->internal_md.type == DIR_CONTINUE)
        {
            P_r(&pentry_parent->object.dir_cont.pdir_begin->lock);
            dir_handle = pentry_parent->object.dir_cont.pdir_begin->object.dir_begin.handle;
            V_r(&pentry_parent->object.dir_cont.pdir_begin->lock);
        }

    object_attributes.asked_attributes = pclient->attrmask;
    switch (type)
        {
        case REGULAR_FILE:
#ifdef _USE_MFSL
            cache_inode_get_attributes(pentry_parent, &parent_attributes);
            fsal_status = MFSL_create(&pentry_parent->mobject,
                                      pname, pcontext,
                                      &pclient->mfsl_context,
                                      mode, &object_handle,
                                      &object_attributes, &parent_attributes);
#else
            fsal_status = FSAL_create(&dir_handle,
                                      pname, pcontext, mode,
                                      &object_handle, &object_attributes);
#endif
            break;

        case DIR_BEGINNING:
#ifdef _USE_MFSL
            cache_inode_get_attributes(pentry_parent, &parent_attributes);
            fsal_status = MFSL_mkdir(&pentry_parent->mobject,
                                     pname, pcontext,
                                     &pclient->mfsl_context,
                                     mode, &object_handle,
                                     &object_attributes, &parent_attributes);
#else
            fsal_status = FSAL_mkdir(&dir_handle,
                                     pname, pcontext, mode,
                                     &object_handle, &object_attributes);
#endif
            break;

        case SYMBOLIC_LINK:
#ifdef _USE_MFSL
            cache_inode_get_attributes(pentry_parent, &object_attributes);
            fsal_status = MFSL_symlink(&pentry_parent->mobject,
                                       pname, &pcreate_arg->link_content,
                                       pcontext, &pclient->mfsl_context,
                                       mode, &object_handle, &object_attributes);
#else
            fsal_status = FSAL_symlink(&dir_handle,
                                       pname, &pcreate_arg->link_content,
                                       pcontext, mode, &object_handle,
                                       &object_attributes);
#endif
            break;

        case SOCKET_FILE:
#ifdef _USE_MFSL
            fsal_status = MFSL_mknode(&pentry_parent->mobject, pname,
                                      pcontext, &pclient->mfsl_context, mode,
                                      FSAL_TYPE_SOCK, NULL, /* no dev_t needed for socket file */
                                      &object_handle, &object_attributes);
#else
            fsal_status = FSAL_mknode(&dir_handle, pname, pcontext,
                                      mode, FSAL_TYPE_SOCK, NULL, /* no dev_t needed for socket file */
                                      &object_handle, &object_attributes);
#endif
            break;

        case FIFO_FILE:
#ifdef _USE_MFSL
            fsal_status = MFSL_mknode(&pentry_parent->mobject, pname,
                                      pcontext, &pclient->mfsl_context,
                                      mode, FSAL_TYPE_FIFO, NULL, /* no dev_t needed for FIFO file */
                                      &object_handle, &object_attributes);
#else
            fsal_status = FSAL_mknode(&dir_handle, pname, pcontext,
                                      mode, FSAL_TYPE_FIFO, NULL, /* no dev_t needed for FIFO file */
                                      &object_handle, &object_attributes);
#endif
            break;

        case BLOCK_FILE:
#ifdef _USE_MFSL
            fsal_status = MFSL_mknode(&pentry_parent->mobject,
                                      pname, pcontext,
                                      &pclient->mfsl_context,
                                      mode, FSAL_TYPE_BLK,
                                      &pcreate_arg->dev_spec,
                                      &object_handle, &object_attributes);
#else
            fsal_status = FSAL_mknode(&dir_handle,
                                      pname, pcontext,
                                      mode, FSAL_TYPE_BLK,
                                      &pcreate_arg->dev_spec,
                                      &object_handle, &object_attributes);
#endif
            break;

        case CHARACTER_FILE:
#ifdef _USE_MFSL
            fsal_status = MFSL_mknode(&pentry_parent->mobject,
                                      pname, pcontext,
                                      &pclient->mfsl_context,
                                      mode, FSAL_TYPE_CHR,
                                      &pcreate_arg->dev_spec,
                                      &object_handle, &object_attributes);
#else
            fsal_status = FSAL_mknode(&dir_handle,
                                      pname, pcontext,
                                      mode, FSAL_TYPE_CHR,
                                      &pcreate_arg->dev_spec,
                                      &object_handle, &object_attributes);
#endif
            break;

        default:
            /* we should never go there */
            *pstatus = CACHE_INODE_INCONSISTENT_ENTRY;
            V_w(&pentry_parent->lock);

            /* stats */
            inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);

            return NULL;
            break;
        }

    /* Check for the result */
    if(FSAL_IS_ERROR(fsal_status))
        {
            *pstatus = cache_inode_error_convert(fsal_status);
            V_w(&pentry_parent->lock);

            if(fsal_status.major == ERR_FSAL_STALE)
                {
                    cache_inode_status_t kill_status;
                    LogEvent(COMPONENT_CACHE_INODE,
                             "cache_inode_create: Stale FSAL File Handle "
                             "detected for pentry = %p",
                             pentry_parent);

                    cache_inode_kill_entry(pentry_parent, ht,
                                           pclient, &kill_status);
                    if(kill_status != CACHE_INODE_SUCCESS)
                        LogCrit(COMPONENT_CACHE_INODE, "cache_inode_create: "
                                "Could not kill entry %p, status = %u",
                                pentry_parent, kill_status);
                    *pstatus = CACHE_INODE_FSAL_ESTALE;
                }
            /* stats */
            inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);

            return NULL;
        }
    else
        {
#ifdef _USE_MFSL
            fsal_data.handle = object_handle.handle;
#else
            fsal_data.handle = object_handle;
#endif
            fsal_data.cookie = DIR_START;

            pentry = cache_inode_new_entry(&fsal_data, &object_attributes,
                                           type, pcreate_arg, NULL,
                                           ht, pclient, pcontext,
                                           TRUE, /* This is a creation and not a population */
                                           pstatus);
            if (pentry == NULL)
                {
                    *pstatus = CACHE_INODE_INSERT_ERROR;
                    V_w(&pentry_parent->lock);

                    /* stats */
                    inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);
                    return NULL;
                }
#ifdef _USE_MFSL
            /* Copy the MFSL object to the cache */
            memcpy((char *)&(pentry->mobject),
                   (char *)&object_handle, sizeof(mfsl_object_t));
#endif

            /* Add this entry to the directory */
            status = cache_inode_add_cached_dirent(pentry_parent,
                                                   pname, pentry,
                                                   NULL, ht,
                                                   pclient, pcontext,
                                                   pstatus);
            if (status != CACHE_INODE_SUCCESS)
                {
                    V_w(&pentry_parent->lock);

                    /* stats */
                    inc_func_err_unrecover(pclient, CACHE_INODE_CREATE);
                    return NULL;
                }
        }

#ifdef _USE_PNFS
    if((type == REGULAR_FILE) &&
       (pcreate_arg != NULL) &&
       (pcreate_arg->use_pnfs == TRUE))
        {
            pnfs_status = pnfs_create_ds_file(&pclient->pnfsclient,
                                              pentry->object.file.attributes.fileid,
                                              &pentry->object.file.pnfs_file.ds_file);
            if (pnfs_status != NFS4_OK)
                {
                    V_w(&pentry_parent->lock);
                    LogDebug(COMPONENT_CACHE_INODE, "OPEN PNFS CREATE DS FILE : Error %u",
                             pnfs_status);

                    *pstatus = CACHE_INODE_IO_ERROR;
                    return NULL;
                }
        }
#endif

       /* Update the parent cached attributes */
       if(pentry_parent->internal_md.type == DIR_BEGINNING)
           dir_begin = &pentry_parent->object.dir_begin;
       else
           dir_begin = &pentry_parent->object.dir_cont.pdir_begin->object.dir_begin;

       dir_begin->attributes.mtime.seconds = time(NULL);
       dir_begin->attributes.mtime.nseconds = 0;
       dir_begin->attributes.ctime = dir_begin->attributes.mtime;
       /*
        * if the created object is a directory, it contains a link
        * to its parent : '..'. Thus the numlink attr must be increased.
        */
       if(type == DIR_BEGINNING)
           {
               dir_begin->attributes.numlinks++;
           }
       /* Get the attributes in return */
       *pattr = object_attributes;

       /* valid the parent */
       *pstatus = cache_inode_valid(pentry_parent,
                                    CACHE_INODE_OP_SET,
                                    pclient);
       /* release the lock for the parent */
       V_w(&pentry_parent->lock);

       /* stat */
       if(*pstatus != CACHE_INODE_SUCCESS)
           inc_func_err_retryable(pclient, CACHE_INODE_CREATE);
       else
           inc_func_success(pclient, CACHE_INODE_CREATE);

       return pentry;
}
Ejemplo n.º 21
0
/**
 *
 * cache_inode_lookup_sw: looks up for a name in a directory indicated by a
 * cached entry.
 * 
 * Looks up for a name in a directory indicated by a cached entry. The directory
 * should have been cached before.
 *
 * @param pentry_parent [IN]    entry for the parent directory to be managed.
 * @param name          [IN]    name of the entry that we are looking for in the
 *        cache.
 * @param pattr         [OUT]   attributes for the entry that we have found.
 * @param ht            [IN]    hash table used for the cache, unused in this
 *        call.
 * @param pclient       [INOUT] ressource allocated by the client for the nfs
 * management.
 * @param pcontext         [IN]    FSAL credentials 
 * @param pstatus       [OUT]   returned status.
 * @param use_mutex     [IN]    if TRUE, mutex management is done, not if equal
 * to FALSE.
 * 
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the
 *  entry
 *
 */
cache_entry_t *cache_inode_lookup_sw(cache_entry_t        * pentry_parent,
                                     fsal_name_t          * pname,
                                     cache_inode_policy_t   policy,
                                     fsal_attrib_list_t   * pattr,
                                     hash_table_t         * ht,
                                     cache_inode_client_t * pclient,
                                     fsal_op_context_t    * pcontext,
                                     cache_inode_status_t * pstatus, 
                                     int use_mutex)
{

  cache_inode_dir_entry_t dirent_key[1], *dirent;
  struct avltree_node *dirent_node;
  cache_inode_dir_entry_t *new_dir_entry;
  cache_entry_t *pentry = NULL;
  fsal_status_t fsal_status;
#ifdef _USE_MFSL
  mfsl_object_t object_handle;
#else
  fsal_handle_t object_handle;
#endif
  fsal_handle_t dir_handle;
  fsal_attrib_list_t object_attributes;
  cache_inode_create_arg_t create_arg;
  cache_inode_file_type_t type;
  cache_inode_status_t cache_status;
  cache_inode_fsal_data_t new_entry_fsdata;
  fsal_accessflags_t access_mask = 0;

  memset(&create_arg, 0, sizeof(create_arg));
  memset( (char *)&new_entry_fsdata, 0, sizeof( new_entry_fsdata ) ) ; 

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

  /* stats */
  (pclient->stat.nb_call_total)++;
  (pclient->stat.func_stats.nb_call[CACHE_INODE_LOOKUP])++;

  /* We should not renew entries when !use_mutex (because unless we
   * make the flag explicit (shared vs. exclusive), we don't know
   * whether a mutating operation is safe--and, the caller should have
   * already renewed the entry */
  if(use_mutex == TRUE) {
      P_w(&pentry_parent->lock);

      cache_status = cache_inode_renew_entry(pentry_parent, pattr, ht,
                                             pclient, pcontext, pstatus);
      if(cache_status != CACHE_INODE_SUCCESS)
      {
          V_w(&pentry_parent->lock);
          inc_func_err_retryable(pclient, CACHE_INODE_GETATTR);
          LogDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_lookup: returning %d(%s) from cache_inode_renew_entry",
                       *pstatus, cache_inode_err_str(*pstatus));
          return NULL;
      }

      /* RW Lock goes for writer to reader */
      rw_lock_downgrade(&pentry_parent->lock);
  }

  if(pentry_parent->internal_md.type != DIRECTORY)
    {
      /* Parent is no directory base, return NULL */
      *pstatus = CACHE_INODE_NOT_A_DIRECTORY;

      /* stats */
      (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

      if(use_mutex == TRUE)
        V_r(&pentry_parent->lock);

      return NULL;
    }

  /* if name is ".", use the input value */
  if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT))
    {
      pentry = pentry_parent;
    }
  else if(!FSAL_namecmp(pname, (fsal_name_t *) & FSAL_DOT_DOT))
    {
      /* Directory do only have exactly one parent. This a limitation in all FS,
       * which implies that hard link are forbidden on directories (so that
       * they exists only in one dir).  Because of this, the parent list is
       * always limited to one element for a dir.  Clients SHOULD never 
       * 'lookup( .. )' in something that is no dir. */
      pentry =
          cache_inode_lookupp_no_mutex(pentry_parent, ht, pclient, pcontext,
                                       pstatus);
    }
  else
    {
      /* This is a "regular lookup" (not on "." or "..") */

      /* Check is user (as specified by the credentials) is authorized to 
       * lookup the directory or not */
      access_mask = FSAL_MODE_MASK_SET(FSAL_X_OK) |
                    FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR);
      if(cache_inode_access_no_mutex(pentry_parent,
                                     access_mask,
                                     ht,
                                     pclient,
                                     pcontext,
                                     pstatus) != CACHE_INODE_SUCCESS)
        {
          if(use_mutex == TRUE)
            V_r(&pentry_parent->lock);

          (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++;
          return NULL;
        }

      /* We first try avltree_lookup by name.  If that fails, we dispatch to
       * the fsal. */

      FSAL_namecpy(&dirent_key->name, pname);
      dirent_node = avltree_lookup(&dirent_key->node_n,
				   &pentry_parent->object.dir.dentries);
      if (dirent_node) {
      	  dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t,
					node_n);
	  pentry = dirent->pentry;
      }

      if(pentry == NULL)
        {
          LogDebug(COMPONENT_CACHE_INODE, "Cache Miss detected");

          dir_handle = pentry_parent->handle;
          object_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL

#ifdef _USE_MFSL_ASYNC
          if(!mfsl_async_is_object_asynchronous(&pentry_parent->mobject))
            {
              /* If the parent is asynchronous, rely on the content of the cache
               * inode parent entry.
               *
               * /!\ If the fs behind the FSAL is touched in a non-nfs way,
               * there will be huge incoherencies.
               */
#endif                          /* _USE_MFSL_ASYNC */

              fsal_status = MFSL_lookup(&pentry_parent->mobject,
                                        pname,
                                        pcontext,
                                        &pclient->mfsl_context,
                                        &object_handle, &object_attributes, NULL);
#ifdef _USE_MFSL_ASYNC
            }
          else
            {
              LogMidDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_lookup chose to bypass FSAL and trusted his cache for name=%s",
                       pname->name);
              fsal_status.major = ERR_FSAL_NOENT;
              fsal_status.minor = ENOENT;
            }
#endif                          /* _USE_MFSL_ASYNC */

#else
          fsal_status =
              FSAL_lookup(&dir_handle, pname, pcontext, &object_handle,
                          &object_attributes);
#endif                          /* _USE_MFSL */

          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);

              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* Stale File Handle to be detected and managed */
              if(fsal_status.major == ERR_FSAL_STALE)
                {
                  cache_inode_status_t kill_status;

                  LogEvent(COMPONENT_CACHE_INODE,
                           "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                           pentry_parent, fsal_status.major, fsal_status.minor);

                  if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status) !=
                     CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_pentry_parent: Could not kill entry %p, status = %u",
                            pentry_parent, kill_status);

                  *pstatus = CACHE_INODE_FSAL_ESTALE;
                }

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

          type = cache_inode_fsal_type_convert(object_attributes.type);

          /* If entry is a symlink, this value for be cached */
          if(type == SYMBOLIC_LINK)
            {
              if( CACHE_INODE_KEEP_CONTENT( policy ) )
#ifdef _USE_MFSL
               {
                fsal_status =
                    MFSL_readlink(&object_handle, pcontext, &pclient->mfsl_context,
                                  &create_arg.link_content, &object_attributes, NULL);
               }
#else
               {
                fsal_status =
                    FSAL_readlink(&object_handle, pcontext, &create_arg.link_content,
                                  &object_attributes);
               }
             else
              { 
                 fsal_status.major = ERR_FSAL_NO_ERROR ;
                 fsal_status.minor = 0 ;
              }
#endif
              if(FSAL_IS_ERROR(fsal_status))
                {
                  *pstatus = cache_inode_error_convert(fsal_status);
                  if(use_mutex == TRUE)
                    V_r(&pentry_parent->lock);

                  /* Stale File Handle to be detected and managed */
                  if(fsal_status.major == ERR_FSAL_STALE)
                    {
                      cache_inode_status_t kill_status;

                      LogEvent(COMPONENT_CACHE_INODE,
                               "cache_inode_lookup: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                               pentry_parent, fsal_status.major, fsal_status.minor);

                      if(cache_inode_kill_entry(pentry_parent, NO_LOCK, ht, pclient, &kill_status)
                         != CACHE_INODE_SUCCESS)
                        LogCrit(COMPONENT_CACHE_INODE,
                                "cache_inode_pentry_parent: Could not kill entry %p, status = %u",
                                pentry_parent, kill_status);

                      *pstatus = CACHE_INODE_FSAL_ESTALE;
                    }

                  /* stats */
                  (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

                  return NULL;
                }
            }

          /* Allocation of a new entry in the cache */
#ifdef _USE_MFSL
          new_entry_fsdata.handle = object_handle.handle;
#else
          new_entry_fsdata.handle = object_handle;
#endif
          new_entry_fsdata.cookie = 0;

          if((pentry = cache_inode_new_entry( &new_entry_fsdata, 
                                              &object_attributes,
                                              type, 
                                              policy,
                                              &create_arg, 
                                              NULL, 
                                              ht, 
                                              pclient, 
                                              pcontext, 
                                              FALSE,      /* This is a population and not a creation */
                                              pstatus ) ) == NULL )
            {
              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

          /* Entry was found in the FSAL, add this entry to the parent
           * directory */
          cache_status = cache_inode_add_cached_dirent(pentry_parent,
                                                       pname,
                                                       pentry,
                                                       ht,
						       &new_dir_entry,
						       pclient,
						       pcontext,
						       pstatus);

          if(cache_status != CACHE_INODE_SUCCESS
             && cache_status != CACHE_INODE_ENTRY_EXISTS)
            {
              if(use_mutex == TRUE)
                V_r(&pentry_parent->lock);

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUP])++;

              return NULL;
            }

        } /* cached lookup fail (try fsal) */
Ejemplo n.º 22
0
/**
 *
 * cache_inode_renew_entry: Renews the attributes for an entry.
 *
 * Sets the attributes for an entry located in the cache by its address. Attributes are provided
 * with compliance to the underlying FSAL semantics. Attributes that are set are returned in "*pattr".
 *
 * @param pentry_parent [IN] entry for the parent directory to be managed.
 * @param pattr [OUT] renewed attributes for the entry that we have found.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
   @return Other errors shows a FSAL error.
 *
 */
cache_inode_status_t cache_inode_renew_entry(cache_entry_t * pentry,
                                             fsal_attrib_list_t * pattr,
                                             hash_table_t * ht,
                                             cache_inode_client_t * pclient,
                                             fsal_op_context_t * pcontext,
                                             cache_inode_status_t * pstatus)
{
  fsal_handle_t *pfsal_handle = NULL;
  fsal_status_t fsal_status;
  fsal_attrib_list_t object_attributes;
  fsal_path_t link_content;
  time_t current_time = time(NULL);
  time_t entry_time = pentry->internal_md.refresh_time;

  /* If we do nothing (no expiration) then everything is all right */
  *pstatus = CACHE_INODE_SUCCESS;

  if(isFullDebug(COMPONENT_CACHE_INODE))
    {
      char *type;
      char grace[20], grace2[20];
      unsigned int elapsed = (unsigned int)current_time - (unsigned int)entry_time;
      int print = 1;

      cache_inode_expire_to_str(pclient->expire_type_attr, pclient->grace_period_attr, grace);

      switch(pentry->internal_md.type)
        {
          case UNASSIGNED:
            type = "UNASSIGNED";
            break;
          case REGULAR_FILE:
            type = "REGULAR_FILE";
            break;
          case CHARACTER_FILE:
            type = "CHARACTER_FILE";
            break;
          case BLOCK_FILE:
            type = "BLOCK_FILE";
            break;
          case SYMBOLIC_LINK:
            print = 0;
            cache_inode_expire_to_str(pclient->expire_type_link, pclient->grace_period_link, grace2);
            LogDebug(COMPONENT_CACHE_INODE,
                     "Renew Entry test of %p for SYMBOLIC_LINK elapsed time=%u, grace_period_attr=%s, grace_period_link=%s",
                     pentry, elapsed, grace, grace2);
            break;
          case SOCKET_FILE:
            type = "SOCKET_FILE";
            break;
          case FIFO_FILE:
            type = "FIFO_FILE";
            break;
          case DIRECTORY:
            print = 0;
            cache_inode_expire_to_str(pclient->expire_type_dirent, pclient->grace_period_dirent, grace2);
            LogDebug(COMPONENT_CACHE_INODE,
                     "Renew Entry test of %p for DIRECTORY elapsed time=%u, grace_period_attr=%s, grace_period_dirent=%s, has_been_readdir=%u",
                     pentry, elapsed, grace, grace2,
                     pentry->object.dir.has_been_readdir);
            break;
          case FS_JUNCTION:
            type = "FS_JUNCTION";
            break;
          case RECYCLED:
            type = "RECYCLED";
            break;
          default:
            type = "UNKNOWN";
            break;
        }
      if(print)
        {
          LogDebug(COMPONENT_CACHE_INODE,
                   "Renew Entry test of %p for %s elapsed time=%u, grace_period_attr=%s",
                   pentry, type, elapsed, grace);
        }
    }

  /* An entry that is a regular file with an associated File Content Entry won't
   * expire until data exists in File Content Cache, to avoid attributes incoherency */

  /* @todo: BUGAZOMEU: I got serious doubts on the following blocks: possible trouble if using data caching */
  if(pentry->internal_md.type == REGULAR_FILE &&
     pentry->object.file.pentry_content != NULL)
    {
      /* Successfull exit without having nothing to do ... */

      LogDebug(COMPONENT_CACHE_INODE,
               "Entry %p is a REGULAR_FILE with associated data cached %p, no expiration",
               pentry, pentry->object.file.pentry_content);

      *pstatus = CACHE_INODE_SUCCESS;
      return *pstatus;
    }

  LogDebug(COMPONENT_CACHE_INODE,
           "cache_inode_renew_entry use getattr/mtime checking %d, is dir "
	   "beginning %d, has bit in mask %d, has been readdir %d state %d",
           pclient->getattr_dir_invalidation,
           pentry->internal_md.type == DIRECTORY,
           (int) FSAL_TEST_MASK(pclient->attrmask, FSAL_ATTR_MTIME),
	   pentry->object.dir.has_been_readdir,
	   pentry->internal_md.valid_state);
  /* Do we use getattr/mtime checking */
  if(pclient->getattr_dir_invalidation &&
     pentry->internal_md.type == DIRECTORY &&
     FSAL_TEST_MASK(pclient->attrmask, FSAL_ATTR_MTIME) /*&&
     pentry->object.dir.has_been_readdir == CACHE_INODE_YES*/)
    {
      /* This checking is to be done ... */
      LogDebug(COMPONENT_CACHE_INODE,
               "cache_inode_renew_entry testing directory mtime");
      pfsal_handle = &pentry->object.dir.handle;

      /* Call FSAL to get the attributes */
      object_attributes.asked_attributes = pclient->attrmask;

      fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes);

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)",
                       pentry, __LINE__, fsal_status.major, fsal_status.minor);

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_renew_entry: Could not kill entry %p, status = %u",
                         pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }
          /* stats */
          (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++;

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_renew_entry: returning %d (%s) from FSAL_getattrs for getattr/mtime checking",
                   *pstatus, cache_inode_err_str(*pstatus));
          return *pstatus;
        }

      LogDebug(COMPONENT_CACHE_INODE,
               "cache_inode_renew_entry: Entry=%p, type=%d, Cached Time=%d, FSAL Time=%d",
               pentry, pentry->internal_md.type,
               pentry->object.dir.attributes.mtime.seconds,
               object_attributes.mtime.seconds);

      /* Compare the FSAL mtime and the cached mtime */
      if(pentry->object.dir.attributes.mtime.seconds <
         object_attributes.mtime.seconds)
        {
          /* Cached directory content is obsolete, it must be renewed */
          cache_inode_set_attributes(pentry, &object_attributes);

          /* Return the attributes as set */
          if(pattr != NULL)
            *pattr = object_attributes;

          /* Set the directory content as "to be renewed" */
          /* Next call to cache_inode_readdir will repopulate the dirent array */
          pentry->object.dir.has_been_readdir = CACHE_INODE_RENEW_NEEDED;

          /* Set the refresh time for the cache entry */
          pentry->internal_md.refresh_time = time(NULL);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_renew_entry: cached directory content for entry %p must be renewed, due to getattr mismatch",
                   pentry);

          if(cache_inode_invalidate_all_cached_dirent(pentry, ht, pclient, pstatus)
             != CACHE_INODE_SUCCESS)
            {
              /* Should never happen */
              LogCrit(COMPONENT_CACHE_INODE,
                      "cache_inode_invalidate_all_cached_dirent returned %d (%s)",
                      *pstatus, cache_inode_err_str(*pstatus));
              return *pstatus;
            }

        }                       /* if( pentry->object.dir.attributes.mtime < object_attributes.asked_attributes.mtime ) */
    }

  /* if( pclient->getattr_dir_invalidation && ... */
  /* Check for dir content expiration and/or staleness */
  if(pentry->internal_md.type == DIRECTORY &&
     pclient->expire_type_dirent != CACHE_INODE_EXPIRE_NEVER &&
     pentry->object.dir.has_been_readdir == CACHE_INODE_YES &&
     ((current_time - entry_time >= pclient->grace_period_dirent)
      || (pentry->internal_md.valid_state == STALE)))
    {
      /* Would be better if state was a flag that we could and/or the bits but
       * in any case we need to get rid of stale so we only go through here
       * once.
       */
      if ( pentry->internal_md.valid_state == STALE )
	pentry->internal_md.valid_state = VALID;

      /* stats */
      (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++;

      /* Log */
      LogDebug(COMPONENT_CACHE_INODE,
	       "Case 1: cached directory entries for entry %p must be renewed"
	       " (has been readdir)", pentry);

      if(isFullDebug(COMPONENT_CACHE_INODE))
        {
          char name[1024];
	  struct avltree_node *d_node;
	  cache_inode_dir_entry_t *d_dirent;
	  int i = 0;
	  
	  d_node = avltree_first(&pentry->object.dir.dentries);
      	  do {
              d_dirent = avltree_container_of(d_node, cache_inode_dir_entry_t,
					      node_n);
	      if (d_dirent->pentry->internal_md.valid_state == VALID) {
	          FSAL_name2str(&(d_dirent->name), name, 1023);
                  LogDebug(COMPONENT_CACHE_INODE,
                           "cache_inode_renew_entry: Entry %d %s",
                           i, name);
	      }
	      i++;
          } while ((d_node = avltree_next(d_node)));
        }

      /* Do the getattr if it had not being done before */
      if(pfsal_handle == NULL)
        {
          pfsal_handle = &pentry->object.dir.handle;

          /* Call FSAL to get the attributes */
          object_attributes.asked_attributes = pclient->attrmask;

          fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes);

          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);

              /* stats */
              (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++;

              if(fsal_status.major == ERR_FSAL_STALE)
                {
                  cache_inode_status_t kill_status;

                  LogEvent(COMPONENT_CACHE_INODE,
                           "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)",
                           pentry, __LINE__,fsal_status.major, fsal_status.minor );

                  if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                     CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_renew_entry: Could not kill entry %p, status = %u",
                            pentry, kill_status);

                  *pstatus = CACHE_INODE_FSAL_ESTALE;
                }

              LogDebug(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for directory entries (1)",
                       *pstatus, cache_inode_err_str(*pstatus));
              return *pstatus;
            }
        }

      cache_inode_set_attributes(pentry, &object_attributes);

      /* Return the attributes as set */
      if(pattr != NULL)
        *pattr = object_attributes;

      /* Set the directory content as "to be renewed" */
      /* Next call to cache_inode_readdir will repopulate the dirent array */
      pentry->object.dir.has_been_readdir = CACHE_INODE_RENEW_NEEDED;

      /* Set the refresh time for the cache entry */
      pentry->internal_md.refresh_time = time(NULL);

    }

  /* if( pentry->internal_md.type == DIRECTORY && ... */
  /* if the directory has not been readdir, only update its attributes */
  else if(pentry->internal_md.type == DIRECTORY &&
          pclient->expire_type_attr != CACHE_INODE_EXPIRE_NEVER &&
          pentry->object.dir.has_been_readdir != CACHE_INODE_YES &&
	  ((current_time - entry_time >= pclient->grace_period_attr) || (pentry->internal_md.valid_state == STALE)))
    {
      /* Would be better if state was a flag that we could and/or the bits but
       * in any case we need to get rid of stale so we only go through here
       * once.
       */
      if ( pentry->internal_md.valid_state == STALE )
         pentry->internal_md.valid_state = VALID;

      /* stats */
      (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++;

      /* Log */
      LogDebug(COMPONENT_CACHE_INODE,
	       "Case 2: cached directory entries for entry %p must be renewed (has not been readdir)",
               pentry);

      if(isFullDebug(COMPONENT_CACHE_INODE))
        {
          char name[1024];
	  struct avltree_node *d_node;
	  cache_inode_dir_entry_t *d_dirent;
	  int i = 0;
	  
	  d_node = avltree_first(&pentry->object.dir.dentries);
      	  do {
              d_dirent = avltree_container_of(d_node, cache_inode_dir_entry_t,
					      node_n);
	      if (d_dirent->pentry->internal_md.valid_state == VALID) {
	          FSAL_name2str(&(d_dirent->name), name, 1023);
                  LogDebug(COMPONENT_CACHE_INODE,
                           "cache_inode_renew_entry: Entry %d %s",
                           i, name);
	      }
	      i++;
          } while ((d_node = avltree_next(d_node)));
        }

      pfsal_handle = &pentry->object.dir.handle;

      /* Call FSAL to get the attributes */
      object_attributes.asked_attributes = pclient->attrmask;

      fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes);

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          /* stats */
          (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++;

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)",
                       pentry, __LINE__,fsal_status.major, fsal_status.minor );

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_renew_entry: Could not kill entry %p, status = %u",
                         pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for directory entries (2)",
                   *pstatus, cache_inode_err_str(*pstatus));
          return *pstatus;
        }

      cache_inode_set_attributes(pentry, &object_attributes);

      /* Return the attributes as set */
      if(pattr != NULL)
        *pattr = object_attributes;

      /* Set the refresh time for the cache entry */
      pentry->internal_md.refresh_time = time(NULL);

    }

  /* else if( pentry->internal_md.type == DIRECTORY && ... */
  /* Check for attributes expiration in other cases */
  else if(pentry->internal_md.type != DIRECTORY &&
          pclient->expire_type_attr != CACHE_INODE_EXPIRE_NEVER &&
	  ((current_time - entry_time >= pclient->grace_period_attr)
	   || (pentry->internal_md.valid_state == STALE)))
    {
      /* Would be better if state was a flag that we could and/or the bits but
       * in any case we need to get rid of stale so we only go through here
       * once.
       */
      if ( pentry->internal_md.valid_state == STALE )
	pentry->internal_md.valid_state = VALID;
      
      /* stats */
      (pclient->stat.func_stats.nb_call[CACHE_INODE_RENEW_ENTRY])++;

      /* Log */
      LogDebug(COMPONENT_CACHE_INODE,
               "Attributes for entry %p must be renewed", pentry);

      switch (pentry->internal_md.type)
        {
        case REGULAR_FILE:
          pfsal_handle = &pentry->object.file.handle;
          break;

        case SYMBOLIC_LINK:
          assert(pentry->object.symlink);
          pfsal_handle = &pentry->object.symlink->handle;
          break;

        case SOCKET_FILE:
        case FIFO_FILE:
        case CHARACTER_FILE:
        case BLOCK_FILE:
          pfsal_handle = &pentry->object.special_obj.handle;
          break;

        case DIRECTORY:
        case FS_JUNCTION:
        case UNASSIGNED:
        case RECYCLED:
          LogCrit(COMPONENT_CACHE_INODE,
                  "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s",
                  pentry->internal_md.type, __LINE__, __FILE__);
          *pstatus = CACHE_INODE_BAD_TYPE;
          return *pstatus;
        }

      /* Call FSAL to get the attributes */
      object_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
      fsal_status = FSAL_getattrs_descriptor(&(cache_inode_fd(pentry)->fsal_file), pfsal_handle, pcontext, &object_attributes);
#else
      fsal_status = FSAL_getattrs_descriptor(cache_inode_fd(pentry), pfsal_handle, pcontext, &object_attributes);
#endif
      if(FSAL_IS_ERROR(fsal_status) && fsal_status.major == ERR_FSAL_NOT_OPENED)
        {
          //TODO: LOOKATME !!!!!
          fsal_status = FSAL_getattrs(pfsal_handle, pcontext, &object_attributes);
        }
      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          /* stats */
          (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY])++;

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)",
                       pentry, __LINE__,fsal_status.major, fsal_status.minor );

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_renew_entry: Could not kill entry %p, status = %u",
                        pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_renew_entry returning %d (%s) from FSAL_getattrs for non directories",
                   *pstatus, cache_inode_err_str(*pstatus));
          return *pstatus;
        }

      /* Keep the new attribute in cache */
      cache_inode_set_attributes(pentry, &object_attributes);

      /* Return the attributes as set */
      if(pattr != NULL)
        *pattr = object_attributes;

      /* Set the refresh time for the cache entry */
      pentry->internal_md.refresh_time = time(NULL);

    }

  /* if(  pentry->internal_md.type   != DIR_CONTINUE && ... */
  /* Check for link content expiration */
  if(pentry->internal_md.type == SYMBOLIC_LINK &&
     pclient->expire_type_link != CACHE_INODE_EXPIRE_NEVER &&
     ((current_time - entry_time >= pclient->grace_period_link)
      || (pentry->internal_md.valid_state == STALE)))
    {
      assert(pentry->object.symlink);
      pfsal_handle = &pentry->object.symlink->handle;
      /* Would be better if state was a flag that we could and/or the bits but
       * in any case we need to get rid of stale so we only go through here
       * once.
       */
      if ( pentry->internal_md.valid_state == STALE )
	pentry->internal_md.valid_state = VALID;

      assert(pentry->object.symlink);
      pfsal_handle = &pentry->object.symlink->handle;

      /* Log */
      LogDebug(COMPONENT_CACHE_INODE,
               "cached link content for entry %p must be renewed", pentry);

      FSAL_CLEAR_MASK(object_attributes.asked_attributes);
      FSAL_SET_MASK(object_attributes.asked_attributes, pclient->attrmask);

      if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) )
       {
#ifdef _USE_MFSL
      fsal_status =
          MFSL_readlink(&pentry->mobject, pcontext, &pclient->mfsl_context, &link_content,
                        &object_attributes, NULL);
#else
      fsal_status =
          FSAL_readlink(pfsal_handle, pcontext, &link_content, &object_attributes);
#endif
        }
      else
        { 
          fsal_status.major = ERR_FSAL_NO_ERROR ;
          fsal_status.minor = 0 ;
        }

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY] += 1;

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry: Stale FSAL File Handle detected for pentry = %p, line %u, fsal_status=(%u,%u)",
                       pentry, __LINE__,fsal_status.major, fsal_status.minor );

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                       "cache_inode_renew_entry: Could not kill entry %p, status = %u",
                       pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

        }
      else
        {
	  assert(pentry->object.symlink);
          fsal_status = FSAL_pathcpy(&pentry->object.symlink->content, &link_content); /* copy ctor? */
          if(FSAL_IS_ERROR(fsal_status))
            {
              *pstatus = cache_inode_error_convert(fsal_status);
              /* stats */
              pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_RENEW_ENTRY] += 1;
            }
        }

      /* Set the refresh time for the cache entry */
      pentry->internal_md.refresh_time = time(NULL);

    }

  /* if( pentry->internal_md.type == SYMBOLIC_LINK && ... */
  LogDebug(COMPONENT_CACHE_INODE, "cache_inode_renew_entry returning %d (%s)",
           *pstatus, cache_inode_err_str(*pstatus));
  return *pstatus;
}                               /* cache_inode_renew_entry */
Ejemplo n.º 23
0
/**
 *
 * cache_inode_lookupp_sw: looks up (and caches) the parent directory for a directory. A switches tells is mutex are use.
 * 
 * Looks up (and caches) the parent directory for a directory.
 *
 * @param pentry [IN] entry whose parent is to be obtained.
 * @param ht [IN] hash table used for the cache, unused in this call.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials 
 * @param pstatus [OUT] returned status.
 * @param use_mutex [IN] if TRUE mutex are use, not otherwise.
 * 
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry
 *
 */
cache_entry_t *cache_inode_lookupp_sw(cache_entry_t * pentry,
                                      hash_table_t * ht,
                                      cache_inode_client_t * pclient,
                                      fsal_op_context_t * pcontext,
                                      cache_inode_status_t * pstatus, int use_mutex)
{
  cache_entry_t *pentry_parent = NULL;
  fsal_status_t fsal_status;
  fsal_attrib_list_t object_attributes;
  cache_inode_fsal_data_t fsdata;

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

  /* stats */
  pclient->stat.nb_call_total += 1;
  pclient->stat.func_stats.nb_call[CACHE_INODE_LOOKUP] += 1;

  /* The entry should be a directory */
  if(use_mutex)
    P_r(&pentry->lock);
  if(pentry->internal_md.type != DIR_BEGINNING)
    {
      if(use_mutex)
        V_r(&pentry->lock);
      *pstatus = CACHE_INODE_BAD_TYPE;

      /* stats */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1;

      return NULL;
    }

  /* Renew the entry (to avoid having it being garbagged */
  if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) !=
     CACHE_INODE_SUCCESS)
    {
      pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR] += 1;
      return NULL;
    }

  /* Does the parent belongs to the cache ? */
  if(pentry->parent_list && pentry->parent_list->parent)
    {
      /* YES, the parent is cached, use the pentry that we have found */
      pentry_parent = pentry->parent_list->parent;
    }
  else
    {
      /* NO, the parent is not cached, query FSAL to get it and cache the result */
      object_attributes.asked_attributes = pclient->attrmask;
      fsal_status =
          FSAL_lookup(&pentry->object.dir_begin.handle, (fsal_name_t *) & FSAL_DOT_DOT,
                      pcontext, &fsdata.handle, &object_attributes);

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);
          if(use_mutex)
            V_r(&pentry->lock);

          /* Stale File Handle to be detected and managed */
          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,"cache_inode_lookupp: Stale FSAL FH detected for pentry %p",
                         pentry);

              if(cache_inode_kill_entry(pentry, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,"cache_inode_remove: Could not kill entry %p, status = %u",
                           pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1;

          return NULL;
        }

      /* Call cache_inode_get to populate the cache with the parent entry */
      fsdata.cookie = 0;

      if((pentry_parent = cache_inode_get(&fsdata,
                                          &object_attributes,
                                          ht, pclient, pcontext, pstatus)) == NULL)
        {
          if(use_mutex)
            V_r(&pentry->lock);

          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LOOKUPP] += 1;

          return NULL;
        }
    }

  *pstatus = cache_inode_valid(pentry_parent, CACHE_INODE_OP_GET, pclient);
  if(use_mutex)
    V_r(&pentry->lock);

  /* stat */
  if(*pstatus != CACHE_INODE_SUCCESS)
    pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_LOOKUPP] += 1;
  else
    pclient->stat.func_stats.nb_success[CACHE_INODE_LOOKUPP] += 1;

  return pentry_parent;
}                               /* cache_inode_lookupp_sw */
Ejemplo n.º 24
0
cache_inode_status_t
cache_inode_remove(cache_entry_t *entry, const char *name)
{
	cache_entry_t *to_remove_entry = NULL;
	fsal_status_t fsal_status = { 0, 0 };
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	cache_inode_status_t status_ref_entry = CACHE_INODE_SUCCESS;

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

	/* Factor this somewhat.  In the case where the directory hasn't
	   been populated, the entry may not exist in the cache and we'd
	   be bringing it in just to dispose of it. */

	/* Looks up for the entry to remove */
	status =
	    cache_inode_lookup_impl(entry, name, &to_remove_entry);

	if (to_remove_entry == NULL) {
		LogFullDebug(COMPONENT_CACHE_INODE, "lookup %s failure %s",
			     name, cache_inode_err_str(status));
		goto out;
	}

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

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

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

			status = CACHE_INODE_DIR_NOT_EMPTY;
			goto out;
		}

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

	LogDebug(COMPONENT_CACHE_INODE, "%s", name);

	if (is_open(to_remove_entry)) {
		/* entry is not locked and seems to be open for fd caching
		 * purpose.
		 * candidate for closing since unlink of an open file results
		 * in 'silly rename' on certain platforms */
		status =
		    cache_inode_close(to_remove_entry,
				      CACHE_INODE_FLAG_REALLYCLOSE);
		if (status != CACHE_INODE_SUCCESS) {
			/* non-fatal error. log the warning and move on */
			LogCrit(COMPONENT_CACHE_INODE,
				"Error closing %s before unlink: %s.", name,
				cache_inode_err_str(status));
		}
	}

	fsal_status =
	    entry->obj_handle->obj_ops.unlink(entry->obj_handle, name);

	if (FSAL_IS_ERROR(fsal_status)) {
		if (fsal_status.major == ERR_FSAL_STALE)
			cache_inode_kill_entry(entry);

		status = cache_inode_error_convert(fsal_status);

		LogFullDebug(COMPONENT_CACHE_INODE, "unlink %s failure %s",
			     name, cache_inode_err_str(status));

		if (to_remove_entry->type == DIRECTORY
		    && status == CACHE_INODE_DIR_NOT_EMPTY) {
			/* its dirent tree is probably stale, flush it
			 * to try and make things right again */
			PTHREAD_RWLOCK_wrlock(&to_remove_entry->content_lock);
			(void)
			    cache_inode_invalidate_all_cached_dirent
			    (to_remove_entry);
			PTHREAD_RWLOCK_unlock(&to_remove_entry->content_lock);
		}
		goto out;
	}

	/* Remove the entry from parent dir_entries avl */
	PTHREAD_RWLOCK_wrlock(&entry->content_lock);
	status_ref_entry = cache_inode_remove_cached_dirent(entry, name);
	LogDebug(COMPONENT_CACHE_INODE,
		 "cache_inode_remove_cached_dirent %s status %s", name,
		 cache_inode_err_str(status_ref_entry));
	PTHREAD_RWLOCK_unlock(&entry->content_lock);

	status_ref_entry = cache_inode_refresh_attrs_locked(entry);

	if (FSAL_IS_ERROR(fsal_status)) {
		status = cache_inode_error_convert(fsal_status);
		LogFullDebug(COMPONENT_CACHE_INODE,
			     "not sure this code makes sense %s failure %s",
			     name, cache_inode_err_str(status));
		goto out;
	}

	/* Update the attributes for the removed entry */
	(void)cache_inode_refresh_attrs_locked(to_remove_entry);

	status = status_ref_entry;
	if (status != CACHE_INODE_SUCCESS) {
		LogDebug(COMPONENT_CACHE_INODE,
			 "cache_inode_refresh_attrs_locked(entry %p %s) "
			 "returned %s", entry, name,
			 cache_inode_err_str(status_ref_entry));
	}

out:
	LogFullDebug(COMPONENT_CACHE_INODE, "remove %s: status=%s", name,
		     cache_inode_err_str(status));

	/* This is for the reference taken by lookup */
	if (to_remove_entry)
		cache_inode_put(to_remove_entry);

	return status;
}
Ejemplo n.º 25
0
cache_inode_status_t cache_inode_readlink(cache_entry_t * pentry, fsal_path_t * plink_content, hash_table_t * ht,       /* Unused, kept for protototype's homogeneity */
                                          cache_inode_client_t * pclient,
                                          fsal_op_context_t * pcontext,
                                          cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;
  fsal_attrib_list_t attr ;

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

  /* stats */
  (pclient->stat.nb_call_total)++;
  (pclient->stat.func_stats.nb_call[CACHE_INODE_READLINK])++;

  /* Lock the entry */
  P_w(&pentry->lock);
  if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) !=
     CACHE_INODE_SUCCESS)
    {
      (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK])++;
      V_w(&pentry->lock);
      return *pstatus;
    }
  /* RW_Lock obtained as writer turns to reader */
  rw_lock_downgrade(&pentry->lock);

  switch (pentry->internal_md.type)
    {
    case REGULAR_FILE:
    case DIRECTORY:
    case CHARACTER_FILE:
    case BLOCK_FILE:
    case SOCKET_FILE:
    case FIFO_FILE:
    case UNASSIGNED:
    case FS_JUNCTION:
    case RECYCLED:
      *pstatus = CACHE_INODE_BAD_TYPE;
      V_r(&pentry->lock);

      /* stats */
      pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1;

      return *pstatus;
      break;

    case SYMBOLIC_LINK:
      assert(pentry->object.symlink);
      if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) )
        {
          fsal_status = FSAL_pathcpy(plink_content, &(pentry->object.symlink->content)); /* need copy ctor? */
        }
      else
        {
           /* Content is not cached, call FSAL_readlink here */
           fsal_status = FSAL_readlink( &pentry->handle, pcontext, plink_content, &attr ) ; 
        }

      if(FSAL_IS_ERROR(fsal_status))
        {
          *pstatus = cache_inode_error_convert(fsal_status);
          V_r(&pentry->lock);

          if(fsal_status.major == ERR_FSAL_STALE)
            {
              cache_inode_status_t kill_status;

              LogEvent(COMPONENT_CACHE_INODE,
                       "cache_inode_readlink: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)",
                       pentry, fsal_status.major, fsal_status.minor);

              if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) !=
                 CACHE_INODE_SUCCESS)
                LogCrit(COMPONENT_CACHE_INODE,
                        "cache_inode_readlink: Could not kill entry %p, status = %u",
                        pentry, kill_status);

              *pstatus = CACHE_INODE_FSAL_ESTALE;
            }
          /* stats */
          pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1;

          return *pstatus;
        }

      break;
    }

  /* Release the entry */
  *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient);
  V_r(&pentry->lock);

  /* stat */
  if(*pstatus != CACHE_INODE_SUCCESS)
    pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK] += 1;
  else
    pclient->stat.func_stats.nb_success[CACHE_INODE_READLINK] += 1;

  return *pstatus;
}                               /* cache_inode_readlink */
Ejemplo n.º 26
0
/**
 *
 * cache_inode_link: hardlinks a pentry to another.
 *
 * Hard links a pentry to another. This is basically a equivalent of FSAL_link in the cache inode layer.
 *
 * @param pentry_src [IN] entry pointer the entry to be linked. This can't be a directory.
 * @param pentry_dir_dest [INOUT] entry pointer for the destination directory in which the link will be created.
 * @param plink_name [IN] pointer to the name of the object in the destination directory.
 * @param pattr [OUT] attributes for the linked attributes after the operation.
 * @param ht [INOUT] hash table used for the cache.
 * @param pclient [INOUT] ressource allocated by the client for the nfs management.
 * @param pcontext [IN] FSAL credentials
 * @param pstatus [OUT] returned status.
 *
 * @return CACHE_INODE_SUCCESS if operation is a success \n
 * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry\n
 * @return CACHE_INODE_BAD_TYPE either source or destination have incorrect type\n
 * @return CACHE_INODE_ENTRY_EXISTS entry of that name already exists in destination.
 *
 */
cache_inode_status_t cache_inode_link(cache_entry_t * pentry_src,
                                      cache_entry_t * pentry_dir_dest,
                                      fsal_name_t * plink_name,
                                      fsal_attrib_list_t * pattr,
                                      hash_table_t * ht,
                                      cache_inode_client_t * pclient,
                                      fsal_op_context_t * pcontext,
                                      cache_inode_status_t * pstatus)
{
    fsal_status_t fsal_status;
    fsal_handle_t handle_src;
    fsal_handle_t handle_dest;
    fsal_attrib_list_t link_attributes;
#ifdef _USE_MFSL
    fsal_attrib_list_t dirdest_attributes;
#endif
    cache_inode_status_t status;
    cache_entry_t *pentry_lookup = NULL;
    fsal_attrib_list_t lookup_attributes;

    fsal_size_t save_size = 0;
    fsal_size_t save_spaceused = 0;
    fsal_time_t save_mtime = {
        .seconds = 0,
        .nseconds = 0
    };

    fsal_accessflags_t access_mask = 0;

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

    /* stats */
    pclient->stat.nb_call_total += 1;
    pclient->stat.func_stats.nb_call[CACHE_INODE_LINK] += 1;

    /* Is the destination a directory ? */
    if(pentry_dir_dest->internal_md.type != DIR_BEGINNING &&
            pentry_dir_dest->internal_md.type != DIR_CONTINUE)
    {
        /* Bad type .... */
        *pstatus = CACHE_INODE_BAD_TYPE;
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;

        return *pstatus;
    }

    /* Check if caller is allowed to perform the operation */
    access_mask = FSAL_MODE_MASK_SET(FSAL_W_OK) |
                  FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE);
    if((status = cache_inode_access(pentry_dir_dest,
                                    access_mask,
                                    ht, pclient, pcontext, &status)) != CACHE_INODE_SUCCESS)
    {
        *pstatus = status;

        /* stats */
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;

        /* pentry is a directory */
        return *pstatus;
    }

    /* Check if an entry of the same name doesn't exist in the destination directory */
    if((pentry_lookup = cache_inode_lookup(pentry_dir_dest,
                                           plink_name,
                                           &lookup_attributes,
                                           ht, pclient, pcontext, pstatus)) != NULL)
    {
        /* There exists such an entry... */
        *pstatus = CACHE_INODE_ENTRY_EXISTS;
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;

        return *pstatus;
    }

    /* The pentry to be hardlinked can't be a DIR_BEGINNING or a DIR_CONTINUE */
    if(pentry_src->internal_md.type == DIR_BEGINNING ||
            pentry_src->internal_md.type == DIR_CONTINUE)
    {
        /* Bad type .... */
        *pstatus = CACHE_INODE_BAD_TYPE;
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;

        return *pstatus;
    }

#if 0
    if( pentry_src->internal_md.type == REGULAR_FILE )
        printf( "=== link === %p | inode=%llu\n", pentry_src, pentry_src->object.file.attributes.fileid ) ;
#endif

    /* At this point, we know that the entry does not exist in destination directory, we know that the
     * destination is actually a directory and that the source is no directory */

    /* Lock the source */
    P_w(&pentry_src->lock);

    /* Lock the target dir */
    P_w(&pentry_dir_dest->lock);

    /* Get the handles */
    switch (pentry_src->internal_md.type)
    {
    case REGULAR_FILE:
        handle_src = pentry_src->object.file.handle;
        break;

    case SYMBOLIC_LINK:
        handle_src = pentry_src->object.symlink.handle;
        break;

    case FS_JUNCTION:
    case DIR_BEGINNING:
        handle_src = pentry_src->object.dir_begin.handle;
        break;

    case DIR_CONTINUE:
        /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont)
         * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */
        P_r(&pentry_src->object.dir_cont.pdir_begin->lock);
        handle_src = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle;
        V_r(&pentry_src->object.dir_cont.pdir_begin->lock);
        break;

    case CHARACTER_FILE:
    case BLOCK_FILE:
    case SOCKET_FILE:
    case FIFO_FILE:
        handle_src = pentry_src->object.special_obj.handle;
        break;

    case UNASSIGNED:
    case RECYCLED:
        LogCrit(COMPONENT_CACHE_INODE,
                "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s",
                pentry_src->internal_md.type, __LINE__, __FILE__);
        *pstatus = CACHE_INODE_BAD_TYPE;
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;
        return *pstatus;
    }

    switch (pentry_dir_dest->internal_md.type)
    {
    case FS_JUNCTION:
    case DIR_BEGINNING:
        handle_dest = pentry_dir_dest->object.dir_begin.handle;
        break;

    case DIR_CONTINUE:
        /* lock the related dir_begin (dir begin are garbagge collected AFTER their related dir_cont)
         * this means that if a DIR_CONTINUE exists, its pdir pointer is not endless */
        P_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock);
        handle_dest = pentry_src->object.dir_cont.pdir_begin->object.dir_begin.handle;
        V_r(&pentry_dir_dest->object.dir_cont.pdir_begin->lock);
        break;

    default:
        LogCrit(COMPONENT_CACHE_INODE,
                "WARNING: unknown source pentry type: internal_md.type=%d, line %d in file %s",
                pentry_src->internal_md.type, __LINE__, __FILE__);
        *pstatus = CACHE_INODE_BAD_TYPE;
        pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_LINK] += 1;
        return *pstatus;
    }

    /* If object is a data cached regular file, keeps it mtime and size, STEP 1 */
    if((pentry_src->internal_md.type == REGULAR_FILE)
            && (pentry_src->object.file.pentry_content != NULL))
    {
        save_mtime = pentry_src->object.file.attributes.mtime;
        save_size = pentry_src->object.file.attributes.filesize;
        save_spaceused = pentry_src->object.file.attributes.spaceused;
    }

    /* Do the link at FSAL level */
    link_attributes.asked_attributes = pclient->attrmask;
#ifdef _USE_MFSL
    cache_inode_get_attributes(pentry_src, &link_attributes);
    cache_inode_get_attributes(pentry_dir_dest, &dirdest_attributes);
    fsal_status =
        MFSL_link(&pentry_src->mobject, &pentry_dir_dest->mobject, plink_name, pcontext,
                  &pclient->mfsl_context, &link_attributes, NULL);
#else
    fsal_status =
        FSAL_link(&handle_src, &handle_dest, plink_name, pcontext, &link_attributes);
#endif
    if(FSAL_IS_ERROR(fsal_status))
    {
        *pstatus = cache_inode_error_convert(fsal_status);
        V_w(&pentry_dir_dest->lock);
        V_w(&pentry_src->lock);

        if(fsal_status.major == ERR_FSAL_STALE)
        {
            cache_inode_status_t kill_status;
            fsal_status_t getattr_status;

            LogEvent(COMPONENT_CACHE_INODE,
                     "cache_inode_link: Stale FSAL File Handle detected for at least one in pentry = %p and pentry = %p",
                     pentry_src, pentry_dir_dest);

            /* Use FSAL_getattrs to find which entry is staled */
            getattr_status = FSAL_getattrs(&handle_src, pcontext, &link_attributes);
            if(getattr_status.major == ERR_FSAL_ACCESS)
            {
                LogEvent(COMPONENT_CACHE_INODE,
                         "cache_inode_link: Stale FSAL File Handle detected for pentry = %p",
                         pentry_src);

                if(cache_inode_kill_entry(pentry_src, ht, pclient, &kill_status) !=
                        CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_link: Could not kill entry %p, status = %u",
                            pentry_src, kill_status);
            }

            getattr_status = FSAL_getattrs(&handle_dest, pcontext, &link_attributes);
            if(getattr_status.major == ERR_FSAL_ACCESS)
            {
                LogEvent(COMPONENT_CACHE_INODE,
                         "cache_inode_link: Stale FSAL File Handle detected for pentry = %p",
                         pentry_dir_dest);

                if(cache_inode_kill_entry(pentry_dir_dest, ht, pclient, &kill_status) !=
                        CACHE_INODE_SUCCESS)
                    LogCrit(COMPONENT_CACHE_INODE,
                            "cache_inode_link: Could not kill entry %p, status = %u",
                            pentry_dir_dest, kill_status);
            }

        }
        *pstatus = CACHE_INODE_FSAL_ESTALE;

        return *pstatus;
    }

    /* If object is a data cached regular file, keeps it mtime and size, STEP 2 */
    if((pentry_src->internal_md.type == REGULAR_FILE)
            && (pentry_src->object.file.pentry_content != NULL))
    {
        link_attributes.mtime = save_mtime;
        link_attributes.filesize = save_size;
        link_attributes.spaceused = save_spaceused;
    }

    /* Update cached attributes */
    cache_inode_set_attributes(pentry_src, &link_attributes);

    /* Add the new entry in the destination directory */
    if(cache_inode_add_cached_dirent(pentry_dir_dest,
                                     plink_name,
                                     pentry_src,
                                     NULL,
                                     ht, pclient, pcontext, &status) != CACHE_INODE_SUCCESS)
    {
        V_w(&pentry_dir_dest->lock);
        V_w(&pentry_src->lock);
        return *pstatus;
    }

    /* Regular exit */

    /* return the attributes */
    *pattr = link_attributes;

    /* Validate the entries */
    *pstatus = cache_inode_valid(pentry_src, CACHE_INODE_OP_SET, pclient);

    /* Release the target dir */
    V_w(&pentry_dir_dest->lock);

    /* Release the source */
    V_w(&pentry_src->lock);

    /* stats */
    if(*pstatus != CACHE_INODE_SUCCESS)
        pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_LINK] += 1;
    else
        pclient->stat.func_stats.nb_success[CACHE_INODE_LINK] += 1;

    return *pstatus;
}