/** * @brief Set the attributes for a file. * * This function sets the attributes of a file, both in the cache and * in the underlying filesystem. * * @param[in] entry Entry whose attributes are to be set * @param[in,out] attr Attributes to set/result of set * * @retval CACHE_INODE_SUCCESS if operation is a success */ cache_inode_status_t cache_inode_setattr(cache_entry_t *entry, struct attrlist *attr, bool is_open_write) { struct fsal_obj_handle *obj_handle = entry->obj_handle; fsal_status_t fsal_status = { 0, 0 }; fsal_acl_t *saved_acl = NULL; fsal_acl_status_t acl_status = 0; cache_inode_status_t status = CACHE_INODE_SUCCESS; uint64_t before; const struct user_cred *creds = op_ctx->creds; /* True if we have taken the content lock on 'entry' */ bool content_locked = false; if ((attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) && (entry->type != REGULAR_FILE)) { LogWarn(COMPONENT_CACHE_INODE, "Attempt to truncate non-regular file: type=%d", entry->type); status = CACHE_INODE_BAD_TYPE; goto out; } /* Is it allowed to change times ? */ if (!op_ctx->fsal_export->exp_ops.fs_supports(op_ctx->fsal_export, fso_cansettime) && (FSAL_TEST_MASK (attr->mask, (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME)))) { status = CACHE_INODE_INVALID_ARGUMENT; goto out; } /* Get wrlock on attr_lock and verify attrs */ status = cache_inode_lock_trust_attrs(entry, true); if (status != CACHE_INODE_SUCCESS) return status; /* Do permission checks */ status = cache_inode_check_setattr_perms(entry, attr, is_open_write); if (status != CACHE_INODE_SUCCESS) goto unlock; if (attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); content_locked = true; } /* Test for the following condition from chown(2): * * When the owner or group of an executable file are changed by an * unprivileged user the S_ISUID and S_ISGID mode bits are cleared. * POSIX does not specify whether this also should happen when * root does the chown(); the Linux behavior depends on the kernel * version. In case of a non-group-executable file (i.e., one for * which the S_IXGRP bit is not set) the S_ISGID bit indicates * mandatory locking, and is not cleared by a chown(). * */ if (creds->caller_uid != 0 && (FSAL_TEST_MASK(attr->mask, ATTR_OWNER) || FSAL_TEST_MASK(attr->mask, ATTR_GROUP)) && ((entry->obj_handle->attrs->mode & (S_IXOTH | S_IXUSR | S_IXGRP)) != 0) && ((entry->obj_handle->attrs->mode & (S_ISUID | S_ISGID)) != 0)) { /* Non-priviledged user changing ownership on an executable * file with S_ISUID or S_ISGID bit set, need to be cleared. */ if (!FSAL_TEST_MASK(attr->mask, ATTR_MODE)) { /* Mode wasn't being set, so set it now, start with * the current attributes. */ attr->mode = entry->obj_handle->attrs->mode; FSAL_SET_MASK(attr->mask, ATTR_MODE); } /* Don't clear S_ISGID if the file isn't group executable. * In that case, S_ISGID indicates mandatory locking and * is not cleared by chown. */ if ((entry->obj_handle->attrs->mode & S_IXGRP) != 0) attr->mode &= ~S_ISGID; /* Clear S_ISUID. */ attr->mode &= ~S_ISUID; } /* Test for the following condition from chmod(2): * * If the calling process is not privileged (Linux: does not have * the CAP_FSETID capability), and the group of the file does not * match the effective group ID of the process or one of its * supplementary group IDs, the S_ISGID bit will be turned off, * but this will not cause an error to be returned. * * We test the actual mode being set before testing for group * membership since that is a bit more expensive. */ if (creds->caller_uid != 0 && FSAL_TEST_MASK(attr->mask, ATTR_MODE) && (attr->mode & S_ISGID) != 0 && not_in_group_list(entry->obj_handle->attrs->group)) { /* Clear S_ISGID */ attr->mode &= ~S_ISGID; } saved_acl = entry->obj_handle->attrs->acl; before = entry->obj_handle->attrs->change; fsal_status = obj_handle->obj_ops.setattrs(obj_handle, attr); if (FSAL_IS_ERROR(fsal_status)) { status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from setattrs"); cache_inode_kill_entry(entry); } goto unlock; } fsal_status = obj_handle->obj_ops.getattrs(obj_handle); *attr = *entry->obj_handle->attrs; if (FSAL_IS_ERROR(fsal_status)) { status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from getattrs"); cache_inode_kill_entry(entry); } goto unlock; } if (before == entry->obj_handle->attrs->change) entry->obj_handle->attrs->change++; /* Decrement refcount on saved ACL */ nfs4_acl_release_entry(saved_acl, &acl_status); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "Failed to release old acl, status=%d", acl_status); cache_inode_fixup_md(entry); /* Copy the complete set of new attributes out. */ *attr = *entry->obj_handle->attrs; status = CACHE_INODE_SUCCESS; unlock: if (content_locked) PTHREAD_RWLOCK_unlock(&entry->content_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); out: return status; }
/** * @brief Set the attributes for a file. * * This function sets the attributes of a file, both in the cache and * in the underlying filesystem. * * @param[in] entry Entry whose attributes are to be set * @param[in,out] attr Attributes to set/result of set * * @retval CACHE_INODE_SUCCESS if operation is a success */ cache_inode_status_t cache_inode_setattr(cache_entry_t *entry, struct attrlist *attr, bool is_open_write) { struct fsal_obj_handle *obj_handle = entry->obj_handle; fsal_status_t fsal_status = { 0, 0 }; fsal_acl_t *saved_acl = NULL; fsal_acl_status_t acl_status = 0; cache_inode_status_t status = CACHE_INODE_SUCCESS; uint64_t before; /* True if we have taken the content lock on 'entry' */ bool content_locked = false; if ((attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) && (entry->type != REGULAR_FILE)) { LogWarn(COMPONENT_CACHE_INODE, "Attempt to truncate non-regular file: type=%d", entry->type); status = CACHE_INODE_BAD_TYPE; } /* Is it allowed to change times ? */ if (!op_ctx->fsal_export->ops->fs_supports(op_ctx->fsal_export, fso_cansettime) && (FSAL_TEST_MASK (attr->mask, (ATTR_ATIME | ATTR_CREATION | ATTR_CTIME | ATTR_MTIME)))) { status = CACHE_INODE_INVALID_ARGUMENT; goto out; } /* Get wrlock on attr_lock and verify attrs */ status = cache_inode_lock_trust_attrs(entry, true); if (status != CACHE_INODE_SUCCESS) return status; /* Do permission checks */ status = cache_inode_check_setattr_perms(entry, attr, is_open_write); if (status != CACHE_INODE_SUCCESS) goto unlock; if (attr->mask & (ATTR_SIZE | ATTR4_SPACE_RESERVED)) { PTHREAD_RWLOCK_wrlock(&entry->content_lock); content_locked = true; } saved_acl = obj_handle->attributes.acl; before = obj_handle->attributes.change; fsal_status = obj_handle->ops->setattrs(obj_handle, attr); if (FSAL_IS_ERROR(fsal_status)) { status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from truncate"); cache_inode_kill_entry(entry); } goto unlock; } fsal_status = obj_handle->ops->getattrs(obj_handle); *attr = obj_handle->attributes; if (FSAL_IS_ERROR(fsal_status)) { status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from setattrs"); cache_inode_kill_entry(entry); } goto unlock; } if (before == obj_handle->attributes.change) obj_handle->attributes.change++; /* Decrement refcount on saved ACL */ nfs4_acl_release_entry(saved_acl, &acl_status); if (acl_status != NFS_V4_ACL_SUCCESS) LogCrit(COMPONENT_CACHE_INODE, "Failed to release old acl, status=%d", acl_status); cache_inode_fixup_md(entry); /* Copy the complete set of new attributes out. */ *attr = entry->obj_handle->attributes; status = CACHE_INODE_SUCCESS; unlock: if (content_locked) PTHREAD_RWLOCK_unlock(&entry->content_lock); PTHREAD_RWLOCK_unlock(&entry->attr_lock); out: return status; }
cache_inode_status_t cache_inode_setattr(cache_entry_t *entry, fsal_attrib_list_t *attr, fsal_op_context_t *context, int is_open_write, cache_inode_status_t *status) { fsal_status_t fsal_status = {0, 0}; int got_content_lock = FALSE; #ifdef _USE_NFS4_ACL fsal_acl_t *saved_acl = NULL; fsal_acl_status_t acl_status = 0; #endif /* _USE_NFS4_ACL */ if ((entry->type == UNASSIGNED) || (entry->type == RECYCLED)) { LogWarn(COMPONENT_CACHE_INODE, "WARNING: unknown source entry type: type=%d, " "line %d in file %s", entry->type, __LINE__, __FILE__); *status = CACHE_INODE_BAD_TYPE; goto out; } if ((attr->asked_attributes & FSAL_ATTR_SIZE) && (entry->type != REGULAR_FILE)) { LogWarn(COMPONENT_CACHE_INODE, "Attempt to truncate non-regular file: type=%d", entry->type); *status = CACHE_INODE_BAD_TYPE; goto out; } /* Get wrlock on attr_lock and verify attrs */ *status = cache_inode_lock_trust_attrs(entry, context, TRUE); if(*status != CACHE_INODE_SUCCESS) return *status; /* Do permission checks */ if(cache_inode_check_setattr_perms(entry, attr, context, is_open_write, status) != CACHE_INODE_SUCCESS) { goto unlock; } if (attr->asked_attributes & FSAL_ATTR_SIZE) { PTHREAD_RWLOCK_WRLOCK(&entry->content_lock); got_content_lock = TRUE; } #ifdef _USE_NFS4_ACL saved_acl = entry->attributes.acl; #endif /* _USE_NFS4_ACL */ fsal_status = FSAL_setattrs(&entry->handle, context, attr, &entry->attributes); if (FSAL_IS_ERROR(fsal_status)) { *status = cache_inode_error_convert(fsal_status); if (fsal_status.major == ERR_FSAL_STALE) { LogEvent(COMPONENT_CACHE_INODE, "FSAL returned STALE from setattrs"); cache_inode_kill_entry(entry); } goto unlock; } else { #ifdef _USE_NFS4_ACL /* Decrement refcount on saved ACL */ nfs4_acl_release_entry(saved_acl, &acl_status); if (acl_status != NFS_V4_ACL_SUCCESS) { LogCrit(COMPONENT_CACHE_INODE, "Failed to release old acl, status=%d", acl_status); } #endif /* _USE_NFS4_ACL */ } cache_inode_fixup_md(entry); /* Copy the complete set of new attributes out. */ *attr = entry->attributes; set_mounted_on_fileid(entry, attr, context->export_context->fe_export); *status = CACHE_INODE_SUCCESS; unlock: if(got_content_lock) { PTHREAD_RWLOCK_UNLOCK(&entry->content_lock); } PTHREAD_RWLOCK_UNLOCK(&entry->attr_lock); out: return *status; } /* cache_inode_setattr */