cache_entry_t *cache_inode_weakref_get(gweakref_t *ref, uint32_t flags) { pthread_rwlock_t *lock = NULL; cache_entry_t *entry = (cache_entry_t *) gweakref_lookupex(cache_inode_wt, ref, &lock); if (entry) { if (cache_inode_lru_ref(entry, flags) != CACHE_INODE_SUCCESS) { pthread_rwlock_unlock(lock); return NULL; } pthread_rwlock_unlock(lock); } return (entry); }
/** * * @brief invalidates an entry in the cache * * This function invalidates the related cache entry correponding to a * FSAL handle. It is designed to be called when an FSAL upcall is * triggered. * * @param[in] handle FSAL handle for the entry to be invalidated * @param[out] status Returned status * * @retval CACHE_INODE_SUCCESS if operation is a success * @retval CACHE_INODE_INVALID_ARGUMENT bad parameter(s) as input * @retval CACHE_INODE_NOT_FOUND if entry is not cached * @retval CACHE_INODE_STATE_CONFLICT if invalidating this entry would * result is state conflict * @retval CACHE_INODE_INCONSISTENT_ENTRY if entry is not consistent * @retval Other errors shows a FSAL error. * */ cache_inode_status_t cache_inode_invalidate(cache_inode_fsal_data_t *fsal_data, cache_inode_status_t *status, uint32_t flags) { hash_buffer_t key, value; int rc = 0 ; cache_entry_t *entry; struct hash_latch latch; if (status == NULL || fsal_data == NULL) { *status = CACHE_INODE_INVALID_ARGUMENT; goto out; } /* Locate the entry in the cache */ FSAL_ExpandHandle(NULL, /* pcontext but not used... */ FSAL_DIGEST_SIZEOF, &fsal_data->fh_desc); /* Turn the input to a hash key */ key.pdata = fsal_data->fh_desc.start; key.len = fsal_data->fh_desc.len; if ((rc = HashTable_GetLatch(fh_to_cache_entry_ht, &key, &value, FALSE, &latch)) == HASHTABLE_ERROR_NO_SUCH_KEY) { /* Entry is not cached */ HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch); *status = CACHE_INODE_NOT_FOUND; return *status; } else if (rc != HASHTABLE_SUCCESS) { LogCrit(COMPONENT_CACHE_INODE, "Unexpected error %u while calling HashTable_GetLatch", rc) ; *status = CACHE_INODE_INVALID_ARGUMENT; goto out; } entry = value.pdata; if (cache_inode_lru_ref(entry, 0) != CACHE_INODE_SUCCESS) { HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch); *status = CACHE_INODE_NOT_FOUND; return *status; } HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch); PTHREAD_RWLOCK_WRLOCK(&entry->attr_lock); PTHREAD_RWLOCK_WRLOCK(&entry->content_lock); /* We can invalidate entries with state just fine. We force Cache_inode to contact the FSAL for any use of content or attributes, and if the FSAL indicates the entry is stale, it can be disposed of then. */ /* We should have a way to invalidate content and attributes separately. Or at least a way to invalidate attributes without invalidating content (since any change in content really ought to modify mtime, at least.) */ if ((flags & CACHE_INODE_INVALIDATE_CLEARBITS) != 0) atomic_clear_uint32_t_bits(&entry->flags, CACHE_INODE_TRUST_ATTRS | CACHE_INODE_DIR_POPULATED | CACHE_INODE_TRUST_CONTENT); /* The main reason for holding the lock at this point is so we don't clear the trust bits while someone is populating the directory or refreshing attributes. */ if (((flags & CACHE_INODE_INVALIDATE_CLOSE) != 0) && (entry->type == REGULAR_FILE)) { cache_inode_close(entry, NULL, (CACHE_INODE_FLAG_REALLYCLOSE | CACHE_INODE_FLAG_CONTENT_HAVE | CACHE_INODE_FLAG_CONTENT_HOLD), status); } PTHREAD_RWLOCK_UNLOCK(&entry->attr_lock); PTHREAD_RWLOCK_UNLOCK(&entry->content_lock); cache_inode_lru_unref(entry, 0); out: /* Memory copying attributes with every call is expensive. Let's not do it. */ return (*status); } /* cache_inode_invalidate */
int nfs4_op_restorefh(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_restorefh"; /* First of all, set the reply to zero to make sure it contains no parasite information */ memset(resp, 0, sizeof(struct nfs_resop4)); resp->resop = NFS4_OP_RESTOREFH; res_RESTOREFH.status = NFS4_OK; /* If there is no currentFH, teh return an error */ if(nfs4_Is_Fh_Empty(&(data->savedFH))) { /* There is no current FH, return NFS4ERR_RESTOREFH (cg RFC3530, page 202) */ res_RESTOREFH.status = NFS4ERR_RESTOREFH; return res_RESTOREFH.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->savedFH))) { res_RESTOREFH.status = NFS4ERR_BADHANDLE; return res_RESTOREFH.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->savedFH))) { res_RESTOREFH.status = NFS4ERR_FHEXPIRED; return res_RESTOREFH.status; } /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */ if(data->pexport == NULL) { res_RESTOREFH.status = nfs4_SetCompoundExport(data); if(res_RESTOREFH.status != NFS4_OK) { LogCrit(COMPONENT_NFS_V4, "Error %d in nfs4_SetCompoundExport", res_RESTOREFH.status); return res_RESTOREFH.status; } } /* Copy the data from current FH to saved FH */ memcpy(data->currentFH.nfs_fh4_val, data->savedFH.nfs_fh4_val, data->savedFH.nfs_fh4_len); data->currentFH.nfs_fh4_len = data->savedFH.nfs_fh4_len; /* If current and saved entry are identical, get no references and make no changes. */ if (data->current_entry == data->saved_entry) { goto out; } if (data->current_entry) { cache_inode_put(data->current_entry); data->current_entry = NULL; } data->current_entry = data->saved_entry; data->current_filetype = data->saved_filetype; /* Take another reference. As of now the filehandle is both saved and current and both must be counted. Protect in case of pseudofs handle. */ if (data->current_entry) { if (cache_inode_lru_ref(data->current_entry, LRU_FLAG_NONE) != CACHE_INODE_SUCCESS) { resp->nfs_resop4_u.opgetfh.status = NFS4ERR_STALE; data->current_entry = NULL; return resp->nfs_resop4_u.opgetfh.status; } } out: if(isFullDebug(COMPONENT_NFS_V4)) { char str[LEN_FH_STR]; sprint_fhandle4(str, &data->currentFH); LogFullDebug(COMPONENT_NFS_V4, "RESTORE FH: Current FH %s", str); } return NFS4_OK; } /* nfs4_op_restorefh */
int _9p_walk(struct _9p_request_data *req9p, void *worker_data, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; unsigned int i = 0; u16 *msgtag = NULL; u32 *fid = NULL; u32 *newfid = NULL; u16 *nwname = NULL; u16 *wnames_len; char *wnames_str; uint64_t fileid; cache_inode_status_t cache_status; cache_entry_t *pentry = NULL; char name[MAXNAMLEN]; u16 *nwqid; struct _9p_fid *pfid = NULL; struct _9p_fid *pnewfid = NULL; /* Now Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, newfid, u32); _9p_getptr(cursor, nwname, u16); LogDebug(COMPONENT_9P, "TWALK: tag=%u fid=%u newfid=%u nwname=%u", (u32) *msgtag, *fid, *newfid, *nwname); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); if (*newfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, worker_data, msgtag, EIO, plenout, preply); } op_ctx = &pfid->op_context; pnewfid = gsh_calloc(1, sizeof(struct _9p_fid)); if (pnewfid == NULL) return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); /* Is this a lookup or a fid cloning operation ? */ if (*nwname == 0) { /* Cloning operation */ memcpy((char *)pnewfid, (char *)pfid, sizeof(struct _9p_fid)); /* Set the new fid id */ pnewfid->fid = *newfid; /* This is not a TATTACH fid */ pnewfid->from_attach = false; /* Increments refcount */ (void) cache_inode_lru_ref(pnewfid->pentry, LRU_REQ_STALE_OK); } else { /* the walk is in fact a lookup */ pentry = pfid->pentry; for (i = 0; i < *nwname; i++) { _9p_getstr(cursor, wnames_len, wnames_str); snprintf(name, MAXNAMLEN, "%.*s", *wnames_len, wnames_str); LogDebug(COMPONENT_9P, "TWALK (lookup): tag=%u fid=%u newfid=%u (component %u/%u :%s)", (u32) *msgtag, *fid, *newfid, i + 1, *nwname, name); if (pnewfid->pentry == pentry) pnewfid->pentry = NULL; /* refcount +1 */ cache_status = cache_inode_lookup(pentry, name, &pnewfid->pentry); if (pnewfid->pentry == NULL) { gsh_free(pnewfid); return _9p_rerror(req9p, worker_data, msgtag, _9p_tools_errno(cache_status), plenout, preply); } if (pentry != pfid->pentry) cache_inode_put(pentry); pentry = pnewfid->pentry; } pnewfid->fid = *newfid; pnewfid->op_context = pfid->op_context; pnewfid->ppentry = pfid->pentry; strncpy(pnewfid->name, name, MAXNAMLEN-1); /* gdata ref is not hold : the pfid, which use same gdata */ /* will be clunked after pnewfid */ /* This clunk release the gdata */ pnewfid->gdata = pfid->gdata; /* This is not a TATTACH fid */ pnewfid->from_attach = false; cache_status = cache_inode_fileid(pnewfid->pentry, &fileid); if (cache_status != CACHE_INODE_SUCCESS) { gsh_free(pnewfid); return _9p_rerror(req9p, worker_data, msgtag, _9p_tools_errno(cache_status), plenout, preply); } /* Build the qid */ /* No cache, we want the client to stay synchronous * with the server */ pnewfid->qid.version = 0; pnewfid->qid.path = fileid; pnewfid->specdata.xattr.xattr_id = 0; pnewfid->specdata.xattr.xattr_content = NULL; switch (pnewfid->pentry->type) { case REGULAR_FILE: case CHARACTER_FILE: case BLOCK_FILE: case SOCKET_FILE: case FIFO_FILE: pnewfid->qid.type = _9P_QTFILE; break; case SYMBOLIC_LINK: pnewfid->qid.type = _9P_QTSYMLINK; break; case DIRECTORY: pnewfid->qid.type = _9P_QTDIR; break; default: LogMajor(COMPONENT_9P, "implementation error, you should not see this message !!!!!!"); gsh_free(pnewfid); return _9p_rerror(req9p, worker_data, msgtag, EINVAL, plenout, preply); break; } } /* keep info on new fid */ req9p->pconn->fids[*newfid] = pnewfid; /* As much qid as requested fid */ nwqid = nwname; /* Hold refcount on gdata */ uid2grp_hold_group_data(pnewfid->gdata); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RWALK); _9p_setptr(cursor, msgtag, u16); _9p_setptr(cursor, nwqid, u16); for (i = 0; i < *nwqid; i++) { /** @todo: should be different qids * for each directory walked through */ _9p_setqid(cursor, pnewfid->qid); } _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RWALK: tag=%u fid=%u newfid=%u nwqid=%u fileid=%llu pentry=%p refcount=%i", (u32) *msgtag, *fid, *newfid, *nwqid, (unsigned long long)pnewfid->qid.path, pnewfid->pentry, pnewfid->pentry->lru.refcnt); return 1; }
int _9p_xattrwalk(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *attrfid = NULL; u16 *name_len; char *name_str; size_t attrsize = 0; fsal_status_t fsal_status; char name[MAXNAMLEN]; fsal_xattrent_t xattrs_arr[XATTRS_ARRAY_LEN]; int eod_met = false; unsigned int nb_xattrs_read = 0; unsigned int i = 0; char *xattr_cursor = NULL; unsigned int tmplen = 0; struct _9p_fid *pfid = NULL; struct _9p_fid *pxattrfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, attrfid, u32); LogDebug(COMPONENT_9P, "TXATTRWALK: tag=%u fid=%u attrfid=%u", (u32) *msgtag, *fid, *attrfid); _9p_getstr(cursor, name_len, name_str); if (*name_len == 0) LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=(LIST XATTR)", (u32) *msgtag, *fid, *attrfid); else LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=%.*s", (u32) *msgtag, *fid, *attrfid, *name_len, name_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); if (*attrfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } pxattrfid = gsh_calloc(1, sizeof(struct _9p_fid)); if (pxattrfid == NULL) return _9p_rerror(req9p, msgtag, ENOMEM, plenout, preply); /* set op_ctx, it will be useful if FSAL is later called */ _9p_init_opctx(pfid, req9p); /* Initiate xattr's fid by copying file's fid in it */ memcpy((char *)pxattrfid, (char *)pfid, sizeof(struct _9p_fid)); snprintf(name, MAXNAMLEN, "%.*s", *name_len, name_str); pxattrfid->specdata.xattr.xattr_content = gsh_malloc(XATTR_BUFFERSIZE); if (pxattrfid->specdata.xattr.xattr_content == NULL) { gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ENOMEM, plenout, preply); } if (*name_len == 0) { /* xattrwalk is used with an empty name, * this is a listxattr request */ fsal_status = pxattrfid->pentry->obj_handle->obj_ops.list_ext_attrs( pxattrfid->pentry->obj_handle, FSAL_XATTR_RW_COOKIE, /* Start with RW cookie, * hiding RO ones */ xattrs_arr, XATTRS_ARRAY_LEN, /** @todo fix static length */ &nb_xattrs_read, &eod_met); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } /* if all xattrent are not read, * returns ERANGE as listxattr does */ if (eod_met != true) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } xattr_cursor = pxattrfid->specdata.xattr.xattr_content; attrsize = 0; for (i = 0; i < nb_xattrs_read; i++) { tmplen = snprintf(xattr_cursor, MAXNAMLEN, "%s", xattrs_arr[i].xattr_name); xattr_cursor[tmplen] = '\0'; /* Just to be sure */ /* +1 for trailing '\0' */ xattr_cursor += tmplen + 1; attrsize += tmplen + 1; /* Make sure not to go beyond the buffer */ if (attrsize > XATTR_BUFFERSIZE) { gsh_free(pxattrfid->specdata.xattr. xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); } } } else { /* xattrwalk has a non-empty name, use regular setxattr */ fsal_status = pxattrfid->pentry->obj_handle->obj_ops. getextattr_id_by_name(pxattrfid->pentry->obj_handle, name, &pxattrfid->specdata.xattr.xattr_id); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); /* ENOENT for xattr is ENOATTR */ if (fsal_status.major == ERR_FSAL_NOENT) return _9p_rerror(req9p, msgtag, ENOATTR, plenout, preply); else return _9p_rerror(req9p, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } fsal_status = pxattrfid->pentry->obj_handle->obj_ops. getextattr_value_by_name(pxattrfid->pentry->obj_handle, name, pxattrfid->specdata.xattr. xattr_content, XATTR_BUFFERSIZE, &attrsize); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); /* fsal_status.minor is a valid errno code */ return _9p_rerror(req9p, msgtag, fsal_status.minor, plenout, preply); } } req9p->pconn->fids[*attrfid] = pxattrfid; /* Increments refcount as we're manually making a new copy */ (void) cache_inode_lru_ref(pfid->pentry, LRU_REQ_STALE_OK); /* hold reference on gdata */ uid2grp_hold_group_data(pxattrfid->gdata); get_gsh_export_ref(pfid->export); get_9p_user_cred_ref(pfid->ucred); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRWALK); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, attrsize, u64); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRWALK: tag=%u fid=%u attrfid=%u name=%.*s size=%llu", (u32) *msgtag, *fid, *attrfid, *name_len, name_str, (unsigned long long)attrsize); return 1; } /* _9p_xattrwalk */
/** * * @brief Gets an entry by using its fsdata as a key and caches it if needed. * * Gets an entry by using its fsdata as a key and caches it if needed. * * If a cache entry is returned, its refcount is incremented by one. * * It turns out we do need cache_inode_get_located functionality for * cases like lookupp on an entry returning itself when it isn't a * root. Therefore, if the 'associated' parameter is equal to the got * cache entry, a reference count is incremented but the structure * pointed to by attr is NOT filled in. * * @param[in] fsdata File system data * @param[out] attr The attributes of the got entry * @param[in] context FSAL credentials * @param[in] associated Entry that may be equal to the got entry * @param[out] status Returned status * * @return If successful, the pointer to the entry; NULL otherwise * */ cache_entry_t * cache_inode_get(cache_inode_fsal_data_t *fsdata, fsal_attrib_list_t *attr, fsal_op_context_t *context, cache_entry_t *associated, cache_inode_status_t *status) { hash_buffer_t key, value; cache_entry_t *entry = NULL; fsal_status_t fsal_status = {0, 0}; cache_inode_create_arg_t create_arg = { .newly_created_dir = FALSE }; cache_inode_file_type_t type = UNASSIGNED; hash_error_t hrc = 0; fsal_attrib_list_t fsal_attributes; fsal_handle_t *file_handle; struct hash_latch latch; /* Set the return default to CACHE_INODE_SUCCESS */ *status = CACHE_INODE_SUCCESS; /* Turn the input to a hash key on our own. */ key.pdata = fsdata->fh_desc.start; key.len = fsdata->fh_desc.len; hrc = HashTable_GetLatch(fh_to_cache_entry_ht, &key, &value, FALSE, &latch); if ((hrc != HASHTABLE_SUCCESS) && (hrc != HASHTABLE_ERROR_NO_SUCH_KEY)) { /* This should not happened */ *status = CACHE_INODE_HASH_TABLE_ERROR; LogCrit(COMPONENT_CACHE_INODE, "Hash access failed with code %d" " - this should not have happened", hrc); return NULL; } if (hrc == HASHTABLE_SUCCESS) { /* Entry exists in the cache and was found */ entry = value.pdata; /* take an extra reference within the critical section */ if (cache_inode_lru_ref(entry, LRU_REQ_INITIAL) != CACHE_INODE_SUCCESS) { /* Dead entry. Treat like a lookup failure. */ entry = NULL; } else { if (entry == associated) { /* Take a quick exit so we don't invert lock ordering. */ HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch); return entry; } } } HashTable_ReleaseLatched(fh_to_cache_entry_ht, &latch); if (!context) { /* Upcalls have no access to fsal_op_context_t, so just return the entry without revalidating it or creating a new one. */ if (entry == NULL) { *status = CACHE_INODE_NOT_FOUND; } return entry; } if (!entry) { /* Cache miss, allocate a new entry */ file_handle = (fsal_handle_t *) fsdata->fh_desc.start; /* First, call FSAL to know what the object is */ fsal_attributes.asked_attributes = cache_inode_params.attrmask; fsal_status = FSAL_getattrs(file_handle, context, &fsal_attributes); if (FSAL_IS_ERROR(fsal_status)) { *status = cache_inode_error_convert(fsal_status); LogDebug(COMPONENT_CACHE_INODE, "cache_inode_get: cache_inode_status=%u " "fsal_status=%u,%u ", *status, fsal_status.major, fsal_status.minor); return NULL; } /* The type has to be set in the attributes */ if (!FSAL_TEST_MASK(fsal_attributes.supported_attributes, FSAL_ATTR_TYPE)) { *status = CACHE_INODE_FSAL_ERROR; return NULL; } /* Get the cache_inode file type */ type = cache_inode_fsal_type_convert(fsal_attributes.type); if (type == SYMBOLIC_LINK) { fsal_attributes.asked_attributes = cache_inode_params.attrmask; fsal_status = FSAL_readlink(file_handle, context, &create_arg.link_content, &fsal_attributes); if (FSAL_IS_ERROR(fsal_status)) { *status = cache_inode_error_convert(fsal_status); return NULL; } } if ((entry = cache_inode_new_entry(fsdata, &fsal_attributes, type, &create_arg, status)) == NULL) { return NULL; } } *status = CACHE_INODE_SUCCESS; /* This is the replacement for cache_inode_renew_entry. Rather than calling that function at the start of every cache_inode call with the inode locked, we call cache_inode_check trust to perform 'heavyweight' (timed expiration of cached attributes, getattr-based directory trust) checks the first time after getting an inode. It does all of the checks read-locked and only acquires a write lock if there's something requiring a change. There is a second light-weight check done before use of cached data that checks whether the bits saying that inode attributes or inode content are trustworthy have been cleared by, for example, FSAL_CB. To summarize, the current implementation is that policy-based trust of validity is checked once per logical series of operations at cache_inode_get, and asynchronous trust is checked with use (when the attributes are locked for reading, for example.) */ if ((*status = cache_inode_check_trust(entry, context)) != CACHE_INODE_SUCCESS) { goto out_put; } /* Set the returned attributes */ *status = cache_inode_lock_trust_attrs(entry, context); /* cache_inode_lock_trust_attrs may fail, in that case, the attributes are wrong and pthread_rwlock_unlock can't be called again */ if(*status != CACHE_INODE_SUCCESS) { goto out_put; } *attr = entry->attributes; pthread_rwlock_unlock(&entry->attr_lock); return entry; out_put: cache_inode_put(entry); entry = NULL; return entry; } /* cache_inode_get */
int _9p_xattrwalk(struct _9p_request_data *req9p, void *worker_data, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *attrfid = NULL; u16 *name_len; char *name_str; u64 attrsize = 0LL; fsal_status_t fsal_status; char name[MAXNAMLEN]; fsal_xattrent_t xattrs_tab[255]; int eod_met = false; unsigned int nb_xattrs_read = 0; unsigned int i = 0; char *xattr_cursor = NULL; unsigned int tmplen = 0; struct _9p_fid *pfid = NULL; struct _9p_fid *pxattrfid = NULL; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, attrfid, u32); LogDebug(COMPONENT_9P, "TXATTRWALK: tag=%u fid=%u attrfid=%u", (u32) *msgtag, *fid, *attrfid); _9p_getstr(cursor, name_len, name_str); if (*name_len == 0) LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=(LIST XATTR)", (u32) *msgtag, *fid, *attrfid); else LogDebug(COMPONENT_9P, "TXATTRWALK (component): tag=%u fid=%u attrfid=%u name=%.*s", (u32) *msgtag, *fid, *attrfid, *name_len, name_str); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); if (*attrfid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, worker_data, msgtag, EIO, plenout, preply); } pxattrfid = gsh_calloc(1, sizeof(struct _9p_fid)); if (pxattrfid == NULL) return _9p_rerror(req9p, worker_data, msgtag, ENOMEM, plenout, preply); /* Initiate xattr's fid by copying file's fid in it */ memcpy((char *)pxattrfid, (char *)pfid, sizeof(struct _9p_fid)); snprintf(name, MAXNAMLEN, "%.*s", *name_len, name_str); pxattrfid->specdata.xattr.xattr_content = gsh_malloc(XATTR_BUFFERSIZE); if (pxattrfid->specdata.xattr.xattr_content == NULL) { gsh_free(pxattrfid); return _9p_rerror(req9p, worker_data, msgtag, ENOMEM, plenout, preply); } if (*name_len == 0) { /* xattrwalk is used with an empty name, * this is a listxattr request */ fsal_status = pxattrfid->pentry->obj_handle->ops->list_ext_attrs( pxattrfid->pentry->obj_handle, &pfid->op_context, FSAL_XATTR_RW_COOKIE, /* Start with RW cookie, * hiding RO ones */ xattrs_tab, 100, /* static array size for now */ &nb_xattrs_read, &eod_met); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, worker_data, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } /* if all xattrent are not read, * returns ERANGE as listxattr does */ if (eod_met != TRUE) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); } xattr_cursor = pxattrfid->specdata.xattr.xattr_content; attrsize = 0LL; for (i = 0; i < nb_xattrs_read; i++) { tmplen = snprintf(xattr_cursor, MAXNAMLEN, "%s", xattrs_tab[i].xattr_name); xattr_cursor[tmplen] = '\0'; /* Just to be sure */ /* +1 for trailing '\0' */ xattr_cursor += tmplen + 1; attrsize += tmplen + 1; /* Make sure not to go beyond the buffer */ if (attrsize > XATTR_BUFFERSIZE) { gsh_free(pxattrfid->specdata.xattr. xattr_content); gsh_free(pxattrfid); return _9p_rerror(req9p, worker_data, msgtag, ERANGE, plenout, preply); } } } else { /* xattrwalk has a non-empty name, use regular setxattr */ fsal_status = pxattrfid->pentry->obj_handle->ops-> getextattr_id_by_name(pxattrfid->pentry->obj_handle, &pfid->op_context, name, &pxattrfid->specdata.xattr.xattr_id); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); /* Hook dedicated to ACL management. When attributes * system.posix_acl_access is used, it can't be * created, but can be written anyway. * To do this, return ENODATA instead of ENOATTR * In this case, we do created what's needed to * setxattr() into the special xattr */ if (!strncmp(name, "system.posix_acl_access", MAXNAMLEN)) return _9p_rerror(req9p, worker_data, msgtag, ENODATA, plenout, preply); /* ENOENT for xattr is ENOATTR */ if (fsal_status.major == ERR_FSAL_NOENT) return _9p_rerror(req9p, worker_data, msgtag, ENOATTR, plenout, preply); else return _9p_rerror(req9p, worker_data, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } fsal_status = pxattrfid->pentry->obj_handle->ops-> getextattr_value_by_name(pxattrfid->pentry->obj_handle, &pfid->op_context, name, pxattrfid->specdata.xattr. xattr_content, XATTR_BUFFERSIZE, &attrsize); if (FSAL_IS_ERROR(fsal_status)) { gsh_free(pxattrfid->specdata.xattr.xattr_content); gsh_free(pxattrfid); if (fsal_status.minor == ENODATA) { return _9p_rerror(req9p, worker_data, msgtag, ENODATA, plenout, preply); } return _9p_rerror(req9p, worker_data, msgtag, _9p_tools_errno (cache_inode_error_convert (fsal_status)), plenout, preply); } } req9p->pconn->fids[*attrfid] = pxattrfid; /* Increments refcount so it won't fall below 0 when we clunk later */ cache_inode_lru_ref(pxattrfid->pentry, LRU_REQ_INITIAL); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RXATTRWALK); _9p_setptr(cursor, msgtag, u16); _9p_setvalue(cursor, attrsize, u64); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RXATTRWALK: tag=%u fid=%u attrfid=%u name=%.*s size=%llu", (u32) *msgtag, *fid, *attrfid, *name_len, name_str, (unsigned long long)attrsize); return 1; } /* _9p_xattrwalk */