/** * * nfs4_op_getattr: Gets attributes for an entry in the FSAL. * * Gets attributes for an entry in the FSAL. * * @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 * */ int nfs4_op_getattr(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { fsal_attrib_list_t attr; cache_inode_status_t cache_status; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_getattr"; /* This is a NFS4_OP_GETTAR */ resp->resop = NFS4_OP_GETATTR; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_GETATTR4.status = NFS4ERR_NOFILEHANDLE; return NFS4ERR_NOFILEHANDLE; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_GETATTR4.status = NFS4ERR_BADHANDLE; return NFS4ERR_BADHANDLE; } /* Tests if teh Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_GETATTR4.status = NFS4ERR_FHEXPIRED; return NFS4ERR_FHEXPIRED; } /* Pseudo Fs management */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_getattr_pseudo(op, data, resp); /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_getattr_xattr(op, data, resp); if(isFullDebug(COMPONENT_NFS_V4)) { char str[LEN_FH_STR]; sprint_fhandle4(str, &data->currentFH); LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_GETATTR: Current FH %s", str); } /* Sanity check: if no attributes are wanted, nothing is to be done. * In this case NFS4_OK is to be returned */ if(arg_GETATTR4.attr_request.bitmap4_len == 0) { res_GETATTR4.status = NFS4_OK; return res_GETATTR4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access_Bitmap(&arg_GETATTR4.attr_request, FATTR4_ATTR_READ)) { res_GETATTR4.status = NFS4ERR_INVAL; return res_GETATTR4.status; } if( !nfs4_bitmap4_Remove_Unsupported( &arg_GETATTR4.attr_request ) ) { res_GETATTR4.status = NFS4ERR_SERVERFAULT ; return res_GETATTR4.status; } /* * Get attributes. */ if(cache_inode_getattr(data->current_entry, &attr, data->ht, data->pclient, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) { if(nfs4_FSALattr_To_Fattr(data->pexport, &attr, &(res_GETATTR4.GETATTR4res_u.resok4.obj_attributes), data, &(data->currentFH), &(arg_GETATTR4.attr_request)) != 0) res_GETATTR4.status = NFS4ERR_SERVERFAULT; else res_GETATTR4.status = NFS4_OK; return res_GETATTR4.status; } res_GETATTR4.status = nfs4_Errno(cache_status); return res_GETATTR4.status; } /* nfs4_op_getattr */
int nfs_Fsstat(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t * pres) { fsal_dynamicfsinfo_t dynamicinfo; cache_inode_status_t cache_status; cache_entry_t *pentry = NULL; fsal_attrib_list_t attr; int rc = NFS_REQ_OK; if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(preq->rq_vers, &(parg->arg_statfs2), &(parg->arg_fsstat3.fsroot), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Fsstat handle: %s", str); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow = FALSE; } /* convert file handle to vnode */ if((pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_statfs2), &(parg->arg_fsstat3.fsroot), NULL, &(pres->res_statfs2.status), &(pres->res_fsstat3.status), NULL, NULL, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ /* return NFS_REQ_DROP ; */ goto out; } /* Get statistics and convert from cache */ if((cache_status = cache_inode_statfs(pentry, &dynamicinfo, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { /* This call is costless, the pentry was cached during call to nfs_FhandleToCache */ if((cache_status = cache_inode_getattr(pentry, &attr, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_bytes = %zu dynamicinfo.free_bytes = %zu dynamicinfo.avail_bytes = %zu", dynamicinfo.total_bytes, dynamicinfo.free_bytes, dynamicinfo.avail_bytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_files = %llu dynamicinfo.free_files = %llu dynamicinfo.avail_files = %llu", dynamicinfo.total_files, dynamicinfo.free_files, dynamicinfo.avail_files); switch (preq->rq_vers) { case NFS_V2: pres->res_statfs2.STATFS2res_u.info.tsize = NFS2_MAXDATA; pres->res_statfs2.STATFS2res_u.info.bsize = DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.blocks = dynamicinfo.total_bytes / DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.bfree = dynamicinfo.free_bytes / DEV_BSIZE; pres->res_statfs2.STATFS2res_u.info.bavail = dynamicinfo.avail_bytes / DEV_BSIZE; pres->res_statfs2.status = NFS_OK; break; case NFS_V3: nfs_SetPostOpAttr(pexport, &attr, &(pres->res_fsstat3.FSSTAT3res_u .resok.obj_attributes)); pres->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes; pres->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files; pres->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files; pres->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files; pres->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0; /* volatile FS */ pres->res_fsstat3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu", pres->res_fsstat3.FSSTAT3res_u.resok.tbytes, pres->res_fsstat3.FSSTAT3res_u.resok.fbytes, pres->res_fsstat3.FSSTAT3res_u.resok.abytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu", pres->res_fsstat3.FSSTAT3res_u.resok.tfiles, pres->res_fsstat3.FSSTAT3res_u.resok.ffiles, pres->res_fsstat3.FSSTAT3res_u.resok.afiles); break; } rc = NFS_REQ_OK; goto out; } } /* At this point we met an error */ if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_statfs2.status, &pres->res_fsstat3.status, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; out: /* return references */ if (pentry) cache_inode_put(pentry); return (rc); } /* nfs_Fsstat */
int nfs4_op_link(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_link"; cache_entry_t * dir_pentry = NULL; cache_entry_t * file_pentry = NULL; cache_inode_status_t cache_status; fsal_attrib_list_t attr; fsal_name_t newname; resp->resop = NFS4_OP_LINK; res_LINK4.status = NFS4_OK; /* Do basic checks on a filehandle */ res_LINK4.status = nfs4_sanity_check_FH(data, 0LL); if(res_LINK4.status != NFS4_OK) return res_LINK4.status; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->savedFH))) { res_LINK4.status = NFS4ERR_NOFILEHANDLE; return res_LINK4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->savedFH))) { res_LINK4.status = NFS4ERR_BADHANDLE; return res_LINK4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->savedFH))) { res_LINK4.status = NFS4ERR_FHEXPIRED; return res_LINK4.status; } /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_LINK4.status = NFS4ERR_ROFS; return res_LINK4.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_LINK4.status = nfs4_SetCompoundExport(data); if(res_LINK4.status != NFS4_OK) return res_LINK4.status; } /* * This operation creates a hard link, for the file represented by the saved FH, in directory represented by currentFH under the * name arg_LINK4.target */ /* Crossing device is not allowed */ if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid != ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid) { res_LINK4.status = NFS4ERR_XDEV; return res_LINK4.status; } /* If name is empty, return EINVAL */ if(arg_LINK4.newname.utf8string_len == 0) { res_LINK4.status = NFS4ERR_INVAL; return res_LINK4.status; } /* Check for name to long */ if(arg_LINK4.newname.utf8string_len > FSAL_MAX_NAME_LEN) { res_LINK4.status = NFS4ERR_NAMETOOLONG; return res_LINK4.status; } /* Convert the UFT8 objname to a regular string */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_LINK4.newname, &newname))) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } /* Sanity check: never create a link named '.' or '..' */ if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT)) { res_LINK4.status = NFS4ERR_BADNAME; return res_LINK4.status; } /* get info from compound data */ dir_pentry = data->current_entry; /* Destination FH (the currentFH) must be a directory */ if(data->current_filetype != DIRECTORY) { res_LINK4.status = NFS4ERR_NOTDIR; return res_LINK4.status; } /* Target object (the savedFH) must not be a directory */ if(data->saved_filetype == DIRECTORY) { res_LINK4.status = NFS4ERR_ISDIR; return res_LINK4.status; } /* We have to keep track of the 'change' file attribute for reply structure */ if((cache_status = cache_inode_getattr(dir_pentry, &attr, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } res_LINK4.LINK4res_u.resok4.cinfo.before = cache_inode_get_changeid4(dir_pentry); /* Convert savedFH into a vnode */ file_pentry = data->saved_entry; /* make the link */ if(cache_inode_link(file_pentry, dir_pentry, &newname, &attr, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } res_LINK4.LINK4res_u.resok4.cinfo.after = cache_inode_get_changeid4(dir_pentry); res_LINK4.LINK4res_u.resok4.cinfo.atomic = FALSE; res_LINK4.status = NFS4_OK; return NFS4_OK; } /* nfs4_op_link */
int nfs3_Mknod(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t * pres) { cache_entry_t *parent_pentry = NULL; fsal_attrib_list_t parent_attr; fsal_attrib_list_t *ppre_attr; fsal_attrib_list_t attr_parent_after; cache_inode_file_type_t parent_filetype; cache_inode_file_type_t nodetype; char *str_file_name = NULL; fsal_name_t file_name; cache_inode_status_t cache_status; cache_inode_status_t cache_status_lookup; fsal_accessmode_t mode = 0; cache_entry_t *node_pentry = NULL; fsal_attrib_list_t attr; cache_inode_create_arg_t create_arg; fsal_handle_t *pfsal_handle; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif memset(&create_arg, 0, sizeof(create_arg)); if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; sprint_fhandle3(str, &(parg->arg_mknod3.where.dir)); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_Mknod handle: %s name: %s", str, parg->arg_mknod3.where.name); } /* to avoid setting them on each error case */ pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; /* retrieve parent entry */ if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, NULL, &(parg->arg_mknod3.where.dir), NULL, NULL, &(pres->res_mknod3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ return rc; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: new node name must be non-null; parent must be a * directory. */ if(parent_filetype != DIRECTORY) { pres->res_mknod3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } str_file_name = parg->arg_mknod3.where.name; switch (parg->arg_mknod3.what.type) { case NF3CHR: case NF3BLK: if(parg->arg_mknod3.what.mknoddata3_u.device.dev_attributes.mode.set_it) mode = (fsal_accessmode_t) parg->arg_mknod3.what.mknoddata3_u.device.dev_attributes. mode.set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; create_arg.dev_spec.major = parg->arg_mknod3.what.mknoddata3_u.device.spec.specdata1; create_arg.dev_spec.minor = parg->arg_mknod3.what.mknoddata3_u.device.spec.specdata2; break; case NF3FIFO: case NF3SOCK: if(parg->arg_mknod3.what.mknoddata3_u.pipe_attributes.mode.set_it) mode = (fsal_accessmode_t) parg->arg_mknod3.what.mknoddata3_u.pipe_attributes.mode. set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; create_arg.dev_spec.major = 0; create_arg.dev_spec.minor = 0; break; default: pres->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } switch (parg->arg_mknod3.what.type) { case NF3CHR: nodetype = CHARACTER_FILE; break; case NF3BLK: nodetype = BLOCK_FILE; break; case NF3FIFO: nodetype = FIFO_FILE; break; case NF3SOCK: nodetype = SOCKET_FILE; break; default: pres->res_mknod3.status = NFS3ERR_BADTYPE; rc = NFS_REQ_OK; goto out; } //if(str_file_name == NULL || strlen(str_file_name) == 0) if(str_file_name == NULL || *str_file_name == '\0' ) { pres->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { pres->res_mknod3.status = NFS3ERR_DQUOT; return NFS_REQ_OK; } #endif /* _USE_QUOTA */ /* convert node name */ if((cache_status = cache_inode_error_convert(FSAL_str2name(str_file_name, 0, &file_name))) == CACHE_INODE_SUCCESS) { /* * Lookup node to see if it exists. If so, use it. Otherwise * create a new one. */ node_pentry = cache_inode_lookup(parent_pentry, &file_name, &attr, pcontext, &cache_status_lookup); if(cache_status_lookup == CACHE_INODE_NOT_FOUND) { /* Create the node */ if((node_pentry = cache_inode_create(parent_pentry, &file_name, nodetype, mode, &create_arg, &attr, pcontext, &cache_status)) != NULL) { MKNOD3resok *rok = &pres->res_mknod3.MKNOD3res_u.resok; /* * Get the FSAL handle for this entry */ pfsal_handle = &node_pentry->handle; /* Build file handle */ pres->res_mknod3.status = nfs3_AllocateFH(&rok->obj.post_op_fh3_u.handle); if(pres->res_mknod3.status != NFS3_OK) return NFS_REQ_OK; if(nfs3_FSALToFhandle(&rok->obj.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { gsh_free(rok->obj.post_op_fh3_u.handle.data.data_val); pres->res_mknod3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ rok->obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr, &rok->obj_attributes); /* Get the attributes of the parent after the operation */ if(cache_inode_getattr(parent_pentry, &attr_parent_after, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { pres->res_mknod3.status = nfs3_Errno(cache_status); rc = NFS_REQ_OK; goto out; } /* Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &rok->dir_wcc); pres->res_mknod3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } /* mknod sucess */ } /* not found */ else { /* object already exists or failure during lookup */ if(cache_status_lookup == CACHE_INODE_SUCCESS) { /* Trying to create an entry that already exists */ pres->res_mknod3.status = NFS3ERR_EXIST; } else { /* Server fault */ pres->res_mknod3.status = NFS3ERR_INVAL; } nfs_SetWccData(pexport, NULL, NULL, &(pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc)); rc = NFS_REQ_OK; goto out; } } /* If we are here, there was an error */ rc = nfs_SetFailedStatus(pexport, preq->rq_vers, cache_status, NULL, &pres->res_mknod3.status, NULL, ppre_attr, &(pres->res_mknod3.MKNOD3res_u.resfail.dir_wcc), NULL, NULL); out: /* return references */ if (parent_pentry) cache_inode_put(parent_pentry); if (node_pentry) cache_inode_put(node_pentry); return (rc); } /* nfs3_Mknod */
int nfs_Create(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { char *str_file_name = NULL; fsal_name_t file_name; fsal_accessmode_t mode = 0; cache_entry_t *file_pentry = NULL; cache_entry_t *parent_pentry = NULL; fsal_attrib_list_t parent_attr; fsal_attrib_list_t attr; fsal_attrib_list_t attr_parent_after; fsal_attrib_list_t attr_newfile; fsal_attrib_list_t attributes_create; fsal_attrib_list_t *ppre_attr; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; cache_inode_status_t cache_status_lookup; cache_inode_file_type_t parent_filetype; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: str_file_name = parg->arg_create2.where.name; break; case NFS_V3: str_file_name = parg->arg_create3.where.name; break; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_create2.where.dir), &(parg->arg_create3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Create handle: %s name: %s", str, str_file_name); } if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_create3.where.dir)))) { rc = nfs3_Create_Xattr(parg, pexport, pcontext, preq, pres); goto out; } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_create3.CREATE3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_create3.CREATE3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; } if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_create2.where.dir), &(parg->arg_create3.where.dir), NULL, &(pres->res_dirop2.status), &(pres->res_create3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: new file name must be non-null; parent must be a * directory. */ if(parent_filetype != DIRECTORY) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_NOTDIR; break; case NFS_V3: pres->res_create3.status = NFS3ERR_NOTDIR; break; } rc = NFS_REQ_OK; goto out; } switch (preq->rq_vers) { case NFS_V2: str_file_name = parg->arg_create2.where.name; if(parg->arg_create2.attributes.mode != (unsigned int)-1) { mode = unix2fsal_mode(parg->arg_create2.attributes.mode); } else { mode = 0; } break; case NFS_V3: str_file_name = parg->arg_create3.where.name; if(parg->arg_create3.how.mode == EXCLUSIVE) { /* * Client has not provided mode information. * If the create works, the client will issue * a separate setattr request to fix up the * file's mode, so pick arbitrary value for now. */ mode = 0; } else if(parg->arg_create3.how.createhow3_u.obj_attributes.mode.set_it == TRUE) mode = unix2fsal_mode(parg->arg_create3.how.createhow3_u.obj_attributes.mode. set_mode3_u.mode); else mode = 0; break; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_DQUOT ; break; case NFS_V3: pres->res_create3.status = NFS3ERR_DQUOT; break; } rc = NFS_REQ_OK ; goto out; } #endif /* _USE_QUOTA */ // if(str_file_name == NULL || strlen(str_file_name) == 0) if(str_file_name == NULL || *str_file_name == '\0' ) { if(preq->rq_vers == NFS_V2) pres->res_dirop2.status = NFSERR_IO; if(preq->rq_vers == NFS_V3) pres->res_create3.status = NFS3ERR_INVAL; } else { if((cache_status = cache_inode_error_convert(FSAL_str2name(str_file_name, FSAL_MAX_NAME_LEN, &file_name))) == CACHE_INODE_SUCCESS) { /* * Lookup file to see if it exists. If so, use it. Otherwise * create a new one. */ file_pentry = cache_inode_lookup(parent_pentry, &file_name, &attr, pcontext, &cache_status_lookup); if((cache_status_lookup == CACHE_INODE_NOT_FOUND) || ((cache_status_lookup == CACHE_INODE_SUCCESS) && (parg->arg_create3.how.mode == UNCHECKED))) { /* Create the file */ if((parg->arg_create3.how.mode == UNCHECKED) && (cache_status_lookup == CACHE_INODE_SUCCESS)) { cache_status = CACHE_INODE_SUCCESS; attr_newfile = attr; } else file_pentry = cache_inode_create(parent_pentry, &file_name, REGULAR_FILE, mode, NULL, &attr_newfile, pcontext, &cache_status); if(file_pentry != NULL) { /* * Look at sattr to see if some attributes are to be set at creation time */ attributes_create.asked_attributes = 0ULL; switch (preq->rq_vers) { case NFS_V2: if(nfs2_Sattr_To_FSALattr(&attributes_create, &parg->arg_create2.attributes) == 0) { pres->res_dirop2.status = NFSERR_IO; rc = NFS_REQ_OK; goto out; break; } break; case NFS_V3: if(nfs3_Sattr_To_FSALattr(&attributes_create, &parg->arg_create3.how.createhow3_u. obj_attributes) == 0) { pres->res_create3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } break; } /* Mode is managed above (in cache_inode_create), there is no need * to manage it */ if(attributes_create.asked_attributes & FSAL_ATTR_MODE) attributes_create.asked_attributes &= ~FSAL_ATTR_MODE; /* Some clients (like Solaris 10) try to set the size of the file to 0 * at creation time. The FSAL create empty file, so we ignore this */ if(attributes_create.asked_attributes & FSAL_ATTR_SIZE) attributes_create.asked_attributes &= ~FSAL_ATTR_SIZE; if(attributes_create.asked_attributes & FSAL_ATTR_SPACEUSED) attributes_create.asked_attributes &= ~FSAL_ATTR_SPACEUSED; /* Are there attributes to be set (additional to the mode) ? */ if(attributes_create.asked_attributes != 0ULL && attributes_create.asked_attributes != FSAL_ATTR_MODE) { /* A call to cache_inode_setattr is required */ if(cache_inode_setattr(file_pentry, &attributes_create, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* If we are here, there was an error */ nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_create3.status, NULL, NULL, parent_pentry, ppre_attr, &(pres->res_create3.CREATE3res_u.resfail. dir_wcc), NULL, NULL, NULL); if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } rc = NFS_REQ_OK; goto out; } /* Get the resulting attributes from the Cache Inode */ if(cache_inode_getattr(file_pentry, &attr_newfile, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* If we are here, there was an error */ nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_create3.status, NULL, NULL, parent_pentry, ppre_attr, &(pres->res_create3.CREATE3res_u.resfail. dir_wcc), NULL, NULL, NULL); if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } rc = NFS_REQ_OK; goto out; } } switch (preq->rq_vers) { case NFS_V2: /* Build file handle */ if(nfs2_FSALToFhandle( &(pres->res_dirop2.DIROP2res_u.diropok.file), &file_pentry->handle, pexport) == 0) pres->res_dirop2.status = NFSERR_IO; else { if(!nfs2_FSALattr_To_Fattr( pexport, &attr_newfile, &(pres->res_dirop2.DIROP2res_u. diropok.attributes))) pres->res_dirop2.status = NFSERR_IO; else pres->res_dirop2.status = NFS_OK; } break; case NFS_V3: /* Build file handle */ pres->res_create3.status = nfs3_AllocateFH(&pres->res_create3.CREATE3res_u .resok.obj.post_op_fh3_u.handle); if (pres->res_create3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ if(nfs3_FSALToFhandle( &(pres->res_create3.CREATE3res_u.resok .obj.post_op_fh3_u.handle), &file_pentry->handle, pexport) == 0) { gsh_free(pres->res_create3.CREATE3res_u.resok.obj. post_op_fh3_u.handle.data.data_val); pres->res_create3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ pres->res_create3.CREATE3res_u.resok.obj.handle_follows = TRUE; /* Get the attributes of the parent after the operation */ attr_parent_after = parent_pentry->attributes; /* Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr_newfile, &(pres->res_create3.CREATE3res_u.resok. obj_attributes)); /* * Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &(pres->res_create3.CREATE3res_u .resok.dir_wcc)); pres->res_create3.status = NFS3_OK; break; } /* switch */ rc = NFS_REQ_OK; goto out; } } else { if(cache_status_lookup == CACHE_INODE_SUCCESS) { /* Trying to create a file that already exists */ cache_status = CACHE_INODE_ENTRY_EXISTS; switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_EXIST; break; case NFS_V3: pres->res_create3.status = NFS3ERR_EXIST; break; } } else { /* Server fault */ cache_status = cache_status_lookup; switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_IO; break; case NFS_V3: pres->res_create3.status = NFS3ERR_INVAL; break; } } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_create3.status, NULL, NULL, parent_pentry, ppre_attr, &(pres->res_create3.CREATE3res_u.resfail.dir_wcc), NULL, NULL, NULL); rc = NFS_REQ_OK; goto out; } /* if( cache_status_lookup == CACHE_INODE_NOT_FOUND ) */ } } /* Set the exit status */ nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_create3.status, NULL, NULL, parent_pentry, ppre_attr, &(pres->res_create3.CREATE3res_u.resfail.dir_wcc), NULL, NULL, NULL); /* If we are here, there was an error */ if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } rc = NFS_REQ_OK; out: /* return references */ if (file_pentry) cache_inode_put(file_pentry); if (parent_pentry) cache_inode_put(parent_pentry); return (rc); } /* nfs_Create */
int nfs4_op_create(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *pentry_parent = NULL; cache_entry_t *pentry_new = NULL; fsal_attrib_list_t attr_parent; fsal_attrib_list_t attr_new; fsal_attrib_list_t sattr; fsal_handle_t *pnewfsal_handle = NULL; nfs_fh4 newfh4; cache_inode_status_t cache_status; int convrc = 0; fsal_accessmode_t mode = 0600; fsal_name_t name; cache_inode_create_arg_t create_arg; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_create"; unsigned int i = 0; resp->resop = NFS4_OP_CREATE; res_CREATE4.status = NFS4_OK; /* If the filehandle is Empty */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_CREATE4.status = NFS4ERR_NOFILEHANDLE; return res_CREATE4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_CREATE4.status = NFS4ERR_BADHANDLE; return res_CREATE4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_CREATE4.status = NFS4ERR_FHEXPIRED; return res_CREATE4.status; } /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_CREATE4.status = NFS4ERR_ROFS; return res_CREATE4.status; } /* Ask only for supported attributes */ if(!nfs4_Fattr_Supported(&arg_CREATE4.createattrs)) { res_CREATE4.status = NFS4ERR_ATTRNOTSUPP; return res_CREATE4.status; } /* Do not use READ attr, use WRITE attr */ if(!nfs4_Fattr_Check_Access(&arg_CREATE4.createattrs, FATTR4_ATTR_WRITE)) { res_CREATE4.status = NFS4ERR_INVAL; return res_CREATE4.status; } /* Check for name to long */ if(arg_CREATE4.objname.utf8string_len > FSAL_MAX_NAME_LEN) { res_CREATE4.status = NFS4ERR_NAMETOOLONG; return res_CREATE4.status; } /* * This operation is used to create a non-regular file, * this means: - a symbolic link * - a block device file * - a character device file * - a socket file * - a fifo * - a directory * * You can't use this operation to create a regular file, you have to use NFS4_OP_OPEN for this */ /* Convert the UFT8 objname to a regular string */ if(arg_CREATE4.objname.utf8string_len == 0) { res_CREATE4.status = NFS4ERR_INVAL; return res_CREATE4.status; } if(utf82str(name.name, &arg_CREATE4.objname) == -1) { res_CREATE4.status = NFS4ERR_INVAL; return res_CREATE4.status; } name.len = strlen(name.name); /* Sanuty check: never create a directory named '.' or '..' */ if(arg_CREATE4.objtype.type == NF4DIR) { if(!FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT_DOT)) { res_CREATE4.status = NFS4ERR_BADNAME; return res_CREATE4.status; } } /* Filename should contain not slash */ for(i = 0; i < name.len; i++) { if(name.name[i] == '/') { res_CREATE4.status = NFS4ERR_BADCHAR; return res_CREATE4.status; } } /* Convert current FH into a cached entry, the current_pentry (assocated with the current FH will be used for this */ pentry_parent = data->current_entry; /* The currentFH must point to a directory (objects are always created within a directory) */ if(data->current_filetype != DIR_BEGINNING && data->current_filetype != DIR_CONTINUE) { res_CREATE4.status = NFS4ERR_NOTDIR; return res_CREATE4.status; } /* get attributes of parent directory, for 'change4' info replyed */ if((cache_status = cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* Change info for client cache coherency, pentry internal_md is used for that */ memset(&(res_CREATE4.CREATE4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_CREATE4.CREATE4res_u.resok4.cinfo.before = (changeid4) pentry_parent->internal_md.mod_time; /* Convert the incoming fattr4 to a vattr structure, if such arguments are supplied */ if(arg_CREATE4.createattrs.attrmask.bitmap4_len != 0) { /* Arguments were supplied, extract them */ convrc = nfs4_Fattr_To_FSAL_attr(&sattr, &(arg_CREATE4.createattrs)); if(convrc == 0) { res_CREATE4.status = NFS4ERR_ATTRNOTSUPP; return res_CREATE4.status; } if(convrc == -1) { res_CREATE4.status = NFS4ERR_BADXDR; return res_CREATE4.status; } } /* Create either a symbolic link or a directory */ switch (arg_CREATE4.objtype.type) { case NF4LNK: /* Convert the name to link from into a regular string */ if(arg_CREATE4.objtype.createtype4_u.linkdata.utf8string_len == 0) { res_CREATE4.status = NFS4ERR_INVAL; return res_CREATE4.status; } else { if(utf82str (create_arg.link_content.path, &arg_CREATE4.objtype.createtype4_u.linkdata) == -1) { res_CREATE4.status = NFS4ERR_INVAL; return res_CREATE4.status; } create_arg.link_content.len = strlen(create_arg.link_content.path); } /* do the symlink operation */ if((pentry_new = cache_inode_create(pentry_parent, &name, SYMBOLIC_LINK, mode, &create_arg, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; case NF4DIR: /* Create a new directory */ /* do the symlink operation */ if((pentry_new = cache_inode_create(pentry_parent, &name, DIR_BEGINNING, mode, &create_arg, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; case NF4SOCK: /* Create a new socket file */ if((pentry_new = cache_inode_create(pentry_parent, &name, SOCKET_FILE, mode, NULL, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; case NF4FIFO: /* Create a new socket file */ if((pentry_new = cache_inode_create(pentry_parent, &name, FIFO_FILE, mode, NULL, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; case NF4CHR: create_arg.dev_spec.major = arg_CREATE4.objtype.createtype4_u.devdata.specdata1; create_arg.dev_spec.minor = arg_CREATE4.objtype.createtype4_u.devdata.specdata2; /* Create a new socket file */ if((pentry_new = cache_inode_create(pentry_parent, &name, CHARACTER_FILE, mode, &create_arg, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; case NF4BLK: create_arg.dev_spec.major = arg_CREATE4.objtype.createtype4_u.devdata.specdata1; create_arg.dev_spec.minor = arg_CREATE4.objtype.createtype4_u.devdata.specdata2; /* Create a new socket file */ if((pentry_new = cache_inode_create(pentry_parent, &name, BLOCK_FILE, mode, &create_arg, &attr_new, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* If entry exists pentry_new is not null but cache_status was set */ if(cache_status == CACHE_INODE_ENTRY_EXISTS) { res_CREATE4.status = NFS4ERR_EXIST; return res_CREATE4.status; } break; default: /* Should never happen, but return NFS4ERR_BADTYPE in this case */ res_CREATE4.status = NFS4ERR_BADTYPE; return res_CREATE4.status; break; } /* switch( arg_CREATE4.objtype.type ) */ /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_new, &cache_status)) == NULL) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* Allocation of a new file handle */ if(nfs4_AllocateFH(&newfh4) != NFS4_OK) { res_CREATE4.status = NFS4ERR_SERVERFAULT; return res_CREATE4.status; } /* Building the new file handle */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_CREATE4.status = NFS4ERR_SERVERFAULT; return res_CREATE4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); /* No do not need newfh any more */ Mem_Free((char *)newfh4.nfs_fh4_val); /* Set the mode if requested */ /* Use the same fattr mask for reply, if one attribute was not settable, NFS4ERR_ATTRNOTSUPP was replyied */ res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_len = arg_CREATE4.createattrs.attrmask.bitmap4_len; if(arg_CREATE4.createattrs.attrmask.bitmap4_len != 0) { if((cache_status = cache_inode_setattr(pentry_new, &sattr, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } /* Allocate a new bitmap */ res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_val = (unsigned int *)Mem_Alloc(res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_len * sizeof(u_int)); if(res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_val == NULL) { res_CREATE4.status = NFS4ERR_SERVERFAULT; return res_CREATE4.status; } memset(res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_val, 0, res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_len); memcpy(res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_val, arg_CREATE4.createattrs.attrmask.bitmap4_val, res_CREATE4.CREATE4res_u.resok4.attrset.bitmap4_len * sizeof(u_int)); } /* Get the change info on parent directory after the operation was successfull */ if((cache_status = cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_CREATE4.status = nfs4_Errno(cache_status); return res_CREATE4.status; } memset(&(res_CREATE4.CREATE4res_u.resok4.cinfo.after), 0, sizeof(changeid4)); res_CREATE4.CREATE4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; /* Operation is supposed to be atomic .... */ res_CREATE4.CREATE4res_u.resok4.cinfo.atomic = TRUE; LogFullDebug(COMPONENT_NFS_V4, " CREATE CINFO before = %llu after = %llu atomic = %d\n", res_CREATE4.CREATE4res_u.resok4.cinfo.before, res_CREATE4.CREATE4res_u.resok4.cinfo.after, res_CREATE4.CREATE4res_u.resok4.cinfo.atomic); /* @todo : BUGAZOMEU: fair ele free dans cette fonction */ /* Keep the vnode entry for the file in the compound data */ data->current_entry = pentry_new; data->current_filetype = pentry_new->internal_md.type; /* If you reach this point, then no error occured */ res_CREATE4.status = NFS4_OK; return res_CREATE4.status; } /* nfs4_op_create */
int nfs_Mkdir(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { char *str_dir_name = NULL; fsal_accessmode_t mode = 0; cache_entry_t *dir_pentry = NULL; cache_entry_t *parent_pentry = NULL; fsal_attrib_list_t parent_attr; fsal_attrib_list_t attr; fsal_attrib_list_t *ppre_attr; fsal_attrib_list_t attr_parent_after; cache_inode_file_type_t parent_filetype; fsal_handle_t *pfsal_handle; fsal_name_t dir_name; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; cache_inode_status_t cache_status_lookup; cache_inode_create_arg_t create_arg; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif memset(&create_arg, 0, sizeof(create_arg)); if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: str_dir_name = parg->arg_mkdir2.where.name; break; case NFS_V3: str_dir_name = parg->arg_mkdir3.where.name; break; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_mkdir2.where.dir), &(parg->arg_mkdir3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Mkdir handle: %s name: %s", str, str_dir_name); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; } if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_mkdir2.where.dir), &(parg->arg_mkdir3.where.dir), NULL, &(pres->res_dirop2.status), &(pres->res_mkdir3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: */ if(parent_filetype != DIRECTORY) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_NOTDIR; break; case NFS_V3: pres->res_mkdir3.status = NFS3ERR_NOTDIR; break; } rc = NFS_REQ_OK; goto out; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { switch (preq->rq_vers) { case NFS_V2: pres->res_dirop2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_mkdir3.status = NFS3ERR_DQUOT; break; } rc = NFS_REQ_OK ; goto out; } #endif /* _USE_QUOTA */ switch (preq->rq_vers) { case NFS_V2: str_dir_name = parg->arg_mkdir2.where.name; if(parg->arg_mkdir2.attributes.mode != (unsigned int)-1) { mode = (fsal_accessmode_t) parg->arg_mkdir2.attributes.mode; } else { mode = (fsal_accessmode_t) 0; } break; case NFS_V3: str_dir_name = parg->arg_mkdir3.where.name; if(parg->arg_mkdir3.attributes.mode.set_it == TRUE) mode = (fsal_accessmode_t) parg->arg_mkdir3.attributes.mode.set_mode3_u.mode; else mode = (fsal_accessmode_t) 0; break; } //if(str_dir_name == NULL || strlen(str_dir_name) == 0) if(str_dir_name == NULL || *str_dir_name == '\0' ) { if(preq->rq_vers == NFS_V2) pres->res_dirop2.status = NFSERR_IO; if(preq->rq_vers == NFS_V3) pres->res_mkdir3.status = NFS3ERR_INVAL; } else { /* Make the directory */ if((cache_status = cache_inode_error_convert(FSAL_str2name(str_dir_name, 0, &dir_name))) == CACHE_INODE_SUCCESS) { /* * Lookup file to see if it exists. If so, use it. Otherwise * create a new one. */ dir_pentry = cache_inode_lookup(parent_pentry, &dir_name, &attr, pcontext, &cache_status_lookup); if(cache_status_lookup == CACHE_INODE_NOT_FOUND) { /* The create_arg structure contains the information "newly created directory" * to be passed to cache_inode_new_entry from cache_inode_create */ create_arg.newly_created_dir = TRUE; /* Create the directory */ if((dir_pentry = cache_inode_create(parent_pentry, &dir_name, DIRECTORY, mode, &create_arg, &attr, pcontext, &cache_status)) != NULL) { /* * Get the FSAL handle for this entry */ pfsal_handle = &dir_pentry->handle; if(preq->rq_vers == NFS_V2) { DIROP2resok *d2ok = &pres->res_dirop2.DIROP2res_u.diropok; /* Build file handle */ if(!nfs2_FSALToFhandle(&d2ok->file, pfsal_handle, pexport)) pres->res_dirop2.status = NFSERR_IO; else { /* * Build entry * attributes */ if(nfs2_FSALattr_To_Fattr(pexport, &attr, &d2ok->attributes) == 0) pres->res_dirop2.status = NFSERR_IO; else pres->res_dirop2.status = NFS_OK; } } else { MKDIR3resok *d3ok = &pres->res_mkdir3.MKDIR3res_u.resok; /* Build file handle */ pres->res_mkdir3.status = nfs3_AllocateFH(&d3ok->obj.post_op_fh3_u.handle); if(pres->res_mkdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } if(nfs3_FSALToFhandle(&d3ok->obj.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { gsh_free(d3ok->obj.post_op_fh3_u.handle.data.data_val); pres->res_mkdir3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ d3ok->obj.handle_follows = TRUE; /* * Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr, &d3ok->obj_attributes); /* Get the attributes of the parent after the operation */ if(cache_inode_getattr(parent_pentry, &attr_parent_after, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { pres->res_mkdir3.status = nfs3_Errno(cache_status); rc = NFS_REQ_OK; goto out; } /* * Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &d3ok->dir_wcc); pres->res_mkdir3.status = NFS3_OK; } rc = NFS_REQ_OK; goto out; } } /* If( cache_status_lookup == CACHE_INODE_NOT_FOUND ) */ else { switch (preq->rq_vers) { case NFS_V2: if(cache_status_lookup == CACHE_INODE_SUCCESS) pres->res_dirop2.status = NFSERR_EXIST; else pres->res_dirop2.status = NFSERR_IO; break; case NFS_V3: if(cache_status_lookup == CACHE_INODE_SUCCESS) pres->res_mkdir3.status = NFS3ERR_EXIST; else pres->res_mkdir3.status = NFS3ERR_INVAL; nfs_SetWccData(pexport, ppre_attr, NULL, &(pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc)); break; } rc = NFS_REQ_OK; goto out; } } } /* If we are here, there was an error */ rc = nfs_SetFailedStatus(pexport, preq->rq_vers, cache_status, &pres->res_dirop2.status, &pres->res_mkdir3.status, NULL, ppre_attr, &(pres->res_mkdir3.MKDIR3res_u.resfail.dir_wcc), NULL, NULL); out: /* return references */ if (dir_pentry) cache_inode_put(dir_pentry); if (parent_pentry) cache_inode_put(parent_pentry); return (rc); }
int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { fsal_attrib_list_t file_attr; cache_inode_status_t cache_status; fattr4 file_attr4; int rc = 0; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_verify"; resp->resop = NFS4_OP_VERIFY; res_VERIFY4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_VERIFY4.status = NFS4ERR_NOFILEHANDLE; return NFS4ERR_NOFILEHANDLE; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_VERIFY4.status = NFS4ERR_BADHANDLE; return NFS4ERR_BADHANDLE; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_VERIFY4.status = NFS4ERR_FHEXPIRED; return NFS4ERR_FHEXPIRED; } /* operation is always permitted on pseudofs */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_VERIFY4.status = NFS4_OK; return res_VERIFY4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access(&arg_VERIFY4.obj_attributes, FATTR4_ATTR_READ)) { res_VERIFY4.status = NFS4ERR_INVAL; return res_VERIFY4.status; } /* Ask only for supported attributes */ if(!nfs4_Fattr_Supported(&arg_VERIFY4.obj_attributes)) { res_VERIFY4.status = NFS4ERR_ATTRNOTSUPP; return res_VERIFY4.status; } /* Get the cache inode attribute */ if((cache_status = cache_inode_getattr(data->current_entry, &file_attr, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_VERIFY4.status = NFS4ERR_INVAL; return res_VERIFY4.status; } if(nfs4_FSALattr_To_Fattr(data->pexport, &file_attr, &file_attr4, data, &(data->currentFH), &(arg_VERIFY4.obj_attributes.attrmask)) != 0) { res_VERIFY4.status = NFS4ERR_SERVERFAULT; return res_VERIFY4.status; } if((rc = nfs4_Fattr_cmp(&(arg_VERIFY4.obj_attributes), &file_attr4)) == TRUE) res_VERIFY4.status = NFS4_OK; else { if(rc == -1) res_VERIFY4.status = NFS4ERR_INVAL; else res_VERIFY4.status = NFS4ERR_NOT_SAME; } return res_VERIFY4.status; } /* nfs4_op_verify */
int nfs_Symlink(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { char *str_symlink_name = NULL; fsal_name_t symlink_name; char *str_target_path = NULL; cache_inode_create_arg_t create_arg; fsal_accessmode_t mode = 0777; cache_entry_t *symlink_pentry = NULL; cache_entry_t *parent_pentry; cache_inode_file_type_t parent_filetype; fsal_attrib_list_t parent_attr; fsal_attrib_list_t attr_symlink; fsal_attrib_list_t attributes_symlink; fsal_attrib_list_t attr_parent_after; fsal_attrib_list_t *ppre_attr; cache_inode_status_t cache_status; cache_inode_status_t cache_status_parent; fsal_handle_t *pfsal_handle; int rc = NFS_REQ_OK; #ifdef _USE_QUOTA fsal_status_t fsal_status ; #endif memset(&create_arg, 0, sizeof(create_arg)); if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: str_symlink_name = parg->arg_symlink2.from.name; str_target_path = parg->arg_symlink2.to; break; case NFS_V3: str_symlink_name = parg->arg_symlink3.where.name; str_target_path = parg->arg_symlink3.symlink.symlink_data; break; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_symlink2.from.dir), &(parg->arg_symlink3.where.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Symlink handle: %s name: %s target: %s", str, str_symlink_name, str_target_path); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_symlink3.SYMLINK3res_u.resfail.dir_wcc.before.attributes_follow = FALSE; pres->res_symlink3.SYMLINK3res_u.resfail.dir_wcc.after.attributes_follow = FALSE; ppre_attr = NULL; } /* Convert directory file handle into a vnode */ if((parent_pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_symlink2.from.dir), &(parg->arg_symlink3.where.dir), NULL, &(pres->res_stat2), &(pres->res_symlink3.status), NULL, &parent_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out;; } /* get directory attributes before action (for V3 reply) */ ppre_attr = &parent_attr; /* Extract the filetype */ parent_filetype = cache_inode_fsal_type_convert(parent_attr.type); /* * Sanity checks: new directory name must be non-null; parent must be * a directory. */ if(parent_filetype != DIRECTORY) { switch (preq->rq_vers) { case NFS_V2: pres->res_stat2 = NFSERR_NOTDIR; break; case NFS_V3: pres->res_symlink3.status = NFS3ERR_NOTDIR; break; } rc = NFS_REQ_OK; goto out; } #ifdef _USE_QUOTA /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = FSAL_check_quota( pexport->fullpath, FSAL_QUOTA_INODES, FSAL_OP_CONTEXT_TO_UID( pcontext ) ) ; if( FSAL_IS_ERROR( fsal_status ) ) { switch (preq->rq_vers) { case NFS_V2: pres->res_stat2 = NFSERR_DQUOT ; break; case NFS_V3: pres->res_symlink3.status = NFS3ERR_DQUOT; break; } rc = NFS_REQ_OK; goto out; } #endif /* _USE_QUOTA */ switch (preq->rq_vers) { case NFS_V2: str_symlink_name = parg->arg_symlink2.from.name; str_target_path = parg->arg_symlink2.to; break; case NFS_V3: str_symlink_name = parg->arg_symlink3.where.name; str_target_path = parg->arg_symlink3.symlink.symlink_data; break; } if(str_symlink_name == NULL || *str_symlink_name == '\0'|| str_target_path == NULL || *str_target_path == '\0' || FSAL_IS_ERROR(FSAL_str2name(str_symlink_name, 0, &symlink_name)) || FSAL_IS_ERROR(FSAL_str2path(str_target_path, 0, &create_arg.link_content))) { cache_status = CACHE_INODE_INVALID_ARGUMENT; } else { /* Make the symlink */ if((symlink_pentry = cache_inode_create(parent_pentry, &symlink_name, SYMBOLIC_LINK, mode, &create_arg, &attr_symlink, pcontext, &cache_status)) != NULL) { switch (preq->rq_vers) { case NFS_V2: pres->res_stat2 = NFS_OK; break; case NFS_V3: /* Build file handle */ pfsal_handle = &symlink_pentry->handle; /* Some clients (like the Spec NFS benchmark) set attributes with the NFSPROC3_SYMLINK request */ if(nfs3_Sattr_To_FSALattr(&attributes_symlink, &parg->arg_symlink3.symlink.symlink_attributes) == 0) { pres->res_create3.status = NFS3ERR_INVAL; rc = NFS_REQ_OK; goto out; } /* Mode is managed above (in cache_inode_create), there is no need * to manage it */ if(attributes_symlink.asked_attributes & FSAL_ATTR_MODE) attributes_symlink.asked_attributes &= ~FSAL_ATTR_MODE; /* Some clients (like Solaris 10) try to set the size of the file to 0 * at creation time. The FSAL create empty file, so we ignore this */ if(attributes_symlink.asked_attributes & FSAL_ATTR_SIZE) attributes_symlink.asked_attributes &= ~FSAL_ATTR_SIZE; if(attributes_symlink.asked_attributes & FSAL_ATTR_SPACEUSED) attributes_symlink.asked_attributes &= ~FSAL_ATTR_SPACEUSED; /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&pworker->export_perms, &pworker->user_credentials, &attributes_symlink); /* Are there attributes to be set (additional to the mode) ? */ if(attributes_symlink.asked_attributes != 0ULL && attributes_symlink.asked_attributes != FSAL_ATTR_MODE) { /* A call to cache_inode_setattr is required */ if(cache_inode_setattr(symlink_pentry, &attributes_symlink, pcontext, FALSE, &cache_status) != CACHE_INODE_SUCCESS) { goto out_error; } } if ((pres->res_symlink3.status = (nfs3_AllocateFH(&pres->res_symlink3.SYMLINK3res_u .resok.obj.post_op_fh3_u.handle))) != NFS3_OK) { pres->res_symlink3.status = NFS3ERR_IO; rc = NFS_REQ_OK; goto out; } if(nfs3_FSALToFhandle (&pres->res_symlink3.SYMLINK3res_u.resok.obj.post_op_fh3_u.handle, pfsal_handle, pexport) == 0) { gsh_free(pres->res_symlink3.SYMLINK3res_u.resok.obj. post_op_fh3_u.handle.data.data_val); pres->res_symlink3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* The the parent pentry attributes for building Wcc Data */ if(cache_inode_getattr(parent_pentry, &attr_parent_after, pcontext, &cache_status_parent) != CACHE_INODE_SUCCESS) { gsh_free(pres->res_symlink3.SYMLINK3res_u.resok.obj. post_op_fh3_u.handle.data.data_val); pres->res_symlink3.status = NFS3ERR_BADHANDLE; rc = NFS_REQ_OK; goto out; } /* Set Post Op Fh3 structure */ pres->res_symlink3.SYMLINK3res_u.resok.obj.handle_follows = TRUE; /* Build entry attributes */ nfs_SetPostOpAttr(pexport, &attr_symlink, &(pres->res_symlink3.SYMLINK3res_u .resok.obj_attributes)); /* Build Weak Cache Coherency data */ nfs_SetWccData(pexport, ppre_attr, &attr_parent_after, &(pres->res_symlink3.SYMLINK3res_u.resok.dir_wcc)); pres->res_symlink3.status = NFS3_OK; break; } /* switch */ rc = NFS_REQ_OK; goto out; } } out_error: rc = nfs_SetFailedStatus(pexport, preq->rq_vers, cache_status, &pres->res_stat2, &pres->res_symlink3.status, NULL, ppre_attr, &(pres->res_symlink3.SYMLINK3res_u.resfail.dir_wcc), NULL, NULL); out: /* return references */ if (parent_pentry) cache_inode_put(parent_pentry); if (symlink_pentry) cache_inode_put(symlink_pentry); return (rc); } /* nfs_Symlink */
int nfs4_op_rename(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_rename"; cache_entry_t * dst_entry = NULL; cache_entry_t * src_entry = NULL; cache_entry_t * tst_entry_dst = NULL; cache_entry_t * tst_entry_src = NULL; fsal_attrib_list_t attr_dst; fsal_attrib_list_t attr_src; fsal_attrib_list_t attr_tst_dst; fsal_attrib_list_t attr_tst_src; cache_inode_status_t cache_status; fsal_status_t fsal_status; fsal_handle_t * handlenew = NULL; fsal_handle_t * handleold = NULL; fsal_name_t oldname; fsal_name_t newname; resp->resop = NFS4_OP_RENAME; res_RENAME4.status = NFS4_OK; /* Do basic checks on a filehandle */ res_RENAME4.status = nfs4_sanity_check_FH(data, 0LL); if(res_RENAME4.status != NFS4_OK) return res_RENAME4.status; /* Do basic checks on saved filehandle */ /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->savedFH))) { res_RENAME4.status = NFS4ERR_NOFILEHANDLE; return res_RENAME4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->savedFH))) { res_RENAME4.status = NFS4ERR_BADHANDLE; return res_RENAME4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->savedFH))) { res_RENAME4.status = NFS4ERR_FHEXPIRED; return res_RENAME4.status; } /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->savedFH))) { res_RENAME4.status = NFS4ERR_ROFS; return res_RENAME4.status; } if (nfs_in_grace()) { res_RENAME4.status = NFS4ERR_GRACE; return res_RENAME4.status; } /* get the names from the RPC input */ cache_status = utf8_to_name(&arg_RENAME4.oldname, &oldname); if(cache_status != CACHE_INODE_SUCCESS) { res_RENAME4.status = nfs4_Errno(cache_status); return res_RENAME4.status; } cache_status = utf8_to_name(&arg_RENAME4.newname, &newname); if(cache_status != CACHE_INODE_SUCCESS) { res_RENAME4.status = nfs4_Errno(cache_status); return res_RENAME4.status; } /* Sanuty check: never rename to '.' or '..' */ if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT)) { res_RENAME4.status = NFS4ERR_BADNAME; return res_RENAME4.status; } /* Sanuty check: never rename to '.' or '..' */ if(!FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&oldname, (fsal_name_t *) & FSAL_DOT_DOT)) { res_RENAME4.status = NFS4ERR_BADNAME; return res_RENAME4.status; } /* * This operation renames * - the object in directory pointed by savedFH, named arg_RENAME4.oldname * into * - an object in directory pointed by currentFH, named arg_RENAME4.newname * * Because of this, we will use 2 entry and we have verified both currentFH and savedFH */ /* No Cross Device */ if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid != ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid) { res_RENAME4.status = NFS4ERR_XDEV; return res_RENAME4.status; } /* destination must be a directory */ dst_entry = data->current_entry; if(data->current_filetype != DIRECTORY) { res_RENAME4.status = NFS4ERR_NOTDIR; return res_RENAME4.status; } /* Convert saved FH into a vnode */ src_entry = data->saved_entry; /* Source must be a directory */ if(data->saved_filetype != DIRECTORY) { res_RENAME4.status = NFS4ERR_NOTDIR; return res_RENAME4.status; } /* Renaming a file to himself is allowed, returns NFS4_OK */ if(src_entry == dst_entry) { if(!FSAL_namecmp(&oldname, &newname)) { res_RENAME4.status = NFS4_OK; return res_RENAME4.status; } } /* For the change_info4, get the 'change' attributes for both directories */ if((cache_status = cache_inode_getattr(src_entry, &attr_src, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_RENAME4.status = nfs4_Errno(cache_status); return res_RENAME4.status; } #ifdef BUGAZOMEU /* Ne devrait pas se produire dans le cas de exportid differents */ /* Both object must resides on the same filesystem, return NFS4ERR_XDEV if not */ if(attr_src.va_rdev != attr_dst.va_rdev) { res_RENAME4.status = NFS4ERR_XDEV; return res_RENAME4.status; } #endif /* Lookup oldfile to see if it exists (refcount +1) */ if((tst_entry_src = cache_inode_lookup(src_entry, &oldname, &attr_tst_src, data->pcontext, &cache_status)) == NULL) { res_RENAME4.status = nfs4_Errno(cache_status); goto release; } /* Lookup file with new name to see if it already exists (refcount +1), * I expect to get NO_ERROR or ENOENT, anything else means an error */ tst_entry_dst = cache_inode_lookup(dst_entry, &newname, &attr_tst_dst, data->pcontext, &cache_status); if((cache_status != CACHE_INODE_SUCCESS) && (cache_status != CACHE_INODE_NOT_FOUND)) { /* Unexpected status at this step, exit with an error */ res_RENAME4.status = nfs4_Errno(cache_status); goto release; } if(cache_status == CACHE_INODE_NOT_FOUND) tst_entry_dst = NULL; /* Just to make sure */ /* Renaming a file to one of its own hardlink is allowed, return NFS4_OK */ if(tst_entry_src == tst_entry_dst) { res_RENAME4.status = NFS4_OK; goto release; } /* Renaming dir into existing file should return NFS4ERR_EXIST */ if ((tst_entry_src->type == DIRECTORY) && ((tst_entry_dst != NULL) && (tst_entry_dst->type == REGULAR_FILE))) { res_RENAME4.status = NFS4ERR_EXIST; goto release; } /* Renaming file into existing dir should return NFS4ERR_EXIST */ if(tst_entry_src->type == REGULAR_FILE) { if(tst_entry_dst != NULL) { if(tst_entry_dst->type == DIRECTORY) { res_RENAME4.status = NFS4ERR_EXIST; goto release; } } } /* Renaming dir1 into existing, nonempty dir2 should return NFS4ERR_EXIST * Renaming file into existing, nonempty dir should return NFS4ERR_EXIST */ if(tst_entry_dst != NULL) { if((tst_entry_dst->type == DIRECTORY) && ((tst_entry_src->type == DIRECTORY) || (tst_entry_src->type == REGULAR_FILE))) { if(cache_inode_is_dir_empty_WithLock(tst_entry_dst) == CACHE_INODE_DIR_NOT_EMPTY) { res_RENAME4.status = NFS4ERR_EXIST; goto release; } } } res_RENAME4.RENAME4res_u.resok4.source_cinfo.before = cache_inode_get_changeid4(src_entry); res_RENAME4.RENAME4res_u.resok4.target_cinfo.before = cache_inode_get_changeid4(dst_entry); if(cache_status == CACHE_INODE_SUCCESS) { /* New entry already exists, its attributes are in attr_tst_*, check for old entry to see if types are compatible */ handlenew = &tst_entry_dst->handle; handleold = &tst_entry_src->handle; if(!FSAL_handlecmp(handlenew, handleold, &fsal_status)) { /* For the change_info4, get the 'change' attributes for both directories */ res_RENAME4.RENAME4res_u.resok4.source_cinfo.before = cache_inode_get_changeid4(src_entry); res_RENAME4.RENAME4res_u.resok4.target_cinfo.before = cache_inode_get_changeid4(dst_entry); res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE; res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE; res_RENAME4.status = NFS4_OK; goto release; } else { /* Destination exists and is something different from source */ if(( tst_entry_src->type == REGULAR_FILE && tst_entry_dst->type == REGULAR_FILE ) || ( tst_entry_src->type == DIRECTORY && tst_entry_dst->type == DIRECTORY )) { if(cache_inode_rename(src_entry, &oldname, dst_entry, &newname, &attr_src, &attr_dst, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_RENAME4.status = nfs4_Errno(cache_status); goto release; } } else { res_RENAME4.status = NFS4ERR_EXIST; goto release; } } } else { /* New entry does not already exist, call cache_entry_rename */ if(cache_inode_rename(src_entry, &oldname, dst_entry, &newname, &attr_src, &attr_dst, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_RENAME4.status = nfs4_Errno(cache_status); goto release; } } /* If you reach this point, then everything was alright */ /* For the change_info4, get the 'change' attributes for both directories */ res_RENAME4.RENAME4res_u.resok4.source_cinfo.after = cache_inode_get_changeid4(src_entry); res_RENAME4.RENAME4res_u.resok4.target_cinfo.after = cache_inode_get_changeid4(dst_entry); res_RENAME4.RENAME4res_u.resok4.target_cinfo.atomic = FALSE; res_RENAME4.RENAME4res_u.resok4.source_cinfo.atomic = FALSE; res_RENAME4.status = nfs4_Errno(cache_status); release: if (tst_entry_src) (void) cache_inode_put(tst_entry_src); if (tst_entry_dst) (void) cache_inode_put(tst_entry_dst); return (res_RENAME4.status); } /* nfs4_op_rename */
int nfs_Read(nfs_arg_t *parg, exportlist_t *pexport, fsal_op_context_t *pcontext, nfs_worker_data_t *pworker, struct svc_req *preq, nfs_res_t *pres) { cache_entry_t *pentry; fsal_attrib_list_t attr; fsal_attrib_list_t pre_attr; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; size_t size = 0; size_t read_size = 0; fsal_off_t offset = 0; void *data = NULL; cache_inode_file_type_t filetype; fsal_boolean_t eof_met=FALSE; int rc = NFS_REQ_OK; if(isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; switch (preq->rq_vers) { case NFS_V2: offset = parg->arg_read2.offset; size = parg->arg_read2.count; break; case NFS_V3: offset = parg->arg_read3.offset; size = parg->arg_read3.count; } nfs_FhandleToStr(preq->rq_vers, &(parg->arg_read2.file), &(parg->arg_read3.file), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Read handle: %s start: %llx len: %llx", str, (unsigned long long) offset, (unsigned long long) size); } if(preq->rq_vers == NFS_V3) { /* to avoid setting it on each error case */ pres->res_read3.READ3res_u.resfail.file_attributes.attributes_follow = FALSE; /* initialize for read of size 0 */ pres->res_read3.READ3res_u.resok.eof = FALSE; pres->res_read3.READ3res_u.resok.count = 0; pres->res_read3.READ3res_u.resok.data.data_val = NULL; pres->res_read3.READ3res_u.resok.data.data_len = 0; pres->res_read3.status = NFS3_OK; } else if(preq->rq_vers == NFS_V2) { /* initialize for read of size 0 */ pres->res_read2.READ2res_u.readok.data.nfsdata2_val = NULL; pres->res_read2.READ2res_u.readok.data.nfsdata2_len = 0; pres->res_attr2.status = NFS_OK; } /* Convert file handle into a cache entry */ if((pentry = nfs_FhandleToCache(preq->rq_vers, &(parg->arg_read2.file), &(parg->arg_read3.file), NULL, &(pres->res_read2.status), &(pres->res_read3.status), NULL, &pre_attr, pcontext, &rc)) == NULL) { /* Stale NFS FH ? */ goto out; } if((preq->rq_vers == NFS_V3) && (nfs3_Is_Fh_Xattr(&(parg->arg_read3.file)))) { rc = nfs3_Read_Xattr(parg, pexport, pcontext, preq, pres); goto out; } if(cache_inode_access(pentry, FSAL_READ_ACCESS, pcontext, &cache_status) != CACHE_INODE_SUCCESS) { switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = nfs2_Errno(cache_status); break; case NFS_V3: pres->res_read3.status = nfs3_Errno(cache_status); break; } rc = NFS_REQ_OK; goto out; } /* Extract the filetype */ filetype = cache_inode_fsal_type_convert(pre_attr.type); /* Sanity check: read only from a regular file */ if(filetype != REGULAR_FILE) { switch (preq->rq_vers) { case NFS_V2: /* * In the RFC tell it not good but it does * not tell what to do ... */ pres->res_attr2.status = NFSERR_ISDIR; break; case NFS_V3: if(filetype == DIRECTORY) pres->res_read3.status = NFS3ERR_ISDIR; else pres->res_read3.status = NFS3ERR_INVAL; break; } rc = NFS_REQ_OK; goto out; } /* For MDONLY export, reject write operation */ /* Request of type MDONLY_RO were rejected at the nfs_rpc_dispatcher level */ /* This is done by replying EDQUOT (this error is known for not disturbing the client's requests cache */ if(pexport->access_type == ACCESSTYPE_MDONLY || pexport->access_type == ACCESSTYPE_MDONLY_RO) { switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_read3.status = NFS3ERR_DQUOT; break; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; goto out; } /* Extract the argument from the request */ switch (preq->rq_vers) { case NFS_V2: offset = parg->arg_read2.offset; /* beginoffset is obsolete */ size = parg->arg_read2.count; /* totalcount is obsolete */ break; case NFS_V3: offset = parg->arg_read3.offset; size = parg->arg_read3.count; break; } /* * do not exceed maxium READ offset if set */ if((pexport->options & EXPORT_OPTION_MAXOFFSETREAD) == EXPORT_OPTION_MAXOFFSETREAD) { LogFullDebug(COMPONENT_NFSPROTO, "-----> Read offset=%llu count=%llu MaxOffSet=%llu", (unsigned long long) offset, (unsigned long long) size, (unsigned long long) pexport->MaxOffsetRead); if((fsal_off_t) (offset + size) > pexport->MaxOffsetRead) { LogEvent(COMPONENT_NFSPROTO, "NFS READ: A client tryed to violate max file size %llu for exportid #%hu", (unsigned long long) pexport->MaxOffsetRead, pexport->id); switch (preq->rq_vers) { case NFS_V2: pres->res_attr2.status = NFSERR_DQUOT; break; case NFS_V3: pres->res_read3.status = NFS3ERR_INVAL; break; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; goto out; } } /* * We should not exceed the FSINFO rtmax field for * the size */ if(((pexport->options & EXPORT_OPTION_MAXREAD) == EXPORT_OPTION_MAXREAD) && size > pexport->MaxRead) { /* * The client asked for too much, normally * this should not happen because the client * is calling nfs_Fsinfo at mount time and so * is aware of the server maximum write size */ size = pexport->MaxRead; } if(size == 0) { nfs_read_ok(pexport, preq, pres, NULL, 0, &pre_attr, 0); rc = NFS_REQ_OK; goto out; } else { data = gsh_malloc(size); if(data == NULL) { rc = NFS_REQ_DROP; goto out; } if((cache_inode_rdwr(pentry, CACHE_INODE_READ, offset, size, &read_size, data, &eof_met, pcontext, CACHE_INODE_SAFE_WRITE_TO_FS, &cache_status) == CACHE_INODE_SUCCESS) && (cache_inode_getattr(pentry, &attr, pcontext, &cache_status)) == CACHE_INODE_SUCCESS) { nfs_read_ok(pexport, preq, pres, data, read_size, &attr, ((offset + read_size) >= attr.filesize)); rc = NFS_REQ_OK; goto out; } gsh_free(data); } /* If we are here, there was an error */ if(nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } nfs_SetFailedStatus(pcontext, pexport, preq->rq_vers, cache_status, &pres->res_read2.status, &pres->res_read3.status, pentry, &(pres->res_read3.READ3res_u.resfail.file_attributes), NULL, NULL, NULL, NULL, NULL, NULL); rc = NFS_REQ_OK; out: /* return references */ if (pentry) cache_inode_put(pentry); return (rc); } /* nfs_Read */
int nfs41_op_open(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *pentry_parent = NULL; cache_entry_t *pentry_lookup = NULL; cache_entry_t *pentry_newfile = NULL; fsal_handle_t *pnewfsal_handle = NULL; fsal_attrib_list_t attr_parent; fsal_attrib_list_t attr; fsal_attrib_list_t attr_newfile; fsal_attrib_list_t sattr; fsal_openflags_t openflags = 0; cache_inode_status_t cache_status; nfsstat4 rc; int retval; fsal_name_t filename; bool_t AttrProvided = FALSE; fsal_accessmode_t mode = 0600; nfs_fh4 newfh4; nfs_client_id_t nfs_clientid; nfs_worker_data_t *pworker = NULL; int convrc = 0; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_open"; cache_inode_state_data_t candidate_data; cache_inode_state_type_t candidate_type; cache_inode_state_t *pfile_state = NULL; cache_inode_state_t *pstate_found_iterate = NULL; cache_inode_state_t *pstate_previous_iterate = NULL; cache_inode_state_t *pstate_found_same_owner = NULL; cache_inode_open_owner_name_t owner_name; cache_inode_open_owner_name_t *powner_name = NULL; cache_inode_open_owner_t *powner = NULL; bool_t open_owner_known = FALSE; resp->resop = NFS4_OP_OPEN; res_OPEN4.status = NFS4_OK; uint32_t tmp_attr[2]; uint_t tmp_int = 2; int pnfs_status; cache_inode_create_arg_t create_arg; pworker = (nfs_worker_data_t *) data->pclient->pworker; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_NOFILEHANDLE; return res_OPEN4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_BADHANDLE; return res_OPEN4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_FHEXPIRED; return res_OPEN4.status; } /* This can't be done on the pseudofs */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_ROFS; return res_OPEN4.status; } /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_open_xattr(op, data, resp); /* If data->current_entry is empty, repopulate it */ if(data->current_entry == NULL) { if((data->current_entry = nfs_FhandleToCache(NFS_V4, NULL, NULL, &(data->currentFH), NULL, NULL, &(res_OPEN4.status), &attr, data->pcontext, data->pclient, data->ht, &retval)) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } } /* Set parent */ pentry_parent = data->current_entry; /* First switch is based upon claim type */ switch (arg_OPEN4.claim.claim) { case CLAIM_DELEGATE_CUR: case CLAIM_DELEGATE_PREV: /* Check for name length */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len > FSAL_MAX_NAME_LEN) { res_OPEN4.status = NFS4ERR_NAMETOOLONG; return res_OPEN4.status; } /* get the filename from the argument, it should not be empty */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len == 0) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } res_OPEN4.status = NFS4ERR_NOTSUPP; return res_OPEN4.status; break; case CLAIM_NULL: /* Check for name length */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len > FSAL_MAX_NAME_LEN) { res_OPEN4.status = NFS4ERR_NAMETOOLONG; return res_OPEN4.status; } /* get the filename from the argument, it should not be empty */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len == 0) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } /* Check if asked attributes are correct */ if(arg_OPEN4.openhow.openflag4_u.how.mode == GUARDED4 || arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4) { if(!nfs4_Fattr_Supported (&arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs)) { res_OPEN4.status = NFS4ERR_ATTRNOTSUPP; return res_OPEN4.status; } /* Do not use READ attr, use WRITE attr */ if(!nfs4_Fattr_Check_Access (&arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs, FATTR4_ATTR_WRITE)) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } } /* Check if filename is correct */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_OPEN4.claim.open_claim4_u. file, &filename))) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Check parent */ pentry_parent = data->current_entry; /* Parent must be a directory */ if((pentry_parent->internal_md.type != DIR_BEGINNING) && (pentry_parent->internal_md.type != DIR_CONTINUE)) { /* Parent object is not a directory... */ if(pentry_parent->internal_md.type == SYMBOLIC_LINK) res_OPEN4.status = NFS4ERR_SYMLINK; else res_OPEN4.status = NFS4ERR_NOTDIR; return res_OPEN4.status; } /* What kind of open is it ? */ LogFullDebug(COMPONENT_NFS_V4, " OPEN: Claim type = %d Open Type = %d Share Deny = %d Share Access = %d \n", arg_OPEN4.claim.claim, arg_OPEN4.openhow.opentype, arg_OPEN4.share_deny, arg_OPEN4.share_access); /* It this a known client id ? */ LogDebug(COMPONENT_NFS_V4, "OPEN Client id = %llx", arg_OPEN4.owner.clientid); /* Is this open_owner known ? */ if(!nfs_convert_open_owner(&arg_OPEN4.owner, &owner_name)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } if(!nfs_open_owner_Get_Pointer(&owner_name, &powner)) { /* This open owner is not known yet, allocated and set up a new one */ GET_PREALLOC(powner, data->pclient->pool_open_owner, data->pclient->nb_pre_state_v4, cache_inode_open_owner_t, next); GET_PREALLOC(powner_name, data->pclient->pool_open_owner_name, data->pclient->nb_pre_state_v4, cache_inode_open_owner_name_t, next); if(powner == NULL || powner_name == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } memcpy((char *)powner_name, (char *)&owner_name, sizeof(cache_inode_open_owner_name_t)); /* set up the content of the open_owner */ powner->confirmed = FALSE; powner->seqid = 1; /* NFSv4.1 specific, initial seqid is 1 */ powner->related_owner = NULL; powner->next = NULL; powner->clientid = arg_OPEN4.owner.clientid; powner->owner_len = arg_OPEN4.owner.owner.owner_len; memcpy((char *)powner->owner_val, (char *)arg_OPEN4.owner.owner.owner_val, arg_OPEN4.owner.owner.owner_len); powner->owner_val[powner->owner_len] = '\0'; pthread_mutex_init(&powner->lock, NULL); if(!nfs_open_owner_Set(powner_name, powner)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } } /* Status of parent directory before the operation */ if((cache_status = cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.before = (changeid4) pentry_parent->internal_md.mod_time; /* CLient may have provided fattr4 to set attributes at creation time */ if(arg_OPEN4.openhow.openflag4_u.how.mode == GUARDED4 || arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4) { if(arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs.attrmask. bitmap4_len != 0) { /* Convert fattr4 so nfs4_sattr */ convrc = nfs4_Fattr_To_FSAL_attr(&sattr, &(arg_OPEN4.openhow.openflag4_u.how. createhow4_u.createattrs)); if(convrc == 0) { res_OPEN4.status = NFS4ERR_ATTRNOTSUPP; return res_OPEN4.status; } if(convrc == -1) { res_OPEN4.status = NFS4ERR_BADXDR; return res_OPEN4.status; } AttrProvided = TRUE; } } /* Second switch is based upon "openhow" */ switch (arg_OPEN4.openhow.opentype) { case OPEN4_CREATE: /* a new file is to be created */ /* Does a file with this name already exist ? */ pentry_lookup = cache_inode_lookup(pentry_parent, &filename, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status); if(cache_status != CACHE_INODE_NOT_FOUND) { /* if open is UNCHECKED, return NFS4_OK (RFC3530 page 172) */ if(arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4 && (cache_status == CACHE_INODE_SUCCESS)) { /* If the file is opened for write, OPEN4 while deny share write access, * in this case, check caller has write access to the file */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) { if(cache_inode_access(pentry_lookup, FSAL_W_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_WRONLY; } /* Same check on read: check for readability of a file before opening it for read */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ) { if(cache_inode_access(pentry_lookup, FSAL_R_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDONLY; } if(AttrProvided == TRUE) /* Set the attribute if provided */ { if((cache_status = cache_inode_setattr(pentry_lookup, &sattr, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset = arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs. attrmask; } else res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 0; /* Same check on write */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) { if(cache_inode_access(pentry_lookup, FSAL_W_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDWR; } /* Set the state for the related file */ /* Prepare state management structure */ candidate_type = CACHE_INODE_STATE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; if(cache_inode_add_state(pentry_lookup, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_lookup, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_SHARE_DENIED; res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; if((res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val = (uint32_t *) Mem_Alloc(res_OPEN4.OPEN4res_u.resok4.attrset. bitmap4_len * sizeof(uint32_t))) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.after), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; res_OPEN4.OPEN4res_u.resok4.stateid.seqid = pfile_state->seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pfile_state->stateid_other, 12); /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_lookup, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_lookup; data->current_filetype = REGULAR_FILE; res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } /* if open is EXCLUSIVE, but verifier is the same, return NFS4_OK (RFC3530 page 173) */ if(arg_OPEN4.openhow.openflag4_u.how.mode == EXCLUSIVE4) { if((pentry_lookup != NULL) && (pentry_lookup->internal_md.type == REGULAR_FILE)) { pstate_found_iterate = NULL; pstate_previous_iterate = NULL; do { cache_inode_state_iterate(pentry_lookup, &pstate_found_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &cache_status); if(cache_status == CACHE_INODE_STATE_ERROR) break; if(cache_status == CACHE_INODE_INVALID_ARGUMENT) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } /* Check is open_owner is the same */ if(pstate_found_iterate != NULL) { if((pstate_found_iterate->state_type == CACHE_INODE_STATE_SHARE) && !memcmp(arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->powner->owner_val, pstate_found_iterate->powner->owner_len) && !memcmp(pstate_found_iterate->state_data.share. oexcl_verifier, arg_OPEN4.openhow.openflag4_u.how. createhow4_u.createverf, NFS4_VERIFIER_SIZE)) { /* A former open EXCLUSIVE with same owner and verifier was found, resend it */ res_OPEN4.OPEN4res_u.resok4.stateid.seqid = pstate_found_iterate->seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pstate_found_iterate->stateid_other, 12); memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.after), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_lookup, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_lookup; data->current_filetype = REGULAR_FILE; /* regular exit */ res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } } /* if( pstate_found_iterate != NULL ) */ pstate_previous_iterate = pstate_found_iterate; } while(pstate_found_iterate != NULL); } } /* Managing GUARDED4 mode */ if(cache_status != CACHE_INODE_SUCCESS) res_OPEN4.status = nfs4_Errno(cache_status); else res_OPEN4.status = NFS4ERR_EXIST; /* File already exists */ return res_OPEN4.status; } /* if( cache_status != CACHE_INODE_NOT_FOUND ), if file already exists basically */ LogFullDebug(COMPONENT_NFS_V4, " OPEN open.how = %d\n", arg_OPEN4.openhow.openflag4_u.how.mode); create_arg.use_pnfs = FALSE; #ifdef _USE_PNFS /* set the file has "managed via pNFS" */ if(data->pexport->options & EXPORT_OPTION_USE_PNFS) create_arg.use_pnfs = TRUE; #endif /* _USE_PNFS */ /* Create the file, if we reach this point, it does not exist, we can create it */ if((pentry_newfile = cache_inode_create(pentry_parent, &filename, REGULAR_FILE, mode, &create_arg, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { /* If the file already exists, this is not an error if open mode is UNCHECKED */ if(cache_status != CACHE_INODE_ENTRY_EXISTS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } else { /* If this point is reached, then the file already exists, cache_status == CACHE_INODE_ENTRY_EXISTS and pentry_newfile == NULL This probably means EXCLUSIVE4 mode is used and verifier matches. pentry_newfile is then set to pentry_lookup */ pentry_newfile = pentry_lookup; } } /* Prepare state management structure */ candidate_type = CACHE_INODE_STATE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; candidate_data.share.lockheld = 0; /* If file is opened under mode EXCLUSIVE4, open verifier should be kept to detect non vicious double open */ if(arg_OPEN4.openhow.openflag4_u.how.mode == EXCLUSIVE4) { strncpy(candidate_data.share.oexcl_verifier, arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createverf, NFS4_VERIFIER_SIZE); } if(cache_inode_add_state(pentry_newfile, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } if(AttrProvided == TRUE) /* Set the attribute if provided */ { if((cache_status = cache_inode_setattr(pentry_newfile, &sattr, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } } /* Set the openflags variable */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) openflags |= FSAL_O_RDONLY; if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_READ) openflags |= FSAL_O_WRONLY; if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) openflags = FSAL_O_RDWR; if(arg_OPEN4.share_access != 0) openflags = FSAL_O_RDWR; /* @todo : BUGAZOMEU : Something better later */ /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_newfile, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } break; case OPEN4_NOCREATE: /* It was not a creation, but a regular open */ /* The filehandle to the new file replaces the current filehandle */ if(pentry_newfile == NULL) { if((pentry_newfile = cache_inode_lookup(pentry_parent, &filename, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } } /* OPEN4 is to be done on a file */ if(pentry_newfile->internal_md.type != REGULAR_FILE) { if(pentry_newfile->internal_md.type == DIR_BEGINNING || pentry_newfile->internal_md.type == DIR_CONTINUE) { res_OPEN4.status = NFS4ERR_ISDIR; return res_OPEN4.status; } else if(pentry_newfile->internal_md.type == SYMBOLIC_LINK) { res_OPEN4.status = NFS4ERR_SYMLINK; return res_OPEN4.status; } else { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } } /* If the file is opened for write, OPEN4 while deny share write access, * in this case, check caller has write access to the file */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) { if(cache_inode_access(pentry_newfile, FSAL_W_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_WRONLY; } /* Same check on read: check for readability of a file before opening it for read */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ) { if(cache_inode_access(pentry_newfile, FSAL_R_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDONLY; } /* Same check on write */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) { if(cache_inode_access(pentry_newfile, FSAL_W_OK, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDWR; } /* Try to find if the same open_owner already has acquired a stateid for this file */ pstate_found_iterate = NULL; pstate_previous_iterate = NULL; do { cache_inode_state_iterate(pentry_newfile, &pstate_found_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &cache_status); if(cache_status == CACHE_INODE_STATE_ERROR) break; /* Get out of the loop */ if(cache_status == CACHE_INODE_INVALID_ARGUMENT) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } /* Check is open_owner is the same */ if(pstate_found_iterate != NULL) { if((pstate_found_iterate->state_type == CACHE_INODE_STATE_SHARE) && (pstate_found_iterate->powner->clientid == arg_OPEN4.owner.clientid) && ((pstate_found_iterate->powner->owner_len == arg_OPEN4.owner.owner.owner_len) && (!memcmp (arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->powner->owner_val, pstate_found_iterate->powner->owner_len)))) { /* We'll be re-using the found state */ pstate_found_same_owner = pstate_found_iterate; } else { /* This is a different owner, check for possible conflicts */ if(memcmp(arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->powner->owner_val, pstate_found_iterate->powner->owner_len)) { switch (pstate_found_iterate->state_type) { case CACHE_INODE_STATE_SHARE: if((pstate_found_iterate->state_data.share. share_access & OPEN4_SHARE_ACCESS_WRITE) && (arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE)) { res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } break; } } } /* In all cases opening in read access a read denied file or write access to a write denied file * should fail, even if the owner is the same, see discussion in 14.2.16 and 8.9 */ if(pstate_found_iterate->state_type == CACHE_INODE_STATE_SHARE) { /* deny read access on read denied file */ if((pstate_found_iterate->state_data.share. share_deny & OPEN4_SHARE_DENY_READ) && (arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ)) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); powner->seqid += 1; res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } /* deny write access on write denied file */ if((pstate_found_iterate->state_data.share. share_deny & OPEN4_SHARE_DENY_WRITE) && (arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE)) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } } } /* if( pstate_found_iterate != NULL ) */ pstate_previous_iterate = pstate_found_iterate; } while(pstate_found_iterate != NULL); if(pstate_found_same_owner != NULL) { pfile_state = pstate_found_same_owner; pfile_state->seqid += 1; P(powner->lock); powner->seqid += 1; V(powner->lock); } else { /* Set the state for the related file */ /* Prepare state management structure */ candidate_type = CACHE_INODE_STATE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; if(cache_inode_add_state(pentry_newfile, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } } /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_newfile, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->lock); powner->seqid += 1; V(powner->lock); res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } break; default: /* Seqid has to be incremented even in this case */ if(powner != NULL) { P(powner->lock); powner->seqid += 1; V(powner->lock); } res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; break; } /* switch( arg_OPEN4.openhow.opentype ) */ break; case CLAIM_PREVIOUS: break; default: /* Seqid has to be incremented even in this case */ if(powner != NULL) { P(powner->lock); powner->seqid += 1; V(powner->lock); } res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; break; } /* switch( arg_OPEN4.claim.claim ) */ /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_newfile, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_newfile; data->current_filetype = REGULAR_FILE; /* No do not need newfh any more */ Mem_Free((char *)newfh4.nfs_fh4_val); /* Status of parent directory after the operation */ if((cache_status = cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; if((res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val = (uint32_t *) Mem_Alloc(res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len * sizeof(uint32_t))) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val[0] = 0; /* No Attributes set */ res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val[1] = 0; /* No Attributes set */ if(arg_OPEN4.openhow.opentype == OPEN4_CREATE) { tmp_int = 2; tmp_attr[0] = FATTR4_SIZE; tmp_attr[1] = FATTR4_MODE; nfs4_list_to_bitmap4(&(res_OPEN4.OPEN4res_u.resok4.attrset), &tmp_int, tmp_attr); res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; } res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; res_OPEN4.OPEN4res_u.resok4.stateid.seqid = powner->seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pfile_state->stateid_other, 12); /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* regular exit */ res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } /* nfs41_op_open */
int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_verify"; fsal_attrib_list_t file_attr; cache_inode_status_t cache_status; fattr4 file_attr4; int rc = 0; resp->resop = NFS4_OP_VERIFY; res_VERIFY4.status = NFS4_OK; /* Do basic checks on a filehandle */ res_VERIFY4.status = nfs4_sanity_check_FH(data, 0LL); if(res_VERIFY4.status != NFS4_OK) return res_VERIFY4.status; /* operation is always permitted on pseudofs */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_VERIFY4.status = NFS4_OK; return res_VERIFY4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access(&arg_VERIFY4.obj_attributes, FATTR4_ATTR_READ)) { res_VERIFY4.status = NFS4ERR_INVAL; return res_VERIFY4.status; } /* Ask only for supported attributes */ if(!nfs4_Fattr_Supported(&arg_VERIFY4.obj_attributes)) { res_VERIFY4.status = NFS4ERR_ATTRNOTSUPP; return res_VERIFY4.status; } /* Get the cache inode attribute */ if((cache_status = cache_inode_getattr(data->current_entry, &file_attr, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_VERIFY4.status = NFS4ERR_INVAL; return res_VERIFY4.status; } if(nfs4_FSALattr_To_Fattr(data->pexport, &file_attr, &file_attr4, data, &(data->currentFH), &(arg_VERIFY4.obj_attributes.attrmask)) != 0) { res_VERIFY4.status = NFS4ERR_SERVERFAULT; return res_VERIFY4.status; } if((rc = nfs4_Fattr_cmp(&(arg_VERIFY4.obj_attributes), &file_attr4)) == TRUE) res_VERIFY4.status = NFS4_OK; else { if(rc == -1) res_VERIFY4.status = NFS4ERR_INVAL; else res_VERIFY4.status = NFS4ERR_NOT_SAME; } nfs4_Fattr_Free(&file_attr4); return res_VERIFY4.status; } /* nfs4_op_verify */
cache_inode_status_t cache_inode_readdir(cache_entry_t *directory, uint64_t cookie, unsigned int *nbfound, bool *eod_met, attrmask_t attrmask, cache_inode_getattr_cb_t cb, void *opaque) { /* The entry being examined */ cache_inode_dir_entry_t *dirent = NULL; /* The node in the tree being traversed */ struct avltree_node *dirent_node; /* The access mask corresponding to permission to list directory entries */ fsal_accessflags_t access_mask = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR)); fsal_accessflags_t access_mask_attr = (FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_LIST_DIR) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_EXECUTE)); cache_inode_status_t status = CACHE_INODE_SUCCESS; cache_inode_status_t attr_status; struct cache_inode_readdir_cb_parms cb_parms = { opaque, NULL, true, 0, true }; bool retry_stale = true; LogFullDebug(COMPONENT_NFS_READDIR, "Enter...."); /* readdir can be done only with a directory */ if (directory->type != DIRECTORY) { status = CACHE_INODE_NOT_A_DIRECTORY; /* no lock acquired so far, just return status */ LogFullDebug(COMPONENT_NFS_READDIR, "Not a directory"); return status; } /* cache_inode_lock_trust_attrs can return an error, and no lock will * be acquired */ status = cache_inode_lock_trust_attrs(directory, false); if (status != CACHE_INODE_SUCCESS) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs status=%s", cache_inode_err_str(status)); return status; } /* Adjust access mask if ACL is asked for. * NOTE: We intentionally do NOT check ACE4_READ_ATTR. */ if ((attrmask & ATTR_ACL) != 0) { access_mask |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); access_mask_attr |= FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_ACL); } /* Check if user (as specified by the credentials) is authorized to read * the directory or not */ status = cache_inode_access_no_mutex(directory, access_mask); if (status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "permission check for directory status=%s", cache_inode_err_str(status)); PTHREAD_RWLOCK_unlock(&directory->attr_lock); return status; } if (attrmask != 0) { /* Check for access permission to get attributes */ attr_status = cache_inode_access_no_mutex(directory, access_mask_attr); if (attr_status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "permission check for attributes " "status=%s", cache_inode_err_str(attr_status)); } } else /* No attributes requested, we don't need permission */ attr_status = CACHE_INODE_SUCCESS; PTHREAD_RWLOCK_rdlock(&directory->content_lock); PTHREAD_RWLOCK_unlock(&directory->attr_lock); if (! ((directory->flags & CACHE_INODE_TRUST_CONTENT) && (directory->flags & CACHE_INODE_DIR_POPULATED))) { PTHREAD_RWLOCK_unlock(&directory->content_lock); PTHREAD_RWLOCK_wrlock(&directory->content_lock); status = cache_inode_readdir_populate(directory); if (status != CACHE_INODE_SUCCESS) { LogFullDebug(COMPONENT_NFS_READDIR, "cache_inode_readdir_populate status=%s", cache_inode_err_str(status)); goto unlock_dir; } } /* deal with initial cookie value: * 1. cookie is invalid (-should- be checked by caller) * 2. cookie is 0 (first cookie) -- ok * 3. cookie is > than highest dirent position (error) * 4. cookie <= highest dirent position but > highest cached cookie * (currently equivalent to #2, because we pre-populate the cookie * avl) * 5. cookie is in cached range -- ok */ if (cookie > 0) { /* N.B., cache_inode_avl_qp_insert_s ensures k > 2 */ if (cookie < 3) { status = CACHE_INODE_BAD_COOKIE; LogFullDebug(COMPONENT_NFS_READDIR, "Bad cookie"); goto unlock_dir; } /* we assert this can now succeed */ dirent = cache_inode_avl_lookup_k(directory, cookie, CACHE_INODE_FLAG_NEXT_ACTIVE); if (!dirent) { /* Linux (3.4, etc) has been observed to send readdir * at the offset of the last entry's cookie, and * returns no dirents to userland if that readdir * notfound or badcookie. */ if (cache_inode_avl_lookup_k (directory, cookie, CACHE_INODE_FLAG_NONE)) { /* yup, it was the last entry */ LogFullDebug(COMPONENT_NFS_READDIR, "EOD because empty result"); *eod_met = true; goto unlock_dir; } LogFullDebug(COMPONENT_NFS_READDIR, "seek to cookie=%" PRIu64 " fail", cookie); status = CACHE_INODE_BAD_COOKIE; goto unlock_dir; } /* dirent is the NEXT entry to return, since we sent * CACHE_INODE_FLAG_NEXT_ACTIVE */ dirent_node = &dirent->node_hk; } else { /* initial readdir */ dirent_node = avltree_first(&directory->object.dir.avl.t); } LogFullDebug(COMPONENT_NFS_READDIR, "About to readdir in cache_inode_readdir: directory=%p " "cookie=%" PRIu64 " collisions %d", directory, cookie, directory->object.dir.avl.collisions); /* Now satisfy the request from the cached readdir--stop when either * the requested sequence or dirent sequence is exhausted */ *nbfound = 0; *eod_met = false; for (; cb_parms.in_result && dirent_node; dirent_node = avltree_next(dirent_node)) { cache_entry_t *entry = NULL; cache_inode_status_t tmp_status = 0; dirent = avltree_container_of(dirent_node, cache_inode_dir_entry_t, node_hk); estale_retry: LogFullDebug(COMPONENT_NFS_READDIR, "Lookup direct %s", dirent->name); entry = cache_inode_get_keyed(&dirent->ckey, CIG_KEYED_FLAG_NONE, &tmp_status); if (!entry) { LogFullDebug(COMPONENT_NFS_READDIR, "Lookup returned %s", cache_inode_err_str(tmp_status)); if (retry_stale && tmp_status == CACHE_INODE_ESTALE) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - retrying entry", cache_inode_err_str(tmp_status), dirent->name); retry_stale = false; /* only one retry per * dirent */ goto estale_retry; } if (tmp_status == CACHE_INODE_NOT_FOUND || tmp_status == CACHE_INODE_ESTALE) { /* Directory changed out from under us. Invalidate it, skip the name, and keep going. */ atomic_clear_uint32_t_bits( &directory->flags, CACHE_INODE_TRUST_CONTENT); LogDebug(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - skipping entry", cache_inode_err_str(tmp_status), dirent->name); continue; } else { /* Something is more seriously wrong, probably an inconsistency. */ status = tmp_status; LogCrit(COMPONENT_NFS_READDIR, "cache_inode_get_keyed returned %s " "for %s - bailing out", cache_inode_err_str(status), dirent->name); goto unlock_dir; } } LogFullDebug(COMPONENT_NFS_READDIR, "cache_inode_readdir: dirent=%p name=%s " "cookie=%" PRIu64 " (probes %d)", dirent, dirent->name, dirent->hk.k, dirent->hk.p); cb_parms.name = dirent->name; cb_parms.attr_allowed = attr_status == CACHE_INODE_SUCCESS; cb_parms.cookie = dirent->hk.k; tmp_status = cache_inode_getattr(entry, &cb_parms, cb, CB_ORIGINAL); if (tmp_status != CACHE_INODE_SUCCESS) { cache_inode_lru_unref(entry, LRU_FLAG_NONE); if (tmp_status == CACHE_INODE_ESTALE) { if (retry_stale) { LogDebug(COMPONENT_NFS_READDIR, "cache_inode_getattr returned " "%s for %s - retrying entry", cache_inode_err_str (tmp_status), dirent->name); retry_stale = false; /* only one retry * per dirent */ goto estale_retry; } /* Directory changed out from under us. Invalidate it, skip the name, and keep going. */ atomic_clear_uint32_t_bits( &directory->flags, CACHE_INODE_TRUST_CONTENT); LogDebug(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs " "returned %s for %s - skipping entry", cache_inode_err_str(tmp_status), dirent->name); continue; } status = tmp_status; LogCrit(COMPONENT_NFS_READDIR, "cache_inode_lock_trust_attrs returned %s for " "%s - bailing out", cache_inode_err_str(status), dirent->name); goto unlock_dir; } (*nbfound)++; cache_inode_lru_unref(entry, LRU_FLAG_NONE); if (!cb_parms.in_result) { LogDebug(COMPONENT_NFS_READDIR, "bailing out due to entry not in result"); break; } } /* We have reached the last node and every node traversed was added to the result */ LogDebug(COMPONENT_NFS_READDIR, "dirent_node = %p, nbfound = %u, in_result = %s", dirent_node, *nbfound, cb_parms.in_result ? "TRUE" : "FALSE"); if (!dirent_node && cb_parms.in_result) *eod_met = true; else *eod_met = false; unlock_dir: PTHREAD_RWLOCK_unlock(&directory->content_lock); return status; } /* cache_inode_readdir */