Esempio n. 1
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;
}
Esempio n. 2
0
int init_server_pkgs(void)
{
	cache_inode_status_t cache_status;
	state_status_t state_status;

	/* init uid2grp cache */
	uid2grp_cache_init();

	/* Cache Inode Initialisation */
	cache_status = cache_inode_init();
	if (cache_status != CACHE_INODE_SUCCESS) {
		LogCrit(COMPONENT_INIT,
			"Cache Inode Layer could not be initialized, status=%s",
			cache_inode_err_str(cache_status));
		return -1;
	}

	state_status = state_lock_init();
	if (state_status != STATE_SUCCESS) {
		LogCrit(COMPONENT_INIT,
			"State Lock Layer could not be initialized, status=%s",
			state_err_str(state_status));
		return -1;
	}
	LogInfo(COMPONENT_INIT, "Cache Inode library successfully initialized");

	/* Init the IP/name cache */
	LogDebug(COMPONENT_INIT, "Now building IP/name cache");
	if (nfs_Init_ip_name() != IP_NAME_SUCCESS) {
		LogCrit(COMPONENT_INIT,
			"Error while initializing IP/name cache");
		return -1;
	}
	LogInfo(COMPONENT_INIT, "IP/name cache successfully initialized");

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

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

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

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

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

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

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

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

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

			status = CACHE_INODE_DIR_NOT_EMPTY;
			goto out;
		}

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


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

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

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

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

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

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

	status_ref_dir_src = cache_inode_refresh_attrs_locked(dir_src);

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

	LogFullDebug(COMPONENT_CACHE_INODE, "done refreshing attributes");

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

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

		goto out;
	}

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

	status = status_ref_dir_src;

	if (status == CACHE_INODE_SUCCESS)
		status = status_ref_dir_dst;

	if (status == CACHE_INODE_SUCCESS)
		status = status_ref_dst;

	if (status != CACHE_INODE_SUCCESS)
		goto out;

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

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

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

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

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

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

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

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

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

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

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

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

out:
	if (lookup_src)
		cache_inode_put(lookup_src);

	if (lookup_dst)
		cache_inode_put(lookup_dst);

	return status;
}
Esempio n. 4
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) */
Esempio n. 5
0
static void nfs_Init(const nfs_start_info_t *p_start_info)
{
	cache_inode_status_t cache_status;
	state_status_t state_status;
	int rc = 0;
#ifdef _HAVE_GSSAPI
	gss_buffer_desc gss_service_buf;
	OM_uint32 maj_stat, min_stat;
	char GssError[MAXNAMLEN + 1];
#endif

#ifdef USE_DBUS
	/* DBUS init */
	gsh_dbus_pkginit();
#ifdef USE_DBUS_STATS
	dbus_export_init();
	dbus_client_init();
#endif
#endif

	/* Cache Inode Initialisation */
	cache_status = cache_inode_init();
	if (cache_status != CACHE_INODE_SUCCESS) {
		LogFatal(COMPONENT_INIT,
			 "Cache Inode Layer could not be initialized, status=%s",
			 cache_inode_err_str(cache_status));
	}

	state_status = state_lock_init(nfs_param.cache_param.cookie_param);
	if (state_status != STATE_SUCCESS) {
		LogFatal(COMPONENT_INIT,
			 "State Lock Layer could not be initialized, status=%s",
			 state_err_str(state_status));
	}
	LogInfo(COMPONENT_INIT, "Cache Inode library successfully initialized");

	/* Cache Inode LRU (call this here, rather than as part of
	   cache_inode_init() so the GC policy has been set */
	rc = cache_inode_lru_pkginit();
	if (rc != 0) {
		LogFatal(COMPONENT_INIT,
			 "Unable to initialize LRU subsystem: %d.", rc);
	}

	/* finish the job with exports by caching the root entries
	 */
	exports_pkginit();

	nfs41_session_pool =
	    pool_init("NFSv4.1 session pool", sizeof(nfs41_session_t),
		      pool_basic_substrate, NULL, NULL, NULL);

	request_pool =
	    pool_init("Request pool", sizeof(request_data_t),
		      pool_basic_substrate, NULL,
		      NULL /* FASTER constructor_request_data_t */ ,
		      NULL);
	if (!request_pool) {
		LogCrit(COMPONENT_INIT, "Error while allocating request pool");
		LogError(COMPONENT_INIT, ERR_SYS, ERR_MALLOC, errno);
		Fatal();
	}

	request_data_pool =
	    pool_init("Request Data Pool", sizeof(nfs_request_data_t),
		      pool_basic_substrate, NULL,
		      NULL /* FASTER constructor_nfs_request_data_t */ ,
		      NULL);
	if (!request_data_pool) {
		LogCrit(COMPONENT_INIT,
			"Error while allocating request data pool");
		LogError(COMPONENT_INIT, ERR_SYS, ERR_MALLOC, errno);
		Fatal();
	}

	dupreq_pool =
	    pool_init("Duplicate Request Pool", sizeof(dupreq_entry_t),
		      pool_basic_substrate, NULL, NULL, NULL);
	if (!(dupreq_pool)) {
		LogCrit(COMPONENT_INIT,
			"Error while allocating duplicate request pool");
		LogError(COMPONENT_INIT, ERR_SYS, ERR_MALLOC, errno);
		Fatal();
	}
#ifdef _USE_ASYNC_CACHE_INODE
	/* Start the TAD and synclets for writeback cache inode */
	cache_inode_async_init(nfs_param.cache_layers_param.
			       cache_inode_client_param);
#endif

	/* If rpcsec_gss is used, set the path to the keytab */
#ifdef _HAVE_GSSAPI
#ifdef HAVE_KRB5
	if (nfs_param.krb5_param.active_krb5) {
		OM_uint32 gss_status = GSS_S_COMPLETE;

		if (nfs_param.krb5_param.keytab[0] != '\0')
			gss_status =
			    krb5_gss_register_acceptor_identity(nfs_param.
								krb5_param.
								keytab);

		if (gss_status != GSS_S_COMPLETE) {
			log_sperror_gss(GssError, gss_status, 0);
			LogFatal(COMPONENT_INIT,
				 "Error setting krb5 keytab to value %s is %s",
				 nfs_param.krb5_param.keytab, GssError);
		}
		LogInfo(COMPONENT_INIT,
			"krb5 keytab path successfully set to %s",
			nfs_param.krb5_param.keytab);
#endif				/* HAVE_KRB5 */

		/* Set up principal to be use for GSSAPPI within GSSRPC/KRB5 */
		gss_service_buf.value = nfs_param.krb5_param.svc.principal;
		gss_service_buf.length =
			strlen(nfs_param.krb5_param.svc.principal) + 1;
		/* The '+1' is not to be forgotten, for the '\0' at the end */

		maj_stat = gss_import_name(&min_stat, &gss_service_buf,
					   (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
					   &nfs_param.krb5_param.svc.gss_name);
		if (maj_stat != GSS_S_COMPLETE) {
			log_sperror_gss(GssError, maj_stat, min_stat);
			LogFatal(COMPONENT_INIT,
				 "Error importing gss principal %s is %s",
				 nfs_param.krb5_param.svc.principal, GssError);
		}

		if (nfs_param.krb5_param.svc.gss_name == GSS_C_NO_NAME)
			LogInfo(COMPONENT_INIT,
				"Regression:  svc.gss_name == GSS_C_NO_NAME");

		LogInfo(COMPONENT_INIT, "gss principal \"%s\" successfully set",
			nfs_param.krb5_param.svc.principal);

		/* Set the principal to GSSRPC */
		if (!svcauth_gss_set_svc_name
		    (nfs_param.krb5_param.svc.gss_name)) {
			LogFatal(COMPONENT_INIT,
				 "Impossible to set gss principal to GSSRPC");
		}

		/* Don't release name until shutdown, it will be used by the
		 * backchannel. */

#ifdef HAVE_KRB5
	}			/*  if( nfs_param.krb5_param.active_krb5 ) */
#endif				/* HAVE_KRB5 */
#endif				/* _HAVE_GSSAPI */

	/* RPC Initialisation - exits on failure */
	nfs_Init_svc();
	LogInfo(COMPONENT_INIT, "RPC ressources successfully initialized");

	/* Admin initialisation */
	nfs_Init_admin_thread();

	LogEvent(COMPONENT_INIT, "Initializing ID Mapper.");
	if (!idmapper_init())
		LogFatal(COMPONENT_INIT, "Failed initializing ID Mapper.");
	else
		LogEvent(COMPONENT_INIT, "ID Mapper successfully initialized.");

	/* Init the NFSv4 Clientid cache */
	LogDebug(COMPONENT_INIT, "Now building NFSv4 clientid cache");
	if (nfs_Init_client_id(&nfs_param.client_id_param) !=
	    CLIENT_ID_SUCCESS) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing NFSv4 clientid cache");
	}
	LogInfo(COMPONENT_INIT,
		"NFSv4 clientid cache successfully initialized");

	/* Init duplicate request cache */
	dupreq2_pkginit();
	LogInfo(COMPONENT_INIT,
		"duplicate request hash table cache successfully initialized");

	/* Init the IP/name cache */
	LogDebug(COMPONENT_INIT, "Now building IP/name cache");
	if (nfs_Init_ip_name(nfs_param.ip_name_param) != IP_NAME_SUCCESS) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing IP/name cache");
	}
	LogInfo(COMPONENT_INIT, "IP/name cache successfully initialized");

	/* Init The NFSv4 State id cache */
	LogDebug(COMPONENT_INIT, "Now building NFSv4 State Id cache");
	if (nfs4_Init_state_id(&nfs_param.state_id_param) != 0) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing NFSv4 State Id cache");
	}
	LogInfo(COMPONENT_INIT,
		"NFSv4 State Id cache successfully initialized");

	/* Init The NFSv4 Open Owner cache */
	LogDebug(COMPONENT_INIT, "Now building NFSv4 Owner cache");
	if (Init_nfs4_owner(&nfs_param.nfs4_owner_param) != 0) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing NFSv4 Owner cache");
	}
	LogInfo(COMPONENT_INIT,
		"NFSv4 Open Owner cache successfully initialized");

	if (nfs_param.core_param.enable_NLM) {
		/* Init The NLM Owner cache */
		LogDebug(COMPONENT_INIT, "Now building NLM Owner cache");
		if (Init_nlm_hash() != 0) {
			LogFatal(COMPONENT_INIT,
				 "Error while initializing NLM Owner cache");
		}
		LogInfo(COMPONENT_INIT,
			"NLM Owner cache successfully initialized");
		nlm_init();
	}
#ifdef _USE_9P
	/* Init the 9P lock owner cache */
	LogDebug(COMPONENT_INIT, "Now building 9P Owner cache");
	if (Init_9p_hash() != 0) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing 9P Owner cache");
	}
	LogInfo(COMPONENT_INIT, "9P Owner cache successfully initialized");
#endif

	LogDebug(COMPONENT_INIT, "Now building NFSv4 Session Id cache");
	if (nfs41_Init_session_id(&nfs_param.session_id_param) != 0) {
		LogFatal(COMPONENT_INIT,
			 "Error while initializing NFSv4 Session Id cache");
	}
	LogInfo(COMPONENT_INIT,
		"NFSv4 Session Id cache successfully initialized");

	LogDebug(COMPONENT_INIT, "Now building NFSv4 ACL cache");
	if (nfs4_acls_init() != 0) {
		LogCrit(COMPONENT_INIT, "Error while initializing NFSv4 ACLs");
		exit(1);
	}
	LogInfo(COMPONENT_INIT, "NFSv4 ACL cache successfully initialized");

#ifdef _USE_9P
	LogDebug(COMPONENT_INIT, "Now building 9P resources");
	if (_9p_init(&nfs_param._9p_param)) {
		LogCrit(COMPONENT_INIT,
			"Error while initializing 9P Resources");
		exit(1);
	}
	LogInfo(COMPONENT_INIT, "9P resources successfully initialized");
#endif				/* _USE_9P */

	/* Creates the pseudo fs */
	LogDebug(COMPONENT_INIT, "Now building pseudo fs");
	rc = nfs4_ExportToPseudoFS();
	if (rc != 0)
		LogFatal(COMPONENT_INIT,
			 "Error %d while initializing NFSv4 pseudo file system",
			 rc);

	LogInfo(COMPONENT_INIT,
		"NFSv4 pseudo file system successfully initialized");

	/* Save Ganesha thread credentials with Frank's routine for later use */
	fsal_save_ganesha_credentials();

	/* Create stable storage directory, this needs to be done before
	 * starting the recovery thread.
	 */
	nfs4_create_recov_dir();

	/* initialize grace and read in the client IDs */
	nfs4_init_grace();
	nfs4_load_recov_clids(NULL);

	/* Start grace period */
	nfs4_start_grace(NULL);

	/* callback dispatch */
	nfs_rpc_cb_pkginit();
#ifdef _USE_CB_SIMULATOR
	nfs_rpc_cbsim_pkginit();
#endif				/*  _USE_CB_SIMULATOR */

}				/* nfs_Init */
/**
 *
 * cache_inode_close: closes the local fd in the FSAL.
 *
 * Closes the local fd in the FSAL.
 *
 * No lock management is done in this layer: the related pentry in the cache inode layer is
 * locked and will prevent from concurent accesses.
 *
 * @param pentry  [IN] entry in file content layer whose content is to be accessed.
 * @param pclient [IN]  ressource allocated by the client for the nfs management.
 * @pstatus       [OUT] returned status.
 *
 * @return CACHE_CONTENT_SUCCESS is successful .
 *
 */
cache_inode_status_t cache_inode_close(cache_entry_t * pentry,
                                       cache_inode_client_t * pclient,
                                       cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;

  if((pentry == NULL) || (pclient == NULL) || (pstatus == NULL))
    return CACHE_CONTENT_INVALID_ARGUMENT;

  if(pentry->internal_md.type != REGULAR_FILE)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* if nothing is opened, do nothing */
  if(pentry->object.file.open_fd.fileno < 0)
    {
      *pstatus = CACHE_INODE_SUCCESS;
      return *pstatus;
    }

  /* if locks are held in the file, do not close */
  if( cache_inode_file_holds_state( pentry ) )
    {
      *pstatus = CACHE_INODE_SUCCESS; /** @todo : PhD : May be CACHE_INODE_STATE_CONFLICTS would be better ? */
      return *pstatus;
    }

  if((pclient->use_fd_cache == 0) ||
     (time(NULL) - pentry->object.file.open_fd.last_op > pclient->retention) ||
     (pentry->object.file.open_fd.fileno > (int)(pclient->max_fd)))
    {

      LogDebug(COMPONENT_CACHE_INODE,
               "cache_inode_close: pentry %p, fileno = %d, lastop=%d ago",
               pentry, pentry->object.file.open_fd.fileno,
               (int)(time(NULL) - pentry->object.file.open_fd.last_op));

#ifdef _USE_MFSL
      fsal_status = MFSL_close(&(pentry->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_close(&(pentry->object.file.open_fd.fd));
#endif

      pentry->object.file.open_fd.fileno = 0;
      pentry->object.file.open_fd.last_op = 0;

      if(FSAL_IS_ERROR(fsal_status) && (fsal_status.major != ERR_FSAL_NOT_OPENED))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_close: returning %d(%s) from FSAL_close",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }
    }
#ifdef _USE_PROXY
  /* If proxy if used, free the name if needed */
  if(pentry->object.file.pname != NULL)
    {
      Mem_Free((char *)(pentry->object.file.pname));
      pentry->object.file.pname = NULL;
    }
  pentry->object.file.pentry_parent_open = NULL;
#endif

  *pstatus = CACHE_CONTENT_SUCCESS;

  return *pstatus;
}                               /* cache_content_close */
cache_inode_status_t cache_inode_open_by_name(cache_entry_t * pentry_dir,
                                              fsal_name_t * pname,
                                              cache_entry_t * pentry_file,
                                              cache_inode_client_t * pclient,
                                              fsal_openflags_t openflags,
                                              fsal_op_context_t * pcontext,
                                              cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;
  fsal_size_t save_filesize = 0;
  fsal_size_t save_spaceused = 0;
  fsal_time_t save_mtime = {
    .seconds = 0,
    .nseconds = 0
  };

  if((pentry_dir == NULL) || (pname == NULL) || (pentry_file == NULL) ||
     (pclient == NULL) || (pcontext == NULL) || (pstatus == NULL))
    return CACHE_INODE_INVALID_ARGUMENT;

  if((pentry_dir->internal_md.type != DIRECTORY))
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  if(pentry_file->internal_md.type != REGULAR_FILE)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* Open file need to be closed, unless it is already open as read/write */
  if((pentry_file->object.file.open_fd.openflags != FSAL_O_RDWR) &&
     (pentry_file->object.file.open_fd.openflags != 0) &&
     (pentry_file->object.file.open_fd.fileno >= 0) &&
     (pentry_file->object.file.open_fd.openflags != openflags))
    {
#ifdef _USE_MFSL
      fsal_status =
          MFSL_close(&(pentry_file->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_close(&(pentry_file->object.file.open_fd.fd));
#endif
      if(FSAL_IS_ERROR(fsal_status) && (fsal_status.major != ERR_FSAL_NOT_OPENED))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open_by_name: returning %d(%s) from FSAL_close",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

      pentry_file->object.file.open_fd.last_op = 0;
      pentry_file->object.file.open_fd.fileno = 0;
    }

  if(pentry_file->object.file.open_fd.last_op == 0
     || pentry_file->object.file.open_fd.fileno == 0)
    {
      LogDebug(COMPONENT_FSAL,
               "cache_inode_open_by_name: pentry %p: lastop=0", pentry_file);

      /* Keep coherency with the cache_content */
      if(pentry_file->object.file.pentry_content != NULL)
        {
          save_filesize = pentry_file->object.file.attributes.filesize;
          save_spaceused = pentry_file->object.file.attributes.spaceused;
          save_mtime = pentry_file->object.file.attributes.mtime;
        }

      /* opened file is not preserved yet */
#ifdef _USE_MFSL
      fsal_status = MFSL_open_by_name(&(pentry_dir->mobject),
                                      pname,
                                      pcontext,
                                      &pclient->mfsl_context,
                                      openflags,
                                      &pentry_file->object.file.open_fd.mfsl_fd,
                                      &(pentry_file->object.file.attributes),
#ifdef _USE_PNFS
                                      &pentry_file->object.file.pnfs_file ) ;
#else
                                      NULL );
#endif /* _USE_PNFS */

#else
      fsal_status = FSAL_open_by_name(&(pentry_dir->object.file.handle),
                                      pname,
                                      pcontext,
                                      openflags,
                                      &pentry_file->object.file.open_fd.fd,
                                      &(pentry_file->object.file.attributes));
#endif

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

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open_by_name: returning %d(%s) from FSAL_open_by_name",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

#ifdef _USE_PROXY

      /* If proxy if used, we should keep the name of the file to do FSAL_rcp if needed */
      if((pentry_file->object.file.pname =
          (fsal_name_t *) Mem_Alloc_Label(sizeof(fsal_name_t), "fsal_name_t")) == NULL)
        {
          *pstatus = CACHE_INODE_MALLOC_ERROR;

          return *pstatus;
        }

      pentry_file->object.file.pentry_parent_open = pentry_dir;
      pentry_file->object.file.pname->len = pname->len;
      memcpy((char *)(pentry_file->object.file.pname->name), (char *)(pname->name),
             FSAL_MAX_NAME_LEN);

#endif

      /* Keep coherency with the cache_content */
      if(pentry_file->object.file.pentry_content != NULL)
        {
          pentry_file->object.file.attributes.filesize = save_filesize;
          pentry_file->object.file.attributes.spaceused = save_spaceused;
          pentry_file->object.file.attributes.mtime = save_mtime;
        }

#ifdef _USE_MFSL
      pentry_file->object.file.open_fd.fileno =
          (int)FSAL_FILENO(&(pentry_file->object.file.open_fd.mfsl_fd.fsal_file));
#else
      pentry_file->object.file.open_fd.fileno =
          (int)FSAL_FILENO(&(pentry_file->object.file.open_fd.fd));
#endif
      pentry_file->object.file.open_fd.last_op = time(NULL);
      pentry_file->object.file.open_fd.openflags = openflags;

      LogDebug(COMPONENT_FSAL,
               "cache_inode_open_by_name: pentry %p: fd=%u",
               pentry_file, pentry_file->object.file.open_fd.fileno);

    }

  /* regular exit */
  pentry_file->object.file.open_fd.last_op = time(NULL);

  /* if file descriptor is too high, garbage collect FDs */
  if(pclient->use_fd_cache
     && (pentry_file->object.file.open_fd.fileno > pclient->max_fd))
    {
      if(cache_inode_gc_fd(pclient, pstatus) != CACHE_INODE_SUCCESS)
        {
          LogCrit(COMPONENT_CACHE_INODE_GC,
                  "FAILURE performing FD garbage collection");
          return *pstatus;
        }
    }

  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;

}                               /* cache_inode_open_by_name */
cache_inode_status_t cache_inode_open(cache_entry_t * pentry,
                                      cache_inode_client_t * pclient,
                                      fsal_openflags_t openflags,
                                      fsal_op_context_t * pcontext,
                                      cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;

  if((pentry == NULL) || (pclient == NULL) || (pcontext == NULL) || (pstatus == NULL))
    return CACHE_INODE_INVALID_ARGUMENT;

  if(pentry->internal_md.type != REGULAR_FILE)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* Open file need to be closed, unless it is already open as read/write */
  if((pentry->object.file.open_fd.openflags != FSAL_O_RDWR) &&
     (pentry->object.file.open_fd.openflags != 0) &&
     (pentry->object.file.open_fd.fileno != 0) &&
     (pentry->object.file.open_fd.openflags != openflags))
    {
#ifdef _USE_MFSL
      fsal_status = MFSL_close(&(pentry->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_close(&(pentry->object.file.open_fd.fd));
#endif
      if(FSAL_IS_ERROR(fsal_status) && (fsal_status.major != ERR_FSAL_NOT_OPENED))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open: returning %d(%s) from FSAL_close",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

      /* force re-openning */
      pentry->object.file.open_fd.last_op = 0;
      pentry->object.file.open_fd.fileno = 0;

    }

  if((pentry->object.file.open_fd.last_op == 0)
     || (pentry->object.file.open_fd.fileno == 0))
    {
      /* opened file is not preserved yet */
#ifdef _USE_MFSL
      fsal_status = MFSL_open(&(pentry->mobject),
                              pcontext,
                              &pclient->mfsl_context,
                              openflags,
                              &pentry->object.file.open_fd.mfsl_fd,
                              &(pentry->object.file.attributes),
                              NULL );
#else
      fsal_status = FSAL_open(&(pentry->object.file.handle),
                              pcontext,
                              openflags,
                              &pentry->object.file.open_fd.fd,
                              &(pentry->object.file.attributes));
#endif

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

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open: returning %d(%s) from FSAL_open",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

#ifdef _USE_MFSL
      pentry->object.file.open_fd.fileno = FSAL_FILENO(&(pentry->object.file.open_fd.mfsl_fd.fsal_file));
#else
      pentry->object.file.open_fd.fileno = FSAL_FILENO(&(pentry->object.file.open_fd.fd));
#endif
      pentry->object.file.open_fd.openflags = openflags;

      LogDebug(COMPONENT_CACHE_INODE,
               "cache_inode_open: pentry %p: lastop=0, fileno = %d, openflags = %d",
               pentry, pentry->object.file.open_fd.fileno, (int) openflags);
    }

  /* regular exit */
  pentry->object.file.open_fd.last_op = time(NULL);

  /* if file descriptor is too high, garbage collect FDs */
  if(pclient->use_fd_cache
     && (pentry->object.file.open_fd.fileno > pclient->max_fd))
    {
      if(cache_inode_gc_fd(pclient, pstatus) != CACHE_INODE_SUCCESS)
        {
          LogCrit(COMPONENT_CACHE_INODE_GC,
                  "FAILURE performing FD garbage collection");
          return *pstatus;
        }
    }

  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;

}                               /* cache_inode_open */
Esempio n. 9
0
/**
 *
 * @brief Checks permissions on an entry for setattrs
 *
 * This function acquires the attribute lock on the supplied cache
 * entry then checks if the supplied credentials are sufficient to
 * perform the required setattrs.
 *
 * @param[in] entry   The object to be checked
 * @param[in] attr    Attributes to set/result of set
 *
 * @return CACHE_INODE_SUCCESS if operation is a success
 */
cache_inode_status_t
cache_inode_check_setattr_perms(cache_entry_t *entry,
				struct attrlist *attr,
				bool is_open_write)
{
	cache_inode_status_t status = CACHE_INODE_SUCCESS;
	fsal_accessflags_t access_check = 0;
	bool not_owner;
	char *note = "";
	const struct user_cred *creds = op_ctx->creds;

	if (isDebug(COMPONENT_CACHE_INODE) || isDebug(COMPONENT_NFS_V4_ACL)) {
		char *setattr_size = "";
		char *setattr_owner = "";
		char *setattr_owner_group = "";
		char *setattr_mode = "";
		char *setattr_acl = "";
		char *setattr_mtime = "";
		char *setattr_atime = "";

		if (FSAL_TEST_MASK(attr->mask, ATTR_SIZE))
			setattr_size = " SIZE";

		if (FSAL_TEST_MASK(attr->mask, ATTR_OWNER))
			setattr_owner = " OWNER";

		if (FSAL_TEST_MASK(attr->mask, ATTR_GROUP))
			setattr_owner_group = " GROUP";

		if (FSAL_TEST_MASK(attr->mask, ATTR_MODE))
			setattr_mode = " MODE";

		if (FSAL_TEST_MASK(attr->mask, ATTR_ACL))
			setattr_acl = " ACL";

		if (FSAL_TEST_MASK(attr->mask, ATTR_ATIME))
			setattr_atime = " ATIME";
		else if (FSAL_TEST_MASK(attr->mask, ATTR_ATIME_SERVER))
			setattr_atime = " ATIME_SERVER";

		if (FSAL_TEST_MASK(attr->mask, ATTR_MTIME))
			setattr_mtime = " MTIME";
		else if (FSAL_TEST_MASK(attr->mask, ATTR_MTIME_SERVER))
			setattr_mtime = " MTIME_SERVER";

		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "SETATTR %s%s%s%s%s%s%s", setattr_size,
			    setattr_owner, setattr_owner_group, setattr_mode,
			    setattr_acl, setattr_mtime, setattr_atime);
	}

	/* Shortcut, if current user is root, then we can just bail out with
	 * success. */
	if (creds->caller_uid == 0) {
		note = " (Ok for root user)";
		goto out;
	}

	not_owner = (creds->caller_uid != entry->obj_handle->attributes.owner);

	/* Only ownership change need to be checked for owner */
	if (FSAL_TEST_MASK(attr->mask, ATTR_OWNER)) {
		/* non-root is only allowed to "take ownership of file" */
		if (attr->owner != creds->caller_uid) {
			status = CACHE_INODE_FSAL_EPERM;
			note = " (new OWNER was not user)";
			goto out;
		}

		/* Owner of file will always be able to "change" the owner to
		 * himself. */
		if (not_owner) {
			access_check |=
			    FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER);
			LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
				    "Change OWNER requires FSAL_ACE_PERM_WRITE_OWNER");
		}
	}
	if (FSAL_TEST_MASK(attr->mask, ATTR_GROUP)) {
		/* non-root is only allowed to change group_owner to a group
		 * user is a member of. */
		int not_in_group = not_in_group_list(attr->group);

		if (not_in_group) {
			status = CACHE_INODE_FSAL_EPERM;
			note = " (user is not member of new GROUP)";
			goto out;
		}
		/* Owner is always allowed to change the group_owner of a file
		 * to a group they are a member of.
		 */
		if (not_owner) {
			access_check |=
			    FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_OWNER);
			LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
				    "Change GROUP requires FSAL_ACE_PERM_WRITE_OWNER");
		}
	}

	/* Any attribute after this is always changeable by the owner.
	 * And the above attributes have already been validated as a valid
	 * change for the file owner to make. Note that the owner may be
	 * setting ATTR_OWNER but at this point it MUST be to himself, and
	 * thus is no-op and does not need FSAL_ACE_PERM_WRITE_OWNER.
	 */
	if (!not_owner) {
		note = " (Ok for owner)";
		goto out;
	}

	if (FSAL_TEST_MASK(attr->mask, ATTR_MODE)
	    || FSAL_TEST_MASK(attr->mask, ATTR_ACL)) {
		/* Changing mode or ACL requires ACE4_WRITE_ACL */
		access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ACL);
		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "Change MODE or ACL requires FSAL_ACE_PERM_WRITE_ACL");
	}

	if (FSAL_TEST_MASK(attr->mask, ATTR_SIZE) && !is_open_write) {
		/* Changing size requires owner or write permission */
	  /** @todo: does FSAL_ACE_PERM_APPEND_DATA allow enlarging the file? */
		access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA);
		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "Change SIZE requires FSAL_ACE_PERM_WRITE_DATA");
	}

	/* Check if just setting atime and mtime to "now" */
	if ((FSAL_TEST_MASK(attr->mask, ATTR_MTIME_SERVER)
	     || FSAL_TEST_MASK(attr->mask, ATTR_ATIME_SERVER))
	    && !FSAL_TEST_MASK(attr->mask, ATTR_MTIME)
	    && !FSAL_TEST_MASK(attr->mask, ATTR_ATIME)) {
		/* If either atime and/or mtime are set to "now" then need only
		 * have write permission.
		 *
		 * Technically, client should not send atime updates, but if
		 * they really do, we'll let them to make the perm check a bit
		 * simpler. */
		access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA);
		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "Change ATIME and MTIME to NOW requires FSAL_ACE_PERM_WRITE_DATA");
	} else if (FSAL_TEST_MASK(attr->mask, ATTR_MTIME_SERVER)
		   || FSAL_TEST_MASK(attr->mask, ATTR_ATIME_SERVER)
		   || FSAL_TEST_MASK(attr->mask, ATTR_MTIME)
		   || FSAL_TEST_MASK(attr->mask, ATTR_ATIME)) {
		/* Any other changes to atime or mtime require owner, root, or
		 * ACES4_WRITE_ATTRIBUTES.
		 *
		 * NOTE: we explicity do NOT check for update of atime only to
		 * "now". Section 10.6 of both RFC 3530 and RFC 5661 document
		 * the reasons clients should not do atime updates.
		 */
		access_check |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_ATTR);
		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "Change ATIME and/or MTIME requires FSAL_ACE_PERM_WRITE_ATTR");
	}

	if (isDebug(COMPONENT_CACHE_INODE) || isDebug(COMPONENT_NFS_V4_ACL)) {
		char *need_write_owner = "";
		char *need_write_acl = "";
		char *need_write_data = "";
		char *need_write_attr = "";

		if (access_check & FSAL_ACE_PERM_WRITE_OWNER)
			need_write_owner = " WRITE_OWNER";

		if (access_check & FSAL_ACE_PERM_WRITE_ACL)
			need_write_acl = " WRITE_ACL";

		if (access_check & FSAL_ACE_PERM_WRITE_DATA)
			need_write_data = " WRITE_DATA";

		if (access_check & FSAL_ACE_PERM_WRITE_ATTR)
			need_write_attr = " WRITE_ATTR";

		LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
			    "Requires %s%s%s%s", need_write_owner,
			    need_write_acl, need_write_data, need_write_attr);
	}

	if (entry->obj_handle->attributes.acl) {
		status =
		    cache_inode_access_no_mutex(entry, access_check);

		note = " (checked ACL)";
		goto out;
	}

	if (access_check != FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA)) {
		/* Without an ACL, this user is not allowed some operation */
		status = CACHE_INODE_FSAL_EPERM;
		note = " (no ACL to check)";
		goto out;
	}

	status = cache_inode_access_no_mutex(entry, FSAL_W_OK);

	note = " (checked mode)";

 out:

	LogDebugCIA(COMPONENT_CACHE_INODE, COMPONENT_NFS_V4_ACL,
		    "Access check returned %s%s", cache_inode_err_str(status),
		    note);

	return status;
}
Esempio n. 10
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;
}
cache_inode_status_t cache_inode_open_by_name(cache_entry_t * pentry_dir,
                                              fsal_name_t * pname,
                                              cache_entry_t * pentry_file,
                                              cache_inode_client_t * pclient,
                                              fsal_openflags_t openflags,
                                              fsal_op_context_t * pcontext,
                                              cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;
  fsal_size_t save_filesize = 0;
  fsal_size_t save_spaceused = 0;
  fsal_time_t save_mtime = {
    .seconds = 0,
    .nseconds = 0
  };

  if((pentry_dir == NULL) || (pname == NULL) || (pentry_file == NULL) ||
     (pclient == NULL) || (pcontext == NULL) || (pstatus == NULL))
    return CACHE_INODE_INVALID_ARGUMENT;

  if((pentry_dir->internal_md.type != DIRECTORY))
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  if(pentry_file->internal_md.type != REGULAR_FILE)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* Open file need to be closed, unless it is already open as read/write */
  if((pentry_file->object.file.open_fd.openflags != FSAL_O_RDWR) &&
     (pentry_file->object.file.open_fd.openflags != 0) &&
     (pentry_file->object.file.open_fd.fileno != 0) &&
     (pentry_file->object.file.open_fd.openflags != openflags))
    {
#ifdef _USE_MFSL
      fsal_status =
          MFSL_close(&(pentry_file->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_close(&(pentry_file->object.file.open_fd.fd));
#endif
      if(FSAL_IS_ERROR(fsal_status) && (fsal_status.major != ERR_FSAL_NOT_OPENED))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open_by_name: returning %d(%s) from FSAL_close",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

      pentry_file->object.file.open_fd.last_op = 0;
      pentry_file->object.file.open_fd.fileno = 0;
    }

  if(pentry_file->object.file.open_fd.last_op == 0
     || pentry_file->object.file.open_fd.fileno == 0)
    {
      LogFullDebug(COMPONENT_FSAL,
               "cache_inode_open_by_name: pentry %p: lastop=0", pentry_file);

      /* Keep coherency with the cache_content */
      if(pentry_file->object.file.pentry_content != NULL)
        {
          save_filesize = pentry_file->attributes.filesize;
          save_spaceused = pentry_file->attributes.spaceused;
          save_mtime = pentry_file->attributes.mtime;
        }

      /* opened file is not preserved yet */
#ifdef _USE_MFSL
      fsal_status = MFSL_open_by_name(&(pentry_dir->mobject),
                                      pname,
                                      pcontext,
                                      &pclient->mfsl_context,
                                      openflags,
                                      &pentry_file->object.file.open_fd.mfsl_fd,
                                      &(pentry_file->attributes),
                                      NULL );

#else
      fsal_status = FSAL_open_by_name(&(pentry_dir->handle),
                                      pname,
                                      pcontext,
                                      openflags,
                                      &pentry_file->object.file.open_fd.fd,
                                      &(pentry_file->attributes));
#endif

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

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_open_by_name: returning %d(%s) from FSAL_open_by_name",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }

#ifdef _USE_PROXY

      /* If proxy if used, we should keep the name of the file to do FSAL_rcp if needed */
      if((pentry_file->object.file.pname =
          (fsal_name_t *) Mem_Alloc_Label(sizeof(fsal_name_t), "fsal_name_t")) == NULL)
        {
          *pstatus = CACHE_INODE_MALLOC_ERROR;

          return *pstatus;
        }

      pentry_file->object.file.pentry_parent_open = pentry_dir;
      pentry_file->object.file.pname->len = pname->len;
      memcpy((char *)(pentry_file->object.file.pname->name), (char *)(pname->name),
             FSAL_MAX_NAME_LEN);

#endif

      /* Keep coherency with the cache_content */
      if(pentry_file->object.file.pentry_content != NULL)
        {
          pentry_file->attributes.filesize = save_filesize;
          pentry_file->attributes.spaceused = save_spaceused;
          pentry_file->attributes.mtime = save_mtime;
        }

#ifdef _USE_MFSL
      pentry_file->object.file.open_fd.fileno =
          (int)FSAL_FILENO(&(pentry_file->object.file.open_fd.mfsl_fd.fsal_file));
#else
      pentry_file->object.file.open_fd.fileno =
          (int)FSAL_FILENO(&(pentry_file->object.file.open_fd.fd));
#endif
      pentry_file->object.file.open_fd.last_op = time(NULL);
      pentry_file->object.file.open_fd.openflags = openflags;

      LogFullDebug(COMPONENT_FSAL,
               "cache_inode_open_by_name: pentry %p: fd=%u",
               pentry_file, pentry_file->object.file.open_fd.fileno);

    }

  /* regular exit */
  pentry_file->object.file.open_fd.last_op = time(NULL);

  /* if file descriptor is too high, garbage collect FDs */
  if(pclient->use_fd_cache
     && (pentry_file->object.file.open_fd.fileno > pclient->max_fd))
    {
      if(cache_inode_gc_fd(pclient, pstatus) != CACHE_INODE_SUCCESS)
        {
          LogCrit(COMPONENT_CACHE_INODE_GC,
                  "FAILURE performing FD garbage collection");
          return *pstatus;
        }
    }

  *pstatus = CACHE_INODE_SUCCESS;
  return *pstatus;

}                               /* cache_inode_open_by_name */

/**
 *
 * cache_inode_close: closes the local fd in the FSAL.
 *
 * Closes the local fd in the FSAL.
 *
 * No lock management is done in this layer: the related pentry in the cache inode layer is
 * locked and will prevent from concurent accesses.
 *
 * @param pentry  [IN] entry in file content layer whose content is to be accessed.
 * @param pclient [IN]  ressource allocated by the client for the nfs management.
 * @pstatus       [OUT] returned status.
 *
 * @return CACHE_CONTENT_SUCCESS is successful .
 *
 */
cache_inode_status_t cache_inode_close(cache_entry_t * pentry,
                                       cache_inode_client_t * pclient,
                                       cache_inode_status_t * pstatus)
{
  fsal_status_t fsal_status;

  if((pentry == NULL) || (pclient == NULL) || (pstatus == NULL))
    return CACHE_CONTENT_INVALID_ARGUMENT;

  if(pentry->internal_md.type != REGULAR_FILE)
    {
      *pstatus = CACHE_INODE_BAD_TYPE;
      return *pstatus;
    }

  /* if nothing is opened, do nothing */
  if(pentry->object.file.open_fd.fileno < 0)
    {
      *pstatus = CACHE_INODE_SUCCESS;
      return *pstatus;
    }

  /* if locks are held in the file, do not close */
  if( cache_inode_file_holds_state( pentry ) )
    {
      *pstatus = CACHE_INODE_SUCCESS; /** @todo : PhD : May be CACHE_INODE_STATE_CONFLICTS would be better ? */
      return *pstatus;
    }

  if((pclient->use_fd_cache == 0) ||
     (time(NULL) - pentry->object.file.open_fd.last_op > pclient->retention) ||
     (pentry->object.file.open_fd.fileno > (int)(pclient->max_fd)))
    {

      LogFullDebug(COMPONENT_CACHE_INODE,
               "cache_inode_close: pentry %p, fileno = %d, lastop=%d ago",
               pentry, pentry->object.file.open_fd.fileno,
               (int)(time(NULL) - pentry->object.file.open_fd.last_op));

#ifdef _USE_MFSL
      fsal_status = MFSL_close(&(pentry->object.file.open_fd.mfsl_fd), &pclient->mfsl_context, NULL);
#else
      fsal_status = FSAL_close(&(pentry->object.file.open_fd.fd));
#endif

      pentry->object.file.open_fd.fileno = 0;
      pentry->object.file.open_fd.last_op = 0;

      if(FSAL_IS_ERROR(fsal_status) && (fsal_status.major != ERR_FSAL_NOT_OPENED))
        {
          *pstatus = cache_inode_error_convert(fsal_status);

          LogDebug(COMPONENT_CACHE_INODE,
                   "cache_inode_close: returning %d(%s) from FSAL_close",
                   *pstatus, cache_inode_err_str(*pstatus));

          return *pstatus;
        }
    }
#ifdef _USE_PROXY
  /* If proxy if used, free the name if needed */
  if(pentry->object.file.pname != NULL)
    {
      Mem_Free((char *)(pentry->object.file.pname));
      pentry->object.file.pname = NULL;
    }
  pentry->object.file.pentry_parent_open = NULL;
#endif

  *pstatus = CACHE_CONTENT_SUCCESS;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		cache_entry_t *entry = NULL;
		cache_inode_status_t tmp_status = 0;

		dirent =
		    avltree_container_of(dirent_node, cache_inode_dir_entry_t,
					 node_hk);

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

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

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

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

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

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

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

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

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

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

			status = tmp_status;

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

			goto unlock_dir;
		}

		(*nbfound)++;

		cache_inode_lru_unref(entry, LRU_FLAG_NONE);

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

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

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

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

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

}				/* cache_inode_readdir */
Esempio n. 13
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 */
Esempio n. 14
0
static bool
populate_dirent(const char *name, void *dir_state,
		fsal_cookie_t cookie)
{
	struct cache_inode_populate_cb_state *state =
	    (struct cache_inode_populate_cb_state *)dir_state;
	struct fsal_obj_handle *entry_hdl;
	cache_inode_dir_entry_t *new_dir_entry = NULL;
	cache_entry_t *cache_entry = NULL;
	fsal_status_t fsal_status = { 0, 0 };
	struct fsal_obj_handle *dir_hdl = state->directory->obj_handle;

	fsal_status = dir_hdl->obj_ops.lookup(dir_hdl, name, &entry_hdl);
	if (FSAL_IS_ERROR(fsal_status)) {
		*state->status = cache_inode_error_convert(fsal_status);
		if (*state->status == CACHE_INODE_FSAL_XDEV) {
			LogInfo(COMPONENT_NFS_READDIR,
				"Ignoring XDEV entry %s",
				name);
			*state->status = CACHE_INODE_SUCCESS;
			return true;
		}
		LogInfo(COMPONENT_CACHE_INODE,
			"Lookup failed on %s in dir %p with %s",
			name, dir_hdl, cache_inode_err_str(*state->status));
		return !cache_param.retry_readdir;
	}

	LogFullDebug(COMPONENT_NFS_READDIR, "Creating entry for %s", name);

	*state->status =
	    cache_inode_new_entry(entry_hdl, CACHE_INODE_FLAG_NONE,
				  &cache_entry);

	if (cache_entry == NULL) {
		*state->status = CACHE_INODE_NOT_FOUND;
		/* we do not free entry_hdl because it is consumed by
		   cache_inode_new_entry */
		LogEvent(COMPONENT_NFS_READDIR,
			 "cache_inode_new_entry failed with %s",
			 cache_inode_err_str(*state->status));
		return false;
	}

	if (cache_entry->type == DIRECTORY) {
		/* Insert Parent's key */
		cache_inode_key_dup(&cache_entry->object.dir.parent,
				    &state->directory->fh_hk.key);
	}

	*state->status =
	    cache_inode_add_cached_dirent(state->directory, name, cache_entry,
					  &new_dir_entry);
	/* return initial ref */
	cache_inode_put(cache_entry);

	if ((*state->status != CACHE_INODE_SUCCESS) &&
	    (*state->status != CACHE_INODE_ENTRY_EXISTS)) {
		LogEvent(COMPONENT_NFS_READDIR,
			 "cache_inode_add_cached_dirent failed with %s",
			 cache_inode_err_str(*state->status));
		return false;
	}

	return true;
}
Esempio n. 15
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;
}
Esempio n. 16
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 */