/** * nfs4_op_readdir: The NFS4_OP_READDIR. * * Implements the NFS4_OP_READDIR. If fh is a pseudo FH, then call is routed to routine nfs4_op_readdir_pseudo * * @param op [IN] pointer to nfs4_op arguments * @param data [INOUT] Pointer to the compound request's data * @param resp [IN] Pointer to nfs4_op results * * @return NFS4_OK if ok, any other value show an error. * */ int nfs4_op_readdir(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry = NULL; cache_inode_endofdir_t eod_met; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; cache_inode_status_t cache_status_attr; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_readdir"; unsigned long dircount; unsigned long maxcount; entry4 *entry_nfs_array; cache_inode_dir_entry_t **dirent_array = NULL; verifier4 cookie_verifier; uint64_t cookie = 0; uint64_t end_cookie = 0; fsal_handle_t *entry_FSALhandle; nfs_fh4 entryFH; char val_fh[NFS4_FHSIZE]; entry_name_array_item_t *entry_name_array = NULL; unsigned int estimated_num_entries; unsigned int num_entries; int dir_pentry_unlock = FALSE; unsigned int i = 0; unsigned int outbuffsize = 0 ; unsigned int entrysize = 0 ; bitmap4 RdAttrErrorBitmap = { 1, (uint32_t *) "\0\0\0\b" }; /* 0xB = 11 = FATTR4_RDATTR_ERROR */ attrlist4 RdAttrErrorVals = { 0, NULL }; /* Nothing to be seen here */ resp->resop = NFS4_OP_READDIR; res_READDIR4.status = NFS4_OK; entryFH.nfs_fh4_len = 0; entryFH.nfs_fh4_val = val_fh; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_NOFILEHANDLE; return res_READDIR4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_BADHANDLE; return res_READDIR4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_FHEXPIRED; return res_READDIR4.status; } /* Pseudo Fs management */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_readdir_pseudo(op, data, resp); /* Xattrs management */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_readdir_xattr(op, data, resp); /* You can readdir only within a directory */ dir_pentry = data->current_entry; if(data->current_filetype != DIRECTORY) { res_READDIR4.status = NFS4ERR_NOTDIR; return res_READDIR4.status; } /* get the characteristic value for readdir operation */ dircount = arg_READDIR4.dircount; maxcount = arg_READDIR4.maxcount*0.9; cookie = (unsigned int)arg_READDIR4.cookie; /* dircount is considered meaningless by many nfsv4 client (like the CITI * one). we use maxcount instead. */ /* the Linux 3.0, 3.1.0 clients vs. TCP Ganesha comes out 10x slower * with 500 max entries */ #if 0 /* takes 2s to return 2999 entries */ estimated_num_entries = maxcount / sizeof(entry4); #else /* takes 20s to return 2999 entries */ estimated_num_entries = 50; #endif LogFullDebug(COMPONENT_NFS_V4, "--- nfs4_op_readdir ---> dircount=%lu maxcount=%lu arg_cookie=%" PRIu64" cookie=%"PRIu64" estimated_num_entries=%u", dircount, maxcount, arg_READDIR4.cookie, cookie, estimated_num_entries); /* Do not use a cookie of 1 or 2 (reserved values) */ if(cookie == 1 || cookie == 2) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access_Bitmap(&arg_READDIR4.attr_request, FATTR4_ATTR_READ)) { res_READDIR4.status = NFS4ERR_INVAL; return res_READDIR4.status; } /* If maxcount is too short, return NFS4ERR_TOOSMALL */ if(maxcount < sizeof(entry4) || estimated_num_entries == 0) { res_READDIR4.status = NFS4ERR_TOOSMALL; return res_READDIR4.status; } /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the pentry. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ memset(cookie_verifier, 0, NFS4_VERIFIER_SIZE); if(data->pexport->UseCookieVerifier == 1) memcpy(cookie_verifier, &dir_pentry->internal_md.mod_time, sizeof(time_t)); /* Cookie delivered by the server and used by the client SHOULD not ne 0, 1 or 2 (cf RFC3530, page192) * because theses value are reserved for special use. * 0 - cookie for first READDIR * 1 - reserved for . on client handside * 2 - reserved for .. on client handside * Entries '.' and '..' are not returned also * For these reason, there will be an offset of 3 between NFS4 cookie and * HPSS cookie */ if((cookie != 0) && (data->pexport->UseCookieVerifier == 1)) { if(memcmp(cookie_verifier, arg_READDIR4.cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } } /* The default behaviour is to consider that eof is not reached, the * returned values by cache_inode_readdir will let us know if eod was * reached or not */ res_READDIR4.READDIR4res_u.resok4.reply.eof = FALSE; /* Get prepared for readdir */ if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc( estimated_num_entries * sizeof(cache_inode_dir_entry_t*))) == NULL) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Perform the readdir operation */ if(cache_inode_readdir(dir_pentry, data->pexport->cache_inode_policy, cookie, estimated_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, data->ht, &dir_pentry_unlock, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_READDIR4.status = nfs4_Errno(cache_status); goto out; } /* For an empty directory, we will find only . and .., so reply as if the * end is reached */ if(num_entries == 0) { /* only . and .. */ res_READDIR4.READDIR4res_u.resok4.reply.entries = NULL; res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } else { /* Start computing the outbuffsize */ outbuffsize = sizeof( bool_t) /* eof */ + sizeof( nfsstat4 ) /* READDIR4res::status */ + NFS4_VERIFIER_SIZE /* cookie verifier */ ; /* Allocation of reply structures */ if((entry_name_array = (entry_name_array_item_t *) Mem_Alloc(num_entries * (FSAL_MAX_NAME_LEN + 1))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_name_array, 0, num_entries * (FSAL_MAX_NAME_LEN + 1)); if((entry_nfs_array = (entry4 *) Mem_Alloc(num_entries * sizeof(entry4))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_nfs_array, 0, num_entries * sizeof(entry4)); for(i = 0; i < num_entries; i++) { entry_nfs_array[i].name.utf8string_val = entry_name_array[i]; if(str2utf8(dirent_array[i]->name.name, &entry_nfs_array[i].name) == -1) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Set the cookie value */ entry_nfs_array[i].cookie = dirent_array[i]->cookie; /* Get the pentry for the object's attributes and filehandle */ if( ( pentry = cache_inode_lookup_no_mutex( dir_pentry, &dirent_array[i]->name, data->pexport->cache_inode_policy, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status ) ) == NULL ) { Mem_Free((char *)entry_nfs_array); /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* If file handle is asked in the attributes, provide it */ if(arg_READDIR4.attr_request.bitmap4_val != NULL && (arg_READDIR4.attr_request.bitmap4_val[0] & FATTR4_FILEHANDLE)) { if((entry_FSALhandle = cache_inode_get_fsal_handle(pentry, &cache_status_attr)) == NULL) { /* Faulty Handle or pentry */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } if(!nfs4_FSALToFhandle(&entryFH, entry_FSALhandle, data)) { /* Faulty type */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } } if(nfs4_FSALattr_To_Fattr(data->pexport, &attrlookup, &(entry_nfs_array[i].attrs), data, &entryFH, &(arg_READDIR4.attr_request)) != 0) { /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; } /* Update the size of the output buffer */ entrysize = sizeof( nfs_cookie4 ) ; /* nfs_cookie4 */ entrysize += sizeof( u_int ) ; /* pathname4::utf8strings_len */ entrysize += entry_nfs_array[i].name.utf8string_len ; entrysize += sizeof( u_int ) ; /* bitmap4_len */ entrysize += entry_nfs_array[i].attrs.attrmask.bitmap4_len ; entrysize += sizeof( u_int ) ; /* attrlist4_len */ entrysize += entry_nfs_array[i].attrs.attr_vals.attrlist4_len ; entrysize += sizeof( caddr_t ) ; outbuffsize += entrysize; LogFullDebug(COMPONENT_NFS_V4, " === nfs4_op_readdir ===> i=%u name=%s cookie=%"PRIu64" " "entrysize=%u buffsize=%u", i, dirent_array[i]->name.name, entry_nfs_array[i].cookie, entrysize, outbuffsize); /* Chain the entries together */ entry_nfs_array[i].nextentry = NULL; if(i != 0) { if( outbuffsize < maxcount ) entry_nfs_array[i - 1].nextentry = &(entry_nfs_array[i]); else { LogFullDebug(COMPONENT_NFS_V4, "=== nfs4_op_readdir ===> " "maxcount reached at %u entries name=%s " "cookie=%llu " "buffsize=%u (return early)", i+1, dirent_array[i]->name.name, (unsigned long long)entry_nfs_array[i].cookie, outbuffsize); entry_nfs_array[i - 1].nextentry = NULL ; break ; } } } /* for i */ if((i == num_entries) && (eod_met == END_OF_DIR)) { LogFullDebug(COMPONENT_NFS_V4, "End of directory reached: num_entries=%d i=%d", num_entries, i); /* This is the end of the directory */ res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } /* Put the entry's list in the READDIR reply */ res_READDIR4.READDIR4res_u.resok4.reply.entries = entry_nfs_array; } /* Do not forget to set the verifier */ memcpy((char *)res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); res_READDIR4.status = NFS4_OK; out: /* release read lock on dir_pentry, if requested */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if (dirent_array) { if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, data->pclient ) ; Mem_Free((char *)dirent_array); } return res_READDIR4.status; } /* nfs4_op_readdir */
cache_inode_status_t cache_inode_readlink(cache_entry_t * pentry, fsal_path_t * plink_content, hash_table_t * ht, /* Unused, kept for protototype's homogeneity */ cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_status_t fsal_status; fsal_attrib_list_t attr ; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ (pclient->stat.nb_call_total)++; (pclient->stat.func_stats.nb_call[CACHE_INODE_READLINK])++; /* Lock the entry */ P_w(&pentry->lock); if(cache_inode_renew_entry(pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK])++; V_w(&pentry->lock); return *pstatus; } /* RW_Lock obtained as writer turns to reader */ rw_lock_downgrade(&pentry->lock); switch (pentry->internal_md.type) { case REGULAR_FILE: case DIRECTORY: case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: case UNASSIGNED: case FS_JUNCTION: case RECYCLED: *pstatus = CACHE_INODE_BAD_TYPE; V_r(&pentry->lock); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1; return *pstatus; break; case SYMBOLIC_LINK: assert(pentry->object.symlink); if( CACHE_INODE_KEEP_CONTENT( pentry->policy ) ) { fsal_status = FSAL_pathcpy(plink_content, &(pentry->object.symlink->content)); /* need copy ctor? */ } else { /* Content is not cached, call FSAL_readlink here */ fsal_status = FSAL_readlink( &pentry->handle, pcontext, plink_content, &attr ) ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); V_r(&pentry->lock); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readlink: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readlink: Could not kill entry %p, status = %u", pentry, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READLINK] += 1; return *pstatus; } break; } /* Release the entry */ *pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient); V_r(&pentry->lock); /* stat */ if(*pstatus != CACHE_INODE_SUCCESS) pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READLINK] += 1; else pclient->stat.func_stats.nb_success[CACHE_INODE_READLINK] += 1; return *pstatus; } /* cache_inode_readlink */
/** * * 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) */
int _9p_readdir( _9p_request_data_t * preq9p, void * pworker_data, u32 * plenout, char * preply) { char * cursor = preq9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE ; nfs_worker_data_t * pwkrdata = (nfs_worker_data_t *)pworker_data ; int rc = 0 ; u32 err = 0 ; u16 * msgtag = NULL ; u32 * fid = NULL ; u64 * offset = NULL ; u32 * count = NULL ; u32 dcount = 0 ; u32 recsize = 0 ; u16 name_len = 0 ; char * name_str = NULL ; u8 * qid_type = NULL ; u64 * qid_path = NULL ; char * dcount_pos = NULL ; cache_inode_status_t cache_status; cache_inode_dir_entry_t **dirent_array = NULL; cache_inode_endofdir_t eod_met; cache_entry_t * pentry_dot_dot = NULL ; cache_inode_dir_entry_t dirent_dot ; cache_inode_dir_entry_t dirent_dot_dot ; unsigned int cookie = 0; unsigned int end_cookie = 0; unsigned int estimated_num_entries = 0 ; unsigned int num_entries = 0 ; unsigned int delta = 0 ; int unlock = FALSE ; u64 i = 0LL ; if ( !preq9p || !pworker_data || !plenout || !preply ) return -1 ; _9p_fid_t * pfid = NULL ; /* Get data */ _9p_getptr( cursor, msgtag, u16 ) ; _9p_getptr( cursor, fid, u32 ) ; _9p_getptr( cursor, offset, u64 ) ; _9p_getptr( cursor, count, u32 ) ; LogDebug( COMPONENT_9P, "TREADDIR: tag=%u fid=%u offset=%llu count=%u", (u32)*msgtag, *fid, (unsigned long long)*offset, *count ) ; if( *fid >= _9P_FID_PER_CONN ) { err = ERANGE ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } pfid = &preq9p->pconn->fids[*fid] ; /* Use Cache Inode to read the directory's content */ cookie = (unsigned int)*offset ; /* For each entry, returns: * qid = 13 bytes * offset = 8 bytes * type = 1 byte * namelen = 2 bytes * namestr = ~16 bytes (average size) * ------------------- * total = ~40 bytes (average size) per dentry */ estimated_num_entries = (unsigned int)( *count / 40 ) ; if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc_Label( estimated_num_entries * sizeof(cache_inode_dir_entry_t*), "cache_inode_dir_entry_t in _9p_readdir")) == NULL) { err = EIO ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } /* Is this the first request ? */ if( *offset == 0 ) { /* compute the parent entry */ if( ( pentry_dot_dot = cache_inode_lookupp( pfid->pentry, pwkrdata->ht, &pwkrdata->cache_inode_client, &pfid->fsal_op_context, &cache_status) ) == NULL ) { err = _9p_tools_errno( cache_status ) ; ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } /* Deal with "." and ".." */ dirent_dot.pentry = pfid->pentry ; strcpy( dirent_dot.name.name, "." ) ; dirent_dot.name.len = strlen( dirent_dot.name.name ) ; dirent_array[0] = &dirent_dot ; dirent_dot_dot.pentry = pentry_dot_dot ; strcpy( dirent_dot_dot.name.name, ".." ) ; dirent_dot_dot.name.len = strlen( dirent_dot_dot.name.name ) ; dirent_array[1] = &dirent_dot_dot ; delta = 2 ; } else delta = 0 ; if( *offset == 2 ) { /* offset == 2 as an input as one and only reason: * - a former call with offset=0 was made and the dir was empty * - '.' and '..' were returned and nothing else * - the client makes a new call, expecting it to have empty return */ num_entries = 0 ; /* Empty return */ unlock = FALSE ; } else if(cache_inode_readdir( pfid->pentry, pfid->pexport->cache_inode_policy, cookie, estimated_num_entries - delta, &num_entries, (uint64_t *)&end_cookie, &eod_met, &dirent_array[delta], pwkrdata->ht, &unlock, &pwkrdata->cache_inode_client, &pfid->fsal_op_context, &cache_status) != CACHE_INODE_SUCCESS) { if( unlock ) V_r( &pfid->pentry->lock ) ; err = _9p_tools_errno( cache_status ) ; ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } /* Unlock the directory if needed */ if( unlock ) V_r( &pfid->pentry->lock ) ; /* Never go behind _9P_MAXDIRCOUNT */ if( num_entries > _9P_MAXDIRCOUNT ) num_entries = _9P_MAXDIRCOUNT ; /* Build the reply */ _9p_setinitptr( cursor, preply, _9P_RREADDIR ) ; _9p_setptr( cursor, msgtag, u16 ) ; /* Remember dcount position for later use */ _9p_savepos( cursor, dcount_pos, u32 ) ; /* fills in the dentry in 9P marshalling */ for( i = 0 ; i < num_entries + delta ; i++ ) { recsize = 0 ; /* Build qid */ switch( dirent_array[i]->pentry->internal_md.type ) { case REGULAR_FILE: qid_path = (u64 *)&dirent_array[i]->pentry->object.file.attributes.fileid ; qid_type = &qid_type_file ; break ; case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: qid_path = (u64 *)&dirent_array[i]->pentry->object.special_obj.attributes.fileid ; qid_type = &qid_type_file ; break ; case SYMBOLIC_LINK: qid_path = (u64 *)&dirent_array[i]->pentry->object.symlink->attributes.fileid ; qid_type = &qid_type_symlink; break ; case DIRECTORY: case FS_JUNCTION: qid_path = (u64 *)&dirent_array[i]->pentry->object.dir.attributes.fileid ; qid_type = &qid_type_dir ; break ; case UNASSIGNED: case RECYCLED: default: LogMajor( COMPONENT_9P, "implementation error, you should not see this message !!!!!!" ) ; err = EINVAL ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; break ; } /* Get dirent name information */ name_str = dirent_array[i]->name.name ; name_len = dirent_array[i]->name.len ; /* Add 13 bytes in recsize for qid + 8 bytes for offset + 1 for type + 2 for strlen = 24 bytes*/ recsize = 24 + name_len ; /* Check if there is room left for another dentry */ if( dcount + recsize > *count ) break ; /* exit for loop */ else dcount += recsize ; /* qid in 3 parts */ _9p_setptr( cursor, qid_type, u8 ) ; _9p_setvalue( cursor, 0, u32 ) ; /* qid_version set to 0 to prevent the client from caching */ _9p_setptr( cursor, qid_path, u64 ) ; /* offset */ _9p_setvalue( cursor, i+cookie+1, u64 ) ; /* Type (again ?) */ _9p_setptr( cursor, qid_type, u8 ) ; /* name */ _9p_setstr( cursor, name_len, name_str ) ; LogDebug( COMPONENT_9P, "RREADDIR dentry: recsize=%u dentry={ off=%llu,qid=(type=%u,version=%u,path=%llu),type=%u,name=%s,pentry=%p", recsize, (unsigned long long)i+cookie+1, *qid_type, 0, (unsigned long long)*qid_path, *qid_type, name_str, dirent_array[i]->pentry ) ; } /* for( i = 0 , ... ) */ if( !CACHE_INODE_KEEP_CONTENT( pfid->pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, &pwkrdata->cache_inode_client ) ; Mem_Free((char *)dirent_array); /* Set buffsize in previously saved position */ _9p_setvalue( dcount_pos, dcount, u32 ) ; _9p_setendptr( cursor, preply ) ; _9p_checkbound( cursor, preply, plenout ) ; LogDebug( COMPONENT_9P, "RREADDIR: tag=%u fid=%u dcount=%u", (u32)*msgtag, *fid , dcount ) ; return 1 ; }
/** * * cache_inode_readdir_populate: fully reads a directory in FSAL and caches * the related entries. * * fully reads a directory in FSAL and caches the related entries. No MT * safety managed here !! * * @param pentry [IN] entry for the parent directory to be read. This must be * a DIRECTORY * @param ht [IN] hash table used for the cache, unused in this call. * @param pclient [INOUT] ressource allocated by the client for the nfs * management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * */ cache_inode_status_t cache_inode_readdir_populate( cache_entry_t * pentry_dir, cache_inode_policy_t policy, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { fsal_dir_t fsal_dirhandle; fsal_status_t fsal_status; fsal_attrib_list_t dir_attributes; fsal_cookie_t begin_cookie; fsal_cookie_t end_cookie; fsal_count_t nbfound; fsal_count_t iter; fsal_boolean_t fsal_eod; cache_entry_t *pentry = NULL; cache_entry_t *pentry_parent = pentry_dir; fsal_attrib_list_t object_attributes; cache_inode_create_arg_t create_arg; cache_inode_file_type_t type; cache_inode_status_t cache_status; fsal_dirent_t array_dirent[FSAL_READDIR_SIZE + 20]; cache_inode_fsal_data_t new_entry_fsdata; cache_inode_dir_entry_t *new_dir_entry = NULL; uint64_t i = 0; /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* Only DIRECTORY entries are concerned */ if(pentry_dir->internal_md.type != DIRECTORY) { *pstatus = CACHE_INODE_BAD_TYPE; return *pstatus; } #ifdef _USE_MFSL_ASYNC /* If entry is asynchronous (via MFSL), it should not be repopulated until it is synced */ if(MFSL_ASYNC_is_synced(&pentry_dir->mobject) == FALSE) { /* Directory is asynchronous, do not repopulate it and let it * in the state 'has_been_readdir == FALSE' */ *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } #endif /* If directory is already populated , there is no job to do */ if(pentry_dir->object.dir.has_been_readdir == CACHE_INODE_YES) { *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } /* Invalidate all the dirents */ if(cache_inode_invalidate_all_cached_dirent(pentry_dir, ht, pclient, pstatus) != CACHE_INODE_SUCCESS) return *pstatus; /* Open the directory */ dir_attributes.asked_attributes = pclient->attrmask; #ifdef _USE_MFSL fsal_status = MFSL_opendir(&pentry_dir->mobject, pcontext, &pclient->mfsl_context, &fsal_dirhandle, &dir_attributes, NULL); #else fsal_status = FSAL_opendir(&pentry_dir->object.dir.handle, pcontext, &fsal_dirhandle, &dir_attributes); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_dir, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: Could not kill entry %p, status = %u", pentry_dir, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } /* Loop for readding the directory */ FSAL_SET_COOKIE_BEGINNING(begin_cookie); FSAL_SET_COOKIE_BEGINNING(end_cookie); fsal_eod = FALSE; do { #ifdef _USE_MFSL fsal_status = MFSL_readdir(&fsal_dirhandle, begin_cookie, pclient->attrmask, FSAL_READDIR_SIZE * sizeof(fsal_dirent_t), array_dirent, &end_cookie, &nbfound, &fsal_eod, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_readdir(&fsal_dirhandle, begin_cookie, pclient->attrmask, FSAL_READDIR_SIZE * sizeof(fsal_dirent_t), array_dirent, &end_cookie, &nbfound, &fsal_eod); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); return *pstatus; } for(iter = 0; iter < nbfound; iter++) { LogFullDebug(COMPONENT_NFS_READDIR, "cache readdir populate found entry %s", array_dirent[iter].name.name); /* It is not needed to cache '.' and '..' */ if(!FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&(array_dirent[iter].name), (fsal_name_t *) & FSAL_DOT_DOT)) { LogFullDebug(COMPONENT_NFS_READDIR, "cache readdir populate : do not cache . and .."); continue; } /* If dir entry is a symbolic link, its content has to be read */ if((type = cache_inode_fsal_type_convert(array_dirent[iter].attributes.type)) == SYMBOLIC_LINK) { #ifdef _USE_MFSL mfsl_object_t tmp_mfsl; #endif /* Let's read the link for caching its value */ object_attributes.asked_attributes = pclient->attrmask; if( CACHE_INODE_KEEP_CONTENT( pentry_dir->policy ) ) { #ifdef _USE_MFSL tmp_mfsl.handle = array_dirent[iter].handle; fsal_status = MFSL_readlink(&tmp_mfsl, pcontext, &pclient->mfsl_context, &create_arg.link_content, &object_attributes, NULL); #else fsal_status = FSAL_readlink(&array_dirent[iter].handle, pcontext, &create_arg.link_content, &object_attributes); #endif } else { fsal_status.major = ERR_FSAL_NO_ERROR ; fsal_status.minor = 0 ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_readdir: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry_dir, fsal_status.major, fsal_status.minor ); if(cache_inode_kill_entry(pentry_dir, WT_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: Could not kill entry %p, status = %u", pentry_dir, kill_status); *pstatus = CACHE_INODE_FSAL_ESTALE; } return *pstatus; } } /* Try adding the entry, if it exists then this existing entry is returned */ new_entry_fsdata.handle = array_dirent[iter].handle; new_entry_fsdata.cookie = 0; /* XXX needed? */ if((pentry = cache_inode_new_entry( &new_entry_fsdata, &array_dirent[iter].attributes, type, policy, &create_arg, NULL, ht, pclient, pcontext, FALSE, /* This is population and no creation */ pstatus)) == NULL) return *pstatus; cache_status = cache_inode_add_cached_dirent( pentry_parent, &(array_dirent[iter].name), pentry, ht, &new_dir_entry, pclient, pcontext, pstatus); if(cache_status != CACHE_INODE_SUCCESS && cache_status != CACHE_INODE_ENTRY_EXISTS) return *pstatus; /* * Remember the FSAL readdir cookie associated with this dirent. This * is needed for partial directory reads. * * to_uint64 should be a lightweight operation--it is in the current * default implementation. We think the right thing -should- happen * therefore with if _USE_MFSL. * * I'm ignoring the status because the default operation is a memcmp-- * we lready -have- the cookie. */ if (cache_status != CACHE_INODE_ENTRY_EXISTS) { (void) FSAL_cookie_to_uint64(&array_dirent[iter].handle, pcontext, &array_dirent[iter].cookie, &new_dir_entry->fsal_cookie); /* we are filling in all entries, and the cookie avl was * cleared before adding dirents */ new_dir_entry->cookie = i; /* still an offset */ (void) avltree_insert(&new_dir_entry->node_c, &pentry_parent->object.dir.cookies); } /* !exist */ } /* iter */ /* Get prepared for next step */ begin_cookie = end_cookie; /* next offset */ i++; } while(fsal_eod != TRUE); /* Close the directory */ #ifdef _USE_MFSL fsal_status = MFSL_closedir(&fsal_dirhandle, &pclient->mfsl_context, NULL); #else fsal_status = FSAL_closedir(&fsal_dirhandle); #endif if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); return *pstatus; } /* End of work */ pentry_dir->object.dir.has_been_readdir = CACHE_INODE_YES; *pstatus = CACHE_INODE_SUCCESS; return *pstatus; } /* cache_inode_readdir_populate */
/** * * cache_inode_readdir: Reads a directory. * * Looks up for a name in a directory indicated by a cached entry. The * directory should have been cached before. * * NEW: pending new (C-language) callback based dirent unpacking into caller * structures, we eliminate copies by returning dir entries by pointer. To * permit this, we introduce lock donation. If new int pointer argument * unlock is 1 on return, the calling thread holds pentry read-locked and * must release this lock after dirent processing. * * This is the only function in the cache_inode_readdir.c file that manages MT * safety on a directory cache entry. * * @param pentry [IN] entry for the parent directory to be read. * @param cookie [IN] cookie for the readdir operation (basically the offset). * @param nbwanted [IN] Maximum number of directory entries wanted. * @param peod_met [OUT] A flag to know if end of directory was met during this call. * @param dirent_array [OUT] the resulting array of found directory entries. * @param ht [IN] hash table used for the cache, unused in this call. * @param unlock [OUT] the caller shall release read-lock on pentry when done * @param pclient [INOUT] ressource allocated by the client for the nfs management. * @param pcontext [IN] FSAL credentials * @param pstatus [OUT] returned status. * * @return CACHE_INODE_SUCCESS if operation is a success \n * @return CACHE_INODE_BAD_TYPE if entry is not related to a directory\n * @return CACHE_INODE_LRU_ERROR if allocation error occured when validating the entry * */ cache_inode_status_t cache_inode_readdir(cache_entry_t * dir_pentry, cache_inode_policy_t policy, uint64_t cookie, unsigned int nbwanted, unsigned int *pnbfound, uint64_t *pend_cookie, cache_inode_endofdir_t *peod_met, cache_inode_dir_entry_t **dirent_array, hash_table_t *ht, int *unlock, cache_inode_client_t *pclient, fsal_op_context_t *pcontext, cache_inode_status_t *pstatus) { cache_inode_dir_entry_t dirent_key[1], *dirent; struct avltree_node *dirent_node; fsal_accessflags_t access_mask = 0; uint64_t inoff = cookie; int i = 0; /* Guide to parameters: * the first cookie is parameter 'cookie' * number of entries queried is set by parameter 'nbwanted' * number of found entries before eod is return is '*pnbfound' * '*peod_met' is set if end of directory is encountered */ /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; dirent = NULL; /* Set initial value of unlock */ *unlock = FALSE; /* end cookie initial value is the begin cookie */ LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: setting pend_cookie to cookie=%" PRIu64, cookie); *pend_cookie = cookie; /* stats */ pclient->stat.nb_call_total++; (pclient->stat.func_stats.nb_call[CACHE_INODE_READDIR])++; LogFullDebug(COMPONENT_NFS_READDIR, "--> Cache_inode_readdir: parameters are cookie=%"PRIu64 "nbwanted=%u", cookie, nbwanted); /* Sanity check */ if(nbwanted == 0) { /* Asking for nothing is not a crime !!!!! * build a 'dummy' return in this case */ *pstatus = CACHE_INODE_SUCCESS; *pnbfound = 0; *peod_met = TO_BE_CONTINUED; /* stats */ (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; return *pstatus; } /* Force dir content invalidation if policy enforced no name cache */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) return cache_inode_readdir_nonamecache( dir_pentry, policy, cookie, nbwanted, pnbfound, pend_cookie, peod_met, dirent_array, ht, unlock, pclient, pcontext, pstatus ) ; P_w(&dir_pentry->lock); /* Renew the entry (to avoid having it being garbagged */ if(cache_inode_renew_entry(dir_pentry, NULL, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_GETATTR])++; V_w(&dir_pentry->lock); return *pstatus; } /* readdir can be done only with a directory */ if(dir_pentry->internal_md.type != DIRECTORY) { V_w(&dir_pentry->lock); *pstatus = CACHE_INODE_BAD_TYPE; /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; return *pstatus; } /* Check is user (as specified by the credentials) is authorized to read * the directory or not */ access_mask = FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR); if(cache_inode_access_no_mutex(dir_pentry, access_mask, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { V_w(&dir_pentry->lock); (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; return *pstatus; } /* Is the directory fully cached (this is done if a readdir call is done on the directory) */ if(dir_pentry->object.dir.has_been_readdir != CACHE_INODE_YES) { /* populate the cache */ if(cache_inode_readdir_populate(dir_pentry, policy, ht, pclient, pcontext, pstatus) != CACHE_INODE_SUCCESS) { /* stats */ (pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_READDIR])++; V_w(&dir_pentry->lock); return *pstatus; } } /* deal with dentry cache invalidates */ revalidate_cookie_cache(dir_pentry, pclient); /* Downgrade Writer lock to a reader one. */ rw_lock_downgrade(&dir_pentry->lock); /* 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) { if (cookie < 3) { *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } if ((inoff-3) > avltree_size(&dir_pentry->object.dir.dentries)) { LogCrit(COMPONENT_NFS_V4, "Bad initial cookie %"PRIu64, inoff); *pstatus = CACHE_INODE_BAD_COOKIE; V_r(&dir_pentry->lock); return *pstatus; } /* we assert this can now succeed */ dirent_key->cookie = inoff; dirent_node = avltree_lookup(&dirent_key->node_c, &dir_pentry->object.dir.cookies); if (! dirent_node) { LogCrit(COMPONENT_NFS_READDIR, "%s: seek to cookie=%"PRIu64" fail", __func__, inoff); *pstatus = CACHE_INODE_NOT_FOUND; V_r(&dir_pentry->lock); return *pstatus; } /* switch avls */ dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_c); dirent_node = &dirent->node_n; /* client wants the cookie -after- the last we sent, and * the Linux 3.0 and 3.1.0-rc7 clients misbehave if we * resend the last one */ dirent_node = avltree_next(dirent_node); } else { /* initial readdir */ dirent_node = avltree_first(&dir_pentry->object.dir.dentries); } LogFullDebug(COMPONENT_NFS_READDIR, "About to readdir in cache_inode_readdir: pentry=%p " "cookie=%"PRIu64, dir_pentry, cookie); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *pnbfound = 0; *peod_met = TO_BE_CONTINUED; for(i = 0; i < nbwanted; ++i) { if (!dirent_node) break; dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_n); dirent_array[i] = dirent; (*pnbfound)++; dirent_node = avltree_next(dirent_node); } if (*pnbfound > 0) { if (!dirent) { LogCrit(COMPONENT_CACHE_INODE, "cache_inode_readdir: " "UNEXPECTED CASE: dirent is NULL whereas nbfound>0"); *pstatus = CACHE_INODE_INCONSISTENT_ENTRY; return CACHE_INODE_INCONSISTENT_ENTRY; } *pend_cookie = dirent->cookie; } if (! dirent_node) *peod_met = END_OF_DIR; *pstatus = cache_inode_valid(dir_pentry, CACHE_INODE_OP_GET, pclient); /* stats */ if(*pstatus != CACHE_INODE_SUCCESS) { (pclient->stat.func_stats.nb_err_retryable[CACHE_INODE_READDIR])++; V_r(&dir_pentry->lock); } else { (pclient->stat.func_stats.nb_success[CACHE_INODE_READDIR])++; *unlock = TRUE; } return *pstatus; } /* cache_inode_readdir */
int nfs3_Readdirplus(nfs_arg_t * parg, exportlist_t * pexport, fsal_op_context_t * pcontext, cache_inode_client_t * pclient, hash_table_t * ht, struct svc_req *preq, nfs_res_t * pres) { static char __attribute__ ((__unused__)) funcName[] = "nfs3_Readdirplus"; typedef char entry_name_array_item_t[FSAL_MAX_NAME_LEN]; typedef char fh3_buffer_item_t[NFS3_FHSIZE]; unsigned int delta = 0; cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry_dot_dot = NULL; unsigned long dircount; unsigned long maxcount; fsal_attrib_list_t dir_attr; fsal_attrib_list_t entry_attr; uint64_t begin_cookie; uint64_t end_cookie; uint64_t cache_inode_cookie; cache_inode_dir_entry_t **dirent_array = NULL; cookieverf3 cookie_verifier; int rc; unsigned int i = 0; unsigned int num_entries; unsigned long space_used; unsigned long estimated_num_entries; unsigned long asked_num_entries; cache_inode_file_type_t dir_filetype; cache_inode_endofdir_t eod_met = UNASSIGNED_EOD; cache_inode_status_t cache_status; cache_inode_status_t cache_status_gethandle; fsal_handle_t *pfsal_handle = NULL; entry_name_array_item_t *entry_name_array = NULL; fh3_buffer_item_t *fh3_array = NULL; entryplus3 reference_entry; READDIRPLUS3resok reference_reply; int dir_pentry_unlock = FALSE; if(isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; log_components_t component; sprint_fhandle3(str, &(parg->arg_readdirplus3.dir)); if(isDebug(COMPONENT_NFSPROTO)) component = COMPONENT_NFSPROTO; else component = COMPONENT_NFS_READDIR; LogDebug(component, "REQUEST PROCESSING: Calling nfs3_Readdirplus handle: %s", str); } /* to avoid setting it on each error case */ pres->res_readdir3.READDIR3res_u.resfail.dir_attributes.attributes_follow = FALSE; dircount = parg->arg_readdirplus3.dircount; maxcount = parg->arg_readdirplus3.maxcount; begin_cookie = (unsigned int)parg->arg_readdirplus3.cookie; /* FIXME: This calculation over estimates the number of bytes that * READDIRPLUS3resok will use on the wire by 4 bytes on x86_64. */ space_used = sizeof(reference_reply.dir_attributes.attributes_follow) + sizeof(reference_reply.dir_attributes.post_op_attr_u.attributes) + sizeof(reference_reply.cookieverf) + sizeof(reference_reply.reply.eof); estimated_num_entries = (dircount - space_used + sizeof(entry3 *)) / (sizeof(entry3) - sizeof(char *)*2); // estimated_num_entries *= 4; LogFullDebug(COMPONENT_NFS_READDIR, "nfs3_Readdirplus: dircount=%lu maxcount=%lu begin_cookie=%" PRIu64" space_used=%lu estimated_num_entries=%lu", dircount, maxcount, begin_cookie, space_used, estimated_num_entries); /* Is this a xattr FH ? */ if(nfs3_Is_Fh_Xattr(&(parg->arg_readdirplus3.dir))) return nfs3_Readdirplus_Xattr(parg, pexport, pcontext, pclient, ht, preq, pres); /* Convert file handle into a vnode */ if((dir_pentry = nfs_FhandleToCache(preq->rq_vers, NULL, &(parg->arg_readdirplus3.dir), NULL, NULL, &(pres->res_readdirplus3.status), NULL, &dir_attr, pcontext, pclient, ht, &rc)) == NULL) { /* return NFS_REQ_DROP ; */ return rc; } /* Extract the filetype */ dir_filetype = cache_inode_fsal_type_convert(dir_attr.type); /* Sanity checks -- must be a directory */ if(dir_filetype != DIRECTORY) { pres->res_readdirplus3.status = NFS3ERR_NOTDIR; return NFS_REQ_OK; } /* switch */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the directory. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ if(pexport->UseCookieVerifier) memcpy(cookie_verifier, &(dir_attr.mtime), sizeof(dir_attr.mtime)); /* * nothing to do if != 0 because the area is already full of * zero */ if(pexport->UseCookieVerifier && (begin_cookie != 0)) { /* * Not the first call, so we have to check the cookie * verifier */ if(memcmp(cookie_verifier, parg->arg_readdirplus3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { pres->res_readdirplus3.status = NFS3ERR_BAD_COOKIE; return NFS_REQ_OK; } } if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc_Label( estimated_num_entries * sizeof(cache_inode_dir_entry_t*), "cache_inode_dir_entry_t in nfs3_Readdirplus")) == NULL) { pres->res_readdirplus3.status = NFS3ERR_IO; return NFS_REQ_DROP; } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; /** @todo XXXX fix this--compare nfs4_op_readdir */ /* How many entries will we retry from cache_inode ? */ if(begin_cookie > 1) { asked_num_entries = estimated_num_entries; cache_inode_cookie = begin_cookie; } else { asked_num_entries = ((estimated_num_entries > 2) ? estimated_num_entries - 2 : 0); /* Keep space for '.' and '..' */ cache_inode_cookie = 0; } /* A definition that will be very useful to avoid very long names for variables */ #define RES_READDIRPLUS_REPLY pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply /* Call readdir */ if(cache_inode_readdir(dir_pentry, pexport->cache_inode_policy, cache_inode_cookie, asked_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, ht, &dir_pentry_unlock, pclient, pcontext, &cache_status) == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> Call to cache_inode_readdir( cookie=%" PRIu64", asked=%lu ) -> num_entries = %u", cache_inode_cookie, asked_num_entries, num_entries); if(eod_met == END_OF_DIR) { LogFullDebug(COMPONENT_NFS_READDIR, "+++++++++++++++++++++++++++++++++++++++++> EOD MET "); } /* If nothing was found, return nothing, but if cookie=0, we should return . and .. */ if((num_entries == 0) && (asked_num_entries != 0) && (begin_cookie > 1)) { pres->res_readdirplus3.status = NFS3_OK; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = NULL; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, NULL, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); } else { /* Allocation of the structure for reply */ entry_name_array = (entry_name_array_item_t *) Mem_Alloc_Label(estimated_num_entries * (FSAL_MAX_NAME_LEN + 1), "entry_name_array in nfs3_Readdirplus"); if(entry_name_array == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); return NFS_REQ_DROP; } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries = (entryplus3 *) Mem_Alloc_Label(estimated_num_entries * sizeof(entryplus3), "READDIRPLUS3res_u.resok.reply.entries"); if(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } /* Allocation of the file handles */ fh3_array = (fh3_buffer_item_t *) Mem_Alloc_Label(estimated_num_entries * NFS3_FHSIZE, "Filehandle V3 in nfs3_Readdirplus"); if(fh3_array == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); return NFS_REQ_DROP; } delta = 0; /* manage . and .. */ if(begin_cookie == 0) { /* Fill in '.' */ if(estimated_num_entries > 0) { if((pfsal_handle = cache_inode_get_fsal_handle(dir_pentry, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry * may be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[0]. fileid)); RES_READDIRPLUS_REPLY.entries[0].name = entry_name_array[0]; strcpy(RES_READDIRPLUS_REPLY.entries[0].name, "."); RES_READDIRPLUS_REPLY.entries[0].cookie = 1; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[0]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[0].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[0].name_handle.handle_follows = FALSE; entry_attr = dir_pentry->attributes; /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[0]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[0].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=0 num_entries=%d space_used=%lu maxcount=%lu Name=. FileId=%016llx Cookie=%llu", num_entries, space_used, maxcount, RES_READDIRPLUS_REPLY.entries[0].fileid, RES_READDIRPLUS_REPLY.entries[0].cookie); delta += 1; } } /* Fill in '..' */ if(begin_cookie <= 1) { if(estimated_num_entries > delta) { if((pentry_dot_dot = cache_inode_lookupp_sw(dir_pentry, ht, pclient, pcontext, &cache_status_gethandle, !dir_pentry_unlock)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } if((pfsal_handle = cache_inode_get_fsal_handle(pentry_dot_dot, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[delta]. fileid)); RES_READDIRPLUS_REPLY.entries[delta].name = entry_name_array[delta]; strcpy(RES_READDIRPLUS_REPLY.entries[delta].name, ".."); /* Getting a file handle */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[delta]; if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } RES_READDIRPLUS_REPLY.entries[delta].cookie = 2; RES_READDIRPLUS_REPLY.entries[delta].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[delta].name_handle.handle_follows = FALSE; entry_attr = pentry_dot_dot->attributes; /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[delta]. name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, pentry_dot_dot, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok. reply.entries[delta].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%d num_entries=%d space_used=%lu maxcount=%lu Name=.. FileId=%016llx Cookie=%llu", delta, num_entries, space_used, maxcount, RES_READDIRPLUS_REPLY.entries[delta].fileid, RES_READDIRPLUS_REPLY.entries[delta].cookie); } RES_READDIRPLUS_REPLY.entries[0].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta]); if(num_entries > delta + 1) /* not 0 ??? */ RES_READDIRPLUS_REPLY.entries[delta].nextentry = &(RES_READDIRPLUS_REPLY.entries[delta + 1]); else RES_READDIRPLUS_REPLY.entries[delta].nextentry = NULL; delta += 1; } /* if( begin_cookie == 0 ) */ for(i = delta; i < num_entries + delta; i++) { unsigned long needed; /* maxcount is the size with the FH and attributes overhead, * so entryplus3 is used instead of entry3. The data structures * in nfs23.h have funny padding depending on the arch (32 or 64). * We can't get an accurate estimate by simply using * sizeof(entryplus3). */ /* FIXME: There is still a 4 byte over estimate here on x86_64. */ /** @todo Remove cookie offset calculation in readdir and readdirplus (obsoleted) */ needed = sizeof(reference_entry) + NFS3_FHSIZE + ((strlen(dirent_array[i - delta]->name.name) + 3) & ~3); /* if delta == 1 or 2, then "." and ".." have already been added * to the readdirplus reply. */ if (i == delta) { needed += needed*delta /* size of a dir entry in reply */ - ((strlen(dirent_array[i - delta]->name.name) + 3) & ~3)*delta /* size of filename for current entry */ + 4*delta; /* size of "." and ".." filenames in reply */ } if((space_used += needed) > maxcount) { /* If delta != 0, then we already added "." or ".." to the reply. */ if(i == delta && delta == 0) { /* Not enough room to make even a single reply */ /* after successful cache_inode_readdir, dir_pentry may * be read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_TOOSMALL; return NFS_REQ_OK; } break; /* Make post traitement */ } /* * Get information specific to this entry */ if((pfsal_handle = cache_inode_get_fsal_handle(dirent_array[i - delta]->pentry, &cache_status_gethandle)) == NULL) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = nfs3_Errno(cache_status_gethandle); return NFS_REQ_OK; } /* Now fill in the replyed entryplus3 list */ FSAL_DigestHandle(FSAL_GET_EXP_CTX(pcontext), FSAL_DIGEST_FILEID3, pfsal_handle, (caddr_t) & (RES_READDIRPLUS_REPLY.entries[i].fileid)); FSAL_name2str(&dirent_array[i - delta]->name, entry_name_array[i], FSAL_MAX_NAME_LEN); RES_READDIRPLUS_REPLY.entries[i].name = entry_name_array[i]; LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%u num_entries=%u delta=%u " "num_entries + delta - 1=%u end_cookie=%"PRIu64, i, num_entries, delta, num_entries + delta - 1, end_cookie); if(i != num_entries + delta - 1) RES_READDIRPLUS_REPLY.entries[i].cookie = dirent_array[i - delta]->cookie; else RES_READDIRPLUS_REPLY.entries[i].cookie = end_cookie; RES_READDIRPLUS_REPLY.entries[i].name_attributes.attributes_follow = FALSE; RES_READDIRPLUS_REPLY.entries[i].name_handle.handle_follows = FALSE; entry_attr = dirent_array[i - delta]->pentry->attributes; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.post_op_fh3_u.handle.data.data_val = (char *)fh3_array[i]; /* Compute the NFSv3 file handle */ if(nfs3_FSALToFhandle (&pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i]. name_handle.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); pres->res_readdirplus3.status = NFS3ERR_BADHANDLE; return NFS_REQ_OK; } /* Set PostPoFh3 structure */ pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.handle_follows = TRUE; pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_handle.post_op_fh3_u.handle.data.data_len = sizeof(file_handle_v3_t); nfs_SetPostOpAttr(pcontext, pexport, dirent_array[i - delta]->pentry, &entry_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.entries[i].name_attributes)); LogFullDebug(COMPONENT_NFS_READDIR, "Readdirplus3 -> i=%d, num_entries=%d needed=%lu space_used=%lu maxcount=%lu Name=%s FileId=%016llx Cookie=%llu", i, num_entries, needed, space_used, maxcount, dirent_array[i - delta]->name.name, RES_READDIRPLUS_REPLY.entries[i].fileid, RES_READDIRPLUS_REPLY.entries[i].cookie); RES_READDIRPLUS_REPLY.entries[i].nextentry = NULL; if(i != 0) RES_READDIRPLUS_REPLY.entries[i - 1].nextentry = &(RES_READDIRPLUS_REPLY.entries[i]); } pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; } nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); pres->res_readdirplus3.status = NFS3_OK; if((eod_met == END_OF_DIR) && (i == num_entries + delta)) { /* End of directory */ LogFullDebug(COMPONENT_NFS_READDIR, "============================================================> EOD MET !!!!!!"); pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = TRUE; } else pres->res_readdirplus3.READDIRPLUS3res_u.resok.reply.eof = FALSE; nfs_SetPostOpAttr(pcontext, pexport, dir_pentry, &dir_attr, &(pres->res_readdirplus3.READDIRPLUS3res_u.resok.dir_attributes)); memcpy(pres->res_readdirplus3.READDIRPLUS3res_u.resok.cookieverf, cookie_verifier, sizeof(cookieverf3)); LogFullDebug(COMPONENT_NFS_READDIR, "============================================================"); /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); /* Free the memory */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); return NFS_REQ_OK; } /* If we are here, there was an error */ /* after successful cache_inode_readdir, dir_pentry may be * read locked */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); /* Free the memory */ if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, pclient ) ; Mem_Free((char *)dirent_array); Mem_Free((char *)entry_name_array); Mem_Free((char *)fh3_array); /* Is this a retryable error */ if(nfs_RetryableError(cache_status)) return NFS_REQ_DROP; /* Set failed status */ nfs_SetFailedStatus(pcontext, pexport, NFS_V3, cache_status, NULL, &pres->res_readdirplus3.status, dir_pentry, &(pres->res_readdirplus3.READDIRPLUS3res_u.resfail.dir_attributes), NULL, NULL, NULL, NULL, NULL, NULL); return NFS_REQ_OK; } /* nfs3_Readdirplus */
cache_entry_t *cache_inode_get_located(cache_inode_fsal_data_t * pfsdata, cache_entry_t * plocation, cache_inode_policy_t policy, fsal_attrib_list_t * pattr, hash_table_t * ht, cache_inode_client_t * pclient, fsal_op_context_t * pcontext, cache_inode_status_t * pstatus) { hash_buffer_t key, value; cache_entry_t *pentry = NULL; fsal_status_t fsal_status; cache_inode_create_arg_t create_arg; cache_inode_file_type_t type; int hrc = 0; fsal_attrib_list_t fsal_attributes; cache_inode_fsal_data_t *ppoolfsdata = NULL; memset(&create_arg, 0, sizeof(create_arg)); /* Set the return default to CACHE_INODE_SUCCESS */ *pstatus = CACHE_INODE_SUCCESS; /* stats */ /* cache_invalidate calls this with no context or client */ if (pclient) { pclient->stat.nb_call_total += 1; pclient->stat.func_stats.nb_call[CACHE_INODE_GET] += 1; } /* Turn the input to a hash key */ if(cache_inode_fsaldata_2_key(&key, pfsdata, pclient)) { *pstatus = CACHE_INODE_UNAPPROPRIATED_KEY; /* stats */ /* cache_invalidate calls this with no context or client */ if (pclient) { pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; ppoolfsdata = (cache_inode_fsal_data_t *) key.pdata; ReleaseToPool(ppoolfsdata, &pclient->pool_key); } return NULL; } switch (hrc = HashTable_Get(ht, &key, &value)) { case HASHTABLE_SUCCESS: /* Entry exists in the cache and was found */ pentry = (cache_entry_t *) value.pdata; /* return attributes additionally */ *pattr = pentry->attributes; if ( !pclient ) { /* invalidate. Just return it to mark it stale and go on. */ return( pentry ); } break; case HASHTABLE_ERROR_NO_SUCH_KEY: if ( !pclient ) { /* invalidate. Just return */ return( NULL ); } /* Cache miss, allocate a new entry */ /* XXX I do not think this can happen with avl dirent cache */ if(pfsdata->cookie != DIR_START) { /* added for sanity check */ LogDebug(COMPONENT_CACHE_INODE, "cache_inode_get: pfsdata->cookie != DIR_START (=%"PRIu64") on object whose type is %u", pfsdata->cookie, cache_inode_fsal_type_convert(fsal_attributes.type)); pfsdata->cookie = DIR_START; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); /* redo the call */ return cache_inode_get(pfsdata, policy, pattr, ht, pclient, pcontext, pstatus); } /* First, call FSAL to know what the object is */ fsal_attributes.asked_attributes = pclient->attrmask; fsal_status = FSAL_getattrs(&pfsdata->handle, pcontext, &fsal_attributes); if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_get: cache_inode_status=%u fsal_status=%u,%u ", *pstatus, fsal_status.major, fsal_status.minor); if(fsal_status.major == ERR_FSAL_STALE) { char handle_str[256]; snprintHandle(handle_str, 256, &pfsdata->handle); LogEvent(COMPONENT_CACHE_INODE, "cache_inode_get: Stale FSAL File Handle %s, fsal_status=(%u,%u)", handle_str, fsal_status.major, fsal_status.minor); *pstatus = CACHE_INODE_FSAL_ESTALE; } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* The type has to be set in the attributes */ if(!FSAL_TEST_MASK(fsal_attributes.supported_attributes, FSAL_ATTR_TYPE)) { *pstatus = CACHE_INODE_FSAL_ERROR; /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* Get the cache_inode file type */ type = cache_inode_fsal_type_convert(fsal_attributes.type); if(type == SYMBOLIC_LINK) { if( CACHE_INODE_KEEP_CONTENT( policy ) ) { FSAL_CLEAR_MASK(fsal_attributes.asked_attributes); FSAL_SET_MASK(fsal_attributes.asked_attributes, pclient->attrmask); fsal_status = FSAL_readlink(&pfsdata->handle, pcontext, &create_arg.link_content, &fsal_attributes); } else { fsal_status.major = ERR_FSAL_NO_ERROR ; fsal_status.minor = 0 ; } if(FSAL_IS_ERROR(fsal_status)) { *pstatus = cache_inode_error_convert(fsal_status); /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); if(fsal_status.major == ERR_FSAL_STALE) { cache_inode_status_t kill_status; LogEvent(COMPONENT_CACHE_INODE, "cache_inode_get: Stale FSAL File Handle detected for pentry = %p, fsal_status=(%u,%u)", pentry, fsal_status.major, fsal_status.minor); if(cache_inode_kill_entry(pentry, NO_LOCK, ht, pclient, &kill_status) != CACHE_INODE_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "cache_inode_get: Could not kill entry %p, status = %u, fsal_status=(%u,%u)", pentry, kill_status, fsal_status.major, fsal_status.minor); *pstatus = CACHE_INODE_FSAL_ESTALE; } return NULL; } } /* Add the entry to the cache */ if ( type == 1) LogCrit(COMPONENT_CACHE_INODE,"inode get"); if((pentry = cache_inode_new_entry( pfsdata, &fsal_attributes, type, policy, &create_arg, NULL, /* never used to add a new DIR_CONTINUE within this function */ ht, pclient, pcontext, FALSE, /* This is a population, not a creation */ pstatus ) ) == NULL ) { /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; } /* Set the returned attributes */ *pattr = fsal_attributes; /* Now, exit the switch/case and returns */ break; default: /* This should not happened */ *pstatus = CACHE_INODE_INVALID_ARGUMENT; LogCrit(COMPONENT_CACHE_INODE, "cache_inode_get returning CACHE_INODE_INVALID_ARGUMENT - this should not have happened"); if ( !pclient ) { /* invalidate. Just return */ return( NULL ); } /* stats */ pclient->stat.func_stats.nb_err_unrecover[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return NULL; break; } /* Want to ASSERT pclient at this point */ *pstatus = CACHE_INODE_SUCCESS; if (pentry->object.symlink != NULL) { int stop_here; stop_here = 1; if (stop_here) { stop_here = 2; } } /* valid the found entry, if this is not feasable, returns nothing to the client */ if( plocation != NULL ) { if( plocation != pentry ) { P_w(&pentry->lock); if((*pstatus = cache_inode_valid(pentry, CACHE_INODE_OP_GET, pclient)) != CACHE_INODE_SUCCESS) { V_w(&pentry->lock); pentry = NULL; } V_w(&pentry->lock); } } /* stats */ pclient->stat.func_stats.nb_success[CACHE_INODE_GET] += 1; /* Free this key */ cache_inode_release_fsaldata_key(&key, pclient); return pentry; } /* cache_inode_get_located */
/** * * cache_inode_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 */