static inline int make_file_safe(struct vfs_fsal_obj_handle *dir_hdl, const struct req_op_context *opctx, int dir_fd, const char *name, mode_t unix_mode, uid_t user, gid_t group, struct vfs_fsal_obj_handle **hdl) { int retval; struct stat stat; vfs_file_handle_t *fh; vfs_alloc_handle(fh); retval = fchownat(dir_fd, name, user, group, AT_SYMLINK_NOFOLLOW); if (retval < 0) goto fileerr; /* now that it is owned properly, set accessible mode */ retval = fchmodat(dir_fd, name, unix_mode, 0); if (retval < 0) goto fileerr; retval = vfs_name_to_handle(dir_fd, dir_hdl->obj_handle.fs, name, fh); if (retval < 0) goto fileerr; retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) goto fileerr; /* allocate an obj_handle and fill it up */ *hdl = alloc_handle(dir_fd, fh, dir_hdl->obj_handle.fs, &stat, dir_hdl->handle, name, opctx->fsal_export); if (*hdl == NULL) return ENOMEM; return 0; fileerr: retval = errno; return retval; }
fsal_status_t vfs_open2(struct fsal_obj_handle *obj_hdl, struct state_t *state, fsal_openflags_t openflags, enum fsal_create_mode createmode, const char *name, struct attrlist *attrib_set, fsal_verifier_t verifier, struct fsal_obj_handle **new_obj, struct attrlist *attrs_out, bool *caller_perm_check) { int posix_flags = 0; int fd, dir_fd; int retval = 0; mode_t unix_mode; fsal_status_t status = {0, 0}; struct vfs_fd *my_fd = NULL; struct vfs_fsal_obj_handle *myself, *hdl = NULL; struct stat stat; vfs_file_handle_t *fh = NULL; bool truncated; bool created = false; if (state != NULL) my_fd = (struct vfs_fd *)(state + 1); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); LogAttrlist(COMPONENT_FSAL, NIV_FULL_DEBUG, "attrib_set ", attrib_set, false); fsal2posix_openflags(openflags, &posix_flags); truncated = (posix_flags & O_TRUNC) != 0; LogFullDebug(COMPONENT_FSAL, truncated ? "Truncate" : "No truncate"); if (createmode >= FSAL_EXCLUSIVE) { /* Now fixup attrs for verifier if exclusive create */ set_common_verifier(attrib_set, verifier); } if (name == NULL) { /* This is an open by handle */ struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal->name); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } if (state != NULL) { /* Prepare to take the share reservation, but only if we * are called with a valid state (if state is NULL the * caller is a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); /* Check share reservation conflicts. */ status = check_share_conflict(&myself->u.file.share, openflags, false); if (FSAL_IS_ERROR(status)) { PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* Take the share reservation now by updating the * counters. */ update_share_counters(&myself->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); } else { /* We need to use the global fd to continue, and take * the lock to protect it. */ my_fd = &hdl->u.file.fd; PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); } status = vfs_open_my_fd(myself, openflags, posix_flags, my_fd); if (FSAL_IS_ERROR(status)) { if (state == NULL) { /* Release the lock taken above, and return * since there is nothing to undo. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } else { /* Error - need to release the share */ goto undo_share; } } if (createmode >= FSAL_EXCLUSIVE || truncated) { /* Refresh the attributes */ struct stat stat; retval = fstat(my_fd->fd, &stat); if (retval == 0) { LogFullDebug(COMPONENT_FSAL, "New size = %" PRIx64, stat.st_size); } else { if (errno == EBADF) errno = ESTALE; status = fsalstat(posix2fsal_error(errno), errno); } /* Now check verifier for exclusive, but not for * FSAL_EXCLUSIVE_9P. */ if (!FSAL_IS_ERROR(status) && createmode >= FSAL_EXCLUSIVE && createmode != FSAL_EXCLUSIVE_9P && !check_verifier_stat(&stat, verifier)) { /* Verifier didn't match, return EEXIST */ status = fsalstat(posix2fsal_error(EEXIST), EEXIST); } } if (state == NULL) { /* If no state, release the lock taken above and return * status. */ PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } if (!FSAL_IS_ERROR(status)) { /* Return success. */ return status; } (void) vfs_close_my_fd(my_fd); undo_share: /* Can only get here with state not NULL and an error */ /* On error we need to release our share reservation * and undo the update of the share counters. * This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&obj_hdl->lock); update_share_counters(&myself->u.file.share, openflags, FSAL_O_CLOSED); PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; } /* In this path where we are opening by name, we can't check share * reservation yet since we don't have an object_handle yet. If we * indeed create the object handle (there is no race with another * open by name), then there CAN NOT be a share conflict, otherwise * the share conflict will be resolved when the object handles are * merged. */ #ifdef ENABLE_VFS_DEBUG_ACL if (createmode != FSAL_NO_CREATE) { /* Need to ammend attributes for inherited ACL, these will be * set later. We also need to test for permission to create * since there might be an ACL. */ struct attrlist attrs; fsal_accessflags_t access_type; access_type = FSAL_MODE_MASK_SET(FSAL_W_OK) | FSAL_ACE4_MASK_SET(FSAL_ACE_PERM_ADD_FILE); status = obj_hdl->obj_ops.test_access(obj_hdl, access_type, NULL, NULL, false); if (FSAL_IS_ERROR(status)) return status; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status.major = fsal_inherit_acls(attrib_set, attrs.acl, FSAL_ACE_FLAG_FILE_INHERIT); /* Done with the attrs */ fsal_release_attrs(&attrs); if (FSAL_IS_ERROR(status)) return status; } #endif /* ENABLE_VFS_DEBUG_ACL */ if (createmode != FSAL_NO_CREATE) { /* Now add in O_CREAT and O_EXCL. */ posix_flags |= O_CREAT; /* And if we are at least FSAL_GUARDED, do an O_EXCL create. */ if (createmode >= FSAL_GUARDED) posix_flags |= O_EXCL; /* Fetch the mode attribute to use in the openat system call. */ unix_mode = fsal2unix_mode(attrib_set->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); /* Don't set the mode if we later set the attributes */ FSAL_UNSET_MASK(attrib_set->mask, ATTR_MODE); } if (createmode == FSAL_UNCHECKED && (attrib_set->mask != 0)) { /* If we have FSAL_UNCHECKED and want to set more attributes * than the mode, we attempt an O_EXCL create first, if that * succeeds, then we will be allowed to set the additional * attributes, otherwise, we don't know we created the file * and this can NOT set the attributes. */ posix_flags |= O_EXCL; } dir_fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &status.major); if (dir_fd < 0) return fsalstat(status.major, -dir_fd); /** @todo: not sure what this accomplishes... */ retval = vfs_stat_by_handle(dir_fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Become the user because we are creating an object in this dir. */ if (createmode != FSAL_NO_CREATE) fsal_set_credentials(op_ctx->creds); if ((posix_flags & O_CREAT) != 0) fd = openat(dir_fd, name, posix_flags, unix_mode); else fd = openat(dir_fd, name, posix_flags); if (fd == -1 && errno == EEXIST && createmode == FSAL_UNCHECKED) { /* We tried to create O_EXCL to set attributes and failed. * Remove O_EXCL and retry. We still try O_CREAT again just in * case file disappears out from under us. * * Note that because we have dropped O_EXCL, later on we will * not assume we created the file, and thus will not set * additional attributes. We don't need to separately track * the condition of not wanting to set attributes. */ posix_flags &= ~O_EXCL; fd = openat(dir_fd, name, posix_flags, unix_mode); } /* Preserve errno */ retval = errno; /* If we were creating, restore credentials now. */ if (createmode != FSAL_NO_CREATE) fsal_restore_ganesha_credentials(); if (fd < 0) { status = fsalstat(posix2fsal_error(retval), retval); goto direrr; } /* Remember if we were responsible for creating the file. * Note that in an UNCHECKED retry we MIGHT have re-created the * file and won't remember that. Oh well, so in that rare case we * leak a partially created file if we have a subsequent error in here. * Also notify caller to do permission check if we DID NOT create the * file. Note it IS possible in the case of a race between an UNCHECKED * open and an external unlink, we did create the file, but we will * still force a permission check. Of course that permission check * SHOULD succeed since we also won't set the mode the caller requested * and the default file create permissions SHOULD allow the owner * read/write. */ created = (posix_flags & O_EXCL) != 0; *caller_perm_check = !created; vfs_alloc_handle(fh); retval = vfs_name_to_handle(dir_fd, obj_hdl->fs, name, fh); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } retval = fstat(fd, &stat); if (retval < 0) { retval = errno; status = fsalstat(posix2fsal_error(retval), retval); goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, obj_hdl->fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { status = fsalstat(posix2fsal_error(ENOMEM), ENOMEM); goto fileerr; } /* If we didn't have a state above, use the global fd. At this point, * since we just created the global fd, no one else can have a * reference to it, and thus we can mamnipulate unlocked which is * handy since we can then call setattr2 which WILL take the lock * without a double locking deadlock. */ if (my_fd == NULL) my_fd = &hdl->u.file.fd; my_fd->fd = fd; my_fd->openflags = openflags; *new_obj = &hdl->obj_handle; if (created && attrib_set->mask != 0) { /* Set attributes using our newly opened file descriptor as the * share_fd if there are any left to set (mode and truncate * have already been handled). * * Note that we only set the attributes if we were responsible * for creating the file and we have attributes to set. * * Note if we have ENABLE_VFS_DEBUG_ACL an inherited ACL might * be part of the attributes we are setting here. */ status = (*new_obj)->obj_ops.setattr2(*new_obj, false, state, attrib_set); if (FSAL_IS_ERROR(status)) { /* Release the handle we just allocated. */ (*new_obj)->obj_ops.release(*new_obj); *new_obj = NULL; goto fileerr; } if (attrs_out != NULL) { status = (*new_obj)->obj_ops.getattrs(*new_obj, attrs_out); if (FSAL_IS_ERROR(status) && (attrs_out->mask & ATTR_RDATTR_ERR) == 0) { /* Get attributes failed and caller expected * to get the attributes. Otherwise continue * with attrs_out indicating ATTR_RDATTR_ERR. */ goto fileerr; } } } else if (attrs_out != NULL) { /* Since we haven't set any attributes other than what was set * on create (if we even created), just use the stat results * we used to create the fsal_obj_handle. */ posix2fsal_attributes(&stat, attrs_out); /* Make sure ATTR_RDATTR_ERR is cleared on success. */ attrs_out->mask &= ~ATTR_RDATTR_ERR; } close(dir_fd); if (state != NULL) { /* Prepare to take the share reservation, but only if we are * called with a valid state (if state is NULL the caller is * a stateless create such as NFS v3 CREATE). */ /* This can block over an I/O operation. */ PTHREAD_RWLOCK_wrlock(&(*new_obj)->lock); /* Take the share reservation now by updating the counters. */ update_share_counters(&hdl->u.file.share, FSAL_O_CLOSED, openflags); PTHREAD_RWLOCK_unlock(&(*new_obj)->lock); } return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: close(fd); /* Delete the file if we actually created it. */ if (created) unlinkat(dir_fd, name, 0); direrr: close(dir_fd); return fsalstat(posix2fsal_error(retval), retval); }
static fsal_status_t makesymlink(struct fsal_obj_handle *dir_hdl, const char *name, const char *link_path, struct attrlist *attrib, struct fsal_obj_handle **handle) { struct vfs_fsal_obj_handle *myself, *hdl; int dir_fd = -1; struct stat stat; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; int flags = O_PATH | O_NOACCESS; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); LogDebug(COMPONENT_FSAL, "create %s", name); *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); } myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } dir_fd = vfs_fsal_open(myself, flags, &fsal_error); if (dir_fd < 0) return fsalstat(fsal_error, -dir_fd); flags |= O_NOFOLLOW; /* BSD needs O_NOFOLLOW for * fhopen() of symlinks */ retval = vfs_stat_by_handle(dir_fd, myself->handle, &stat, flags); if (retval < 0) { retval = errno; goto direrr; } /* Become the user because we are creating an object in this dir. */ fsal_set_credentials(op_ctx->creds); retval = symlinkat(link_path, dir_fd, name); if (retval < 0) { retval = errno; fsal_restore_ganesha_credentials(); goto direrr; } fsal_restore_ganesha_credentials(); retval = vfs_name_to_handle(dir_fd, dir_hdl->fs, name, fh); if (retval < 0) { retval = errno; goto linkerr; } /* now get attributes info, * being careful to get the link, not the target */ retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; goto linkerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, dir_hdl->fs, &stat, NULL, name, op_ctx->fsal_export); if (hdl == NULL) { retval = ENOMEM; goto linkerr; } *handle = &hdl->obj_handle; close(dir_fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); linkerr: unlinkat(dir_fd, name, 0); direrr: close(dir_fd); hdlerr: if (retval == ENOENT) fsal_error = ERR_FSAL_STALE; else fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); }
static fsal_status_t makedir(struct fsal_obj_handle *dir_hdl, const char *name, struct attrlist *attrib, struct fsal_obj_handle **handle) { struct vfs_fsal_obj_handle *myself, *hdl; int dir_fd; struct stat stat; mode_t unix_mode; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; int flags = O_PATH | O_NOACCESS; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); LogDebug(COMPONENT_FSAL, "create %s", name); *handle = NULL; /* poison it */ if (!dir_hdl->obj_ops.handle_is(dir_hdl, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", dir_hdl); return fsalstat(ERR_FSAL_NOTDIR, 0); } myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } unix_mode = fsal2unix_mode(attrib->mode) & ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); dir_fd = vfs_fsal_open(myself, flags, &fsal_error); if (dir_fd < 0) return fsalstat(fsal_error, -dir_fd); retval = vfs_stat_by_handle(dir_fd, myself->handle, &stat, flags); if (retval < 0) { retval = errno; goto direrr; } /* Become the user because we are creating an object in this dir. */ fsal_set_credentials(op_ctx->creds); retval = mkdirat(dir_fd, name, unix_mode); if (retval < 0) { retval = errno; fsal_restore_ganesha_credentials(); goto direrr; } fsal_restore_ganesha_credentials(); retval = vfs_name_to_handle(dir_fd, dir_hdl->fs, name, fh); if (retval < 0) { retval = errno; goto fileerr; } retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; goto fileerr; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dir_fd, fh, dir_hdl->fs, &stat, myself->handle, name, op_ctx->fsal_export); if (hdl == NULL) { retval = ENOMEM; goto fileerr; } *handle = &hdl->obj_handle; close(dir_fd); return fsalstat(ERR_FSAL_NO_ERROR, 0); fileerr: unlinkat(dir_fd, name, 0); direrr: close(dir_fd); hdlerr: fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); }
static fsal_status_t lookup(struct fsal_obj_handle *parent, const char *path, struct fsal_obj_handle **handle) { struct vfs_fsal_obj_handle *parent_hdl, *hdl; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval, dirfd; struct stat stat; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); fsal_dev_t dev; struct fsal_filesystem *fs; bool xfsal = false; *handle = NULL; /* poison it first */ parent_hdl = container_of(parent, struct vfs_fsal_obj_handle, obj_handle); if (!parent->obj_ops.handle_is(parent, DIRECTORY)) { LogCrit(COMPONENT_FSAL, "Parent handle is not a directory. hdl = 0x%p", parent); return fsalstat(ERR_FSAL_NOTDIR, 0); } if (parent->fsal != parent->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", parent->fsal->name, parent->fs->fsal != NULL ? parent->fs->fsal->name : "(none)"); retval = EXDEV; goto hdlerr; } fs = parent->fs; dirfd = vfs_fsal_open(parent_hdl, O_PATH | O_NOACCESS, &fsal_error); if (dirfd < 0) return fsalstat(fsal_error, -dirfd); retval = fstatat(dirfd, path, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; goto direrr; } dev = posix2fsal_devt(stat.st_dev); if ((dev.minor != parent_hdl->dev.minor) || (dev.major != parent_hdl->dev.major)) { /* XDEV */ fs = lookup_dev(&dev); if (fs == NULL) { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to " "unknown file system dev=%"PRIu64".%"PRIu64, path, dev.major, dev.minor); retval = EXDEV; goto direrr; } if (fs->fsal != parent->fsal) { xfsal = true; LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s into FSAL %s", path, fs->path, fs->fsal != NULL ? fs->fsal->name : "(none)"); } else { LogDebug(COMPONENT_FSAL, "Lookup of %s crosses filesystem boundary to file system %s", path, fs->path); } } if (xfsal || vfs_name_to_handle(dirfd, fs, path, fh) < 0) { retval = errno; if (((retval == ENOTTY) || (retval == EOPNOTSUPP) || (retval == ENOTSUP) || xfsal) && (fs != parent->fs)) { /* Crossed device into territory not handled by * this FSAL (XFS or VFS). Need to invent a handle. * The made up handle will be JUST the fsid, we * do not expect to see the handle on the wire, and * this handle will not be valid for any form of this * FSAL. */ LogDebug(COMPONENT_FSAL, "vfs_name_to_handle %s, inventing FSAL %s handle for FSAL %s filesystem %s", xfsal ? "skipped" : "failed", parent->fsal->name, fs->fsal != NULL ? fs->fsal->name : "(none)", path); retval = vfs_encode_dummy_handle(fh, fs); if (retval < 0) { retval = errno; goto direrr; } retval = 0; } else { /* Some other error */ goto direrr; } } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(dirfd, fh, fs, &stat, parent_hdl->handle, path, op_ctx->fsal_export); close(dirfd); if (hdl == NULL) { retval = ENOMEM; goto hdlerr; } *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); direrr: close(dirfd); hdlerr: fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); }