int nfs4_op_link(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_link"; cache_entry_t * dir_pentry = NULL; cache_entry_t * file_pentry = NULL; cache_inode_status_t cache_status; fsal_attrib_list_t attr; fsal_name_t newname; resp->resop = NFS4_OP_LINK; res_LINK4.status = NFS4_OK; /* Do basic checks on a filehandle */ res_LINK4.status = nfs4_sanity_check_FH(data, 0LL); if(res_LINK4.status != NFS4_OK) return res_LINK4.status; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->savedFH))) { res_LINK4.status = NFS4ERR_NOFILEHANDLE; return res_LINK4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->savedFH))) { res_LINK4.status = NFS4ERR_BADHANDLE; return res_LINK4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->savedFH))) { res_LINK4.status = NFS4ERR_FHEXPIRED; return res_LINK4.status; } /* Pseudo Fs is explictely a Read-Only File system */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_LINK4.status = NFS4ERR_ROFS; return res_LINK4.status; } /* If data->exportp is null, a junction from pseudo fs was traversed, credp and exportp have to be updated */ if(data->pexport == NULL) { res_LINK4.status = nfs4_SetCompoundExport(data); if(res_LINK4.status != NFS4_OK) return res_LINK4.status; } /* * This operation creates a hard link, for the file represented by the saved FH, in directory represented by currentFH under the * name arg_LINK4.target */ /* Crossing device is not allowed */ if(((file_handle_v4_t *) (data->currentFH.nfs_fh4_val))->exportid != ((file_handle_v4_t *) (data->savedFH.nfs_fh4_val))->exportid) { res_LINK4.status = NFS4ERR_XDEV; return res_LINK4.status; } /* If name is empty, return EINVAL */ if(arg_LINK4.newname.utf8string_len == 0) { res_LINK4.status = NFS4ERR_INVAL; return res_LINK4.status; } /* Check for name to long */ if(arg_LINK4.newname.utf8string_len > FSAL_MAX_NAME_LEN) { res_LINK4.status = NFS4ERR_NAMETOOLONG; return res_LINK4.status; } /* Convert the UFT8 objname to a regular string */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_LINK4.newname, &newname))) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } /* Sanity check: never create a link named '.' or '..' */ if(!FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT) || !FSAL_namecmp(&newname, (fsal_name_t *) & FSAL_DOT_DOT)) { res_LINK4.status = NFS4ERR_BADNAME; return res_LINK4.status; } /* get info from compound data */ dir_pentry = data->current_entry; /* Destination FH (the currentFH) must be a directory */ if(data->current_filetype != DIRECTORY) { res_LINK4.status = NFS4ERR_NOTDIR; return res_LINK4.status; } /* Target object (the savedFH) must not be a directory */ if(data->saved_filetype == DIRECTORY) { res_LINK4.status = NFS4ERR_ISDIR; return res_LINK4.status; } /* We have to keep track of the 'change' file attribute for reply structure */ if((cache_status = cache_inode_getattr(dir_pentry, &attr, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } res_LINK4.LINK4res_u.resok4.cinfo.before = cache_inode_get_changeid4(dir_pentry); /* Convert savedFH into a vnode */ file_pentry = data->saved_entry; /* make the link */ if(cache_inode_link(file_pentry, dir_pentry, &newname, &attr, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_LINK4.status = nfs4_Errno(cache_status); return res_LINK4.status; } res_LINK4.LINK4res_u.resok4.cinfo.after = cache_inode_get_changeid4(dir_pentry); res_LINK4.LINK4res_u.resok4.cinfo.atomic = FALSE; res_LINK4.status = NFS4_OK; return NFS4_OK; } /* nfs4_op_link */
int _9p_link( _9p_request_data_t * preq9p, void * pworker_data, u32 * plenout, char * preply) { char * cursor = preq9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE ; nfs_worker_data_t * pwkrdata = (nfs_worker_data_t *)pworker_data ; u16 * msgtag = NULL ; u32 * dfid = NULL ; u32 * targetfid = NULL ; u16 * name_len = NULL ; char * name_str = NULL ; _9p_fid_t * pdfid = NULL ; _9p_fid_t * ptargetfid = NULL ; cache_entry_t * pentry= NULL ; fsal_attrib_list_t fsalattr ; cache_inode_status_t cache_status ; fsal_name_t link_name ; int rc = 0 ; int err = 0 ; if ( !preq9p || !pworker_data || !plenout || !preply ) return -1 ; /* Get data */ _9p_getptr( cursor, msgtag, u16 ) ; _9p_getptr( cursor, dfid, u32 ) ; _9p_getptr( cursor, targetfid, u32 ) ; _9p_getstr( cursor, name_len, name_str ) ; LogDebug( COMPONENT_9P, "TLINK: tag=%u dfid=%u targetfid=%u name=%.*s", (u32)*msgtag, *dfid, *targetfid, *name_len, name_str ) ; if( *dfid >= _9P_FID_PER_CONN ) { err = ERANGE ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } pdfid = &preq9p->pconn->fids[*dfid] ; if( *targetfid >= _9P_FID_PER_CONN ) { err = ERANGE ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } ptargetfid = &preq9p->pconn->fids[*targetfid] ; /* Let's do the job */ snprintf( link_name.name, FSAL_MAX_NAME_LEN, "%.*s", *name_len, name_str ) ; if( cache_inode_link( ptargetfid->pentry, pdfid->pentry, &link_name, ptargetfid->pexport->cache_inode_policy, &fsalattr, pwkrdata->ht, &pwkrdata->cache_inode_client, &pdfid->fsal_op_context, &cache_status) != CACHE_INODE_SUCCESS ) { err = _9p_tools_errno( cache_status ) ; ; rc = _9p_rerror( preq9p, msgtag, &err, plenout, preply ) ; return rc ; } /* Build the reply */ _9p_setinitptr( cursor, preply, _9P_RLINK ) ; _9p_setptr( cursor, msgtag, u16 ) ; _9p_setendptr( cursor, preply ) ; _9p_checkbound( cursor, preply, plenout ) ; LogDebug( COMPONENT_9P, "TLINK: tag=%u dfid=%u targetfid=%u name=%.*s", (u32)*msgtag, *dfid, *targetfid, *name_len, name_str ) ; return 1 ; }
int nfs3_link(nfs_arg_t *arg, nfs_worker_data_t *worker, struct svc_req *req, nfs_res_t *res) { const char *link_name = arg->arg_link3.link.name; cache_entry_t *target_entry = NULL; cache_entry_t *parent_entry = NULL; pre_op_attr pre_parent = { .attributes_follow = false }; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; short to_exportid = 0; short from_exportid = 0; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_vers, &(arg->arg_link3.file), NULL, strfrom); nfs_FhandleToStr(req->rq_vers, &(arg->arg_link3.link.dir), NULL, strto); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_link handle: %s to " "handle: %s name: %s", strfrom, strto, link_name); } /* to avoid setting it on each error case */ res->res_link3.LINK3res_u.resfail.file_attributes.attributes_follow = FALSE; res->res_link3.LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE; res->res_link3.LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE; /* Get the exportids for the two handles. */ to_exportid = nfs3_FhandleToExportId(&(arg->arg_link3.link.dir)); from_exportid = nfs3_FhandleToExportId(&(arg->arg_link3.file)); /* Validate the to_exportid */ if (to_exportid < 0 || from_exportid < 0) { LogInfo(COMPONENT_DISPATCH, "NFS%d LINK Request from client %s has badly formed handle for link dir", req->rq_vers, op_ctx->client ? op_ctx->client->hostaddr_str : "unknown client"); /* Bad handle, report to client */ res->res_link3.status = NFS3ERR_BADHANDLE; goto out; } /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) { res->res_link3.status = NFS3ERR_XDEV; goto out; } /* Get entry for parent directory */ parent_entry = nfs3_FhandleToCache(&arg->arg_link3.link.dir, &res->res_link3.status, &rc); if (parent_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } nfs_SetPreOpAttr(parent_entry, &pre_parent); target_entry = nfs3_FhandleToCache(&arg->arg_link3.file, &res->res_link3.status, &rc); if (target_entry == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Sanity checks: */ if (parent_entry->type != DIRECTORY) { res->res_link3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } if (link_name == NULL || *link_name == '\0') res->res_link3.status = NFS3ERR_INVAL; else { /* Both objects have to be in the same filesystem */ if (to_exportid != from_exportid) res->res_link3.status = NFS3ERR_XDEV; else { cache_status = cache_inode_link(target_entry, parent_entry, link_name); if (cache_status == CACHE_INODE_SUCCESS) { nfs_SetPostOpAttr(target_entry, &(res->res_link3.LINK3res_u. resok.file_attributes)); nfs_SetWccData(&pre_parent, parent_entry, &(res->res_link3.LINK3res_u. resok.linkdir_wcc)); res->res_link3.status = NFS3_OK; rc = NFS_REQ_OK; goto out; } } /* else */ } /* If we are here, there was an error */ if (nfs_RetryableError(cache_status)) { rc = NFS_REQ_DROP; goto out; } res->res_link3.status = nfs3_Errno(cache_status); nfs_SetPostOpAttr(target_entry, &(res->res_link3.LINK3res_u.resfail.file_attributes)); nfs_SetWccData(&pre_parent, parent_entry, &res->res_link3.LINK3res_u.resfail.linkdir_wcc); rc = NFS_REQ_OK; out: /* return references */ if (target_entry) cache_inode_put(target_entry); if (parent_entry) cache_inode_put(parent_entry); return rc; } /* nfs3_link */ /** * @brief Free the result structure allocated for nfs3_link * * This function frees the result structure allocated for nfs3_link. * * @param[in,out] resp Result structure * */ void nfs3_link_free(nfs_res_t *resp) { return; }
int nfs4_op_link(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { LINK4args * const arg_LINK4 = &op->nfs_argop4_u.oplink; LINK4res * const res_LINK4 = &resp->nfs_resop4_u.oplink; cache_entry_t *dir_entry = NULL; cache_entry_t *file_entry = NULL; cache_inode_status_t cache_status = CACHE_INODE_SUCCESS; char *newname = NULL; resp->resop = NFS4_OP_LINK; res_LINK4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_LINK4->status = nfs4_sanity_check_FH(data, DIRECTORY, false); if (res_LINK4->status != NFS4_OK) goto out; res_LINK4->status = nfs4_sanity_check_saved_FH(data, -DIRECTORY, false); if (res_LINK4->status != NFS4_OK) goto out; /* * 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 */ /* Validate and convert the UFT8 objname to a regular string */ res_LINK4->status = nfs4_utf8string2dynamic(&arg_LINK4->newname, UTF8_SCAN_ALL, &newname); if (res_LINK4->status != NFS4_OK) goto out; /* get info from compound data */ dir_entry = data->current_entry; res_LINK4->LINK4res_u.resok4.cinfo.before = cache_inode_get_changeid4(dir_entry); file_entry = data->saved_entry; /* make the link */ cache_status = cache_inode_link(file_entry, dir_entry, newname); if (cache_status != CACHE_INODE_SUCCESS) { res_LINK4->status = nfs4_Errno(cache_status); goto out; } res_LINK4->LINK4res_u.resok4.cinfo.after = cache_inode_get_changeid4(dir_entry); res_LINK4->LINK4res_u.resok4.cinfo.atomic = FALSE; res_LINK4->status = NFS4_OK; out: if (newname) gsh_free(newname); return res_LINK4->status; } /* nfs4_op_link */