/* lookup * deprecated NULL parent && NULL path implies root handle */ static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct attrlist attrib; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); struct fsal_filesystem *fs; *handle = NULL; /* poison it first */ fs = parent->fs; if (!path) return fsalstat(ERR_FSAL_FAULT, 0); memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; if (!parent->obj_ops.handle_is(parent, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", parent); return fsalstat(ERR_FSAL_NOTDIR, 0); } if (parent->fsal != parent->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", parent->fsal->name, parent->fs->fsal->name); retval = EXDEV; goto hdlerr; } fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.mask |= attrs_out->mask; status = GPFSFSAL_lookup(op_ctx, parent, path, &attrib, fh, &fs); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, fs, &attrib, NULL, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); hdlerr: fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); }
/* create * create a regular file and set its attributes */ fsal_status_t create(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attr_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { struct gpfs_fsal_obj_handle *hdl; fsal_status_t status; /* Use a separate attrlist to getch the actual attributes into */ struct attrlist attrib; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.mask |= attrs_out->mask; status = GPFSFSAL_create(dir_hdl, name, op_ctx, attr_in->mode, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, dir_hdl->fs, &attrib, NULL, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); }
int _9p_lcreate(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *flags = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newfile; u32 iounit = _9P_IOUNIT; struct fsal_obj_handle *pentry_newfile = NULL; char file_name[MAXNAMLEN+1]; fsal_status_t fsal_status; fsal_openflags_t openflags = 0; struct attrlist sattr; fsal_verifier_t verifier; enum fsal_create_mode createmode = FSAL_UNCHECKED; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, flags, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TLCREATE: tag=%u fid=%u name=%.*s flags=0%o mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *flags, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(file_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(file_name, sizeof(file_name), "%.*s", *name_len, name_str); _9p_openflags2FSAL(flags, &openflags); pfid->state->state_data.fid.share_access = _9p_openflags_to_share_access(flags); memset(&verifier, 0, sizeof(verifier)); memset(&sattr, 0, sizeof(sattr)); sattr.valid_mask = ATTR_MODE | ATTR_GROUP; sattr.mode = *mode; sattr.group = *gid; if (*flags & 0x10) { /* Filesize is already 0. */ sattr.valid_mask |= ATTR_SIZE; } if (*flags & 0x1000) { /* If OEXCL, use FSAL_EXCLUSIVE_9P create mode * so that we can pass the attributes specified * above. Verifier is ignored for this create mode * because we don't have to deal with retry. */ createmode = FSAL_EXCLUSIVE_9P; } fsal_status = fsal_open2(pfid->pentry, pfid->state, openflags, createmode, file_name, &sattr, verifier, &pentry_newfile, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* put parent directory entry */ pfid->pentry->obj_ops->put_ref(pfid->pentry); /* Build the qid */ qid_newfile.type = _9P_QTFILE; qid_newfile.version = 0; qid_newfile.path = pentry_newfile->fileid; /* The fid will represent the new file now - we can't fail anymore */ pfid->pentry = pentry_newfile; pfid->qid = qid_newfile; pfid->xattr = NULL; pfid->opens = 1; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RLCREATE); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newfile); _9p_setvalue(cursor, iounit, u32); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RLCREATE: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu) iounit=%u pentry=%p", (u32) *msgtag, *fid, *name_len, name_str, qid_newfile.type, qid_newfile.version, (unsigned long long)qid_newfile.path, iounit, pfid->pentry); return 1; }
int nfs3_readdir(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *dir_obj = NULL; struct fsal_obj_handle *parent_dir_obj = NULL; unsigned long count = 0; uint64_t cookie = 0; uint64_t fsal_cookie = 0; cookieverf3 cookie_verifier; unsigned int num_entries = 0; unsigned long estimated_num_entries = 0; object_file_type_t dir_filetype = 0; bool eod_met = false; fsal_status_t fsal_status = {0, 0}; fsal_status_t fsal_status_gethandle = {0, 0}; int rc = NFS_REQ_OK; struct nfs3_readdir_cb_data tracker = { NULL }; bool use_cookie_verifier = op_ctx_export_has_option( EXPORT_OPTION_USE_COOKIE_VERIFIER); if (isDebug(COMPONENT_NFSPROTO) || isDebug(COMPONENT_NFS_READDIR)) { char str[LEN_FH_STR]; log_components_t component; nfs_FhandleToStr(req->rq_vers, &(arg->arg_readdir3.dir), NULL, str); if (isDebug(COMPONENT_NFSPROTO)) component = COMPONENT_NFSPROTO; else component = COMPONENT_NFS_READDIR; LogDebug(component, "REQUEST PROCESSING: Calling nfs_Readdir handle: %s", str); } READDIR3resok * const RES_READDIR3_OK = &res->res_readdir3.READDIR3res_u.resok; /* to avoid setting it on each error case */ res->res_readdir3.READDIR3res_u.resfail.dir_attributes. attributes_follow = FALSE; /* Look up object for filehandle */ dir_obj = nfs3_FhandleToCache(&(arg->arg_readdir3.dir), &(res->res_readdir3.status), &rc); if (dir_obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } /* Extract the filetype */ dir_filetype = dir_obj->type; /* Sanity checks -- must be a directory */ if (dir_filetype != DIRECTORY) { res->res_readdir3.status = NFS3ERR_NOTDIR; rc = NFS_REQ_OK; goto out; } /* Parse out request arguments and decide how many entries we * want. For NFSv3, deal with the cookie verifier. */ count = arg->arg_readdir3.count; cookie = arg->arg_readdir3.cookie; estimated_num_entries = MIN(count / (sizeof(entry3) - sizeof(char *)), 120); LogFullDebug(COMPONENT_NFS_READDIR, "---> nfs3_readdir: count=%lu cookie=%" PRIu64 " estimated_num_entries=%lu", count, cookie, estimated_num_entries); if (estimated_num_entries == 0) { res->res_readdir3.status = NFS3ERR_TOOSMALL; rc = NFS_REQ_OK; goto out; } /* To make or check the cookie verifier */ memset(cookie_verifier, 0, sizeof(cookieverf3)); /* If cookie verifier is used, then a * non-trivial value is returned to the * client. * * This value is the ctime of the directory. If verifier is * unused (as in many NFS Servers) then only a set of zeros * is returned (trivial value). */ if (use_cookie_verifier) { struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_CTIME); fsal_status = dir_obj->obj_ops.getattrs(dir_obj, &attrs); if (FSAL_IS_ERROR(fsal_status)) { res->res_readdir3.status = nfs3_Errno_status(fsal_status); LogFullDebug(COMPONENT_NFS_READDIR, "getattrs returned %s", msg_fsal_err(fsal_status.major)); goto out; } memcpy(cookie_verifier, &attrs.ctime.tv_sec, sizeof(attrs.ctime.tv_sec)); /* Done with the attrs */ fsal_release_attrs(&attrs); } if (cookie != 0 && use_cookie_verifier) { /* Not the first call, so we have to check the cookie * verifier */ if (memcmp(cookie_verifier, arg->arg_readdir3.cookieverf, NFS3_COOKIEVERFSIZE) != 0) { res->res_readdir3.status = NFS3ERR_BAD_COOKIE; rc = NFS_REQ_OK; goto out; } } tracker.entries = gsh_calloc(estimated_num_entries, sizeof(entry3)); tracker.total_entries = estimated_num_entries; tracker.mem_left = count - sizeof(READDIR3resok); tracker.count = 0; tracker.error = NFS3_OK; /* Adjust the cookie we supply to fsal */ if (cookie > 2) { /* it is not the cookie for "." nor ".." */ fsal_cookie = cookie; } else { fsal_cookie = 0; } /* Fills "." */ if (cookie == 0) { res->res_readdir3.status = nfs_readdir_dot_entry(dir_obj, ".", 1, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } } /* Fills ".." */ if ((cookie <= 1) && (estimated_num_entries > 1)) { /* Get parent pentry */ fsal_status_gethandle = fsal_lookupp(dir_obj, &parent_dir_obj, NULL); if (parent_dir_obj == NULL) { res->res_readdir3.status = nfs3_Errno_status(fsal_status_gethandle); rc = NFS_REQ_OK; goto out; } res->res_readdir3.status = nfs_readdir_dot_entry(parent_dir_obj, "..", 2, nfs3_readdir_callback, &tracker); if (res->res_readdir3.status != NFS3_OK) { rc = NFS_REQ_OK; goto out; } parent_dir_obj->obj_ops.put_ref(parent_dir_obj); parent_dir_obj = NULL; } /* Call readdir */ fsal_status = fsal_readdir(dir_obj, fsal_cookie, &num_entries, &eod_met, 0, /* no attr */ nfs3_readdir_callback, &tracker); if (FSAL_IS_ERROR(fsal_status)) { if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_readdir3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(dir_obj, &res->res_readdir3.READDIR3res_u.resfail. dir_attributes, NULL); goto out; } if (tracker.error != NFS3_OK) { res->res_readdir3.status = tracker.error; nfs_SetPostOpAttr(dir_obj, &res->res_readdir3.READDIR3res_u.resfail. dir_attributes, NULL); goto out; } LogFullDebug(COMPONENT_NFS_READDIR, "-- Readdir -> Call to fsal_readdir(cookie=%" PRIu64 ")", fsal_cookie); if ((num_entries == 0) && (cookie > 1)) { RES_READDIR3_OK->reply.entries = NULL; RES_READDIR3_OK->reply.eof = TRUE; } else { RES_READDIR3_OK->reply.entries = tracker.entries; RES_READDIR3_OK->reply.eof = eod_met; } nfs_SetPostOpAttr(dir_obj, &RES_READDIR3_OK->dir_attributes, NULL); memcpy(RES_READDIR3_OK->cookieverf, cookie_verifier, sizeof(cookieverf3)); res->res_readdir3.status = NFS3_OK; rc = NFS_REQ_OK; out: /* return references */ if (dir_obj) dir_obj->obj_ops.put_ref(dir_obj); if (parent_dir_obj) parent_dir_obj->obj_ops.put_ref(parent_dir_obj); /* Deallocate memory in the event of an error */ if (((res->res_readdir3.status != NFS3_OK) || (rc != NFS_REQ_OK) || ((num_entries == 0) && (cookie > 1))) && (tracker.entries != NULL)) { free_entry3s(tracker.entries); RES_READDIR3_OK->reply.entries = NULL; } return rc; } /* nfs3_readdir */
int _9p_mkdir(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *mode = NULL; u32 *gid = NULL; u16 *name_len = NULL; char *name_str = NULL; struct _9p_fid *pfid = NULL; struct _9p_qid qid_newdir; struct fsal_obj_handle *pentry_newdir = NULL; char dir_name[MAXNAMLEN+1]; fsal_status_t fsal_status; struct attrlist sattr; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getstr(cursor, name_len, name_str); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, gid, u32); LogDebug(COMPONENT_9P, "TMKDIR: tag=%u fid=%u name=%.*s mode=0%o gid=%u", (u32) *msgtag, *fid, *name_len, name_str, *mode, *gid); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); if (*name_len >= sizeof(dir_name)) { LogDebug(COMPONENT_9P, "request with name too long (%u)", *name_len); return _9p_rerror(req9p, msgtag, ENAMETOOLONG, plenout, preply); } snprintf(dir_name, sizeof(dir_name), "%.*s", *name_len, name_str); fsal_prepare_attrs(&sattr, ATTR_MODE); sattr.mode = *mode; sattr.valid_mask = ATTR_MODE; /* Create the directory */ /* BUGAZOMEU: @todo : the gid parameter is not used yet */ fsal_status = fsal_create(pfid->pentry, dir_name, DIRECTORY, &sattr, NULL, &pentry_newdir, NULL); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); pentry_newdir->obj_ops.put_ref(pentry_newdir); /* Build the qid */ qid_newdir.type = _9P_QTDIR; qid_newdir.version = 0; qid_newdir.path = pentry_newdir->fileid; /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RMKDIR); _9p_setptr(cursor, msgtag, u16); _9p_setqid(cursor, qid_newdir); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RMKDIR: tag=%u fid=%u name=%.*s qid=(type=%u,version=%u,path=%llu)", (u32) *msgtag, *fid, *name_len, name_str, qid_newdir.type, qid_newdir.version, (unsigned long long)qid_newdir.path); return 1; }
static void open4_ex(OPEN4args *arg, compound_data_t *data, OPEN4res *res_OPEN4, nfs_client_id_t *clientid, state_owner_t *owner, state_t **file_state, bool *new_state) { /* Parent directory in which to open the file. */ struct fsal_obj_handle *parent = NULL; /* The entry we associated with the desired file before open. */ struct fsal_obj_handle *file_obj = NULL; /* Indicator that file_obj came from lookup. */ bool looked_up_file_obj = false; /* The in_obj to pass to fsal_open2. */ struct fsal_obj_handle *in_obj = NULL; /* The entry open associated with the file. */ struct fsal_obj_handle *out_obj = NULL; fsal_openflags_t openflags = 0; fsal_openflags_t old_openflags = 0; enum fsal_create_mode createmode = FSAL_NO_CREATE; /* The filename to create */ char *filename = NULL; /* The supplied calim type */ open_claim_type4 claim = arg->claim.claim; fsal_verifier_t verifier; struct attrlist sattr; /* Status for fsal calls */ fsal_status_t status = {0, 0}; /* The open state for the file */ bool state_lock_held = false; /* Make sure the attributes are initialized */ memset(&sattr, 0, sizeof(sattr)); /* Make sure... */ *file_state = NULL; *new_state = false; /* Pre-process the claim type */ switch (claim) { case CLAIM_NULL: /* Check parent */ parent = data->current_obj; in_obj = parent; /* Parent must be a directory */ if (parent->type != DIRECTORY) { if (parent->type == SYMBOLIC_LINK) { res_OPEN4->status = NFS4ERR_SYMLINK; goto out; } else { res_OPEN4->status = NFS4ERR_NOTDIR; goto out; } } /* Validate and convert the utf8 filename */ res_OPEN4->status = nfs4_utf8string2dynamic(&arg->claim.open_claim4_u.file, UTF8_SCAN_ALL, &filename); if (res_OPEN4->status != NFS4_OK) goto out; /* Set the createmode if appropriate) */ if (arg->openhow.opentype == OPEN4_CREATE) { open4_ex_create_args(arg, data, res_OPEN4, verifier, &createmode, &sattr); if (res_OPEN4->status != NFS4_OK) goto out; } status = fsal_lookup(parent, filename, &file_obj, NULL); if (!FSAL_IS_ERROR(status)) { /* Check create situations. */ if (arg->openhow.opentype == OPEN4_CREATE) { if (createmode >= FSAL_EXCLUSIVE) { /* Could be a replay, need to continue. */ LogFullDebug(COMPONENT_STATE, "EXCLUSIVE open with existing file %s", filename); } else if (createmode == FSAL_GUARDED) { /* This will be a failure no matter' * what. */ looked_up_file_obj = true; res_OPEN4->status = NFS4ERR_EXIST; goto out; } else { /* FSAL_UNCHECKED, may be a truncate * and we need to pass in the case * of fsal_reopen2 case. */ if (FSAL_TEST_MASK(sattr.valid_mask, ATTR_SIZE) && sattr.filesize == 0) { LogFullDebug(COMPONENT_STATE, "Truncate"); openflags |= FSAL_O_TRUNC; } } } /* We found the file by lookup, discard the filename * and remember that we found the entry by lookup. */ looked_up_file_obj = true; gsh_free(filename); filename = NULL; } else if (status.major != ERR_FSAL_NOENT || arg->openhow.opentype != OPEN4_CREATE) { /* A real error occurred */ res_OPEN4->status = nfs4_Errno_status(status); goto out; } break; /* Both of these just use the current filehandle. */ case CLAIM_PREVIOUS: owner->so_owner.so_nfs4_owner.so_confirmed = true; if (!nfs4_check_deleg_reclaim(clientid, &data->currentFH)) { /* It must have been revoked. Can't reclaim.*/ LogInfo(COMPONENT_NFS_V4, "Can't reclaim delegation"); res_OPEN4->status = NFS4ERR_RECLAIM_BAD; goto out; } openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; case CLAIM_FH: file_obj = data->current_obj; break; case CLAIM_DELEGATE_PREV: /* FIXME: Remove this when we have full support * for CLAIM_DELEGATE_PREV and delegpurge operations */ res_OPEN4->status = NFS4ERR_NOTSUPP; goto out; case CLAIM_DELEGATE_CUR: res_OPEN4->status = open4_claim_deleg(arg, data); if (res_OPEN4->status != NFS4_OK) goto out; openflags |= FSAL_O_RECLAIM; file_obj = data->current_obj; break; default: LogFatal(COMPONENT_STATE, "Programming error. Invalid claim after check."); } if ((arg->share_access & OPEN4_SHARE_ACCESS_READ) != 0) openflags |= FSAL_O_READ; if ((arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0) openflags |= FSAL_O_WRITE; if ((arg->share_deny & OPEN4_SHARE_DENY_READ) != 0) openflags |= FSAL_O_DENY_READ; if ((arg->share_deny & OPEN4_SHARE_DENY_WRITE) != 0) openflags |= FSAL_O_DENY_WRITE_MAND; /* Check if file_obj a REGULAR_FILE */ if (file_obj != NULL && file_obj->type != REGULAR_FILE) { LogDebug(COMPONENT_NFS_V4, "Wrong file type expected REGULAR_FILE actual %s", object_file_type_to_str(file_obj->type)); if (file_obj->type == DIRECTORY) { res_OPEN4->status = NFS4ERR_ISDIR; } else { /* All special nodes must return NFS4ERR_SYMLINK for * proper client behavior per this linux-nfs post: * http://marc.info/?l=linux-nfs&m=131342421825436&w=2 */ res_OPEN4->status = NFS4ERR_SYMLINK; } goto out; } if (file_obj != NULL) { /* Go ahead and take the state lock now. */ PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; in_obj = file_obj; /* Check if any existing delegations conflict with this open. * Delegation recalls will be scheduled if there is a conflict. */ if (state_deleg_conflict(file_obj, (arg->share_access & OPEN4_SHARE_ACCESS_WRITE) != 0)) { res_OPEN4->status = NFS4ERR_DELAY; goto out; } /* Check if there is already a state for this entry and owner. */ *file_state = nfs4_State_Get_Obj(file_obj, owner); if (isFullDebug(COMPONENT_STATE) && *file_state != NULL) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = {sizeof(str), str, str}; display_stateid(&dspbuf, *file_state); LogFullDebug(COMPONENT_STATE, "Found existing state %s", str); } /* Check if open from another export */ if (*file_state != NULL && !state_same_export(*file_state, op_ctx->ctx_export)) { LogEvent(COMPONENT_STATE, "Lock Owner Export Conflict, Lock held for export %" PRIu16" request for export %"PRIu16, state_export_id(*file_state), op_ctx->ctx_export->export_id); res_OPEN4->status = NFS4ERR_INVAL; goto out; } } /* If that did not succeed, allocate a state from the FSAL. */ if (*file_state == NULL) { *file_state = op_ctx->fsal_export->exp_ops.alloc_state( op_ctx->fsal_export, STATE_TYPE_SHARE, NULL); /* Remember we allocated a new state */ *new_state = true; /* We are ready to perform the open (with possible create). * in_obj has been set to the file itself or the parent. * filename is NULL if in_obj is the file itself. * * Permission check has been done on directory if appropriate, * otherwise fsal_open2 will do a directory permission * check. * * fsal_open2 handles the permission check on the file * itself and also handles all the share reservation stuff. * * fsal_open2 returns with a ref on out_obj, which should be * passed to the state. */ LogFullDebug(COMPONENT_STATE, "Calling open2 for %s", filename); status = fsal_open2(in_obj, *file_state, openflags, createmode, filename, &sattr, verifier, &out_obj, NULL); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } } else if (createmode >= FSAL_EXCLUSIVE) { /* We have an EXCLUSIVE create with an existing * state. We still need to verify it, but no need * to call reopen2. */ LogFullDebug(COMPONENT_STATE, "Calling verify2 "); status = fsal_verify2(file_obj, verifier); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops->get_ref(file_obj); } else { old_openflags = file_obj->obj_ops->status2(file_obj, *file_state); /* Open upgrade */ LogFullDebug(COMPONENT_STATE, "Calling reopen2"); status = fsal_reopen2(file_obj, *file_state, openflags | old_openflags, false); if (FSAL_IS_ERROR(status)) { res_OPEN4->status = nfs4_Errno_status(status); goto out; } /* We need an extra reference below. */ file_obj->obj_ops->get_ref(file_obj); } if (file_obj == NULL) { /* We have a new cache inode entry, take the state lock. */ file_obj = out_obj; PTHREAD_RWLOCK_wrlock(&file_obj->state_hdl->state_lock); state_lock_held = true; } /* Now the state_lock is held for sure and we have an extra LRU * reference to file_obj, which is the opened file. */ if (*new_state) { /* The state data to be added */ union state_data candidate_data; /* Tracking data for the open state */ struct state_refer refer, *p_refer = NULL; state_status_t state_status; candidate_data.share.share_access = arg->share_access & OPEN4_SHARE_ACCESS_BOTH; candidate_data.share.share_deny = arg->share_deny; candidate_data.share.share_access_prev = (1 << candidate_data.share.share_access); candidate_data.share.share_deny_prev = (1 << candidate_data.share.share_deny); LogFullDebug(COMPONENT_STATE, "Creating new state access=%x deny=%x access_prev=%x deny_prev=%x", candidate_data.share.share_access, candidate_data.share.share_deny, candidate_data.share.share_access_prev, candidate_data.share.share_deny_prev); /* Record the sequence info */ if (data->minorversion > 0) { memcpy(refer.session, data->session->session_id, sizeof(sessionid4)); refer.sequence = data->sequence; refer.slot = data->slot; p_refer = &refer; } /* We need to register this state now. */ state_status = state_add_impl(file_obj, STATE_TYPE_SHARE, &candidate_data, owner, file_state, p_refer); if (state_status != STATE_SUCCESS) { /* state_add_impl failure closed and freed state. * file_state will also be NULL at this point. Also * release the ref on file_obj, since the state add * failed. */ file_obj->obj_ops->put_ref(file_obj); res_OPEN4->status = nfs4_Errno_state(state_status); *new_state = false; goto out; } glist_init(&(*file_state)->state_data.share.share_lockstates); } res_OPEN4->status = open4_create_fh(data, file_obj, true); if (res_OPEN4->status != NFS4_OK) { if (*new_state) { /* state_del_locked will close the file. */ state_del_locked(*file_state); *file_state = NULL; *new_state = false; } else { /*Do an open downgrade to the old open flags */ status = file_obj->obj_ops->reopen2(file_obj, *file_state, old_openflags); if (FSAL_IS_ERROR(status)) { LogCrit(COMPONENT_NFS_V4, "Failed to allocate handle, reopen2 failed with %s", fsal_err_txt(status)); } /* Need to release the state_lock before the put_ref * call. */ PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); state_lock_held = false; /* Release the extra LRU reference on file_obj. */ file_obj->obj_ops->put_ref(file_obj); goto out; } } /* Since open4_create_fh succeeded the LRU reference to file_obj was * consumed by data->current_obj. */ if (!(*new_state)) { LogFullDebug(COMPONENT_STATE, "Open upgrade old access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); LogFullDebug(COMPONENT_STATE, "Open upgrade to access=%x deny=%x", arg->share_access, arg->share_deny); /* Update share_access and share_deny */ (*file_state)->state_data.share.share_access |= arg->share_access & OPEN4_SHARE_ACCESS_BOTH; (*file_state)->state_data.share.share_deny |= arg->share_deny; /* Update share_access_prev and share_deny_prev */ (*file_state)->state_data.share.share_access_prev |= (1 << (arg->share_access & OPEN4_SHARE_ACCESS_BOTH)); (*file_state)->state_data.share.share_deny_prev |= (1 << arg->share_deny); LogFullDebug(COMPONENT_STATE, "Open upgrade new access=%x deny=%x access_prev=%x deny_prev=%x", (*file_state)->state_data.share.share_access, (*file_state)->state_data.share.share_deny, (*file_state)->state_data.share.share_access_prev, (*file_state)->state_data.share.share_deny_prev); } do_delegation(arg, res_OPEN4, data, owner, *file_state, clientid); out: /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&sattr); if (state_lock_held) PTHREAD_RWLOCK_unlock(&file_obj->state_hdl->state_lock); if (filename) gsh_free(filename); if (res_OPEN4->status != NFS4_OK) { /* Cleanup state on error */ if (*new_state) (*file_state) ->state_exp->exp_ops.free_state( (*file_state)->state_exp, *file_state); else if (*file_state != NULL) dec_state_t_ref(*file_state); *file_state = NULL; } if (looked_up_file_obj) { /* We got file_obj via lookup, we need to unref it. */ file_obj->obj_ops->put_ref(file_obj); } }
int nfs3_lookup(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct fsal_obj_handle *obj_dir = NULL; struct fsal_obj_handle *obj_file = NULL; fsal_status_t fsal_status; char *name = NULL; int rc = NFS_REQ_OK; struct attrlist attrs; /* We have the option of not sending attributes, so set ATTR_RDATTR_ERR. */ fsal_prepare_attrs(&attrs, ATTRS_NFS3 | ATTR_RDATTR_ERR); if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; name = arg->arg_lookup3.what.name; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_lookup3.what.dir), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs_Lookup handle: %s name: %s", str, name); } /* to avoid setting it on each error case */ res->res_lookup3.LOOKUP3res_u.resfail.dir_attributes.attributes_follow = FALSE; obj_dir = nfs3_FhandleToCache(&arg->arg_lookup3.what.dir, &res->res_lookup3.status, &rc); if (obj_dir == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ goto out; } name = arg->arg_lookup3.what.name; fsal_status = fsal_lookup(obj_dir, name, &obj_file, &attrs); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } res->res_lookup3.status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(obj_dir, &res->res_lookup3.LOOKUP3res_u.resfail. dir_attributes, NULL); } else { /* Build FH */ if (nfs3_FSALToFhandle( true, &res->res_lookup3.LOOKUP3res_u.resok.object, obj_file, op_ctx->ctx_export)) { /* Build entry attributes */ nfs_SetPostOpAttr(obj_file, &res->res_lookup3.LOOKUP3res_u. resok.obj_attributes, &attrs); /* Build directory attributes */ nfs_SetPostOpAttr(obj_dir, &res->res_lookup3. LOOKUP3res_u.resok.dir_attributes, NULL); res->res_lookup3.status = NFS3_OK; } else { res->res_lookup3.status = NFS3ERR_BADHANDLE; } } rc = NFS_REQ_OK; out: /* Release the attributes. */ fsal_release_attrs(&attrs); /* return references */ if (obj_dir) obj_dir->obj_ops.put_ref(obj_dir); if (obj_file) obj_file->obj_ops.put_ref(obj_file); return rc; } /* nfs3_lookup */
int _9p_setattr(struct _9p_request_data *req9p, u32 *plenout, char *preply) { char *cursor = req9p->_9pmsg + _9P_HDR_SIZE + _9P_TYPE_SIZE; u16 *msgtag = NULL; u32 *fid = NULL; u32 *valid = NULL; u32 *mode = NULL; u32 *uid = NULL; u32 *gid = NULL; u64 *size = NULL; u64 *atime_sec = NULL; u64 *atime_nsec = NULL; u64 *mtime_sec = NULL; u64 *mtime_nsec = NULL; struct _9p_fid *pfid = NULL; struct attrlist fsalattr; fsal_status_t fsal_status; struct timeval t; /* Get data */ _9p_getptr(cursor, msgtag, u16); _9p_getptr(cursor, fid, u32); _9p_getptr(cursor, valid, u32); _9p_getptr(cursor, mode, u32); _9p_getptr(cursor, uid, u32); _9p_getptr(cursor, gid, u32); _9p_getptr(cursor, size, u64); _9p_getptr(cursor, atime_sec, u64); _9p_getptr(cursor, atime_nsec, u64); _9p_getptr(cursor, mtime_sec, u64); _9p_getptr(cursor, mtime_nsec, u64); LogDebug(COMPONENT_9P, "TSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); if (*fid >= _9P_FID_PER_CONN) return _9p_rerror(req9p, msgtag, ERANGE, plenout, preply); pfid = req9p->pconn->fids[*fid]; /* Check that it is a valid fid */ if (pfid == NULL || pfid->pentry == NULL) { LogDebug(COMPONENT_9P, "request on invalid fid=%u", *fid); return _9p_rerror(req9p, msgtag, EIO, plenout, preply); } _9p_init_opctx(pfid, req9p); if ((op_ctx->export_perms->options & EXPORT_OPTION_WRITE_ACCESS) == 0) return _9p_rerror(req9p, msgtag, EROFS, plenout, preply); /* If a "time" change is required, but not with the "_set" suffix, * use gettimeofday */ if (*valid & (_9P_SETATTR_ATIME | _9P_SETATTR_CTIME | _9P_SETATTR_MTIME)) { if (gettimeofday(&t, NULL) == -1) { LogMajor(COMPONENT_9P, "TSETATTR: tag=%u fid=%u ERROR !! gettimeofday returned -1 with errno=%u", (u32) *msgtag, *fid, errno); return _9p_rerror(req9p, msgtag, errno, plenout, preply); } } /* Let's do the job */ memset((char *)&fsalattr, 0, sizeof(fsalattr)); if (*valid & _9P_SETATTR_MODE) { FSAL_SET_MASK(fsalattr.mask, ATTR_MODE); fsalattr.mode = *mode; } if (*valid & _9P_SETATTR_UID) { FSAL_SET_MASK(fsalattr.mask, ATTR_OWNER); fsalattr.owner = *uid; } if (*valid & _9P_SETATTR_GID) { FSAL_SET_MASK(fsalattr.mask, ATTR_GROUP); fsalattr.group = *gid; } if (*valid & _9P_SETATTR_SIZE) { FSAL_SET_MASK(fsalattr.mask, ATTR_SIZE); fsalattr.filesize = *size; } if (*valid & _9P_SETATTR_ATIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_ATIME); fsalattr.atime.tv_sec = t.tv_sec; fsalattr.atime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_MTIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_MTIME); fsalattr.mtime.tv_sec = t.tv_sec; fsalattr.mtime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_CTIME) { FSAL_SET_MASK(fsalattr.mask, ATTR_CTIME); fsalattr.ctime.tv_sec = t.tv_sec; fsalattr.ctime.tv_nsec = t.tv_usec * 1000; } if (*valid & _9P_SETATTR_ATIME_SET) { FSAL_SET_MASK(fsalattr.mask, ATTR_ATIME); fsalattr.atime.tv_sec = *atime_sec; fsalattr.atime.tv_nsec = *atime_nsec; } if (*valid & _9P_SETATTR_MTIME_SET) { FSAL_SET_MASK(fsalattr.mask, ATTR_MTIME); fsalattr.mtime.tv_sec = *mtime_sec; fsalattr.mtime.tv_nsec = *mtime_nsec; } /* Now set the attr */ fsal_status = fsal_setattr(pfid->pentry, false, pfid->state, &fsalattr); /* Release the attributes (may release an inherited ACL) */ fsal_release_attrs(&fsalattr); if (FSAL_IS_ERROR(fsal_status)) return _9p_rerror(req9p, msgtag, _9p_tools_errno(fsal_status), plenout, preply); /* Build the reply */ _9p_setinitptr(cursor, preply, _9P_RSETATTR); _9p_setptr(cursor, msgtag, u16); _9p_setendptr(cursor, preply); _9p_checkbound(cursor, preply, plenout); LogDebug(COMPONENT_9P, "RSETATTR: tag=%u fid=%u valid=0x%x mode=0%o uid=%u gid=%u size=%" PRIu64 " atime=(%llu|%llu) mtime=(%llu|%llu)", (u32) *msgtag, *fid, *valid, *mode, *uid, *gid, *size, (unsigned long long)*atime_sec, (unsigned long long)*atime_nsec, (unsigned long long)*mtime_sec, (unsigned long long)*mtime_nsec); /* _9p_stat_update(*pmsgtype, TRUE, &pwkrdata->stats._9p_stat_req); */ return 1; }
/** * @brief create GPFS handle * * @param exp_hdl export handle * @param hdl_desc handle description * @param handle object handle * @return status * * Does what original FSAL_ExpandHandle did (sort of) * returns a ref counted handle to be later used in cache_inode etc. * NOTE! you must release this thing when done with it! * BEWARE! Thanks to some holes in the *AT syscalls implementation, * we cannot get an fd on an AF_UNIX socket, nor reliably on block or * character special devices. Sorry, it just doesn't... * we could if we had the handle of the dir it is in, but this method * is for getting handles off the wire for cache entries that have LRU'd. * Ideas and/or clever hacks are welcome... */ fsal_status_t gpfs_create_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { int retval = 0; fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct gpfs_file_handle *fh; struct attrlist attrib; char *link_content = NULL; ssize_t retlink = PATH_MAX - 1; char link_buff[PATH_MAX]; struct fsal_fsid__ fsid; struct fsal_filesystem *fs; struct gpfs_filesystem *gpfs_fs; *handle = NULL; /* poison it first */ if ((hdl_desc->len > (sizeof(struct gpfs_file_handle)))) return fsalstat(ERR_FSAL_FAULT, 0); fh = alloca(hdl_desc->len); memcpy(fh, hdl_desc->addr, hdl_desc->len); /* struct aligned copy */ gpfs_extract_fsid(fh, &fsid); fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find filesystem for fsid=0x%016"PRIx64 ".0x%016"PRIx64" from handle", fsid.major, fsid.minor); return fsalstat(ERR_FSAL_STALE, ESTALE); } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "Non GPFS filesystem fsid=0x%016"PRIx64 ".0x%016"PRIx64" from handle", fsid.major, fsid.minor); return fsalstat(ERR_FSAL_STALE, ESTALE); } gpfs_fs = fs->private_data; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.mask |= attrs_out->mask; status = GPFSFSAL_getattrs(exp_hdl, gpfs_fs, op_ctx, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; if (attrib.type == SYMBOLIC_LINK) { /* I could lazy eval this... */ status = fsal_readlink_by_handle(gpfs_fs->root_fd, fh, link_buff, &retlink); if (FSAL_IS_ERROR(status)) return status; if (retlink < 0 || retlink == PATH_MAX) { retval = errno; if (retlink == PATH_MAX) retval = ENAMETOOLONG; return fsalstat(posix2fsal_error(retval), retval); } link_buff[retlink] = '\0'; link_content = link_buff; } hdl = alloc_handle(fh, fs, &attrib, link_content, exp_hdl); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; return status; }
int nfs4_op_verify(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { VERIFY4args * const arg_VERIFY4 = &op->nfs_argop4_u.opverify; VERIFY4res * const res_VERIFY4 = &resp->nfs_resop4_u.opverify; fattr4 file_attr4; int rc = 0; struct attrlist attrs; resp->resop = NFS4_OP_VERIFY; res_VERIFY4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_VERIFY4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (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; } fsal_prepare_attrs(&attrs, 0); res_VERIFY4->status = bitmap4_to_attrmask_t(&arg_VERIFY4->obj_attributes.attrmask, &attrs.mask); if (res_VERIFY4->status != NFS4_OK) return res_VERIFY4->status; res_VERIFY4->status = file_To_Fattr(data, attrs.mask, &attrs, &file_attr4, &arg_VERIFY4->obj_attributes.attrmask); if (res_VERIFY4->status != NFS4_OK) return res_VERIFY4->status; /* Done with the attrs */ fsal_release_attrs(&attrs); rc = nfs4_Fattr_cmp(&(arg_VERIFY4->obj_attributes), &file_attr4); if (rc == 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 */
static int nfs4_read(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { READ4args * const arg_READ4 = &op->nfs_argop4_u.opread; READ4res * const res_READ4 = &resp->nfs_resop4_u.opread; uint64_t size = 0; size_t read_size = 0; uint64_t offset = 0; bool eof_met = false; void *bufferdata = NULL; fsal_status_t fsal_status = {0, 0}; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; bool sync = false; bool anonymous_started = false; state_owner_t *owner = NULL; bool bypass = false; uint64_t MaxRead = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxRead); uint64_t MaxOffsetRead = atomic_fetch_uint64_t( &op_ctx->ctx_export->MaxOffsetRead); /* Say we are managing NFS4_OP_READ */ resp->resop = NFS4_OP_READ; res_READ4->status = NFS4_OK; /* Do basic checks on a filehandle Only files can be read */ if ((data->minorversion > 0) && nfs4_Is_Fh_DSHandle(&data->currentFH)) { if (io == FSAL_IO_READ) return op_dsread(op, data, resp); else return op_dsread_plus(op, data, resp, info); } res_READ4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_READ4->status != NFS4_OK) return res_READ4->status; obj = data->current_obj; /* Check stateid correctness and get pointer to state (also checks for special stateids) */ res_READ4->status = nfs4_Check_Stateid(&arg_READ4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "READ"); if (res_READ4->status != NFS4_OK) return res_READ4->status; /* NB: After this point, if state_found == NULL, then the stateid is all-0 or all-1 */ if (state_found != NULL) { struct state_deleg *sdeleg; if (info) info->io_advise = state_found->state_data.io_advise; switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); /** * @todo FSF: need to check against existing locks */ break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); /** * @todo FSF: should check that write is in * range of an byte range lock... */ break; case STATE_TYPE_DELEG: /* Check if the delegation state allows READ */ sdeleg = &state_found->state_data.deleg; if (!(sdeleg->sd_type & OPEN_DELEGATE_READ) || (sdeleg->sd_state != DELEG_GRANTED)) { /* Invalid delegation for this operation. */ LogDebug(COMPONENT_STATE, "Delegation type:%d state:%d", sdeleg->sd_type, sdeleg->sd_state); res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } state_open = NULL; break; default: res_READ4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "READ with invalid statid of type %d", state_found->state_type); goto out; } /* This is a read operation, this means that the file MUST have been opened for reading */ if (state_open != NULL && (state_open->state_data.share. share_access & OPEN4_SHARE_ACCESS_READ) == 0) { /* Even if file is open for write, the client * may do accidently read operation (caching). * Because of this, READ is allowed if not * explicitly denied. See page 112 in RFC 7530 * for more details. */ if (state_open->state_data.share. share_deny & OPEN4_SHARE_DENY_READ) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_READ4->status = NFS4ERR_OPENMODE; if (isDebug(COMPONENT_NFS_V4_LOCK)) { char str[LOG_BUFF_LEN] = "\0"; struct display_buffer dspbuf = { sizeof(str), str, str}; display_stateid(&dspbuf, state_found); LogDebug(COMPONENT_NFS_V4_LOCK, "READ %s doesn't have OPEN4_SHARE_ACCESS_READ", str); } goto out; } } /** * @todo : this piece of code looks a bit suspicious * (see Rong's mail) * * @todo: ACE: This works for now. How do we want to * handle owner confirmation across NFSv4.0/NFSv4.1? * Do we want to mark every NFSv4.1 owner * pre-confirmed, or make the check conditional on * minorversion like we do here? */ switch (state_found->state_type) { case STATE_TYPE_SHARE: if (data->minorversion == 0 && !state_owner_confirmed(state_found)) { res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } break; case STATE_TYPE_LOCK: case STATE_TYPE_DELEG: break; default: /* Sanity check: all other types are illegal. * we should not got that place (similar check * above), anyway it costs nothing to add this * test */ res_READ4->status = NFS4ERR_BAD_STATEID; goto out; } } else { /* Special stateid, no open state, check to see if any share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if any share * conflicts The stateid is all-0 or all-1 */ bypass = arg_READ4->stateid.seqid != 0; res_READ4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_READ, arg_READ4->stateid.seqid != 0 ? SHARE_BYPASS_READ : SHARE_BYPASS_NONE)); if (res_READ4->status != NFS4_OK) goto out; anonymous_started = true; } /* Need to permission check the read. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_READ_ACCESS, NULL, NULL, true); if (fsal_status.major == ERR_FSAL_ACCESS) { /* Test for execute permission */ fsal_status = fsal_access(obj, FSAL_MODE_MASK_SET(FSAL_X_OK) | FSAL_ACE4_MASK_SET (FSAL_ACE_PERM_EXECUTE)); } if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the size and offset of the read operation */ offset = arg_READ4->offset; size = arg_READ4->count; if (MaxOffsetRead < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Read offset=%" PRIu64 " size=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetRead); if ((offset + size) > MaxOffsetRead) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetRead, op_ctx->ctx_export->export_id); res_READ4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxRead) { /* the client asked for too much data, this should normally not happen because client will get FATTR4_MAXREAD value at mount time */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "read requested size = %"PRIu64 " read allowed size = %" PRIu64, size, MaxRead); size = MaxRead; } } /* If size == 0, no I/O is to be made and everything is alright */ if (size == 0) { /* A size = 0 can not lead to EOF */ res_READ4->READ4res_u.resok4.eof = false; res_READ4->READ4res_u.resok4.data.data_len = 0; res_READ4->READ4res_u.resok4.data.data_val = NULL; res_READ4->status = NFS4_OK; goto done; } /* Some work is to be done */ bufferdata = gsh_malloc_aligned(4096, size); if (!anonymous_started && data->minorversion == 0) { owner = get_state_owner_ref(state_found); if (owner != NULL) { op_ctx->clientid = &owner->so_owner.so_nfs4_owner.so_clientid; } } if (obj->fsal->m_ops.support_ex(obj)) { /* Call the new fsal_read2 */ fsal_status = fsal_read2(obj, bypass, state_found, offset, size, &read_size, bufferdata, &eof_met, info); } else { /* Call legacy fsal_rdwr */ fsal_status = fsal_rdwr(obj, io, offset, size, &read_size, bufferdata, &eof_met, &sync, info); } if (FSAL_IS_ERROR(fsal_status)) { res_READ4->status = nfs4_Errno_status(fsal_status); gsh_free(bufferdata); res_READ4->READ4res_u.resok4.data.data_val = NULL; goto done; } if (!eof_met) { /** @todo FSF: add a config option for this behavior? */ /* Need to check against filesize for ESXi clients */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_SIZE); if (!FSAL_IS_ERROR(obj->obj_ops.getattrs(obj, &attrs))) eof_met = (offset + read_size) >= attrs.filesize; /* Done with the attrs */ fsal_release_attrs(&attrs); } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; res_READ4->READ4res_u.resok4.data.data_len = read_size; res_READ4->READ4res_u.resok4.data.data_val = bufferdata; LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_READ: offset = %" PRIu64 " read length = %zu eof=%u", offset, read_size, eof_met); /* Is EOF met or not ? */ res_READ4->READ4res_u.resok4.eof = eof_met; /* Say it is ok */ res_READ4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_READ); server_stats_io_done(size, read_size, (res_READ4->status == NFS4_OK) ? true : false, false); out: if (owner != NULL) dec_state_owner_ref(owner); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_READ4->status; } /* nfs4_op_read */
/** * @brief Create a symbolic link. * * @param dir_hdl Handle of the parent dir where the link is to be created. * @param linkname Name of the link to be created. * @param linkcontent Content of the link to be created. * @param op_ctx Authentication context for the operation (user,...). * @param accessmode Mode of the link to be created. * It has no sense in POSIX filesystems. * @param gpfs_fh Pointer to the handle of the created symlink. * @param link_attr Attributes of the newly created symlink. * As input, it defines the attributes that the caller * wants to retrieve (by positioning flags into this structure) * and the output is built considering this input * (it fills the structure according to the flags it contains). * May be NULL. * * @return Major error codes : * - ERR_FSAL_NO_ERROR (no error) * - Another error code if an error occured. */ fsal_status_t GPFSFSAL_symlink(struct fsal_obj_handle *dir_hdl, const char *linkname, const char *linkcontent, const struct req_op_context *op_ctx, uint32_t accessmode, struct gpfs_file_handle *gpfs_fh, struct attrlist *link_attr) { int rc, errsv; fsal_status_t status; int fd; struct gpfs_fsal_obj_handle *gpfs_hdl; struct gpfs_filesystem *gpfs_fs; /* note : link_attr is optional. */ if (!dir_hdl || !op_ctx || !gpfs_fh || !linkname || !linkcontent) return fsalstat(ERR_FSAL_FAULT, 0); gpfs_hdl = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_fs = dir_hdl->fs->private_data; /* Tests if symlinking is allowed by configuration. */ if (!op_ctx->fsal_export->exp_ops. fs_supports(op_ctx->fsal_export, fso_symlink_support)) return fsalstat(ERR_FSAL_NOTSUPP, 0); status = fsal_internal_handle2fd(gpfs_fs->root_fd, gpfs_hdl->handle, &fd, O_RDONLY | O_DIRECTORY, 0); if (FSAL_IS_ERROR(status)) return status; /* build symlink path */ /* create the symlink on the filesystem using the credentials * for proper ownership assignment. */ fsal_set_credentials(op_ctx->creds); rc = symlinkat(linkcontent, fd, linkname); errsv = errno; fsal_restore_ganesha_credentials(); if (rc) { close(fd); return fsalstat(posix2fsal_error(errsv), errsv); } /* now get the associated handle, while there is a race, there is also a race lower down */ status = fsal_internal_get_handle_at(fd, linkname, gpfs_fh); if (FSAL_IS_ERROR(status)) { close(fd); return status; } /* get attributes */ status = GPFSFSAL_getattrs(op_ctx->fsal_export, gpfs_fs, op_ctx, gpfs_fh, link_attr); if (!FSAL_IS_ERROR(status) && link_attr->type != SYMBOLIC_LINK) { /* We could wind up not failing the creation of the symlink * and the only way we know is that the object type isn't * a symlink. */ fsal_release_attrs(link_attr); status = fsalstat(ERR_FSAL_EXIST, 0); } close(fd); return status; }
/** * read_dirents * read the directory and call through the callback function for * each entry. * @param dir_hdl [IN] the directory to read * @param whence [IN] where to start (next) * @param dir_state [IN] pass thru of state to callback * @param cb [IN] callback function * @param eof [OUT] eof marker true == end of dir */ static fsal_status_t read_dirents(struct fsal_obj_handle *dir_hdl, fsal_cookie_t *whence, void *dir_state, fsal_readdir_cb cb, attrmask_t attrmask, bool *eof) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct gpfs_fsal_obj_handle *myself; int dirfd; fsal_status_t status; off_t seekloc = 0; int bpos, cnt, nread; struct dirent64 *dentry; char buf[BUF_SIZE]; struct gpfs_filesystem *gpfs_fs; if (whence != NULL) seekloc = (off_t) *whence; myself = container_of(dir_hdl, struct gpfs_fsal_obj_handle, obj_handle); gpfs_fs = dir_hdl->fs->private_data; status = fsal_internal_handle2fd_at(gpfs_fs->root_fd, myself->handle, &dirfd, O_RDONLY | O_DIRECTORY, 0); if (dirfd < 0) return status; seekloc = lseek(dirfd, seekloc, SEEK_SET); if (seekloc < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } cnt = 0; do { nread = syscall(SYS_getdents64, dirfd, buf, BUF_SIZE); if (nread < 0) { retval = errno; fsal_error = posix2fsal_error(retval); goto done; } if (nread == 0) break; for (bpos = 0; bpos < nread;) { struct fsal_obj_handle *hdl; struct attrlist attrs; bool cb_rc; dentry = (struct dirent64 *)(buf + bpos); if (strcmp(dentry->d_name, ".") == 0 || strcmp(dentry->d_name, "..") == 0) goto skip; /* must skip '.' and '..' */ fsal_prepare_attrs(&attrs, attrmask); status = lookup(dir_hdl, dentry->d_name, &hdl, &attrs); if (FSAL_IS_ERROR(status)) { fsal_error = status.major; goto done; } /* callback to cache inode */ cb_rc = cb(dentry->d_name, hdl, &attrs, dir_state, (fsal_cookie_t) dentry->d_off); fsal_release_attrs(&attrs); if (!cb_rc) goto done; skip: bpos += dentry->d_reclen; cnt++; } } while (nread > 0); *eof = true; done: close(dirfd); return fsalstat(fsal_error, retval); }
/** makesymlink * Note that we do not set mode bits on symlinks for Linux/POSIX * They are not really settable in the kernel and are not checked * anyway (default is 0777) because open uses that target's mode */ static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attr_in, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_status_t status; struct gpfs_fsal_obj_handle *hdl; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); /* Use a separate attrlist to getch the actual attributes into */ struct attrlist attrib; *handle = NULL; /* poison it first */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; fsal_prepare_attrs(&attrib, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attrib.mask |= attrs_out->mask; status = GPFSFSAL_symlink(dir_hdl, name, link_path, op_ctx, attr_in->mode, fh, &attrib); if (FSAL_IS_ERROR(status)) return status; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, dir_hdl->fs, &attrib, link_path, op_ctx->fsal_export); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attrib, true); } else { /* Done with the attrs */ fsal_release_attrs(&attrib); } *handle = &hdl->obj_handle; /* We handled the mode above. */ FSAL_UNSET_MASK(attr_in->mask, ATTR_MODE); if (attr_in->mask) { /* Now per support_ex API, if there are any other attributes * set, go ahead and get them set now. */ status = (*handle)->obj_ops.setattr2(*handle, false, NULL, attr_in); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ LogFullDebug(COMPONENT_FSAL, "setattr2 status=%s", fsal_err_txt(status)); (*handle)->obj_ops.release(*handle); *handle = NULL; } } else { status = fsalstat(ERR_FSAL_NO_ERROR, 0); if (attrs_out != NULL) { /* Make sure ATTR_RDATTR_ERR is cleared on success. */ attrs_out->mask &= ~ATTR_RDATTR_ERR; } } FSAL_SET_MASK(attr_in->mask, ATTR_MODE); return status; }
/** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->mask. The FSAL must manage bypass * or not of share reservations, and a state may be passed. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] attrib_set Attributes to set * * @return FSAL status. */ fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { struct vfs_fsal_obj_handle *myself; fsal_status_t status = {0, 0}; int retval = 0; fsal_openflags_t openflags = FSAL_O_ANY; bool has_lock = false; bool need_fsync = false; bool closefd = false; int my_fd; const char *func; /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } #ifdef ENABLE_VFS_DEBUG_ACL #ifdef ENABLE_RFC_ACL if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE) && !FSAL_TEST_MASK(attrib_set->mask, ATTR_ACL)) { /* Set ACL from MODE */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status = fsal_mode_to_acl(attrib_set, attrs.acl); /* Done with the attrs */ fsal_release_attrs(&attrs); } else { /* If ATTR_ACL is set, mode needs to be adjusted no matter what. * See 7530 s 6.4.1.3 */ if (!FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode = myself->mode; status = fsal_acl_to_mode(attrib_set); } if (FSAL_IS_ERROR(status)) return status; #endif /* ENABLE_RFC_ACL */ #endif /* This is yet another "you can't get there from here". If this object * is a socket (AF_UNIX), an fd on the socket s useless _period_. * If it is for a symlink, without O_PATH, you will get an ELOOP error * and (f)chmod doesn't work for a symlink anyway - not that it matters * because access checking is not done on the symlink but the final * target. * AF_UNIX sockets are also ozone material. If the socket is already * active listeners et al, you can manipulate the mode etc. If it is * just sitting there as in you made it with a mknod. * (one of those leaky abstractions...) * or the listener forgot to unlink it, it is lame duck. */ /* Test if size is being set, make sure file is regular and if so, * require a read/write file descriptor. */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } openflags = FSAL_O_RDWR; } /* Get a usable file descriptor. Share conflict is only possible if * size is being set. */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &need_fsync, &closefd, false); if (FSAL_IS_ERROR(status)) { if (obj_hdl->type == SYMBOLIC_LINK && status.major == ERR_FSAL_PERM) { /* You cannot open_by_handle (XFS) a symlink and it * throws an EPERM error for it. open_by_handle_at * does not throw that error for symlinks so we play a * game here. Since there is not much we can do with * symlinks anyway, say that we did it * but don't actually do anything. * If you *really* want to tweek things * like owners, get a modern linux kernel... */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } LogFullDebug(COMPONENT_FSAL, "find_fd status=%s", fsal_err_txt(status)); goto out; } /** TRUNCATE **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { /** @todo FSF: is this still necessary? * * XXX ESXi volume creation pattern reliably * reached this point in the past, however now that we * only use the already open file descriptor if it is * open read/write, this may no longer fail. * If there is some other error from ftruncate, then * we will needlessly retry, but without more detail * of the original failure, we can't be sure. * Fortunately permission checking is done by * Ganesha before calling here, so we won't get an * EACCES since this call is done as root. We could * get EFBIG, EPERM, or EINVAL. */ /** @todo FSF: re-open if we really still need this */ retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { func = "truncate"; goto fileerr; } } } /** CHMOD **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) { /* The POSIX chmod call doesn't affect the symlink object, but * the entry it points to. So we must ignore it. */ if (obj_hdl->type != SYMBOLIC_LINK) { if (vfs_unopenable_type(obj_hdl->type)) retval = fchmodat( my_fd, myself->u.unopenable.name, fsal2unix_mode(attrib_set->mode), 0); else retval = fchmod( my_fd, fsal2unix_mode(attrib_set->mode)); if (retval != 0) { func = "chmod"; goto fileerr; } } } /** CHOWN **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER | ATTR_GROUP)) { uid_t user = FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER) ? (int)attrib_set->owner : -1; gid_t group = FSAL_TEST_MASK(attrib_set->mask, ATTR_GROUP) ? (int)attrib_set->group : -1; if (vfs_unopenable_type(obj_hdl->type)) retval = fchownat(my_fd, myself->u.unopenable.name, user, group, AT_SYMLINK_NOFOLLOW); else if (obj_hdl->type == SYMBOLIC_LINK) retval = fchownat(my_fd, "", user, group, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); else retval = fchown(my_fd, user, group); if (retval) { func = "chown"; goto fileerr; } } /** UTIME **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTRS_SET_TIME)) { struct timespec timebuf[2]; if (obj_hdl->type == SYMBOLIC_LINK) goto out; /* Setting time on symlinks is illegal */ /* Atime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME_SERVER)) { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME)) { timebuf[0] = attrib_set->atime; } else { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_OMIT; } /* Mtime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME_SERVER)) { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME)) { timebuf[1] = attrib_set->mtime; } else { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_OMIT; } if (vfs_unopenable_type(obj_hdl->type)) retval = vfs_utimesat(my_fd, myself->u.unopenable.name, timebuf, AT_SYMLINK_NOFOLLOW); else retval = vfs_utimes(my_fd, timebuf); if (retval != 0) { func = "utimes"; goto fileerr; } } /** SUBFSAL **/ if (myself->sub_ops && myself->sub_ops->setattrs) { status = myself->sub_ops->setattrs( myself, my_fd, attrib_set->mask, attrib_set); if (FSAL_IS_ERROR(status)) goto out; } errno = 0; fileerr: retval = errno; if (retval != 0) { LogDebug(COMPONENT_FSAL, "%s returned %s", func, strerror(retval)); } status = fsalstat(posix2fsal_error(retval), retval); out: if (closefd) close(my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; }
int nfs4_op_setattr(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp) { SETATTR4args * const arg_SETATTR4 = &op->nfs_argop4_u.opsetattr; SETATTR4res * const res_SETATTR4 = &resp->nfs_resop4_u.opsetattr; struct attrlist sattr; fsal_status_t fsal_status = {0, 0}; const char *tag = "SETATTR"; state_t *state_found = NULL; state_t *state_open = NULL; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; resp->resop = NFS4_OP_SETATTR; res_SETATTR4->status = NFS4_OK; /* Do basic checks on a filehandle */ res_SETATTR4->status = nfs4_sanity_check_FH(data, NO_FILE_TYPE, false); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Don't allow attribute change while we are in grace period. * Required for delegation reclaims and may be needed for other * reclaimable states as well. */ if (nfs_in_grace()) { res_SETATTR4->status = NFS4ERR_GRACE; return res_SETATTR4->status; } /* Get only attributes that are allowed to be read */ if (!nfs4_Fattr_Check_Access (&arg_SETATTR4->obj_attributes, FATTR4_ATTR_WRITE)) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } /* Ask only for supported attributes */ if (!nfs4_Fattr_Supported(&arg_SETATTR4->obj_attributes)) { res_SETATTR4->status = NFS4ERR_ATTRNOTSUPP; return res_SETATTR4->status; } /* Convert the fattr4 in the request to a fsal sattr structure */ res_SETATTR4->status = nfs4_Fattr_To_FSAL_attr(&sattr, &arg_SETATTR4->obj_attributes, data); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* Trunc may change Xtime so we have to start with trunc and * finish by the mtime and atime */ if ((FSAL_TEST_MASK(sattr.mask, ATTR_SIZE)) || (FSAL_TEST_MASK(sattr.mask, ATTR4_SPACE_RESERVED))) { /* Setting the size of a directory is prohibited */ if (data->current_filetype == DIRECTORY) { res_SETATTR4->status = NFS4ERR_ISDIR; return res_SETATTR4->status; } /* Object should be a file */ if (data->current_obj->type != REGULAR_FILE) { res_SETATTR4->status = NFS4ERR_INVAL; return res_SETATTR4->status; } obj = data->current_obj; /* Check stateid correctness and get pointer to state */ res_SETATTR4->status = nfs4_Check_Stateid(&arg_SETATTR4->stateid, data->current_obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, tag); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; /* NB: After this point, if state_found == NULL, then * the stateid is all-0 or all-1 */ if (state_found != NULL) { switch (state_found->state_type) { case STATE_TYPE_SHARE: state_open = state_found; /* Note this causes an extra refcount, but it * simplifies logic below. */ inc_state_t_ref(state_open); break; case STATE_TYPE_LOCK: state_open = state_found->state_data.lock.openstate; inc_state_t_ref(state_open); break; case STATE_TYPE_DELEG: state_open = NULL; break; default: res_SETATTR4->status = NFS4ERR_BAD_STATEID; return res_SETATTR4->status; } /* This is a size operation, this means that * the file MUST have been opened for writing */ if (state_open != NULL && (state_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_SETATTR4->status = NFS4ERR_OPENMODE; return res_SETATTR4->status; } } else { /* Special stateid, no open state, check to * see if any share conflicts */ state_open = NULL; /* Special stateid, no open state, check to see if * any share conflicts The stateid is all-0 or all-1 */ res_SETATTR4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_WRITE, SHARE_BYPASS_NONE)); if (res_SETATTR4->status != NFS4_OK) return res_SETATTR4->status; anonymous_started = true; } } const time_t S_NSECS = 1000000000UL; /* Set the atime and mtime (ctime is not setable) */ /* A carry into seconds considered invalid */ if (sattr.atime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } if (sattr.mtime.tv_nsec >= S_NSECS) { res_SETATTR4->status = NFS4ERR_INVAL; goto done; } /* If owner or owner_group are set, and the credential was * squashed, then we must squash the set owner and owner_group. */ squash_setattr(&sattr); /* If a SETATTR comes with an open stateid, and size is being * set, then the open MUST be for write (checked above), so * is_open_write is simple at this stage, it's just a check that * we have an open owner. */ fsal_status = fsal_setattr(data->current_obj, false, state_found, &sattr); /* Release the attributes (may release an explicit or inherited ACL) */ fsal_release_attrs(&sattr); if (FSAL_IS_ERROR(fsal_status)) { res_SETATTR4->status = nfs4_Errno_status(fsal_status); goto done; } /* Set the replyed structure */ res_SETATTR4->attrsset = arg_SETATTR4->obj_attributes.attrmask; /* Exit with no error */ res_SETATTR4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_WRITE); if (state_found != NULL) dec_state_t_ref(state_found); if (state_open != NULL) dec_state_t_ref(state_open); return res_SETATTR4->status; } /* nfs4_op_setattr */
fsal_status_t vfs_open2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attrib_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { int posix_flags = 0; int fd, dir_fd; int retval = 0; mode_t unix_mode; fsal_status_t status = {0, 0}; struct vfs_fd *my_fd = NULL; struct vfs_fsal_obj_handle *myself, *hdl = NULL; struct stat stat; vfs_file_handle_t *fh = NULL; bool truncated; bool created = false; if (state != NULL) my_fd = (struct vfs_fd *)(state + 1); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrib_set ", attrib_set, false); fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; LogFullDebug(COMPONENT_FSAL, truncated ? "Truncate" : "No truncate"); if (createmode >= FSAL_EXCLUSIVE) { /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attrib_set, verifier); } if (name == NULL) { /* This is an open by handle */ struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } if (state != NULL) { /* Prepare to take the share reservation, but only if we * are called with a valid state (if state is NULL the * caller is a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &hdl->u.file.fd; PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); } status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd); if (FSAL_IS_ERROR(status)) { if (state == NULL) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Refresh the attributes */ struct stat stat; retval = fstat(my_fd->fd, &stat); if (retval == 0) { LogFullDebug(COMPONENT_FSAL, "New size = %" PRIx64, stat.st_size); } else { if (errno == EBADF) errno = ESTALE; status = fsalstat(posix2fsal_error(errno), errno); } /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (!FSAL_IS_ERROR(status) && createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_stat(&stat, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } } if (state == NULL) { /* If no state, release the lock taken above and return * status. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. */ return status; } (void) vfs_close_my_fd(my_fd); undo_share: /* Can only get here with state not NULL and an error */ /* On error we need to release our share reservation * and undo the update of the share counters. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); update_share_counters(&myself->u.file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* In this path where we are opening by name, we can't check share * reservation yet since we don't have an object_handle yet. If we * indeed create the object handle (there is no race with another * open by name), then there CAN NOT be a share conflict, otherwise * the share conflict will be resolved when the object handles are * merged. */ #ifdef ENABLE_VFS_DEBUG_ACL if (createmode != FSAL_NO_CREATE) { /* Need to ammend attributes for inherited ACL, these will be * set later. We also need to test for permission to create * since there might be an ACL. */ struct attrlist attrs; fsal_accessflags_t access_type; access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = obj_hdl->obj_ops.test_access(obj_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib_set, attrs.acl, FSAL_ACE_FLAG_FILE_INHERIT); /* Done with the attrs */ fsal_release_attrs(&attrs); if (FSAL_IS_ERROR(status)) return status; } #endif /* ENABLE_VFS_DEBUG_ACL */ if (createmode != FSAL_NO_CREATE) { /* Now add in O_CREAT and O_EXCL. */ posix_flags |= O_CREAT; /* And if we are at least FSAL_GUARDED, do an O_EXCL create. */ if (createmode >= FSAL_GUARDED) posix_flags |= O_EXCL; /* Fetch the mode attribute to use in the openat system call. */ unix_mode = fsal2unix_mode(attrib_set->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* Don't set the mode if we later set the attributes */ FSAL_UNSET_MASK(attrib_set->mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->mask != 0)) { /* If we have FSAL_UNCHECKED and want to set more attributes * than the mode, we attempt an O_EXCL create first, if that * succeeds, then we will be allowed to set the additional * attributes, otherwise, we don't know we created the file * and this can NOT set the attributes. */ posix_flags |= O_EXCL; } dir_fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &status.major); if (dir_fd < 0) return fsalstat(status.major, -dir_fd); /** @todo: not sure what this accomplishes... */ retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Become the user because we are creating an object in this dir. */ if (createmode != FSAL_NO_CREATE) fsal_set_credentials(op_ctx->creds); if ((posix_flags & O_CREAT) != 0) fd = openat(dir_fd, name, posix_flags, unix_mode); else fd = openat(dir_fd, name, posix_flags); if (fd == -1 && errno == EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry. We still try O_CREAT again just in * case file disappears out from under us. * * Note that because we have dropped O_EXCL, later on we will * not assume we created the file, and thus will not set * additional attributes. We don't need to separately track * the condition of not wanting to set attributes. */ posix_flags &= ~O_EXCL; fd = openat(dir_fd, name, posix_flags, unix_mode); } /* Preserve errno */ retval = errno; /* If we were creating, restore credentials now. */ if (createmode != FSAL_NO_CREATE) fsal_restore_ganesha_credentials(); if (fd < 0) { status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Remember if we were responsible for creating the file. * Note that in an UNCHECKED retry we MIGHT have re-created the * file and won't remember that. Oh well, so in that rare case we * leak a partially created file if we have a subsequent error in here. * Also notify caller to do permission check if we DID NOT create the * file. Note it IS possible in the case of a race between an UNCHECKED * open and an external unlink, we did create the file, but we will * still force a permission check. Of course that permission check * SHOULD succeed since we also won't set the mode the caller requested * and the default file create permissions SHOULD allow the owner * read/write. */ created = (posix_flags & O_EXCL) != 0; *caller_perm_check = !created; vfs_alloc_handle(fh); retval = vfs_name_to_handle(dir_fd, obj_hdl->fs, name, fh); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } retval = fstat(fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, obj_hdl->fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(posix2fsal_error(ENOMEM), ENOMEM); goto fileerr; } /* If we didn't have a state above, use the global fd. At this point, * since we just created the global fd, no one else can have a * reference to it, and thus we can mamnipulate unlocked which is * handy since we can then call setattr2 which WILL take the lock * without a double locking deadlock. */ if (my_fd == NULL) my_fd = &hdl->u.file.fd; my_fd->fd = fd; my_fd->openflags = openflags; *new_obj = &hdl->obj_handle; if (created && attrib_set->mask != 0) { /* Set attributes using our newly opened file descriptor as the * share_fd if there are any left to set (mode and truncate * have already been handled). * * Note that we only set the attributes if we were responsible * for creating the file and we have attributes to set. * * Note if we have ENABLE_VFS_DEBUG_ACL an inherited ACL might * be part of the attributes we are setting here. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; goto fileerr; } if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. Otherwise continue * with attrs_out indicating ATTR_RDATTR_ERR. */ goto fileerr; } } } else if (attrs_out != NULL) { /* Since we haven't set any attributes other than what was set * on create (if we even created), just use the stat results * we used to create the fsal_obj_handle. */ posix2fsal_attributes(&stat, attrs_out); /* Make sure ATTR_RDATTR_ERR is cleared on success. */ attrs_out->mask &= ~ATTR_RDATTR_ERR; } close(dir_fd); if (state != NULL) { /* Prepare to take the share reservation, but only if we are * called with a valid state (if state is NULL the caller is * a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&(*new_obj)->lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&hdl->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: close(fd); /* Delete the file if we actually created it. */ if (created) unlinkat(dir_fd, name, 0); direrr: close(dir_fd); return fsalstat(posix2fsal_error(retval), retval); }
/** * @param exp_hdl Handle * @param path Path * @param handle Reference to handle * * modelled on old api except we don't stuff attributes. * @return Status of operation */ fsal_status_t gpfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle, struct attrlist *attrs_out) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; fsal_status_t fsal_status; int retval = 0; int dir_fd; struct fsal_filesystem *fs; struct gpfs_fsal_obj_handle *hdl; struct attrlist attributes; gpfsfsal_xstat_t buffxstat; struct gpfs_file_handle *fh = alloca(sizeof(struct gpfs_file_handle)); struct fsal_fsid__ fsid; struct gpfs_fsal_export *gpfs_export; memset(fh, 0, sizeof(struct gpfs_file_handle)); fh->handle_size = GPFS_MAX_FH_SIZE; *handle = NULL; /* poison it */ dir_fd = open_dir_by_path_walk(-1, path, &buffxstat.buffstat); fsal_prepare_attrs(&attributes, ATTR_GPFS_ALLOC_HANDLE); if (attrs_out != NULL) attributes.mask |= attrs_out->mask; if (dir_fd < 0) { LogCrit(COMPONENT_FSAL, "Could not open directory for path %s", path); retval = -dir_fd; goto errout; } fsal_status = fsal_internal_fd2handle(dir_fd, fh); if (FSAL_IS_ERROR(fsal_status)) goto fileerr; gpfs_export = container_of(exp_hdl, struct gpfs_fsal_export, export); fsal_status = fsal_get_xstat_by_handle(dir_fd, fh, &buffxstat, NULL, false, (attributes.mask & ATTR_ACL) != 0); if (FSAL_IS_ERROR(fsal_status)) goto fileerr; fsal_status = gpfsfsal_xstat_2_fsal_attributes(&buffxstat, &attributes, gpfs_export->use_acl); LogFullDebug(COMPONENT_FSAL, "fsid=0x%016"PRIx64".0x%016"PRIx64, attributes.fsid.major, attributes.fsid.minor); if (FSAL_IS_ERROR(fsal_status)) goto fileerr; close(dir_fd); gpfs_extract_fsid(fh, &fsid); fs = lookup_fsid(&fsid, GPFS_FSID_TYPE); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find file system for path %s", path); retval = ENOENT; goto errout; } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "File system for path %s did not belong to FSAL %s", path, exp_hdl->fsal->name); retval = EACCES; goto errout; } LogDebug(COMPONENT_FSAL, "filesystem %s for path %s", fs->path, path); /* Make sure ATTR_RDATTR_ERR is cleared on success. */ attributes.mask &= ~ATTR_RDATTR_ERR; /* allocate an obj_handle and fill it up */ hdl = alloc_handle(fh, fs, &attributes, NULL, exp_hdl); if (attrs_out != NULL) { /* Copy the attributes to caller, passing ACL ref. */ fsal_copy_attrs(attrs_out, &attributes, true); } else { /* Done with the attrs */ fsal_release_attrs(&attributes); } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: retval = errno; close(dir_fd); errout: /* Done with attributes */ fsal_release_attrs(&attributes); fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); }