static int nfs4_mds_putfh(compound_data_t *data) { struct file_handle_v4 *v4_handle = (struct file_handle_v4 *)data->currentFH.nfs_fh4_val; struct gsh_export *exporting; struct fsal_export *export; struct gsh_buffdesc fh_desc; struct fsal_obj_handle *new_hdl; fsal_status_t fsal_status = { 0, 0 }; bool changed = true; LogFullDebug(COMPONENT_FILEHANDLE, "NFS4 Handle flags 0x%X export id %d", v4_handle->fhflags1, ntohs(v4_handle->id.exports)); LogFullDebugOpaque(COMPONENT_FILEHANDLE, "NFS4 FSAL Handle %s", LEN_FH_STR, v4_handle->fsopaque, v4_handle->fs_len); /* Find any existing export by the "id" from the handle, * before releasing the old export (to prevent thrashing). */ exporting = get_gsh_export(ntohs(v4_handle->id.exports)); if (exporting == NULL) { LogInfoAlt(COMPONENT_DISPATCH, COMPONENT_EXPORT, "NFS4 Request from client (%s) has invalid export identifier %d", op_ctx->client ? op_ctx->client->hostaddr_str : "unknown", ntohs(v4_handle->id.exports)); return NFS4ERR_STALE; } /* If old CurrentFH had a related export, release reference. */ if (op_ctx->ctx_export != NULL) { changed = ntohs(v4_handle->id.exports) != op_ctx->ctx_export->export_id; put_gsh_export(op_ctx->ctx_export); } /* If old CurrentFH had a related server, release reference. */ if (op_ctx->fsal_pnfs_ds != NULL) { pnfs_ds_put(op_ctx->fsal_pnfs_ds); op_ctx->fsal_pnfs_ds = NULL; } /* Clear out current entry for now */ set_current_entry(data, NULL); /* update _ctx fields needed by nfs4_export_check_access */ op_ctx->ctx_export = exporting; op_ctx->fsal_export = export = exporting->fsal_export; if (changed) { int status; status = nfs4_export_check_access(data->req); if (status != NFS4_OK) { LogFullDebug(COMPONENT_FILEHANDLE, "Export check access failed %s", nfsstat4_to_str(status)); return status; } } fh_desc.len = v4_handle->fs_len; fh_desc.addr = &v4_handle->fsopaque; /* adjust the handle opaque into a cache key */ fsal_status = export->exp_ops.wire_to_host(export, FSAL_DIGEST_NFSV4, &fh_desc, v4_handle->fhflags1); if (FSAL_IS_ERROR(fsal_status)) { LogFullDebug(COMPONENT_FILEHANDLE, "wire_to_host failed %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } fsal_status = export->exp_ops.create_handle(export, &fh_desc, &new_hdl, NULL); if (FSAL_IS_ERROR(fsal_status)) { LogDebug(COMPONENT_FILEHANDLE, "could not get create_handle object error %s", msg_fsal_err(fsal_status.major)); return nfs4_Errno_status(fsal_status); } /* Set the current entry using the ref from get */ set_current_entry(data, new_hdl); /* Put our ref */ new_hdl->obj_ops->put_ref(new_hdl); LogFullDebug(COMPONENT_FILEHANDLE, "File handle is of type %s(%d)", object_file_type_to_str(data->current_filetype), data->current_filetype); return NFS4_OK; }
fsal_status_t find_fd(int *fd, struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, fsal_openflags_t openflags, bool *has_lock, bool *need_fsync, bool *closefd, bool open_for_locks) { struct vfs_fsal_obj_handle *myself; struct vfs_filesystem *vfs_fs; struct vfs_fd temp_fd = {0, -1}, *out_fd = &temp_fd; fsal_status_t status = {ERR_FSAL_NO_ERROR, 0}; int rc, posix_flags; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); vfs_fs = myself->obj_handle.fs->private_data; fsal2posix_openflags(openflags, &posix_flags); /* Handle nom-regular files */ switch (obj_hdl->type) { case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: rc = vfs_open_by_handle(vfs_fs, myself->u.unopenable.dir, O_PATH | O_NOACCESS, &status.major); if (rc < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s openflags 0x%08x", strerror(-rc), O_PATH | O_NOACCESS); return fsalstat(posix2fsal_error(-rc), -rc); } *fd = rc; *closefd = true; return status; case REGULAR_FILE: status = fsal_find_fd((struct fsal_fd **)&out_fd, obj_hdl, (struct fsal_fd *)&myself->u.file.fd, &myself->u.file.share, bypass, state, openflags, vfs_open_func, vfs_close_func, has_lock, need_fsync, closefd, open_for_locks); *fd = out_fd->fd; return status; case SYMBOLIC_LINK: posix_flags |= (O_PATH | O_RDWR | O_NOFOLLOW); break; case FIFO_FILE: posix_flags |= O_NONBLOCK; break; case DIRECTORY: break; case NO_FILE_TYPE: case EXTENDED_ATTR: return fsalstat(posix2fsal_error(EINVAL), EINVAL); } /* Open file descriptor for non-regular files. */ rc = vfs_fsal_open(myself, posix_flags, &status.major); if (rc < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s openflags 0x%08x", strerror(-rc), openflags); return fsalstat(posix2fsal_error(-rc), -rc); } LogFullDebug(COMPONENT_FSAL, "Opened fd=%d for file of type %s", rc, object_file_type_to_str(obj_hdl->type)); *fd = rc; *closefd = true; return status; }
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); } }