static void rules_restore(void) { struct conf c; struct dbinfo dbi; unsigned int f; for (f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0) { if (dbi.id[0] == '\0') continue; (void)run_change("rem", &c, dbi.id, 0); (void)run_change("add", &c, dbi.id, sizeof(dbi.id)); } }
static void update(void) { struct timespec ts; struct conf c; struct dbinfo dbi; unsigned int f, n; char buf[128]; void *ss = &c.c_ss; if (clock_gettime(CLOCK_REALTIME, &ts) == -1) { (*lfun)(LOG_ERR, "clock_gettime failed (%m)"); return; } again: for (n = 0, f = 1; state_iterate(state, &c, &dbi, f) == 1; f = 0, n++) { time_t when = c.c_duration + dbi.last; if (debug > 1) { char b1[64], b2[64]; sockaddr_snprintf(buf, sizeof(buf), "%a:%p", ss); (*lfun)(LOG_DEBUG, "%s:[%u] %s count=%d duration=%d " "last=%s " "now=%s", __func__, n, buf, dbi.count, c.c_duration, fmttime(b1, sizeof(b1), dbi.last), fmttime(b2, sizeof(b2), ts.tv_sec)); } if (c.c_duration == -1 || when >= ts.tv_sec) continue; if (dbi.id[0]) { run_change("rem", &c, dbi.id, 0); sockaddr_snprintf(buf, sizeof(buf), "%a", ss); syslog(LOG_INFO, "released %s/%d:%d after %d seconds", buf, c.c_lmask, c.c_port, c.c_duration); } state_del(state, &c); goto again; } }
int nfs4_op_lock(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_lock"; #ifndef _WITH_NFSV4_LOCKS /* Lock are not supported */ resp->resop = NFS4_OP_LOCK; res_LOCK4.status = NFS4ERR_LOCK_NOTSUPP; return res_LOCK4.status; #else state_status_t state_status; state_data_t candidate_data; state_type_t candidate_type; int rc = 0; seqid4 seqid; state_t * plock_state; /* state for the lock */ state_t * pstate_open; /* state for the open owner */ state_t * pstate_previous_iterate; state_t * pstate_iterate; state_owner_t * plock_owner; state_owner_t * popen_owner; state_owner_t * presp_owner; /* Owner to store response in */ state_owner_t * conflict_owner = NULL; state_nfs4_owner_name_t owner_name; nfs_client_id_t nfs_client_id; state_lock_desc_t lock_desc, conflict_desc; state_blocking_t blocking = STATE_NON_BLOCKING; const char * tag = "LOCK"; LogDebug(COMPONENT_NFS_V4_LOCK, "Entering NFS v4 LOCK handler -----------------------------------------------------"); /* Initialize to sane starting values */ resp->resop = NFS4_OP_LOCK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_LOCK4.status = NFS4ERR_NOFILEHANDLE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Is_Fh_Empty"); return res_LOCK4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_LOCK4.status = NFS4ERR_BADHANDLE; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Is_Fh_Invalid"); return res_LOCK4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_LOCK4.status = NFS4ERR_FHEXPIRED; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Is_Fh_Expired"); return res_LOCK4.status; } /* Lock is done only on a file */ if(data->current_filetype != REGULAR_FILE) { /* Type of the entry is not correct */ switch (data->current_filetype) { case DIR_BEGINNING: case DIR_CONTINUE: res_LOCK4.status = NFS4ERR_ISDIR; break; default: res_LOCK4.status = NFS4ERR_INVAL; break; } LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed wrong file type"); return res_LOCK4.status; } /* Convert lock parameters to internal types */ switch(arg_LOCK4.locktype) { case READ_LT: lock_desc.sld_type = STATE_LOCK_R; blocking = STATE_NON_BLOCKING; break; case WRITE_LT: lock_desc.sld_type = STATE_LOCK_W; blocking = STATE_NON_BLOCKING; break; case READW_LT: lock_desc.sld_type = STATE_LOCK_R; blocking = STATE_NFSV4_BLOCKING; break; case WRITEW_LT: lock_desc.sld_type = STATE_LOCK_W; blocking = STATE_NFSV4_BLOCKING; break; } lock_desc.sld_offset = arg_LOCK4.offset; if(arg_LOCK4.length != STATE_LOCK_OFFSET_EOF) lock_desc.sld_length = arg_LOCK4.length; else lock_desc.sld_length = 0; if(arg_LOCK4.locker.new_lock_owner) { /* New lock owner, Find the open owner */ tag = "LOCK (new owner)"; /* Check stateid correctness and get pointer to state */ if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.open_owner.open_stateid, data->current_entry, 0LL, &pstate_open, data, STATEID_SPECIAL_FOR_LOCK, tag)) != NFS4_OK) { res_LOCK4.status = rc; return res_LOCK4.status; } popen_owner = pstate_open->state_powner; plock_state = NULL; plock_owner = NULL; presp_owner = popen_owner; seqid = arg_LOCK4.locker.locker4_u.open_owner.open_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK New lock owner from open owner", data->current_entry, data->pcontext, popen_owner, &lock_desc); /* Check is the clientid is known or not */ if(nfs_client_id_get(arg_LOCK4.locker.locker4_u.open_owner.lock_owner.clientid, &nfs_client_id) == CLIENT_ID_NOT_FOUND) { res_LOCK4.status = NFS4ERR_STALE_CLIENTID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs_client_id_get"); return res_LOCK4.status; } /* The related stateid is already stored in pstate_open */ /* An open state has been found. Check its type */ if(pstate_open->state_type != STATE_TYPE_SHARE) { res_LOCK4.status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed open stateid is not a SHARE"); return res_LOCK4.status; } /* Lock seqid (seqid wanted for new lock) should be 0 (see newpynfs test LOCK8c) */ if(arg_LOCK4.locker.locker4_u.open_owner.lock_seqid != 0) { res_LOCK4.status = NFS4ERR_BAD_SEQID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed new lock stateid is not 0"); return res_LOCK4.status; } /* Is this lock_owner known ? */ convert_nfs4_owner((open_owner4 *)&arg_LOCK4.locker.locker4_u.open_owner.lock_owner, &owner_name); } else { /* Existing lock owner * Find the lock stateid * From that, get the open_owner */ tag = "LOCK (existing owner)"; /* There was code here before to handle all-0 stateid, but that * really doesn't apply - when we handle temporary locks for * I/O operations (which is where we will see all-0 or all-1 * stateid, those will not come in through nfs4_op_lock. */ /* Check stateid correctness and get pointer to state */ if((rc = nfs4_Check_Stateid(&arg_LOCK4.locker.locker4_u.lock_owner.lock_stateid, data->current_entry, 0LL, &plock_state, data, STATEID_SPECIAL_FOR_LOCK, tag)) != NFS4_OK) { res_LOCK4.status = rc; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed nfs4_Check_Stateid for existing lock owner"); return res_LOCK4.status; } /* An lock state has been found. Check its type */ if(plock_state->state_type != STATE_TYPE_LOCK) { res_LOCK4.status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed existing lock owner, state type is not LOCK"); return res_LOCK4.status; } /* Get the old lockowner. We can do the following 'cast', in NFSv4 lock_owner4 and open_owner4 * are different types but with the same definition*/ plock_owner = plock_state->state_powner; popen_owner = plock_owner->so_owner.so_nfs4_owner.so_related_owner; pstate_open = plock_state->state_data.lock.popenstate; presp_owner = plock_owner; seqid = arg_LOCK4.locker.locker4_u.lock_owner.lock_seqid; LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK Existing lock owner", data->current_entry, data->pcontext, plock_owner, &lock_desc); #ifdef _CONFORM_TO_TEST_LOCK8c /* Check validity of the seqid */ if(arg_LOCK4.locker.locker4_u.lock_owner.lock_seqid != 0) { res_LOCK4.status = NFS4ERR_BAD_SEQID; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed existing lock owner, lock seqid != 0"); return res_LOCK4.status; } #endif } /* if( arg_LOCK4.locker.new_lock_owner ) */ /* Check seqid (lock_seqid or open_seqid) */ if(!Check_nfs4_seqid(presp_owner, seqid, op, data, resp, tag)) { /* Response is all setup for us and LogDebug told what was wrong */ return res_LOCK4.status; } /* Lock length should not be 0 */ if(arg_LOCK4.length == 0LL) { res_LOCK4.status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length == 0"); /* Save the response in the lock or open owner */ Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); return res_LOCK4.status; } /* Check for range overflow. * Comparing beyond 2^64 is not possible int 64 bits precision, * but off+len > 2^64-1 is equivalent to len > 2^64-1 - off */ if(lock_desc.sld_length > (STATE_LOCK_OFFSET_EOF - lock_desc.sld_offset)) { res_LOCK4.status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed length overflow"); /* Save the response in the lock or open owner */ Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); return res_LOCK4.status; } /* Check for conflicts with previously obtained states */ /* TODO FSF: * This will eventually all go into the function of state_lock() * For now, we will keep checking against SHARE * Check against LOCK will be removed * We eventually need to handle special stateids, but not here. */ /* loop into the states related to this pentry to find the related lock */ pstate_iterate = NULL; pstate_previous_iterate = NULL; do { state_iterate(data->current_entry, &pstate_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &state_status); if((state_status == STATE_STATE_ERROR) || (state_status == STATE_INVALID_ARGUMENT)) { res_LOCK4.status = NFS4ERR_INVAL; LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed state_iterate"); /* Save the response in the lock or open owner */ Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); return res_LOCK4.status; } if(pstate_iterate != NULL) { /* For now still check conflicts with SHARE here */ if(pstate_iterate->state_type == STATE_TYPE_SHARE) { /* In a correct POSIX behavior, a write lock should not be allowed on a read-mode file */ if((pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_WRITE) && !(pstate_iterate->state_data.share.share_access & OPEN4_SHARE_ACCESS_WRITE) && (arg_LOCK4.locktype == WRITE_LT)) { /* A conflicting open state, return NFS4ERR_OPENMODE * This behavior is implemented to comply with newpynfs's test LOCK4 */ LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed conflicts with SHARE", data->current_entry, data->pcontext, plock_owner, &lock_desc); res_LOCK4.status = NFS4ERR_OPENMODE; /* Save the response in the lock or open owner */ Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); return res_LOCK4.status; } } } /* if( pstate_iterate != NULL ) */ pstate_previous_iterate = pstate_iterate; } while(pstate_iterate != NULL); /* TODO FSF: * Ok from here on out, stuff is broken... * For new lock owner, need to create a new stateid * And then call state_lock() * If that fails, need to back out any stateid changes * If that succeeds, need to increment seqids */ if(arg_LOCK4.locker.new_lock_owner) { /* A lock owner is always associated with a previously made open * which has itself a previously made stateid */ if(nfs4_owner_Get_Pointer(&owner_name, &plock_owner)) { /* Lock owner already existsc, check lock_seqid */ if(!Check_nfs4_seqid(plock_owner, arg_LOCK4.locker.locker4_u.open_owner.lock_seqid, op, data, resp, "LOCK (new owner but owner exists)")) { /* Response is all setup for us and LogDebug told what was wrong */ return res_LOCK4.status; } } else { /* This lock owner is not known yet, allocated and set up a new one */ plock_owner = create_nfs4_owner(data->pclient, &owner_name, (open_owner4 *) &arg_LOCK4.locker.locker4_u.open_owner.lock_owner, popen_owner, 0); if(plock_owner == NULL) { res_LOCK4.status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to create new lock owner", data->current_entry, data->pcontext, popen_owner, &lock_desc); return res_LOCK4.status; } } /* Prepare state management structure */ candidate_type = STATE_TYPE_LOCK; candidate_data.lock.popenstate = pstate_open; /* Add the lock state to the lock table */ if(state_add(data->current_entry, candidate_type, &candidate_data, plock_owner, data->pclient, data->pcontext, &plock_state, &state_status) != STATE_SUCCESS) { res_LOCK4.status = NFS4ERR_RESOURCE; LogLock(COMPONENT_NFS_V4_LOCK, NIV_DEBUG, "LOCK failed to add new stateid", data->current_entry, data->pcontext, plock_owner, &lock_desc); if(destroy_nfs4_owner(data->pclient, &owner_name) != STATE_SUCCESS) LogDebug(COMPONENT_NFS_V4_LOCK, "destroy_nfs4_owner failed"); return res_LOCK4.status; } } /* if( arg_LOCK4.locker.new_lock_owner ) */ /* Now we have a lock owner and a stateid. * Go ahead and push lock into SAL (and FSAL). */ if(state_lock(data->current_entry, data->pcontext, plock_owner, plock_state, blocking, NULL, /* No block data for now */ &lock_desc, &conflict_owner, &conflict_desc, data->pclient, &state_status) != STATE_SUCCESS) { if(state_status == STATE_LOCK_CONFLICT) { /* A conflicting lock from a different lock_owner, returns NFS4ERR_DENIED */ Process_nfs4_conflict(&res_LOCK4.LOCK4res_u.denied, conflict_owner, &conflict_desc); } LogDebug(COMPONENT_NFS_V4_LOCK, "LOCK failed with status %s", state_err_str(state_status)); res_LOCK4.status = nfs4_Errno_state(state_status); if(arg_LOCK4.locker.new_lock_owner) { /* Need to destroy lock owner and state */ if(state_del(plock_state, data->pclient, &state_status) != STATE_SUCCESS) LogDebug(COMPONENT_NFS_V4_LOCK, "state_del failed with status %s", state_err_str(state_status)); if(destroy_nfs4_owner(data->pclient, &owner_name) != STATE_SUCCESS) LogDebug(COMPONENT_NFS_V4_LOCK, "destroy_nfs4_owner failed"); } /* Save the response in the lock or open owner */ if(res_LOCK4.status != NFS4ERR_RESOURCE && res_LOCK4.status != NFS4ERR_BAD_STATEID) Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); return res_LOCK4.status; } res_LOCK4.status = NFS4_OK; /* Handle stateid/seqid for success */ update_stateid(plock_state, &res_LOCK4.LOCK4res_u.resok4.lock_stateid, data, tag); LogFullDebug(COMPONENT_NFS_V4_LOCK, "LOCK state_seqid = %u, plock_state = %p", plock_state->state_seqid, plock_state); /* update the lock counter in the related open-stateid */ pstate_open->state_data.share.lockheld += 1; /* Save the response in the lock or open owner */ Copy_nfs4_state_req(presp_owner, seqid, op, data, resp, tag); LogLock(COMPONENT_NFS_V4_LOCK, NIV_FULL_DEBUG, "LOCK applied", data->current_entry, data->pcontext, plock_owner, &lock_desc); return res_LOCK4.status; #endif } /* nfs4_op_lock */
int nfs41_op_open(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_open"; cache_entry_t * pentry_parent = NULL; cache_entry_t * pentry_lookup = NULL; cache_entry_t * pentry_newfile = NULL; fsal_handle_t * pnewfsal_handle = NULL; fsal_attrib_list_t attr_parent; fsal_attrib_list_t attr; fsal_attrib_list_t attr_newfile; fsal_attrib_list_t sattr; fsal_openflags_t openflags = 0; cache_inode_status_t cache_status; state_status_t state_status; nfsstat4 rc; int retval; fsal_name_t filename; bool_t AttrProvided = FALSE; fsal_accessmode_t mode = 0600; nfs_fh4 newfh4; nfs_worker_data_t * pworker = NULL; int convrc = 0; state_data_t candidate_data; state_type_t candidate_type; state_t * pfile_state = NULL; state_t * pstate_found_iterate = NULL; state_t * pstate_previous_iterate = NULL; state_t * pstate_found_same_owner = NULL; state_nfs4_owner_name_t owner_name; state_owner_t * powner = NULL; fsal_accessflags_t write_access = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_WRITE_DATA | FSAL_ACE_PERM_APPEND_DATA); fsal_accessflags_t read_access = FSAL_MODE_MASK_SET(FSAL_R_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_READ_DATA); resp->resop = NFS4_OP_OPEN; res_OPEN4.status = NFS4_OK; uint32_t tmp_attr[2]; uint_t tmp_int = 2; #ifdef _USE_PNFS nfs_client_id_t nfs_clientid; bool_t open_owner_known = FALSE; int pnfs_status; #endif cache_inode_create_arg_t create_arg; pworker = (nfs_worker_data_t *) data->pclient->pworker; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_NOFILEHANDLE; return res_OPEN4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_BADHANDLE; return res_OPEN4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_FHEXPIRED; return res_OPEN4.status; } /* This can't be done on the pseudofs */ if(nfs4_Is_Fh_Pseudo(&(data->currentFH))) { res_OPEN4.status = NFS4ERR_ROFS; return res_OPEN4.status; } /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_open_xattr(op, data, resp); /* If data->current_entry is empty, repopulate it */ if(data->current_entry == NULL) { if((data->current_entry = nfs_FhandleToCache(NFS_V4, NULL, NULL, &(data->currentFH), NULL, NULL, &(res_OPEN4.status), &attr, data->pcontext, data->pclient, data->ht, &retval)) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } } /* Set parent */ pentry_parent = data->current_entry; /* First switch is based upon claim type */ switch (arg_OPEN4.claim.claim) { case CLAIM_DELEGATE_CUR: case CLAIM_DELEGATE_PREV: /* Check for name length */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len > FSAL_MAX_NAME_LEN) { res_OPEN4.status = NFS4ERR_NAMETOOLONG; return res_OPEN4.status; } /* get the filename from the argument, it should not be empty */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len == 0) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } res_OPEN4.status = NFS4ERR_NOTSUPP; return res_OPEN4.status; break; case CLAIM_NULL: /* Check for name length */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len > FSAL_MAX_NAME_LEN) { res_OPEN4.status = NFS4ERR_NAMETOOLONG; return res_OPEN4.status; } /* get the filename from the argument, it should not be empty */ if(arg_OPEN4.claim.open_claim4_u.file.utf8string_len == 0) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } /* Check if asked attributes are correct */ if(arg_OPEN4.openhow.openflag4_u.how.mode == GUARDED4 || arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4) { if(!nfs4_Fattr_Supported (&arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs)) { res_OPEN4.status = NFS4ERR_ATTRNOTSUPP; return res_OPEN4.status; } /* Do not use READ attr, use WRITE attr */ if(!nfs4_Fattr_Check_Access (&arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs, FATTR4_ATTR_WRITE)) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } } /* Check if filename is correct */ if((cache_status = cache_inode_error_convert(FSAL_buffdesc2name ((fsal_buffdesc_t *) & arg_OPEN4.claim.open_claim4_u. file, &filename))) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Check parent */ pentry_parent = data->current_entry; /* Parent must be a directory */ if((pentry_parent->internal_md.type != DIR_BEGINNING) && (pentry_parent->internal_md.type != DIR_CONTINUE)) { /* Parent object is not a directory... */ if(pentry_parent->internal_md.type == SYMBOLIC_LINK) res_OPEN4.status = NFS4ERR_SYMLINK; else res_OPEN4.status = NFS4ERR_NOTDIR; return res_OPEN4.status; } /* What kind of open is it ? */ LogFullDebug(COMPONENT_NFS_V4, " OPEN: Claim type = %d Open Type = %d Share Deny = %d Share Access = %d ", arg_OPEN4.claim.claim, arg_OPEN4.openhow.opentype, arg_OPEN4.share_deny, arg_OPEN4.share_access); /* It this a known client id ? */ LogDebug(COMPONENT_NFS_V4, "OPEN Client id = %llx", (long long unsigned int)arg_OPEN4.owner.clientid); /* Is this open_owner known ? */ convert_nfs4_owner(&arg_OPEN4.owner, &owner_name); if(!nfs4_owner_Get_Pointer(&owner_name, &powner)) { /* This open owner is not known yet, allocated and set up a new one */ powner = create_nfs4_owner(data->pclient, &owner_name, &arg_OPEN4.owner, NULL, 1); /* NFSv4.1 specific, initial seqid is 1 */ if(powner == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } } /* Status of parent directory before the operation */ if(cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.before), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.before = (changeid4) pentry_parent->internal_md.mod_time; /* CLient may have provided fattr4 to set attributes at creation time */ if(arg_OPEN4.openhow.openflag4_u.how.mode == GUARDED4 || arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4) { if(arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs.attrmask. bitmap4_len != 0) { /* Convert fattr4 so nfs4_sattr */ convrc = nfs4_Fattr_To_FSAL_attr(&sattr, &(arg_OPEN4.openhow.openflag4_u.how. createhow4_u.createattrs)); if(convrc != NFS4_OK) { res_OPEN4.status = convrc; return res_OPEN4.status; } AttrProvided = TRUE; } } /* Second switch is based upon "openhow" */ switch (arg_OPEN4.openhow.opentype) { case OPEN4_CREATE: /* a new file is to be created */ /* Does a file with this name already exist ? */ pentry_lookup = cache_inode_lookup(pentry_parent, &filename, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status); if(cache_status != CACHE_INODE_NOT_FOUND) { /* if open is UNCHECKED, return NFS4_OK (RFC3530 page 172) */ if(arg_OPEN4.openhow.openflag4_u.how.mode == UNCHECKED4 && (cache_status == CACHE_INODE_SUCCESS)) { /* If the file is opened for write, OPEN4 while deny share write access, * in this case, check caller has write access to the file */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) { if(cache_inode_access(pentry_lookup, write_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_WRONLY; } /* Same check on read: check for readability of a file before opening it for read */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ) { if(cache_inode_access(pentry_lookup, read_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDONLY; } if(AttrProvided == TRUE) /* Set the attribute if provided */ { if(cache_inode_setattr(pentry_lookup, &sattr, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset = arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createattrs. attrmask; } else res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 0; /* Same check on write */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) { if(cache_inode_access(pentry_lookup, write_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDWR; } /* Set the state for the related file */ /* Prepare state management structure */ candidate_type = STATE_TYPE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; if(state_add(pentry_lookup, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &state_status) != STATE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_lookup, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_SHARE_DENIED; res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; if((res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val = (uint32_t *) Mem_Alloc(res_OPEN4.OPEN4res_u.resok4.attrset. bitmap4_len * sizeof(uint32_t))) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.after), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; res_OPEN4.OPEN4res_u.resok4.stateid.seqid = pfile_state->state_seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pfile_state->stateid_other, OTHERSIZE); /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_lookup, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_lookup; data->current_filetype = REGULAR_FILE; res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } /* if open is EXCLUSIVE, but verifier is the same, return NFS4_OK (RFC3530 page 173) */ if(arg_OPEN4.openhow.openflag4_u.how.mode == EXCLUSIVE4) { if((pentry_lookup != NULL) && (pentry_lookup->internal_md.type == REGULAR_FILE)) { pstate_found_iterate = NULL; pstate_previous_iterate = NULL; do { state_iterate(pentry_lookup, &pstate_found_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &state_status); if(state_status == STATE_STATE_ERROR) break; if(state_status == STATE_INVALID_ARGUMENT) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } cache_status = CACHE_INODE_SUCCESS; /* Check is open_owner is the same */ if(pstate_found_iterate != NULL) { if((pstate_found_iterate->state_type == STATE_TYPE_SHARE) && !memcmp(arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->state_powner->so_owner_val, pstate_found_iterate->state_powner->so_owner_len) && !memcmp(pstate_found_iterate->state_data.share. oexcl_verifier, arg_OPEN4.openhow.openflag4_u.how. createhow4_u.createverf, NFS4_VERIFIER_SIZE)) { /* A former open EXCLUSIVE with same owner and verifier was found, resend it */ res_OPEN4.OPEN4res_u.resok4.stateid.seqid = pstate_found_iterate->state_seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pstate_found_iterate->stateid_other, OTHERSIZE); memset(&(res_OPEN4.OPEN4res_u.resok4.cinfo.after), 0, sizeof(changeid4)); res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_lookup, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_lookup; data->current_filetype = REGULAR_FILE; /* regular exit */ res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } } /* if( pstate_found_iterate != NULL ) */ pstate_previous_iterate = pstate_found_iterate; } while(pstate_found_iterate != NULL); } } /* Managing GUARDED4 mode */ if(cache_status != CACHE_INODE_SUCCESS) res_OPEN4.status = nfs4_Errno(cache_status); else res_OPEN4.status = NFS4ERR_EXIST; /* File already exists */ return res_OPEN4.status; } /* if( cache_status != CACHE_INODE_NOT_FOUND ), if file already exists basically */ LogFullDebug(COMPONENT_NFS_V4, " OPEN open.how = %d", arg_OPEN4.openhow.openflag4_u.how.mode); create_arg.use_pnfs = FALSE; #ifdef _USE_PNFS /* set the file has "managed via pNFS" */ if(data->pexport->options & EXPORT_OPTION_USE_PNFS) create_arg.use_pnfs = TRUE; #endif /* _USE_PNFS */ /* Create the file, if we reach this point, it does not exist, we can create it */ if((pentry_newfile = cache_inode_create(pentry_parent, &filename, REGULAR_FILE, mode, &create_arg, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { /* If the file already exists, this is not an error if open mode is UNCHECKED */ if(cache_status != CACHE_INODE_ENTRY_EXISTS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } else { /* If this point is reached, then the file already exists, cache_status == CACHE_INODE_ENTRY_EXISTS and pentry_newfile == NULL This probably means EXCLUSIVE4 mode is used and verifier matches. pentry_newfile is then set to pentry_lookup */ pentry_newfile = pentry_lookup; } } /* Prepare state management structure */ candidate_type = STATE_TYPE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; candidate_data.share.lockheld = 0; /* If file is opened under mode EXCLUSIVE4, open verifier should be kept to detect non vicious double open */ if(arg_OPEN4.openhow.openflag4_u.how.mode == EXCLUSIVE4) { strncpy(candidate_data.share.oexcl_verifier, arg_OPEN4.openhow.openflag4_u.how.createhow4_u.createverf, NFS4_VERIFIER_SIZE); } if(state_add(pentry_newfile, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &state_status) != STATE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } cache_status = CACHE_INODE_SUCCESS; if(AttrProvided == TRUE) /* Set the attribute if provided */ { if((cache_status = cache_inode_setattr(pentry_newfile, &sattr, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } } /* Set the openflags variable */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) openflags |= FSAL_O_RDONLY; if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_READ) openflags |= FSAL_O_WRONLY; if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) openflags = FSAL_O_RDWR; if(arg_OPEN4.share_access != 0) openflags = FSAL_O_RDWR; /* @todo : BUGAZOMEU : Something better later */ /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_newfile, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } break; case OPEN4_NOCREATE: /* It was not a creation, but a regular open */ /* The filehandle to the new file replaces the current filehandle */ if(pentry_newfile == NULL) { if((pentry_newfile = cache_inode_lookup(pentry_parent, &filename, &attr_newfile, data->ht, data->pclient, data->pcontext, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } } /* OPEN4 is to be done on a file */ if(pentry_newfile->internal_md.type != REGULAR_FILE) { if(pentry_newfile->internal_md.type == DIR_BEGINNING || pentry_newfile->internal_md.type == DIR_CONTINUE) { res_OPEN4.status = NFS4ERR_ISDIR; return res_OPEN4.status; } else if(pentry_newfile->internal_md.type == SYMBOLIC_LINK) { res_OPEN4.status = NFS4ERR_SYMLINK; return res_OPEN4.status; } else { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } } /* If the file is opened for write, OPEN4 while deny share write access, * in this case, check caller has write access to the file */ if(arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE) { if(cache_inode_access(pentry_newfile, write_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_WRONLY; } /* Same check on read: check for readability of a file before opening it for read */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ) { if(cache_inode_access(pentry_newfile, read_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDONLY; } /* Same check on write */ if(arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE) { if(cache_inode_access(pentry_newfile, write_access, data->ht, data->pclient, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } openflags = FSAL_O_RDWR; } /* Try to find if the same open_owner already has acquired a stateid for this file */ pstate_found_iterate = NULL; pstate_previous_iterate = NULL; do { state_iterate(pentry_newfile, &pstate_found_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &state_status); if(state_status == STATE_STATE_ERROR) break; /* Get out of the loop */ if(state_status == STATE_INVALID_ARGUMENT) { res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; } cache_status = CACHE_INODE_SUCCESS; /* Check is open_owner is the same */ if(pstate_found_iterate != NULL) { if((pstate_found_iterate->state_type == STATE_TYPE_SHARE) && (pstate_found_iterate->state_powner->so_owner.so_nfs4_owner.so_clientid == arg_OPEN4.owner.clientid) && ((pstate_found_iterate->state_powner->so_owner_len == arg_OPEN4.owner.owner.owner_len) && (!memcmp (arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->state_powner->so_owner_val, pstate_found_iterate->state_powner->so_owner_len)))) { /* We'll be re-using the found state */ pstate_found_same_owner = pstate_found_iterate; } else { /* This is a different owner, check for possible conflicts */ if(memcmp(arg_OPEN4.owner.owner.owner_val, pstate_found_iterate->state_powner->so_owner_val, pstate_found_iterate->state_powner->so_owner_len)) { if(pstate_found_iterate->state_type == STATE_TYPE_SHARE) { if((pstate_found_iterate->state_data.share. share_access & OPEN4_SHARE_ACCESS_WRITE) && (arg_OPEN4.share_deny & OPEN4_SHARE_DENY_WRITE)) { res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } } } } /* In all cases opening in read access a read denied file or write access to a write denied file * should fail, even if the owner is the same, see discussion in 14.2.16 and 8.9 */ if(pstate_found_iterate->state_type == STATE_TYPE_SHARE) { /* deny read access on read denied file */ if((pstate_found_iterate->state_data.share. share_deny & OPEN4_SHARE_DENY_READ) && (arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_READ)) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } /* deny write access on write denied file */ if((pstate_found_iterate->state_data.share. share_deny & OPEN4_SHARE_DENY_WRITE) && (arg_OPEN4.share_access & OPEN4_SHARE_ACCESS_WRITE)) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } } } /* if( pstate_found_iterate != NULL ) */ pstate_previous_iterate = pstate_found_iterate; } while(pstate_found_iterate != NULL); if(pstate_found_same_owner != NULL) { pfile_state = pstate_found_same_owner; pfile_state->state_seqid += 1; P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); } else { /* Set the state for the related file */ /* Prepare state management structure */ candidate_type = STATE_TYPE_SHARE; candidate_data.share.share_deny = arg_OPEN4.share_deny; candidate_data.share.share_access = arg_OPEN4.share_access; if(state_add(pentry_newfile, candidate_type, &candidate_data, powner, data->pclient, data->pcontext, &pfile_state, &state_status) != STATE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_SHARE_DENIED; return res_OPEN4.status; } } /* Open the file */ if(cache_inode_open_by_name(pentry_parent, &filename, pentry_newfile, data->pclient, openflags, data->pcontext, &cache_status) != CACHE_INODE_SUCCESS) { /* Seqid has to be incremented even in this case */ P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); res_OPEN4.status = NFS4ERR_ACCESS; return res_OPEN4.status; } break; default: /* Seqid has to be incremented even in this case */ if(powner != NULL) { P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); } res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; break; } /* switch( arg_OPEN4.openhow.opentype ) */ break; case CLAIM_PREVIOUS: break; default: /* Seqid has to be incremented even in this case */ if(powner != NULL) { P(powner->so_mutex); powner->so_owner.so_nfs4_owner.so_seqid += 1; V(powner->so_mutex); } res_OPEN4.status = NFS4ERR_INVAL; return res_OPEN4.status; break; } /* switch( arg_OPEN4.claim.claim ) */ /* Now produce the filehandle to this file */ if((pnewfsal_handle = cache_inode_get_fsal_handle(pentry_newfile, &cache_status)) == NULL) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } /* Allocation of a new file handle */ if((rc = nfs4_AllocateFH(&newfh4)) != NFS4_OK) { res_OPEN4.status = rc; return res_OPEN4.status; } /* Building a new fh */ if(!nfs4_FSALToFhandle(&newfh4, pnewfsal_handle, data)) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } /* This new fh replaces the current FH */ data->currentFH.nfs_fh4_len = newfh4.nfs_fh4_len; memcpy(data->currentFH.nfs_fh4_val, newfh4.nfs_fh4_val, newfh4.nfs_fh4_len); data->current_entry = pentry_newfile; data->current_filetype = REGULAR_FILE; /* No do not need newfh any more */ Mem_Free((char *)newfh4.nfs_fh4_val); /* Status of parent directory after the operation */ if((cache_status = cache_inode_getattr(pentry_parent, &attr_parent, data->ht, data->pclient, data->pcontext, &cache_status)) != CACHE_INODE_SUCCESS) { res_OPEN4.status = nfs4_Errno(cache_status); return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; if((res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val = (uint32_t *) Mem_Alloc(res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len * sizeof(uint32_t))) == NULL) { res_OPEN4.status = NFS4ERR_SERVERFAULT; return res_OPEN4.status; } res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val[0] = 0; /* No Attributes set */ res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_val[1] = 0; /* No Attributes set */ if(arg_OPEN4.openhow.opentype == OPEN4_CREATE) { tmp_int = 2; tmp_attr[0] = FATTR4_SIZE; tmp_attr[1] = FATTR4_MODE; nfs4_list_to_bitmap4(&(res_OPEN4.OPEN4res_u.resok4.attrset), &tmp_int, tmp_attr); res_OPEN4.OPEN4res_u.resok4.attrset.bitmap4_len = 2; } res_OPEN4.OPEN4res_u.resok4.cinfo.after = (changeid4) pentry_parent->internal_md.mod_time; res_OPEN4.OPEN4res_u.resok4.cinfo.atomic = TRUE; res_OPEN4.OPEN4res_u.resok4.stateid.seqid = powner->so_owner.so_nfs4_owner.so_seqid; memcpy(res_OPEN4.OPEN4res_u.resok4.stateid.other, pfile_state->stateid_other, OTHERSIZE); /* No delegation */ res_OPEN4.OPEN4res_u.resok4.delegation.delegation_type = OPEN_DELEGATE_NONE; res_OPEN4.OPEN4res_u.resok4.rflags = OPEN4_RESULT_LOCKTYPE_POSIX; /* regular exit */ res_OPEN4.status = NFS4_OK; return res_OPEN4.status; } /* nfs41_op_open */
int nfs4_op_read(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs4_op_read"; fsal_seek_t seek_descriptor; fsal_size_t size; fsal_size_t read_size; fsal_off_t offset; fsal_boolean_t eof_met; caddr_t bufferdata; cache_inode_status_t cache_status; state_status_t state_status; state_t * pstate_found = NULL; state_t * pstate_open = NULL; cache_content_status_t content_status; fsal_attrib_list_t attr; cache_entry_t * entry = NULL; state_t * pstate_iterate = NULL; state_t * pstate_previous_iterate = NULL; int rc = 0; cache_content_policy_data_t datapol; datapol.UseMaxCacheSize = FALSE; /* Say we are managing NFS4_OP_READ */ resp->resop = NFS4_OP_READ; res_READ4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_READ4.status = NFS4ERR_NOFILEHANDLE; return res_READ4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_READ4.status = NFS4ERR_BADHANDLE; return res_READ4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_READ4.status = NFS4ERR_FHEXPIRED; return res_READ4.status; } /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_read_xattr(op, data, resp); /* Manage access type MDONLY */ if(data->pexport->access_type == ACCESSTYPE_MDONLY) { res_READ4.status = NFS4ERR_DQUOT; return res_READ4.status; } /* Only files can be read */ if(data->current_filetype != REGULAR_FILE) { /* If the source is no file, return EISDIR if it is a directory and EINAVL otherwise */ if(data->current_filetype == DIR_BEGINNING || data->current_filetype == DIR_CONTINUE) res_READ4.status = NFS4ERR_ISDIR; else res_READ4.status = NFS4ERR_INVAL; return res_READ4.status; } /* vnode to manage is the current one */ entry = data->current_entry; /* Check stateid correctness and get pointer to state * (also checks for special stateids) */ if((rc = nfs4_Check_Stateid(&arg_READ4.stateid, data->current_entry, 0LL, &pstate_found, data, STATEID_SPECIAL_ANY, "READ")) != NFS4_OK) { res_READ4.status = rc; return res_READ4.status; } /* NB: After this points, if pstate_found == NULL, then the stateid is all-0 or all-1 */ if(pstate_found != NULL) { switch(pstate_found->state_type) { case STATE_TYPE_SHARE: pstate_open = pstate_found; break; case STATE_TYPE_LOCK: pstate_open = pstate_found->state_data.lock.popenstate; break; case STATE_TYPE_DELEG: pstate_open = NULL; break; default: res_READ4.status = NFS4ERR_BAD_STATEID; LogDebug(COMPONENT_NFS_V4_LOCK, "READ with invalid statid of type %d", (int) pstate_found->state_type); return res_READ4.status; } /* This is a read operation, this means that the file MUST have been opened for reading */ if(pstate_open != NULL && (pstate_open->state_data.share.share_access & OPEN4_SHARE_ACCESS_READ) == 0) { /* Bad open mode, return NFS4ERR_OPENMODE */ res_READ4.status = NFS4ERR_OPENMODE; LogDebug(COMPONENT_NFS_V4_LOCK, "READ state %p doesn't have OPEN4_SHARE_ACCESS_READ", pstate_found); return res_READ4.status; } /* If NFSv4::Use_OPEN_CONFIRM is set to TRUE in the configuration file, check is state is confirmed */ if(nfs_param.nfsv4_param.use_open_confirm == TRUE) { if(pstate_found->state_powner->so_owner.so_nfs4_owner.so_confirmed == FALSE) { res_READ4.status = NFS4ERR_BAD_STATEID; return res_READ4.status; } } } /* Iterate through file's state to look for conflicts */ pstate_iterate = NULL; pstate_previous_iterate = NULL; do { state_iterate(data->current_entry, &pstate_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &state_status); if(state_status == STATE_STATE_ERROR) break; /* Get out of the loop */ if(state_status == STATE_INVALID_ARGUMENT) { res_READ4.status = NFS4ERR_INVAL; return res_READ4.status; } cache_status = CACHE_INODE_SUCCESS; if(pstate_iterate != NULL) { if(pstate_iterate->state_type == STATE_TYPE_SHARE) { if(pstate_found != pstate_iterate && pstate_open != pstate_iterate) { if(pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_READ) { /* Writing to this file if prohibited, file is write-denied */ LogDebug(COMPONENT_NFS_V4_LOCK, "READ is denied by state %p", pstate_iterate); res_READ4.status = NFS4ERR_LOCKED; return res_READ4.status; } } } } pstate_previous_iterate = pstate_iterate; } while(pstate_iterate != NULL); /* Get the size and offset of the read operation */ offset = arg_READ4.offset; size = arg_READ4.count; LogFullDebug(COMPONENT_NFS_V4, " NFS4_OP_READ: offset = %"PRIu64" length = %llu", offset, size); if((data->pexport->options & EXPORT_OPTION_MAXOFFSETREAD) == EXPORT_OPTION_MAXOFFSETREAD) if((fsal_off_t) (offset + size) > data->pexport->MaxOffsetRead) { res_READ4.status = NFS4ERR_DQUOT; return res_READ4.status; } /* Do not read more than FATTR4_MAXREAD */ if((data->pexport->options & EXPORT_OPTION_MAXREAD) == EXPORT_OPTION_MAXREAD && size > data->pexport->MaxRead) { /* the client asked for too much data, * this should normally not happen because * client will get FATTR4_MAXREAD value at mount time */ size = data->pexport->MaxRead; } /* If size == 0 , no I/O is to be made and everything is alright */ if(size == 0) { res_READ4.READ4res_u.resok4.eof = FALSE; /* end of file was not reached because READ occured, and a size = 0 can not lead to eof */ res_READ4.READ4res_u.resok4.data.data_len = 0; res_READ4.READ4res_u.resok4.data.data_val = NULL; res_READ4.status = NFS4_OK; return res_READ4.status; } if((data->pexport->options & EXPORT_OPTION_USE_DATACACHE) && (entry->object.file.pentry_content == NULL)) { /* Entry is not in datacache, but should be in, cache it . * Several threads may call this function at the first time and a race condition can occur here * in order to avoid this, cache_inode_add_data_cache is "mutex protected" * The first call will create the file content cache entry, the further will return * with error CACHE_INODE_CACHE_CONTENT_EXISTS which is not a pathological thing here */ datapol.UseMaxCacheSize = data->pexport->options & EXPORT_OPTION_MAXCACHESIZE; datapol.MaxCacheSize = data->pexport->MaxCacheSize; /* Status is set in last argument */ cache_inode_add_data_cache(entry, data->ht, data->pclient, data->pcontext, &cache_status); if((cache_status != CACHE_INODE_SUCCESS) && (cache_content_cache_behaviour(entry, &datapol, (cache_content_client_t *) (data->pclient-> pcontent_client), &content_status) == CACHE_CONTENT_FULLY_CACHED) && (cache_status != CACHE_INODE_CACHE_CONTENT_EXISTS)) { res_READ4.status = NFS4ERR_SERVERFAULT; return res_READ4.status; } } /* Some work is to be done */ if((bufferdata = (char *)Mem_Alloc(size)) == NULL) { res_READ4.status = NFS4ERR_SERVERFAULT; return res_READ4.status; } memset((char *)bufferdata, 0, size); seek_descriptor.whence = FSAL_SEEK_SET; seek_descriptor.offset = offset; if(cache_inode_rdwr(entry, CACHE_CONTENT_READ, &seek_descriptor, size, &read_size, &attr, bufferdata, &eof_met, data->ht, data->pclient, data->pcontext, TRUE, &cache_status) != CACHE_INODE_SUCCESS) { res_READ4.status = nfs4_Errno(cache_status); return res_READ4.status; } /* What is the filesize ? */ if((offset + read_size) > attr.filesize) res_READ4.READ4res_u.resok4.eof = TRUE; 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 = %llu eof=%u", offset, read_size, eof_met); /* Is EOF met or not ? */ if(eof_met == TRUE) res_READ4.READ4res_u.resok4.eof = TRUE; else res_READ4.READ4res_u.resok4.eof = FALSE; /* Say it is ok */ res_READ4.status = NFS4_OK; return res_READ4.status; } /* nfs4_op_read */
int nfs41_op_write(struct nfs_argop4 *op, compound_data_t * data, struct nfs_resop4 *resp) { char __attribute__ ((__unused__)) funcname[] = "nfs41_op_write"; fsal_seek_t seek_descriptor; fsal_size_t size; fsal_size_t written_size; fsal_off_t offset; fsal_boolean_t eof_met; bool_t stable_flag = TRUE; caddr_t bufferdata; stable_how4 stable_how; cache_content_status_t content_status; state_t * pstate_found = NULL; cache_inode_status_t cache_status; state_status_t state_status; fsal_attrib_list_t attr; cache_entry_t * entry = NULL; state_t * pstate_iterate = NULL; state_t * pstate_previous_iterate = NULL; cache_content_policy_data_t datapol; datapol.UseMaxCacheSize = FALSE; /* Lock are not supported */ resp->resop = NFS4_OP_WRITE; res_WRITE4.status = NFS4_OK; /* If there is no FH */ if(nfs4_Is_Fh_Empty(&(data->currentFH))) { res_WRITE4.status = NFS4ERR_NOFILEHANDLE; return res_WRITE4.status; } /* If the filehandle is invalid */ if(nfs4_Is_Fh_Invalid(&(data->currentFH))) { res_WRITE4.status = NFS4ERR_BADHANDLE; return res_WRITE4.status; } /* Tests if the Filehandle is expired (for volatile filehandle) */ if(nfs4_Is_Fh_Expired(&(data->currentFH))) { res_WRITE4.status = NFS4ERR_FHEXPIRED; return res_WRITE4.status; } /* If Filehandle points to a xattr object, manage it via the xattrs specific functions */ if(nfs4_Is_Fh_Xattr(&(data->currentFH))) return nfs4_op_write_xattr(op, data, resp); /* Manage access type MDONLY */ if(data->pexport->access_type == ACCESSTYPE_MDONLY) { res_WRITE4.status = NFS4ERR_DQUOT; return res_WRITE4.status; } /* vnode to manage is the current one */ entry = data->current_entry; /* Check for special stateid */ if(!memcmp((char *)all_zero, arg_WRITE4.stateid.other, OTHERSIZE) && arg_WRITE4.stateid.seqid == 0) { /* "All 0 stateid special case", see RFC3530 page 220-221 for details * This will be treated as a client that held no lock at all, * I set pstate_found to NULL to remember this situation later */ pstate_found = NULL; } else if(!memcmp((char *)all_one, arg_WRITE4.stateid.other, OTHERSIZE) && arg_WRITE4.stateid.seqid == 0xFFFFFFFF) { /* "All 1 stateid special case", see RFC3530 page 220-221 for details * This will be treated as a client that held no lock at all, * I set pstate_found to NULL to remember this situation later */ pstate_found = NULL; } /* NB: After this points, if pstate_found == NULL, then the stateid is all-0 or all-1 */ /* Iterate through file's state to look for conflicts */ pstate_iterate = NULL; pstate_previous_iterate = NULL; do { state_iterate(data->current_entry, &pstate_iterate, pstate_previous_iterate, data->pclient, data->pcontext, &state_status); if(state_status == STATE_STATE_ERROR) break; /* Get out of the loop */ if(state_status == STATE_INVALID_ARGUMENT) { res_WRITE4.status = NFS4ERR_INVAL; return res_WRITE4.status; } cache_status = CACHE_INODE_SUCCESS; if(pstate_iterate != NULL) { if(pstate_iterate->state_type == STATE_TYPE_SHARE) { if(pstate_found != pstate_iterate) { if(pstate_iterate->state_data.share.share_deny & OPEN4_SHARE_DENY_WRITE) { /* Writing to this file if prohibited, file is write-denied */ res_WRITE4.status = NFS4ERR_LOCKED; return res_WRITE4.status; } } } } pstate_previous_iterate = pstate_iterate; } while(pstate_iterate != NULL); /* Only files can be written */ if(data->current_filetype != REGULAR_FILE) { /* If the destination is no file, return EISDIR if it is a directory and EINAVL otherwise */ if(data->current_filetype == DIR_BEGINNING || data->current_filetype == DIR_CONTINUE) res_WRITE4.status = NFS4ERR_ISDIR; else res_WRITE4.status = NFS4ERR_INVAL; return res_WRITE4.status; } /* 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, "NFS4_OP_WRITE: offset = %llu length = %llu stable = %d", (unsigned long long)offset, size, stable_how); if((data->pexport->options & EXPORT_OPTION_MAXOFFSETWRITE) == EXPORT_OPTION_MAXOFFSETWRITE) if((fsal_off_t) (offset + size) > data->pexport->MaxOffsetWrite) { res_WRITE4.status = NFS4ERR_DQUOT; return res_WRITE4.status; } /* The size to be written should not be greater than FATTR4_MAXWRITESIZE because this value is asked * by the client at mount time, but we check this by security */ if(((data->pexport->options & EXPORT_OPTION_MAXWRITE) == EXPORT_OPTION_MAXWRITE) && size > data->pexport->MaxWrite) { /* * The client asked for too much data, we * must restrict him */ size = data->pexport->MaxWrite; } /* Where are the data ? */ bufferdata = arg_WRITE4.data.data_val; LogFullDebug(COMPONENT_NFS_V4, "NFS4_OP_WRITE: offset = %llu length = %llu", (unsigned long long)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; memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier, sizeof(verifier4)); res_WRITE4.status = NFS4_OK; return res_WRITE4.status; } if((data->pexport->options & EXPORT_OPTION_USE_DATACACHE) && (cache_content_cache_behaviour(entry, &datapol, (cache_content_client_t *) (data->pclient-> pcontent_client), &content_status) == CACHE_CONTENT_FULLY_CACHED) && (entry->object.file.pentry_content == NULL)) { /* Entry is not in datacache, but should be in, cache it . * Several threads may call this function at the first time and a race condition can occur here * in order to avoid this, cache_inode_add_data_cache is "mutex protected" * The first call will create the file content cache entry, the further will return * with error CACHE_INODE_CACHE_CONTENT_EXISTS which is not a pathological thing here */ datapol.UseMaxCacheSize = data->pexport->options & EXPORT_OPTION_MAXCACHESIZE; datapol.MaxCacheSize = data->pexport->MaxCacheSize; /* Status is set in last argument */ cache_inode_add_data_cache(entry, data->ht, data->pclient, data->pcontext, &cache_status); if((cache_status != CACHE_INODE_SUCCESS) && (cache_status != CACHE_INODE_CACHE_CONTENT_EXISTS)) { res_WRITE4.status = NFS4ERR_SERVERFAULT; return res_WRITE4.status; } } if((nfs_param.core_param.use_nfs_commit == TRUE) && (arg_WRITE4.stable == UNSTABLE4)) { stable_flag = FALSE; } else { stable_flag = TRUE; } /* An actual write is to be made, prepare it */ /* only FILE_SYNC mode is supported */ /* Set up uio to define the transfer */ seek_descriptor.whence = FSAL_SEEK_SET; seek_descriptor.offset = offset; if(cache_inode_rdwr(entry, CACHE_CONTENT_WRITE, &seek_descriptor, size, &written_size, &attr, bufferdata, &eof_met, data->ht, data->pclient, data->pcontext, stable_flag, &cache_status) != CACHE_INODE_SUCCESS) { res_WRITE4.status = nfs4_Errno(cache_status); return res_WRITE4.status; } /* Set the returned value */ if(stable_flag == TRUE) res_WRITE4.WRITE4res_u.resok4.committed = FILE_SYNC4; else res_WRITE4.WRITE4res_u.resok4.committed = UNSTABLE4; res_WRITE4.WRITE4res_u.resok4.count = written_size; memcpy(res_WRITE4.WRITE4res_u.resok4.writeverf, NFS4_write_verifier, sizeof(verifier4)); res_WRITE4.status = NFS4_OK; return res_WRITE4.status; } /* nfs41_op_write */