/** * * @brief The NFSPROC3_LINK * * The NFSPROC3_LINK. * * @param[in] arg NFS argument union * @param[in] req SVC request related to this call * @param[out] res Structure to contain the result of the call * * @retval NFS_REQ_OK if successful * @retval NFS_REQ_DROP if failed but retryable * @retval NFS_REQ_FAILED if failed and not retryable * */ int nfs3_link(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { struct LINK3args *l3_arg = &arg->arg_link3; struct LINK3res *l3_res = &res->res_link3; const char *link_name = l3_arg->link.name; struct fsal_obj_handle *target_obj = NULL; struct fsal_obj_handle *parent_obj = NULL; pre_op_attr pre_parent = {0}; fsal_status_t fsal_status = {0, 0}; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char strto[LEN_FH_STR], strfrom[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->file, NULL, strfrom); nfs_FhandleToStr(req->rq_msg.cb_vers, &l3_arg->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 */ l3_res->LINK3res_u.resfail.file_attributes.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.before.attributes_follow = FALSE; l3_res->LINK3res_u.resfail.linkdir_wcc.after.attributes_follow = FALSE; l3_res->status = nfs3_verify_exportid(l3_arg, req); if (l3_res->status != NFS3_OK) return rc; parent_obj = nfs3_FhandleToCache(&l3_arg->link.dir, &l3_res->status, &rc); if (parent_obj == NULL) return rc; /* Status and rc are set by nfs3_FhandleToCache */ nfs_SetPreOpAttr(parent_obj, &pre_parent); target_obj = nfs3_FhandleToCache(&l3_arg->file, &l3_res->status, &rc); if (target_obj == NULL) { parent_obj->obj_ops.put_ref(parent_obj); return rc; /* Status and rc are set by nfs3_FhandleToCache */ } if (parent_obj->type != DIRECTORY) { l3_res->status = NFS3ERR_NOTDIR; goto out; } if (link_name == NULL || *link_name == '\0') { l3_res->status = NFS3ERR_INVAL; goto out; } fsal_status = fsal_link(target_obj, parent_obj, link_name); if (FSAL_IS_ERROR(fsal_status)) { /* If we are here, there was an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed link: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { rc = NFS_REQ_DROP; goto out; } l3_res->status = nfs3_Errno_status(fsal_status); nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resfail.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resfail.linkdir_wcc); } else { nfs_SetPostOpAttr(target_obj, &l3_res->LINK3res_u.resok.file_attributes, NULL); nfs_SetWccData(&pre_parent, parent_obj, &l3_res->LINK3res_u.resok.linkdir_wcc); l3_res->status = NFS3_OK; } out: /* return references */ target_obj->obj_ops.put_ref(target_obj); parent_obj->obj_ops.put_ref(parent_obj); return rc; } /* nfs3_link */
/** * @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; }
/** * @brief Up Thread * * @param Arg reference to void * */ void *GPFSFSAL_UP_Thread(void *Arg) { struct gpfs_filesystem *gpfs_fs = Arg; struct fsal_up_vector *event_func; char thr_name[16]; int rc = 0; struct pnfs_deviceid devid; struct stat buf; struct glock fl; struct callback_arg callback; struct gpfs_file_handle handle; int reason = 0; int flags = 0; unsigned int *fhP; int retry = 0; struct gsh_buffdesc key; uint32_t expire_time_attr = 0; uint32_t upflags; int errsv = 0; fsal_status_t fsal_status = {0,}; #ifdef _VALGRIND_MEMCHECK memset(&handle, 0, sizeof(handle)); memset(&buf, 0, sizeof(buf)); memset(&fl, 0, sizeof(fl)); memset(&devid, 0, sizeof(devid)); #endif snprintf(thr_name, sizeof(thr_name), "fsal_up_%"PRIu64".%"PRIu64, gpfs_fs->fs->dev.major, gpfs_fs->fs->dev.minor); SetNameFunction(thr_name); LogFullDebug(COMPONENT_FSAL_UP, "Initializing FSAL Callback context for %d.", gpfs_fs->root_fd); /* wait for nfs init completion to get general_fridge * initialized which is needed for processing some upcall events */ nfs_init_wait(); /* Start querying for events and processing. */ while (1) { LogFullDebug(COMPONENT_FSAL_UP, "Requesting event from FSAL Callback interface for %d.", gpfs_fs->root_fd); handle.handle_size = GPFS_MAX_FH_SIZE; handle.handle_key_size = OPENHANDLE_KEY_LEN; handle.handle_version = OPENHANDLE_VERSION; callback.interface_version = GPFS_INTERFACE_VERSION + GPFS_INTERFACE_SUB_VER; callback.mountdirfd = gpfs_fs->root_fd; callback.handle = &handle; callback.reason = &reason; callback.flags = &flags; callback.buf = &buf; callback.fl = &fl; callback.dev_id = &devid; callback.expire_attr = &expire_time_attr; rc = gpfs_ganesha(OPENHANDLE_INODE_UPDATE, &callback); errsv = errno; if (rc != 0) { rc = -(rc); if (rc > GPFS_INTERFACE_VERSION) { LogFatal(COMPONENT_FSAL_UP, "Ganesha version %d mismatch GPFS version %d.", callback.interface_version, rc); return NULL; } if (errsv == EINTR) continue; LogCrit(COMPONENT_FSAL_UP, "OPENHANDLE_INODE_UPDATE failed for %d. rc %d, errno %d (%s) reason %d", gpfs_fs->root_fd, rc, errsv, strerror(errsv), reason); /* @todo 1000 retry logic will go away once the * OPENHANDLE_INODE_UPDATE ioctl separates EINTR * and EUNATCH. */ if (errsv == EUNATCH && ++retry > 1000) LogFatal(COMPONENT_FSAL_UP, "GPFS file system %d has gone away.", gpfs_fs->root_fd); continue; } retry = 0; /* flags is int, but only the least significant 2 bytes * are valid. We are getting random bits into the upper * 2 bytes! Workaround this until the kernel module * gets fixed. */ flags = flags & 0xffff; LogDebug(COMPONENT_FSAL_UP, "inode update: rc %d reason %d update ino %" PRId64 " flags:%x", rc, reason, callback.buf->st_ino, flags); LogFullDebug(COMPONENT_FSAL_UP, "inode update: flags:%x callback.handle:%p handle size = %u handle_type:%d handle_version:%d key_size = %u handle_fsid=%X.%X f_handle:%p expire: %d", *callback.flags, callback.handle, callback.handle->handle_size, callback.handle->handle_type, callback.handle->handle_version, callback.handle->handle_key_size, callback.handle->handle_fsid[0], callback.handle->handle_fsid[1], callback.handle->f_handle, expire_time_attr); callback.handle->handle_version = OPENHANDLE_VERSION; fhP = (int *)&(callback.handle->f_handle[0]); LogFullDebug(COMPONENT_FSAL_UP, " inode update: handle %08x %08x %08x %08x %08x %08x %08x", fhP[0], fhP[1], fhP[2], fhP[3], fhP[4], fhP[5], fhP[6]); /* Here is where we decide what type of event this is * ... open,close,read,...,invalidate? */ key.addr = &handle; key.len = handle.handle_key_size; LogDebug(COMPONENT_FSAL_UP, "Received event to process for %d", gpfs_fs->root_fd); /* We need valid up_vector while processing some of the * events below. Setup up vector and hold the mutex while * processing the event for the entire duration. */ PTHREAD_MUTEX_lock(&gpfs_fs->upvector_mutex); if (!setup_up_vector(gpfs_fs)) { PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); return NULL; } event_func = gpfs_fs->up_vector; switch (reason) { case INODE_LOCK_GRANTED: /* Lock Event */ case INODE_LOCK_AGAIN: /* Lock Event */ { LogMidDebug(COMPONENT_FSAL_UP, "%s: owner %p pid %d type %d start %lld len %lld", reason == INODE_LOCK_GRANTED ? "inode lock granted" : "inode lock again", fl.lock_owner, fl.flock.l_pid, fl.flock.l_type, (long long)fl.flock.l_start, (long long)fl.flock.l_len); fsal_lock_param_t lockdesc = { .lock_sle_type = FSAL_POSIX_LOCK, .lock_type = fl.flock.l_type, .lock_start = fl.flock.l_start, .lock_length = fl.flock.l_len }; if (reason == INODE_LOCK_AGAIN) fsal_status = up_async_lock_avail( general_fridge, event_func, &key, fl.lock_owner, &lockdesc, NULL, NULL); else fsal_status = up_async_lock_grant( general_fridge, event_func, &key, fl.lock_owner, &lockdesc, NULL, NULL); } break; case BREAK_DELEGATION: /* Delegation Event */ LogDebug(COMPONENT_FSAL_UP, "delegation recall: flags:%x ino %" PRId64, flags, callback.buf->st_ino); fsal_status = up_async_delegrecall(general_fridge, event_func, &key, NULL, NULL); break; case LAYOUT_FILE_RECALL: /* Layout file recall Event */ { struct pnfs_segment segment = { .offset = 0, .length = UINT64_MAX, .io_mode = LAYOUTIOMODE4_ANY }; LogDebug(COMPONENT_FSAL_UP, "layout file recall: flags:%x ino %" PRId64, flags, callback.buf->st_ino); fsal_status = up_async_layoutrecall( general_fridge, event_func, &key, LAYOUT4_NFSV4_1_FILES, false, &segment, NULL, NULL, NULL, NULL); } break; case LAYOUT_RECALL_ANY: /* Recall all layouts Event */ LogDebug(COMPONENT_FSAL_UP, "layout recall any: flags:%x ino %" PRId64, flags, callback.buf->st_ino); /** * @todo This functionality needs to be implemented as a * bulk FSID CB_LAYOUTRECALL. RECALL_ANY isn't suitable * since it can't be restricted to just one FSAL. Also * an FSID LAYOUTRECALL lets you have multiplke * filesystems exported from one FSAL and not yank layouts * on all of them when you only need to recall them for one. */ break; case LAYOUT_NOTIFY_DEVICEID: /* Device update Event */ LogDebug(COMPONENT_FSAL_UP, "layout dev update: flags:%x ino %" PRId64 " seq %d fd %d fsid 0x%" PRIx64, flags, callback.buf->st_ino, devid.device_id2, devid.device_id4, devid.devid); memset(&devid, 0, sizeof(devid)); devid.fsal_id = FSAL_ID_GPFS; fsal_status = up_async_notify_device(general_fridge, event_func, NOTIFY_DEVICEID4_DELETE_MASK, LAYOUT4_NFSV4_1_FILES, &devid, true, NULL, NULL); break; case INODE_UPDATE: /* Update Event */ { struct attrlist attr; LogMidDebug(COMPONENT_FSAL_UP, "inode update: flags:%x update ino %" PRId64 " n_link:%d", flags, callback.buf->st_ino, (int)callback.buf->st_nlink); /** @todo: This notification is completely * asynchronous. If we happen to change some * of the attributes later, we end up over * writing those with these possibly stale * values as we don't know when we get to * update with these up call values. We should * probably use time stamp or let the up call * always provide UP_TIMES flag in which case * we can compare the current ctime vs up call * provided ctime before updating the * attributes. * * For now, we think size attribute is more * important than others, so invalidate the * attributes and let ganesha fetch attributes * as needed if this update includes a size * change. We are careless for other attribute * changes, and we may end up with stale values * until this gets fixed! */ if (flags & (UP_SIZE | UP_SIZE_BIG)) { fsal_status = event_func->invalidate( event_func, &key, FSAL_UP_INVALIDATE_CACHE); break; } /* Check for accepted flags, any other changes just invalidate. */ if (flags & ~(UP_SIZE | UP_NLINK | UP_MODE | UP_OWN | UP_TIMES | UP_ATIME | UP_SIZE_BIG)) { fsal_status = event_func->invalidate( event_func, &key, FSAL_UP_INVALIDATE_CACHE); } else { /* buf may not have all attributes set. * Set the mask to what is changed */ attr.valid_mask = 0; attr.acl = NULL; upflags = 0; if (flags & UP_SIZE) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_SIZE | ATTR_SPACEUSED; if (flags & UP_SIZE_BIG) { attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_SIZE | ATTR_SPACEUSED; upflags |= fsal_up_update_filesize_inc | fsal_up_update_spaceused_inc; } if (flags & UP_MODE) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_MODE; if (flags & UP_OWN) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_OWNER | ATTR_GROUP | ATTR_MODE; if (flags & UP_TIMES) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_ATIME | ATTR_CTIME | ATTR_MTIME; if (flags & UP_ATIME) attr.valid_mask |= ATTR_CHGTIME | ATTR_CHANGE | ATTR_ATIME; if (flags & UP_NLINK) attr.valid_mask |= ATTR_NUMLINKS; attr.request_mask = attr.valid_mask; attr.expire_time_attr = expire_time_attr; posix2fsal_attributes(&buf, &attr); fsal_status = event_func->update( event_func, &key, &attr, upflags); if ((flags & UP_NLINK) && (attr.numlinks == 0)) { upflags = fsal_up_nlink; attr.valid_mask = 0; attr.request_mask = 0; fsal_status = up_async_update (general_fridge, event_func, &key, &attr, upflags, NULL, NULL); } } } break; case THREAD_STOP: /* We wanted to terminate this thread */ LogDebug(COMPONENT_FSAL_UP, "Terminating the GPFS up call thread for %d", gpfs_fs->root_fd); PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); return NULL; case INODE_INVALIDATE: LogMidDebug(COMPONENT_FSAL_UP, "inode invalidate: flags:%x update ino %" PRId64, flags, callback.buf->st_ino); upflags = FSAL_UP_INVALIDATE_CACHE; fsal_status = event_func->invalidate_close( event_func, &key, upflags); break; case THREAD_PAUSE: /* File system image is probably going away, but * we don't need to do anything here as we * eventually get other errors that stop this * thread. */ PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); continue; /* get next event */ default: PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); LogWarn(COMPONENT_FSAL_UP, "Unknown event: %d", reason); continue; } PTHREAD_MUTEX_unlock(&gpfs_fs->upvector_mutex); if (FSAL_IS_ERROR(fsal_status) && fsal_status.major != ERR_FSAL_NOENT) { LogWarn(COMPONENT_FSAL_UP, "Event %d could not be processed for fd %d rc %s", reason, gpfs_fs->root_fd, fsal_err_txt(fsal_status)); } } return NULL; } /* GPFSFSAL_UP_Thread */
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_fsstat(nfs_arg_t *arg, struct svc_req *req, nfs_res_t *res) { fsal_dynamicfsinfo_t dynamicinfo; fsal_status_t fsal_status; struct fsal_obj_handle *obj = NULL; int rc = NFS_REQ_OK; if (isDebug(COMPONENT_NFSPROTO)) { char str[LEN_FH_STR]; nfs_FhandleToStr(req->rq_msg.cb_vers, &(arg->arg_fsstat3.fsroot), NULL, str); LogDebug(COMPONENT_NFSPROTO, "REQUEST PROCESSING: Calling nfs3_fsstat handle: %s", str); } /* to avoid setting it on each error case */ res->res_fsstat3.FSSTAT3res_u.resfail.obj_attributes.attributes_follow = FALSE; obj = nfs3_FhandleToCache(&arg->arg_fsstat3.fsroot, &res->res_fsstat3.status, &rc); if (obj == NULL) { /* Status and rc have been set by nfs3_FhandleToCache */ return rc; } /* Get statistics and convert from FSAL */ fsal_status = fsal_statfs(obj, &dynamicinfo); if (FSAL_IS_ERROR(fsal_status)) { /* At this point we met an error */ LogFullDebug(COMPONENT_NFSPROTO, "failed statfs: fsal_status=%s", fsal_err_txt(fsal_status)); if (nfs_RetryableError(fsal_status.major)) { /* Drop retryable errors. */ rc = NFS_REQ_DROP; } else { res->res_fsstat3.status = nfs3_Errno_status(fsal_status); rc = NFS_REQ_OK; } goto out; } LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_bytes=%" PRIu64 " dynamicinfo.free_bytes=%" PRIu64 " dynamicinfo.avail_bytes=%" PRIu64, dynamicinfo.total_bytes, dynamicinfo.free_bytes, dynamicinfo.avail_bytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> dynamicinfo.total_files=%" PRIu64 " dynamicinfo.free_files=%" PRIu64 " dynamicinfo.avail_files=%" PRIu64, dynamicinfo.total_files, dynamicinfo.free_files, dynamicinfo.avail_files); nfs_SetPostOpAttr(obj, &res->res_fsstat3.FSSTAT3res_u.resok.obj_attributes, NULL); res->res_fsstat3.FSSTAT3res_u.resok.tbytes = dynamicinfo.total_bytes; res->res_fsstat3.FSSTAT3res_u.resok.fbytes = dynamicinfo.free_bytes; res->res_fsstat3.FSSTAT3res_u.resok.abytes = dynamicinfo.avail_bytes; res->res_fsstat3.FSSTAT3res_u.resok.tfiles = dynamicinfo.total_files; res->res_fsstat3.FSSTAT3res_u.resok.ffiles = dynamicinfo.free_files; res->res_fsstat3.FSSTAT3res_u.resok.afiles = dynamicinfo.avail_files; /* volatile FS */ res->res_fsstat3.FSSTAT3res_u.resok.invarsec = 0; res->res_fsstat3.status = NFS3_OK; LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tbytes=%llu fbytes=%llu abytes=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tbytes, res->res_fsstat3.FSSTAT3res_u.resok.fbytes, res->res_fsstat3.FSSTAT3res_u.resok.abytes); LogFullDebug(COMPONENT_NFSPROTO, "nfs_Fsstat --> tfiles=%llu fffiles=%llu afiles=%llu", res->res_fsstat3.FSSTAT3res_u.resok.tfiles, res->res_fsstat3.FSSTAT3res_u.resok.ffiles, res->res_fsstat3.FSSTAT3res_u.resok.afiles); rc = NFS_REQ_OK; out: /* return references */ obj->obj_ops.put_ref(obj); return rc; } /* nfs3_fsstat */
static int nfs4_write(struct nfs_argop4 *op, compound_data_t *data, struct nfs_resop4 *resp, fsal_io_direction_t io, struct io_info *info) { WRITE4args * const arg_WRITE4 = &op->nfs_argop4_u.opwrite; WRITE4res * const res_WRITE4 = &resp->nfs_resop4_u.opwrite; uint64_t size = 0; size_t written_size = 0; uint64_t offset; bool eof_met; bool sync = false; void *bufferdata; stable_how4 stable_how; state_t *state_found = NULL; state_t *state_open = NULL; fsal_status_t fsal_status = {0, 0}; struct fsal_obj_handle *obj = NULL; bool anonymous_started = false; struct gsh_buffdesc verf_desc; state_owner_t *owner = NULL; uint64_t MaxWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxWrite); uint64_t MaxOffsetWrite = atomic_fetch_uint64_t(&op_ctx->ctx_export->MaxOffsetWrite); /* Lock are not supported */ resp->resop = NFS4_OP_WRITE; res_WRITE4->status = NFS4_OK; if ((data->minorversion > 0) && (nfs4_Is_Fh_DSHandle(&data->currentFH))) { if (io == FSAL_IO_WRITE) return op_dswrite(op, data, resp); else return op_dswrite_plus(op, data, resp, info); } /* * Do basic checks on a filehandle * Only files can be written */ res_WRITE4->status = nfs4_sanity_check_FH(data, REGULAR_FILE, true); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* if quota support is active, then we should check is the FSAL allows inode creation or not */ fsal_status = op_ctx->fsal_export->exp_ops.check_quota( op_ctx->fsal_export, op_ctx->ctx_export->fullpath, FSAL_QUOTA_INODES); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = NFS4ERR_DQUOT; return res_WRITE4->status; } /* vnode to manage is the current one */ obj = data->current_obj; /* Check stateid correctness and get pointer to state * (also checks for special stateids) */ res_WRITE4->status = nfs4_Check_Stateid(&arg_WRITE4->stateid, obj, &state_found, data, STATEID_SPECIAL_ANY, 0, false, "WRITE"); if (res_WRITE4->status != NFS4_OK) return res_WRITE4->status; /* NB: After this points, 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 * exclusive 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_WRITE) || (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_WRITE4->status = NFS4ERR_BAD_STATEID; return res_WRITE4->status; } state_open = NULL; break; case STATE_TYPE_LAYOUT: state_open = NULL; break; default: res_WRITE4->status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "WRITE with invalid stateid of type %d", (int)state_found->state_type); return res_WRITE4->status; } /* This is a write 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_WRITE4->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, "WRITE %s doesn't have OPEN4_SHARE_ACCESS_WRITE", str); } 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 */ res_WRITE4->status = nfs4_Errno_state( state_share_anonymous_io_start( obj, OPEN4_SHARE_ACCESS_WRITE, SHARE_BYPASS_NONE)); if (res_WRITE4->status != NFS4_OK) goto out; anonymous_started = true; } /* Need to permission check the write. */ fsal_status = obj->obj_ops.test_access(obj, FSAL_WRITE_ACCESS, NULL, NULL, true); if (FSAL_IS_ERROR(fsal_status)) { res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } /* Get the characteristics of the I/O to be made */ offset = arg_WRITE4->offset; size = arg_WRITE4->data.data_len; stable_how = arg_WRITE4->stable; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64 " stable = %d", offset, size, stable_how); if (MaxOffsetWrite < UINT64_MAX) { LogFullDebug(COMPONENT_NFS_V4, "Write offset=%" PRIu64 " count=%" PRIu64 " MaxOffSet=%" PRIu64, offset, size, MaxOffsetWrite); if ((offset + size) > MaxOffsetWrite) { LogEvent(COMPONENT_NFS_V4, "A client tryed to violate max file size %" PRIu64 " for exportid #%hu", MaxOffsetWrite, op_ctx->ctx_export->export_id); res_WRITE4->status = NFS4ERR_FBIG; goto done; } } if (size > MaxWrite) { /* * The client asked for too much data, we * must restrict him */ if (info == NULL || info->io_content.what != NFS4_CONTENT_HOLE) { LogFullDebug(COMPONENT_NFS_V4, "write requested size = %" PRIu64 " write allowed size = %" PRIu64, size, MaxWrite); size = MaxWrite; } } /* Where are the data ? */ bufferdata = arg_WRITE4->data.data_val; LogFullDebug(COMPONENT_NFS_V4, "offset = %" PRIu64 " length = %" PRIu64, offset, size); /* if size == 0 , no I/O) are actually made and everything is alright */ if (size == 0) { res_WRITE4->WRITE4res_u.resok4.count = 0; res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier( op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; goto done; } if (arg_WRITE4->stable == UNSTABLE4) sync = false; else sync = true; 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_write */ fsal_status = fsal_write2(obj, false, state_found, offset, size, &written_size, bufferdata, &sync, info); } else { /* Call legacy fsal_rdwr */ fsal_status = fsal_rdwr(obj, io, offset, size, &written_size, bufferdata, &eof_met, &sync, info); } if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_NFS_V4, "write returned %s", fsal_err_txt(fsal_status)); res_WRITE4->status = nfs4_Errno_status(fsal_status); goto done; } if (!anonymous_started && data->minorversion == 0) op_ctx->clientid = NULL; /* Set the returned value */ if (sync) res_WRITE4->WRITE4res_u.resok4.committed = FILE_SYNC4; else res_WRITE4->WRITE4res_u.resok4.committed = UNSTABLE4; res_WRITE4->WRITE4res_u.resok4.count = written_size; verf_desc.addr = res_WRITE4->WRITE4res_u.resok4.writeverf; verf_desc.len = sizeof(verifier4); op_ctx->fsal_export->exp_ops.get_write_verifier(op_ctx->fsal_export, &verf_desc); res_WRITE4->status = NFS4_OK; done: if (anonymous_started) state_share_anonymous_io_done(obj, OPEN4_SHARE_ACCESS_WRITE); server_stats_io_done(size, written_size, (res_WRITE4->status == NFS4_OK) ? true : false, true); 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_WRITE4->status; } /* nfs4_op_write */
/** 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; }