int nfs4_op_nverify(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { NVERIFY4args * const arg_NVERIFY4 = &op->nfs_argop4_u.opnverify; NVERIFY4res * const res_NVERIFY4 = &resp->nfs_resop4_u.opnverify; fattr4 file_attr4; int rc = 0; resp->resop = NFS4_OP_NVERIFY; res_NVERIFY4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_NVERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_NVERIFY4->status != NFS4_OK) return res_NVERIFY4->status; /* operation is always permitted on pseudofs */ if (nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_NVERIFY4->status = NFS4_OK; return res_NVERIFY4->status; } /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access(&arg_NVERIFY4->obj_attributes, FATTR4_ATTR_READ)) { res_NVERIFY4->status = NFS4ERR_INVAL; return res_NVERIFY4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_NVERIFY4->obj_attributes)) { res_NVERIFY4->status = NFS4ERR_ATTRNOTSUPP; return res_NVERIFY4->status; } res_NVERIFY4->status = cache_entry_To_Fattr(data->current_entry, &file_attr4, data, &(data->currentFH), &(arg_NVERIFY4->obj_attributes.attrmask)); if (res_NVERIFY4->status != NFS4_OK) return res_NVERIFY4->status; rc = nfs4_Fattr_cmp(&arg_NVERIFY4->obj_attributes, &file_attr4); if (rc == false) { res_NVERIFY4->status = NFS4_OK; } else { if (rc == -1) res_NVERIFY4->status = NFS4ERR_INVAL; else res_NVERIFY4->status = NFS4ERR_SAME; } nfs4_Fattr_Free(&file_attr4); return res_NVERIFY4->status; } /* nfs4_op_nverify */
int nfs4_op_remove(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *parent_entry = NULL; fsal_attrib_list_t attr_parent; fsal_name_t name; cache_inode_status_t cache_status; #ifdef _USE_PNFS pnfs_file_t pnfs_file; #endif char __attribute__ ((__unused__)) funcname[] = "nfs4_op_remove"; resp->resop = NFS4_OP_REMOVE; res_REMOVE4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_REMOVE4.status = NFS4ERR_NOFILEHANDLE; return res_REMOVE4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_REMOVE4.status = NFS4ERR_BADHANDLE; return res_REMOVE4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_REMOVE4.status = NFS4ERR_FHEXPIRED; return res_REMOVE4.status; } /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_REMOVE4.status = NFS4ERR_ROFS; return res_REMOVE4.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_remove_xattr(op, data, resp); /* Get the parent entry (aka the current one in the compound data) */ parent_entry = data->current_entry; /* We have to keep track of the 'change' file attribute for reply structure */ memset(&(res_REMOVE4.REMOVE4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_REMOVE4.REMOVE4res_u.resok4.cinfo.before = (changeid4) parent_entry->internal_md.mod_time; /* The operation delete object named arg_REMOVE4.target in directory pointed bt cuurentFH */ /* Make sur the currentFH is pointed a directory */ if(data->current_filetype != DIR_BEGINNING && data->current_filetype != DIR_CONTINUE) { res_REMOVE4.status = NFS4ERR_NOTDIR; return res_REMOVE4.status; } /* Check for name length */ if(arg_REMOVE4.target.utf8string_len > FSAL_MAX_NAME_LEN) { res_REMOVE4.status = NFS4ERR_NAMETOOLONG; return res_REMOVE4.status; } /* get the filename from the argument, it should not be empty */ if(arg_REMOVE4.target.utf8string_len == 0) { res_REMOVE4.status = NFS4ERR_INVAL; return res_REMOVE4.status; } /* NFS4_OP_REMOVE can delete files as well as directory, it replaces NFS3_RMDIR and NFS3_REMOVE * because of this, we have to know if object is a directory or not */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_REMOVE4.target, &name))) != CACHE_INODE_SUCCESS) { res_REMOVE4.status = nfs4_Errno(cache_status); return res_REMOVE4.status; } /* Test RM7: remiving '.' should return NFS4ERR_BADNAME */ if(!FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT_DOT)) { res_REMOVE4.status = NFS4ERR_BADNAME; return res_REMOVE4.status; } if((cache_status = cache_inode_remove(parent_entry, &name, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_REMOVE4.status = nfs4_Errno(cache_status); return res_REMOVE4.status; } /* We have to keep track of the 'change' file attribute for reply structure */ memset(&(res_REMOVE4.REMOVE4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_REMOVE4.REMOVE4res_u.resok4.cinfo.after = (changeid4) parent_entry->internal_md.mod_time; /* Operation was not atomic .... */ res_REMOVE4.REMOVE4res_u.resok4.cinfo.atomic = TRUE; /* If you reach this point, everything was ok */ res_REMOVE4.status = NFS4_OK; return NFS4_OK; } /* nfs4_op_remove */
/** * * 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 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 */
/** * nfs4_op_readdir: The NFS4_OP_READDIR. * * Implements the NFS4_OP_READDIR. If fh is a pseudo FH, then call is routed to routine nfs4_op_readdir_pseudo * * @param op [IN] pointer to nfs4_op arguments * @param data [INOUT] Pointer to the compound request's data * @param resp [IN] Pointer to nfs4_op results * * @return NFS4_OK if ok, any other value show an error. * */ int nfs4_op_readdir(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry = NULL; cache_inode_endofdir_t eod_met; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; cache_inode_status_t cache_status_attr; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_readdir"; unsigned long dircount; unsigned long maxcount; entry4 *entry_nfs_array; cache_inode_dir_entry_t **dirent_array = NULL; verifier4 cookie_verifier; uint64_t cookie = 0; uint64_t end_cookie = 0; fsal_handle_t *entry_FSALhandle; nfs_fh4 entryFH; char val_fh[NFS4_FHSIZE]; entry_name_array_item_t *entry_name_array = NULL; unsigned int estimated_num_entries; unsigned int num_entries; int dir_pentry_unlock = FALSE; unsigned int i = 0; unsigned int outbuffsize = 0 ; unsigned int entrysize = 0 ; bitmap4 RdAttrErrorBitmap = { 1, (uint32_t *) "\0\0\0\b" }; /* 0xB = 11 = FATTR4_RDATTR_ERROR */ attrlist4 RdAttrErrorVals = { 0, NULL }; /* Nothing to be seen here */ resp->resop = NFS4_OP_READDIR; res_READDIR4.status = NFS4_OK; entryFH.nfs_fh4_len = 0; entryFH.nfs_fh4_val = val_fh; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_NOFILEHANDLE; return res_READDIR4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_BADHANDLE; return res_READDIR4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_FHEXPIRED; return res_READDIR4.status; } /* Pseudo Fs management */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_readdir_pseudo(op, data, resp); /* Xattrs management */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_readdir_xattr(op, data, resp); /* You can readdir only within a directory */ dir_pentry = data->current_entry; if(data->current_filetype != DIRECTORY) { res_READDIR4.status = NFS4ERR_NOTDIR; return res_READDIR4.status; } /* get the characteristic value for readdir operation */ dircount = arg_READDIR4.dircount; maxcount = arg_READDIR4.maxcount*0.9; cookie = (unsigned int)arg_READDIR4.cookie; /* dircount is considered meaningless by many nfsv4 client (like the CITI * one). we use maxcount instead. */ /* the Linux 3.0, 3.1.0 clients vs. TCP Ganesha comes out 10x slower * with 500 max entries */ #if 0 /* takes 2s to return 2999 entries */ estimated_num_entries = maxcount / sizeof(entry4); #else /* takes 20s to return 2999 entries */ estimated_num_entries = 50; #endif LogFullDebug(COMPONENT_NFS_V4, "--- nfs4_op_readdir ---> dircount=%lu maxcount=%lu arg_cookie=%" PRIu64" cookie=%"PRIu64" estimated_num_entries=%u", dircount, maxcount, arg_READDIR4.cookie, cookie, estimated_num_entries); /* Do not use a cookie of 1 or 2 (reserved values) */ if(cookie == 1 || cookie == 2) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access_Bitmap(&arg_READDIR4.attr_request, FATTR4_ATTR_READ)) { res_READDIR4.status = NFS4ERR_INVAL; return res_READDIR4.status; } /* If maxcount is too short, return NFS4ERR_TOOSMALL */ if(maxcount < sizeof(entry4) || estimated_num_entries == 0) { res_READDIR4.status = NFS4ERR_TOOSMALL; return res_READDIR4.status; } /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the pentry. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ memset(cookie_verifier, 0, NFS4_VERIFIER_SIZE); if(data->pexport->UseCookieVerifier == 1) memcpy(cookie_verifier, &dir_pentry->internal_md.mod_time, sizeof(time_t)); /* Cookie delivered by the server and used by the client SHOULD not ne 0, 1 or 2 (cf RFC3530, page192) * because theses value are reserved for special use. * 0 - cookie for first READDIR * 1 - reserved for . on client handside * 2 - reserved for .. on client handside * Entries '.' and '..' are not returned also * For these reason, there will be an offset of 3 between NFS4 cookie and * HPSS cookie */ if((cookie != 0) && (data->pexport->UseCookieVerifier == 1)) { if(memcmp(cookie_verifier, arg_READDIR4.cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } } /* The default behaviour is to consider that eof is not reached, the * returned values by cache_inode_readdir will let us know if eod was * reached or not */ res_READDIR4.READDIR4res_u.resok4.reply.eof = FALSE; /* Get prepared for readdir */ if((dirent_array = (cache_inode_dir_entry_t **) Mem_Alloc( estimated_num_entries * sizeof(cache_inode_dir_entry_t*))) == NULL) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Perform the readdir operation */ if(cache_inode_readdir(dir_pentry, data->pexport->cache_inode_policy, cookie, estimated_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, data->ht, &dir_pentry_unlock, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_READDIR4.status = nfs4_Errno(cache_status); goto out; } /* For an empty directory, we will find only . and .., so reply as if the * end is reached */ if(num_entries == 0) { /* only . and .. */ res_READDIR4.READDIR4res_u.resok4.reply.entries = NULL; res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } else { /* Start computing the outbuffsize */ outbuffsize = sizeof( bool_t) /* eof */ + sizeof( nfsstat4 ) /* READDIR4res::status */ + NFS4_VERIFIER_SIZE /* cookie verifier */ ; /* Allocation of reply structures */ if((entry_name_array = (entry_name_array_item_t *) Mem_Alloc(num_entries * (FSAL_MAX_NAME_LEN + 1))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_name_array, 0, num_entries * (FSAL_MAX_NAME_LEN + 1)); if((entry_nfs_array = (entry4 *) Mem_Alloc(num_entries * sizeof(entry4))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_nfs_array, 0, num_entries * sizeof(entry4)); for(i = 0; i < num_entries; i++) { entry_nfs_array[i].name.utf8string_val = entry_name_array[i]; if(str2utf8(dirent_array[i]->name.name, &entry_nfs_array[i].name) == -1) { res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* Set the cookie value */ entry_nfs_array[i].cookie = dirent_array[i]->cookie; /* Get the pentry for the object's attributes and filehandle */ if( ( pentry = cache_inode_lookup_no_mutex( dir_pentry, &dirent_array[i]->name, data->pexport->cache_inode_policy, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status ) ) == NULL ) { Mem_Free((char *)entry_nfs_array); /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } /* If file handle is asked in the attributes, provide it */ if(arg_READDIR4.attr_request.bitmap4_val != NULL && (arg_READDIR4.attr_request.bitmap4_val[0] & FATTR4_FILEHANDLE)) { if((entry_FSALhandle = cache_inode_get_fsal_handle(pentry, &cache_status_attr)) == NULL) { /* Faulty Handle or pentry */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } if(!nfs4_FSALToFhandle(&entryFH, entry_FSALhandle, data)) { /* Faulty type */ Mem_Free((char *)entry_nfs_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; goto out; } } if(nfs4_FSALattr_To_Fattr(data->pexport, &attrlookup, &(entry_nfs_array[i].attrs), data, &entryFH, &(arg_READDIR4.attr_request)) != 0) { /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; } /* Update the size of the output buffer */ entrysize = sizeof( nfs_cookie4 ) ; /* nfs_cookie4 */ entrysize += sizeof( u_int ) ; /* pathname4::utf8strings_len */ entrysize += entry_nfs_array[i].name.utf8string_len ; entrysize += sizeof( u_int ) ; /* bitmap4_len */ entrysize += entry_nfs_array[i].attrs.attrmask.bitmap4_len ; entrysize += sizeof( u_int ) ; /* attrlist4_len */ entrysize += entry_nfs_array[i].attrs.attr_vals.attrlist4_len ; entrysize += sizeof( caddr_t ) ; outbuffsize += entrysize; LogFullDebug(COMPONENT_NFS_V4, " === nfs4_op_readdir ===> i=%u name=%s cookie=%"PRIu64" " "entrysize=%u buffsize=%u", i, dirent_array[i]->name.name, entry_nfs_array[i].cookie, entrysize, outbuffsize); /* Chain the entries together */ entry_nfs_array[i].nextentry = NULL; if(i != 0) { if( outbuffsize < maxcount ) entry_nfs_array[i - 1].nextentry = &(entry_nfs_array[i]); else { LogFullDebug(COMPONENT_NFS_V4, "=== nfs4_op_readdir ===> " "maxcount reached at %u entries name=%s " "cookie=%llu " "buffsize=%u (return early)", i+1, dirent_array[i]->name.name, (unsigned long long)entry_nfs_array[i].cookie, outbuffsize); entry_nfs_array[i - 1].nextentry = NULL ; break ; } } } /* for i */ if((i == num_entries) && (eod_met == END_OF_DIR)) { LogFullDebug(COMPONENT_NFS_V4, "End of directory reached: num_entries=%d i=%d", num_entries, i); /* This is the end of the directory */ res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } /* Put the entry's list in the READDIR reply */ res_READDIR4.READDIR4res_u.resok4.reply.entries = entry_nfs_array; } /* Do not forget to set the verifier */ memcpy((char *)res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); res_READDIR4.status = NFS4_OK; out: /* release read lock on dir_pentry, if requested */ if (dir_pentry_unlock) V_r(&dir_pentry->lock); if (dirent_array) { if( !CACHE_INODE_KEEP_CONTENT( dir_pentry->policy ) ) cache_inode_release_dirent( dirent_array, num_entries, data->pclient ) ; Mem_Free((char *)dirent_array); } return res_READDIR4.status; } /* nfs4_op_readdir */
int nfs4_op_putfh(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { int rc; int error; fsal_attrib_list_t attr; char outstr[1024]; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_putfh"; resp->resop = NFS4_OP_PUTFH; res_PUTFH4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(arg_PUTFH4.object))) { res_PUTFH4.status = NFS4ERR_NOFILEHANDLE; return res_PUTFH4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(arg_PUTFH4.object))) { res_PUTFH4.status = NFS4ERR_BADHANDLE; return res_PUTFH4.status; } /* Tests if teh Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(arg_PUTFH4.object))) { res_PUTFH4.status = NFS4ERR_FHEXPIRED; return res_PUTFH4.status; } /* If no currentFH were set, allocate one */ if(data->currentFH.nfs_fh4_len == 0) { if((error = nfs4_AllocateFH(&(data->currentFH))) != NFS4_OK) { res_PUTFH4.status = error; return res_PUTFH4.status; } } /* The same is to be done with mounted_on_FH */ if(data->mounted_on_FH.nfs_fh4_len == 0) { if((error = nfs4_AllocateFH(&(data->mounted_on_FH))) != NFS4_OK) { res_PUTFH4.status = error; return res_PUTFH4.status; } } /* Copy the filehandle from the reply structure */ data->currentFH.nfs_fh4_len = arg_PUTFH4.object.nfs_fh4_len; data->mounted_on_FH.nfs_fh4_len = arg_PUTFH4.object.nfs_fh4_len; /* Put the data in place */ memcpy(data->currentFH.nfs_fh4_val, arg_PUTFH4.object.nfs_fh4_val, arg_PUTFH4.object.nfs_fh4_len); memcpy(data->mounted_on_FH.nfs_fh4_val, arg_PUTFH4.object.nfs_fh4_val, arg_PUTFH4.object.nfs_fh4_len); nfs4_sprint_fhandle(&arg_PUTFH4.object, outstr); LogDebug(COMPONENT_NFS_V4, "NFS4_OP_PUTFH CURRENTFH BEFORE: File handle = %s", outstr); /* If the filehandle is not pseudo hs file handle, get the entry related to it, otherwise use fake values */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { data->current_entry = NULL; data->current_filetype = DIR_BEGINNING; data->pexport = NULL; /* No exportlist is related to pseudo fs */ } else { /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */ if(data->pexport == NULL) { if((error = nfs4_SetCompoundExport(data)) != NFS4_OK) { res_PUTFH4.status = error; return res_PUTFH4.status; } } /* Build the pentry */ if((data->current_entry = nfs_FhandleToCache(NFS_V4, NULL, NULL, &(data->currentFH), NULL, NULL, &(res_PUTFH4.status), &attr, data->pcontext, data->pclient, data->ht, &rc)) == NULL) { res_PUTFH4.status = NFS4ERR_BADHANDLE; return res_PUTFH4.status; } /* Extract the filetype */ data->current_filetype = cache_inode_fsal_type_convert(attr.type); } /* Trace */ return NFS4_OK; } /* nfs4_op_putfh */
int nfs4_op_lookup(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { fsal_name_t name; char strname[MAXNAMLEN]; #ifndef _NO_XATTRD char objname[MAXNAMLEN]; #endif unsigned int xattr_found = FALSE; cache_entry_t *dir_pentry = NULL; cache_entry_t *file_pentry = NULL; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; fsal_handle_t *pfsal_handle = NULL; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_lookup"; resp->resop = NFS4_OP_LOOKUP; res_LOOKUP4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_LOOKUP4.status = NFS4ERR_NOFILEHANDLE; return res_LOOKUP4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_LOOKUP4.status = NFS4ERR_BADHANDLE; return res_LOOKUP4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_LOOKUP4.status = NFS4ERR_FHEXPIRED; return res_LOOKUP4.status; } /* Check for empty name */ if(op->nfs_argop4_u.oplookup.objname.utf8string_len == 0 || op->nfs_argop4_u.oplookup.objname.utf8string_val == NULL) { res_LOOKUP4.status = NFS4ERR_INVAL; return res_LOOKUP4.status; } /* Check for name to long */ if(op->nfs_argop4_u.oplookup.objname.utf8string_len > FSAL_MAX_NAME_LEN) { res_LOOKUP4.status = NFS4ERR_NAMETOOLONG; return res_LOOKUP4.status; } /* If Filehandle points to a pseudo fs entry, manage it via pseudofs specific functions */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_lookup_pseudo(op, data, resp); #ifndef _NO_XATTRD /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_lookup_xattr(op, data, resp); #endif /* UTF8 strings may not end with \0, but they carry their length */ utf82str(strname, sizeof(strname), &arg_LOOKUP4.objname); #ifndef _NO_XATTRD /* Is this a .xattr.d.<object> name ? */ if(nfs_XattrD_Name(strname, objname)) { strcpy(strname, objname); xattr_found = TRUE; } #endif if((cache_status = cache_inode_error_convert(FSAL_str2name(strname, MAXNAMLEN, &name))) != CACHE_INODE_SUCCESS) { res_LOOKUP4.status = nfs4_Errno(cache_status); return res_LOOKUP4.status; } /* No 'cd .' is allowed return NFS4ERR_BADNAME in this case */ /* No 'cd .. is allowed, return EINVAL in this case. NFS4_OP_LOOKUPP should be use instead */ if(!FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT_DOT)) { res_LOOKUP4.status = NFS4ERR_BADNAME; return res_LOOKUP4.status; } /* Do the lookup in the HPSS Namespace */ file_pentry = NULL; dir_pentry = data->current_entry; /* Sanity check: dir_pentry should be ACTUALLY a directory */ if(dir_pentry->internal_md.type != DIR_BEGINNING && dir_pentry->internal_md.type != DIR_CONTINUE) { /* This is not a directory */ if(dir_pentry->internal_md.type == SYMBOLIC_LINK) res_LOOKUP4.status = NFS4ERR_SYMLINK; else res_LOOKUP4.status = NFS4ERR_NOTDIR; /* Return failed status */ return res_LOOKUP4.status; } /* BUGAZOMEU: Faire la gestion des cross junction traverse */ if((file_pentry = cache_inode_lookup(dir_pentry, &name, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status)) != NULL) { /* Extract the fsal attributes from the cache inode pentry */ pfsal_handle = cache_inode_get_fsal_handle(file_pentry, &cache_status); if(cache_status != CACHE_INODE_SUCCESS) { res_LOOKUP4.status = NFS4ERR_SERVERFAULT; return res_LOOKUP4.status; } /* Convert it to a file handle */ if(!nfs4_FSALToFhandle(&data->currentFH, pfsal_handle, data)) { res_LOOKUP4.status = NFS4ERR_SERVERFAULT; return res_LOOKUP4.status; } /* Copy this to the mounted on FH (if no junction is traversed */ memcpy((char *)(data->mounted_on_FH.nfs_fh4_val), (char *)(data->currentFH.nfs_fh4_val), data->currentFH.nfs_fh4_len); data->mounted_on_FH.nfs_fh4_len = data->currentFH.nfs_fh4_len; #if 0 print_buff((char *)cache_inode_get_fsal_handle(file_pentry, &cache_status), sizeof(fsal_handle_t)); print_buff((char *)cache_inode_get_fsal_handle(dir_pentry, &cache_status), sizeof(fsal_handle_t)); #endif if(isFullDebug(COMPONENT_NFS_V4)) { LogFullDebug(COMPONENT_NFS_V4, "----> nfs4_op_lookup: name=%s dir_pentry=%p looked up pentry=%p", strname, dir_pentry, file_pentry); LogFullDebug(COMPONENT_NFS_V4, "----> FSAL handle parent puis fils dans nfs4_op_lookup"); print_buff(COMPONENT_NFS_V4, (char *)cache_inode_get_fsal_handle(file_pentry, &cache_status), sizeof(fsal_handle_t)); print_buff(COMPONENT_NFS_V4, (char *)cache_inode_get_fsal_handle(dir_pentry, &cache_status), sizeof(fsal_handle_t)); } LogHandleNFS4("NFS4 LOOKUP CURRENT FH: ", &data->currentFH); /* Keep the pointer within the compound data */ data->current_entry = file_pentry; data->current_filetype = file_pentry->internal_md.type; /* Return successfully */ res_LOOKUP4.status = NFS4_OK; #ifndef _NO_XATTRD /* If this is a xattr ghost directory name, update the FH */ if(xattr_found == TRUE) res_LOOKUP4.status = nfs4_fh_to_xattrfh(&(data->currentFH), &(data->currentFH)); #endif if((data->current_entry->internal_md.type == DIR_BEGINNING) && (data->current_entry->object.dir_begin.referral != NULL)) { if(!nfs4_Set_Fh_Referral(&(data->currentFH))) { res_LOOKUP4.status = NFS4ERR_SERVERFAULT; return res_LOOKUP4.status; } } return NFS4_OK; } /* If the part of the code is reached, then something wrong occured in the lookup process, status is not HPSS_E_NOERROR * and contains the code for the error */ res_LOOKUP4.status = nfs4_Errno(cache_status); return res_LOOKUP4.status; } /* nfs4_op_lookup */
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 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 nfs4_op_lookupp(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { fsal_name_t name; cache_entry_t *dir_pentry = NULL; cache_entry_t *file_pentry = NULL; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; int error = 0; fsal_handle_t *pfsal_handle = NULL; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_lookupp"; resp->resop = NFS4_OP_LOOKUPP; resp->nfs_resop4_u.oplookupp.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_LOOKUPP4.status = NFS4ERR_NOFILEHANDLE; return res_LOOKUPP4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_LOOKUPP4.status = NFS4ERR_BADHANDLE; return res_LOOKUPP4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_LOOKUPP4.status = NFS4ERR_FHEXPIRED; return res_LOOKUPP4.status; } /* looking up for parent directory from ROOTFH return NFS4ERR_NOENT (RFC3530, page 166) */ if(data->currentFH.nfs_fh4_len == data->rootFH.nfs_fh4_len && memcmp(data->currentFH.nfs_fh4_val, data->rootFH.nfs_fh4_val, data->currentFH.nfs_fh4_len) == 0) { /* Nothing to do, just reply with success */ res_LOOKUPP4.status = NFS4ERR_NOENT; return res_LOOKUPP4.status; } /* If in pseudoFS, proceed with pseudoFS specific functions */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_lookupp_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_lookupp_xattr(op, data, resp); /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */ if(data->pexport == NULL) { if((error = nfs4_SetCompoundExport(data)) != NFS4_OK) { res_LOOKUPP4.status = error; return res_LOOKUPP4.status; } } /* Preparying for cache_inode_lookup ".." */ file_pentry = NULL; dir_pentry = data->current_entry; name = FSAL_DOT_DOT; /* BUGAZOMEU: Faire la gestion des cross junction traverse */ if((file_pentry = cache_inode_lookup(dir_pentry, &name, data->pexport->cache_inode_policy, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status)) != NULL) { /* Extract the fsal attributes from the cache inode pentry */ pfsal_handle = cache_inode_get_fsal_handle(file_pentry, &cache_status); if(cache_status != CACHE_INODE_SUCCESS) { res_LOOKUPP4.status = NFS4ERR_SERVERFAULT; return res_LOOKUPP4.status; } /* Convert it to a file handle */ if(!nfs4_FSALToFhandle(&data->currentFH, pfsal_handle, data)) { res_LOOKUPP4.status = NFS4ERR_SERVERFAULT; return res_LOOKUPP4.status; } /* Copy this to the mounted on FH (if no junction is traversed */ memcpy((char *)(data->mounted_on_FH.nfs_fh4_val), (char *)(data->currentFH.nfs_fh4_val), data->currentFH.nfs_fh4_len); data->mounted_on_FH.nfs_fh4_len = data->currentFH.nfs_fh4_len; /* Keep the pointer within the compound data */ data->current_entry = file_pentry; data->current_filetype = file_pentry->internal_md.type; /* Return successfully */ res_LOOKUPP4.status = NFS4_OK; return NFS4_OK; } /* If the part of the code is reached, then something wrong occured in the lookup process, status is not HPSS_E_NOERROR * and contains the code for the error */ /* If NFS4ERR_SYMLINK should be returned for a symlink instead of ENOTDIR */ if((cache_status == CACHE_INODE_NOT_A_DIRECTORY) && (dir_pentry->internal_md.type == SYMBOLIC_LINK)) res_LOOKUPP4.status = NFS4ERR_SYMLINK; else res_LOOKUPP4.status = nfs4_Errno(cache_status); return res_LOOKUPP4.status; } /* nfs4_op_lookupp */
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 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_remove(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_remove"; cache_entry_t * parent_entry = NULL; fsal_attrib_list_t attr_parent; fsal_name_t name; cache_inode_status_t cache_status; resp->resop = NFS4_OP_REMOVE; res_REMOVE4.status = NFS4_OK; /* * Do basic checks on a filehandle * Delete arg_REMOVE4.target in directory pointed by currentFH * Make sure the currentFH is pointed a directory */ res_REMOVE4.status = nfs4_sanity_check_FH(data, DIRECTORY); if(res_REMOVE4.status != NFS4_OK) return res_REMOVE4.status; /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_REMOVE4.status = NFS4ERR_ROFS; return res_REMOVE4.status; } if (nfs_in_grace()) { res_REMOVE4.status = NFS4ERR_GRACE; return res_REMOVE4.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_remove_xattr(op, data, resp); /* Get the parent entry (aka the current one in the compound data) */ parent_entry = data->current_entry; /* We have to keep track of the 'change' file attribute for reply structure */ memset(&(res_REMOVE4.REMOVE4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_REMOVE4.REMOVE4res_u.resok4.cinfo.before = cache_inode_get_changeid4(parent_entry); /* Check for name length */ if(arg_REMOVE4.target.utf8string_len > FSAL_MAX_NAME_LEN) { res_REMOVE4.status = NFS4ERR_NAMETOOLONG; return res_REMOVE4.status; } /* get the filename from the argument, it should not be empty */ if(arg_REMOVE4.target.utf8string_len == 0) { res_REMOVE4.status = NFS4ERR_INVAL; return res_REMOVE4.status; } /* NFS4_OP_REMOVE can delete files as well as directory, it replaces NFS3_RMDIR and NFS3_REMOVE * because of this, we have to know if object is a directory or not */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_REMOVE4.target, &name))) != CACHE_INODE_SUCCESS) { res_REMOVE4.status = nfs4_Errno(cache_status); return res_REMOVE4.status; } /* Test RM7: remiving '.' should return NFS4ERR_BADNAME */ if(!FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&name, (fsal_name_t *) & FSAL_DOT_DOT)) { res_REMOVE4.status = NFS4ERR_BADNAME; return res_REMOVE4.status; } if((cache_status = cache_inode_remove(parent_entry, &name, &attr_parent, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_REMOVE4.status = nfs4_Errno(cache_status); return res_REMOVE4.status; } res_REMOVE4.REMOVE4res_u.resok4.cinfo.after = cache_inode_get_changeid4(parent_entry); /* Operation was not atomic .... */ res_REMOVE4.REMOVE4res_u.resok4.cinfo.atomic = FALSE; /* If you reach this point, everything was ok */ res_REMOVE4.status = NFS4_OK; return NFS4_OK; } /* nfs4_op_remove */
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 */
/** * nfs4_op_readdir: The NFS4_OP_READDIR. * * Implements the NFS4_OP_READDIR. If fh is a pseudo FH, then call is routed to routine nfs4_op_readdir_pseudo * * @param op [IN] pointer to nfs4_op arguments * @param data [INOUT] Pointer to the compound request's data * @param resp [IN] Pointer to nfs4_op results * * @return NFS4_OK if ok, any other value show an error. * */ int nfs4_op_readdir(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { cache_entry_t *dir_pentry = NULL; cache_entry_t *pentry = NULL; cache_inode_endofdir_t eod_met; fsal_attrib_list_t attrlookup; cache_inode_status_t cache_status; cache_inode_status_t cache_status_attr; char __attribute__ ((__unused__)) funcname[] = "nfs4_op_readdir"; unsigned long dircount; unsigned long maxcount; entry4 *entry_nfs_array; cache_inode_dir_entry_t *dirent_array; verifier4 cookie_verifier; unsigned int cookie = 0; unsigned int end_cookie = 0; unsigned int *cookie_array; fsal_handle_t *entry_FSALhandle; nfs_fh4 entryFH; char val_fh[NFS4_FHSIZE]; entry_name_array_item_t *entry_name_array; unsigned long space_used; unsigned int estimated_num_entries; unsigned int num_entries; unsigned int i = 0; bitmap4 RdAttrErrorBitmap; attrlist4 RdAttrErrorVals; resp->resop = NFS4_OP_READDIR; res_READDIR4.status = NFS4_OK; entryFH.nfs_fh4_len = 0; entryFH.nfs_fh4_val = val_fh; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_NOFILEHANDLE; return res_READDIR4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_BADHANDLE; return res_READDIR4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_READDIR4.status = NFS4ERR_FHEXPIRED; return res_READDIR4.status; } /* Pseudo Fs management */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_readdir_pseudo(op, data, resp); /* Xattrs management */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_readdir_xattr(op, data, resp); /* You can readdir only within a directory */ dir_pentry = data->current_entry; if(data->current_filetype != DIR_BEGINNING && data->current_filetype != DIR_CONTINUE) { res_READDIR4.status = NFS4ERR_NOTDIR; return res_READDIR4.status; } /* get the caracteristic value for readdir operation */ dircount = arg_READDIR4.dircount; maxcount = arg_READDIR4.maxcount; cookie = (unsigned int)arg_READDIR4.cookie; space_used = sizeof(entry4); /* dircount is considered meaningless by many nfsv4 client (like the CITI one). we use maxcount instead */ estimated_num_entries = maxcount / sizeof(entry4); /* Estimated_num_entries is probably far too big */ LogFullDebug(COMPONENT_NFS_V4, "--- nfs4_op_readdir ---> dircount=%u maxcount=%u arg_cookie=%llu cookie=%d estimated_num_entries=%u\n", dircount, maxcount, arg_READDIR4.cookie, cookie, estimated_num_entries); /* Do not use a cookie of 1 or 2 (reserved values) */ if(cookie == 1 || cookie == 2) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } if(cookie != 0) cookie = cookie - 2; /* 0,1 and 2 are reserved, there is a delta of '3' because of this */ /* Get only attributes that are allowed to be read */ if(!nfs4_Fattr_Check_Access_Bitmap(&arg_READDIR4.attr_request, FATTR4_ATTR_READ)) { res_READDIR4.status = NFS4ERR_INVAL; return res_READDIR4.status; } /* If maxcount is too short, return NFS4ERR_TOOSMALL */ if(maxcount < sizeof(entry4) || estimated_num_entries == 0) { res_READDIR4.status = NFS4ERR_TOOSMALL; return res_READDIR4.status; } /* * If cookie verifier is used, then an non-trivial value is * returned to the client This value is the mtime of * the pentry. If verifier is unused (as in many NFS * Servers) then only a set of zeros is returned (trivial * value) */ memset(cookie_verifier, 0, NFS4_VERIFIER_SIZE); if(data->pexport->UseCookieVerifier == 1) memcpy(cookie_verifier, &dir_pentry->internal_md.mod_time, sizeof(time_t)); /* Cookie delivered by the server and used by the client SHOULD not ne 0, 1 or 2 (cf RFC3530, page192) * because theses value are reserved for special use. * 0 - cookie for first READDIR * 1 - reserved for . on client handside * 2 - reserved for .. on client handside * Entries '.' and '..' are not returned also * For these reason, there will be an offset of 3 between NFS4 cookie and HPSS cookie */ if((cookie != 0) && (data->pexport->UseCookieVerifier == 1)) { if(memcmp(cookie_verifier, arg_READDIR4.cookieverf, NFS4_VERIFIER_SIZE) != 0) { res_READDIR4.status = NFS4ERR_BAD_COOKIE; return res_READDIR4.status; } } /* The default behaviour is to consider that eof is not reached, the returned values by cache_inode_readdir * will let us know if eod was reached or not */ res_READDIR4.READDIR4res_u.resok4.reply.eof = FALSE; /* Get prepared for readdir */ if((dirent_array = (cache_inode_dir_entry_t *) Mem_Alloc(estimated_num_entries * sizeof(cache_inode_dir_entry_t))) == NULL) { res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } if((cookie_array = (unsigned int *)Mem_Alloc(estimated_num_entries * sizeof(unsigned int))) == NULL) { Mem_Free((char *)dirent_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } /* Perform the readdir operation */ if(cache_inode_readdir(dir_pentry, cookie, estimated_num_entries, &num_entries, &end_cookie, &eod_met, dirent_array, cookie_array, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_READDIR4.status = nfs4_Errno(cache_status); return res_READDIR4.status; } /* For an empty directory, we will find only . and .., so reply af if the end if reached */ if(num_entries == 0) { /* only . and .. */ res_READDIR4.READDIR4res_u.resok4.reply.entries = NULL; res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } else { /* Allocation of reply structures */ if((entry_name_array = (entry_name_array_item_t *) Mem_Alloc(num_entries * (FSAL_MAX_NAME_LEN + 1))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } memset((char *)entry_name_array, 0, num_entries * (FSAL_MAX_NAME_LEN + 1)); if((entry_nfs_array = (entry4 *) Mem_Alloc(num_entries * sizeof(entry4))) == NULL) { LogError(COMPONENT_NFS_V4, ERR_SYS, ERR_MALLOC, errno); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } for(i = 0; i < num_entries; i++) { entry_nfs_array[i].name.utf8string_val = entry_name_array[i]; if(str2utf8(dirent_array[i].name.name, &entry_nfs_array[i].name) == -1) { res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } /* Set the cookie value */ if(i != num_entries - 1) entry_nfs_array[i].cookie = cookie_array[i + 1] + 2; /* 0, 1 and 2 are reserved */ else entry_nfs_array[i].cookie = end_cookie + 2; LogFullDebug(COMPONENT_NFS_V4, " === nfs4_op_readdir ===> i=%d name=%s cookie=%llu\n", i, dirent_array[i].name.name, entry_nfs_array[i].cookie); /* Get the pentry for the object's attributes and filehandle */ if((pentry = cache_inode_lookup(dir_pentry, &dirent_array[i].name, &attrlookup, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { Mem_Free((char *)entry_nfs_array); Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } /* If file handle is asked in the attributes, provide it */ if(arg_READDIR4.attr_request.bitmap4_val != NULL && (arg_READDIR4.attr_request.bitmap4_val[0] & FATTR4_FILEHANDLE)) { if((entry_FSALhandle = cache_inode_get_fsal_handle(pentry, &cache_status_attr)) == NULL) { /* Faulty Handle or pentry */ Mem_Free((char *)entry_nfs_array); Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } if(!nfs4_FSALToFhandle(&entryFH, entry_FSALhandle, data)) { /* Faulty type */ Mem_Free((char *)entry_nfs_array); Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); res_READDIR4.status = NFS4ERR_SERVERFAULT; return res_READDIR4.status; } } if(nfs4_FSALattr_To_Fattr(data->pexport, &attrlookup, &(entry_nfs_array[i].attrs), data, &entryFH, &(arg_READDIR4.attr_request)) != 0) { /* Return the fattr4_rdattr_error , cf RFC3530, page 192 */ entry_nfs_array[i].attrs.attrmask = RdAttrErrorBitmap; entry_nfs_array[i].attrs.attr_vals = RdAttrErrorVals; } /* Chain the entries together */ entry_nfs_array[i].nextentry = NULL; if(i != 0) entry_nfs_array[i - 1].nextentry = &(entry_nfs_array[i]); /* This test is there to avoid going further than the buffer provided by the client * the factor "9/10" is there for safety. Its value could be change as beta tests will be done */ if((caddr_t) ((caddr_t) (&entry_nfs_array[i]) - (caddr_t) (&entry_nfs_array[0])) > (caddr_t) (maxcount * 9 / 10)) break; } /* for i */ if((eod_met == END_OF_DIR) && (i == num_entries)) { /* This is the end of the directory */ res_READDIR4.READDIR4res_u.resok4.reply.eof = TRUE; memcpy(res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); } /* Put the entry's list in the READDIR reply */ res_READDIR4.READDIR4res_u.resok4.reply.entries = entry_nfs_array; } /* Do not forget to set the verifier */ memcpy((char *)res_READDIR4.READDIR4res_u.resok4.cookieverf, cookie_verifier, NFS4_VERIFIER_SIZE); Mem_Free((char *)dirent_array); Mem_Free((char *)cookie_array); res_READDIR4.status = NFS4_OK; return res_READDIR4.status; } /* nfs4_op_readdir */
int nfs4_op_access(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_access"; fsal_attrib_list_t attr; fsal_accessflags_t access_mask = 0; cache_inode_status_t cache_status; uint32_t max_access = (ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY | ACCESS4_EXTEND | ACCESS4_DELETE | ACCESS4_EXECUTE); /* initialize output */ res_ACCESS4.ACCESS4res_u.resok4.supported = 0; res_ACCESS4.ACCESS4res_u.resok4.access = 0; resp->resop = NFS4_OP_ACCESS; res_ACCESS4.status = NFS4_OK; /* Do basic checks on a filehandle */ res_ACCESS4.status = nfs4_sanity_check_FH(data, 0LL); if(res_ACCESS4.status != NFS4_OK) return res_ACCESS4.status; /* If Filehandle points to a pseudo fs entry, manage it via pseudofs specific functions */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) return nfs4_op_access_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_access_xattr(op, data, resp); /* Check for input parameter's sanity */ if(arg_ACCESS4.access > max_access) { res_ACCESS4.status = NFS4ERR_INVAL; return res_ACCESS4.status; } /* Get the attributes for the object */ attr = data->current_entry->attributes; /* determine the rights to be tested in FSAL */ if(arg_ACCESS4.access & ACCESS4_READ) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_READ; access_mask |= nfs_get_access_mask(ACCESS4_READ, &attr); } if((arg_ACCESS4.access & ACCESS4_LOOKUP) && (attr.type == FSAL_TYPE_DIR)) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_LOOKUP; access_mask |= nfs_get_access_mask(ACCESS4_LOOKUP, &attr); } if(arg_ACCESS4.access & ACCESS4_MODIFY) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_MODIFY; access_mask |= nfs_get_access_mask(ACCESS4_MODIFY, &attr); } if(arg_ACCESS4.access & ACCESS4_EXTEND) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_EXTEND; access_mask |= nfs_get_access_mask(ACCESS4_EXTEND, &attr); } if((arg_ACCESS4.access & ACCESS4_DELETE) && (attr.type == FSAL_TYPE_DIR)) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_DELETE; access_mask |= nfs_get_access_mask(ACCESS4_DELETE, &attr); } if((arg_ACCESS4.access & ACCESS4_EXECUTE) && (attr.type != FSAL_TYPE_DIR)) { res_ACCESS4.ACCESS4res_u.resok4.supported |= ACCESS4_EXECUTE; access_mask |= nfs_get_access_mask(ACCESS4_EXECUTE, &attr); } nfs4_access_debug("requested access", arg_ACCESS4.access, FSAL_ACE4_MASK(access_mask)); /* Perform the 'access' call */ if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) { res_ACCESS4.ACCESS4res_u.resok4.access = res_ACCESS4.ACCESS4res_u.resok4.supported; nfs4_access_debug("granted access", arg_ACCESS4.access, 0); } if(cache_status == CACHE_INODE_FSAL_EACCESS) { /* * We have to determine which access bits are good one by one */ res_ACCESS4.ACCESS4res_u.resok4.access = 0; access_mask = nfs_get_access_mask(ACCESS4_READ, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_READ; if(attr.type == FSAL_TYPE_DIR) { access_mask = nfs_get_access_mask(ACCESS4_LOOKUP, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_LOOKUP; } access_mask = nfs_get_access_mask(ACCESS4_MODIFY, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_MODIFY; access_mask = nfs_get_access_mask(ACCESS4_EXTEND, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_EXTEND; if(attr.type == FSAL_TYPE_DIR) { access_mask = nfs_get_access_mask(ACCESS4_DELETE, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_DELETE; } if(attr.type != FSAL_TYPE_DIR) { access_mask = nfs_get_access_mask(ACCESS4_EXECUTE, &attr); if(cache_inode_access(data->current_entry, access_mask, data->pcontext, &cache_status) == CACHE_INODE_SUCCESS) res_ACCESS4.ACCESS4res_u.resok4.access |= ACCESS4_EXECUTE; } nfs4_access_debug("reduced access", res_ACCESS4.ACCESS4res_u.resok4.access, 0); } res_ACCESS4.status = NFS4_OK; return res_ACCESS4.status; } /* nfs4_op_access */