s3:utils: let smbstatus report anonymous signing/encryption explicitly
[samba.git] / source3 / modules / vfs_btrfs.c
index b175a8437ce9908cfd99c55c49d5dcb4655f84dd..903125242876febad9c86042210ae5683402f336 100644 (file)
@@ -17,6 +17,8 @@
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include "includes.h"
+#include "system/filesys.h"
 #include <linux/ioctl.h>
 #include <linux/fs.h>
 #include <sys/ioctl.h>
@@ -24,8 +26,6 @@
 #include <fcntl.h>
 #include <dirent.h>
 #include <libgen.h>
-#include "system/filesys.h"
-#include "includes.h"
 #include "smbd/smbd.h"
 #include "smbd/globals.h"
 #include "librpc/gen_ndr/smbXsrv.h"
@@ -86,6 +86,8 @@ static struct vfs_offload_ctx *btrfs_offload_ctx;
 struct btrfs_offload_read_state {
        struct vfs_handle_struct *handle;
        files_struct *fsp;
+       uint32_t flags;
+       uint64_t xferlen;
        DATA_BLOB token;
 };
 
@@ -158,6 +160,8 @@ static void btrfs_offload_read_done(struct tevent_req *subreq)
        status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
                                                state->handle,
                                                state,
+                                               &state->flags,
+                                               &state->xferlen,
                                                &state->token);
        TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
@@ -178,6 +182,8 @@ static void btrfs_offload_read_done(struct tevent_req *subreq)
 static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
                                        struct vfs_handle_struct *handle,
                                        TALLOC_CTX *mem_ctx,
+                                       uint32_t *flags,
+                                       uint64_t *xferlen,
                                        DATA_BLOB *token)
 {
        struct btrfs_offload_read_state *state = tevent_req_data(
@@ -189,6 +195,8 @@ static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
                return status;
        }
 
+       *flags = state->flags;
+       *xferlen = state->xferlen;
        token->length = state->token.length;
        token->data = talloc_move(mem_ctx, &state->token.data);
 
@@ -196,42 +204,85 @@ static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
        return NT_STATUS_OK;
 }
 
-struct btrfs_cc_state {
+struct btrfs_offload_write_state {
        struct vfs_handle_struct *handle;
        off_t copied;
-       struct tevent_req *subreq;      /* non-null if passed to next VFS fn */
+       bool need_unbecome_user;
 };
-static void btrfs_copy_chunk_done(struct tevent_req *subreq);
 
-static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle,
+static void btrfs_offload_write_cleanup(struct tevent_req *req,
+                                       enum tevent_req_state req_state)
+{
+       struct btrfs_offload_write_state *state =
+               tevent_req_data(req,
+               struct btrfs_offload_write_state);
+       bool ok;
+
+       if (!state->need_unbecome_user) {
+               return;
+       }
+
+       ok = unbecome_user_without_service();
+       SMB_ASSERT(ok);
+       state->need_unbecome_user = false;
+}
+
+static void btrfs_offload_write_done(struct tevent_req *subreq);
+
+static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
                                                TALLOC_CTX *mem_ctx,
                                                struct tevent_context *ev,
-                                               struct files_struct *src_fsp,
-                                               off_t src_off,
+                                               uint32_t fsctl,
+                                               DATA_BLOB *token,
+                                               off_t transfer_offset,
                                                struct files_struct *dest_fsp,
                                                off_t dest_off,
-                                               off_t num,
-                                               uint32_t flags)
+                                               off_t num)
 {
-       struct tevent_req *req;
-       struct btrfs_cc_state *cc_state;
+       struct tevent_req *req = NULL;
+       struct btrfs_offload_write_state *state = NULL;
+       struct tevent_req *subreq = NULL;
        struct btrfs_ioctl_clone_range_args cr_args;
        struct lock_struct src_lck;
        struct lock_struct dest_lck;
+       off_t src_off = transfer_offset;
+       files_struct *src_fsp = NULL;
        int ret;
+       bool handle_offload_write = true;
+       bool do_locking = false;
        NTSTATUS status;
+       bool ok;
 
-       req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
+       req = tevent_req_create(mem_ctx, &state,
+                               struct btrfs_offload_write_state);
        if (req == NULL) {
                return NULL;
        }
 
-       if (flags & ~VFS_COPY_CHUNK_FL_MASK_ALL) {
-               tevent_req_nterror(req, NT_STATUS_INVALID_PARAMETER);
+       state->handle = handle;
+
+       tevent_req_set_cleanup_fn(req, btrfs_offload_write_cleanup);
+
+       status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
+                                               token, &src_fsp);
+       if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
        }
 
-       cc_state->handle = handle;
+       switch (fsctl) {
+       case FSCTL_SRV_COPYCHUNK:
+       case FSCTL_SRV_COPYCHUNK_WRITE:
+               do_locking = true;
+               break;
+
+       case FSCTL_DUP_EXTENTS_TO_FILE:
+               /* dup extents does not use locking */
+               break;
+
+       default:
+               handle_offload_write = false;
+               break;
+       }
 
        if (num == 0) {
                /*
@@ -239,22 +290,42 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
                 * all data from @src_offset->EOF! This is certainly not what
                 * the caller expects, and not what vfs_default does.
                 */
-               cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
-                                                               cc_state, ev,
-                                                               src_fsp,
-                                                               src_off,
-                                                               dest_fsp,
-                                                               dest_off,
-                                                               num, flags);
-               if (tevent_req_nomem(cc_state->subreq, req)) {
+               handle_offload_write = false;
+       }
+
+       if (!handle_offload_write) {
+               subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
+                                                        state,
+                                                        ev,
+                                                        fsctl,
+                                                        token,
+                                                        transfer_offset,
+                                                        dest_fsp,
+                                                        dest_off,
+                                                        num);
+               if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
-               tevent_req_set_callback(cc_state->subreq,
-                                       btrfs_copy_chunk_done,
+               tevent_req_set_callback(subreq,
+                                       btrfs_offload_write_done,
                                        req);
                return req;
        }
 
+       status = vfs_offload_token_check_handles(
+               fsctl, src_fsp, dest_fsp);
+       if (!NT_STATUS_IS_OK(status)) {
+               tevent_req_nterror(req, status);
+               return tevent_req_post(req, ev);
+       }
+
+       ok = become_user_without_service_by_fsp(src_fsp);
+       if (!ok) {
+               tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
+               return tevent_req_post(req, ev);
+       }
+       state->need_unbecome_user = true;
+
        status = vfs_stat_fsp(src_fsp);
        if (tevent_req_nterror(req, status)) {
                return tevent_req_post(req, ev);
@@ -266,47 +337,46 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
                return tevent_req_post(req, ev);
        }
 
-       if (src_fsp->op == NULL || dest_fsp->op == NULL) {
-               tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
-               return tevent_req_post(req, ev);
-       }
-
-       if (!(flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
+       if (do_locking) {
                init_strict_lock_struct(src_fsp,
                                        src_fsp->op->global->open_persistent_id,
                                        src_off,
                                        num,
                                        READ_LOCK,
+                                       lp_posix_cifsu_locktype(src_fsp),
                                        &src_lck);
+               if (!SMB_VFS_STRICT_LOCK_CHECK(src_fsp->conn, src_fsp, &src_lck)) {
+                       tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
+                       return tevent_req_post(req, ev);
+               }
+       }
+
+       ok = unbecome_user_without_service();
+       SMB_ASSERT(ok);
+       state->need_unbecome_user = false;
+
+       if (do_locking) {
                init_strict_lock_struct(dest_fsp,
-                                      dest_fsp->op->global->open_persistent_id,
+                                       dest_fsp->op->global->open_persistent_id,
                                        dest_off,
                                        num,
                                        WRITE_LOCK,
+                                       lp_posix_cifsu_locktype(dest_fsp),
                                        &dest_lck);
 
-               if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
-                       tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
-                       return tevent_req_post(req, ev);
-               }
-               if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
-                       SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
+               if (!SMB_VFS_STRICT_LOCK_CHECK(dest_fsp->conn, dest_fsp, &dest_lck)) {
                        tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
                        return tevent_req_post(req, ev);
                }
        }
 
        ZERO_STRUCT(cr_args);
-       cr_args.src_fd = src_fsp->fh->fd;
+       cr_args.src_fd = fsp_get_io_fd(src_fsp);
        cr_args.src_offset = (uint64_t)src_off;
        cr_args.dest_offset = (uint64_t)dest_off;
        cr_args.src_length = (uint64_t)num;
 
-       ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
-       if (!(flags & VFS_COPY_CHUNK_FL_IGNORE_LOCKS)) {
-               SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
-               SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
-       }
+       ret = ioctl(fsp_get_io_fd(dest_fsp), BTRFS_IOC_CLONE_RANGE, &cr_args);
        if (ret < 0) {
                /*
                 * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
@@ -319,57 +389,63 @@ static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle
                          (unsigned long long)cr_args.src_length,
                          (long long)cr_args.src_fd,
                          (unsigned long long)cr_args.src_offset,
-                         dest_fsp->fh->fd,
+                         fsp_get_io_fd(dest_fsp),
                          (unsigned long long)cr_args.dest_offset));
-               cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
-                                                               cc_state, ev,
-                                                               src_fsp,
-                                                               src_off,
-                                                               dest_fsp,
-                                                               dest_off,
-                                                               num, flags);
-               if (tevent_req_nomem(cc_state->subreq, req)) {
+               subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
+                                                        state,
+                                                        ev,
+                                                        fsctl,
+                                                        token,
+                                                        transfer_offset,
+                                                        dest_fsp,
+                                                        dest_off,
+                                                        num);
+               if (tevent_req_nomem(subreq, req)) {
                        return tevent_req_post(req, ev);
                }
                /* wait for subreq completion */
-               tevent_req_set_callback(cc_state->subreq,
-                                       btrfs_copy_chunk_done,
+               tevent_req_set_callback(subreq,
+                                       btrfs_offload_write_done,
                                        req);
                return req;
        }
 
        DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
        /* BTRFS_IOC_CLONE_RANGE is all or nothing */
-       cc_state->copied = num;
+       state->copied = num;
        tevent_req_done(req);
        return tevent_req_post(req, ev);
 }
 
 /* only used if the request is passed through to next VFS module */
-static void btrfs_copy_chunk_done(struct tevent_req *subreq)
+static void btrfs_offload_write_done(struct tevent_req *subreq)
 {
-       struct tevent_req *req = tevent_req_callback_data(
-               subreq, struct tevent_req);
-       struct btrfs_cc_state *cc_state = tevent_req_data(req,
-                                                       struct btrfs_cc_state);
+       struct tevent_req *req =
+               tevent_req_callback_data(subreq,
+               struct tevent_req);
+       struct btrfs_offload_write_state *state =
+               tevent_req_data(req,
+               struct btrfs_offload_write_state);
        NTSTATUS status;
 
-       status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
-                                             cc_state->subreq,
-                                             &cc_state->copied);
+       status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
+                                                subreq,
+                                                &state->copied);
+       TALLOC_FREE(subreq);
        if (tevent_req_nterror(req, status)) {
                return;
        }
        tevent_req_done(req);
 }
 
-static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
-                                     struct tevent_req *req,
-                                     off_t *copied)
+static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
+                                        struct tevent_req *req,
+                                        off_t *copied)
 {
+       struct btrfs_offload_write_state *state =
+               tevent_req_data(req,
+               struct btrfs_offload_write_state);
        NTSTATUS status;
-       struct btrfs_cc_state *cc_state = tevent_req_data(req,
-                                                       struct btrfs_cc_state);
 
        if (tevent_req_is_nterror(req, &status)) {
                DEBUG(4, ("server side copy chunk failed: %s\n",
@@ -379,52 +455,49 @@ static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
        }
 
        DEBUG(10, ("server side copy chunk copied %llu\n",
-                  (unsigned long long)cc_state->copied));
-       *copied = cc_state->copied;
+                  (unsigned long long)state->copied));
+       *copied = state->copied;
        tevent_req_received(req);
        return NT_STATUS_OK;
 }
 
-/*
- * caller must pass a non-null fsp or smb_fname. If fsp is null, then
- * fall back to opening the corresponding file to issue the ioctl.
- */
-static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
-                                     TALLOC_CTX *mem_ctx,
-                                     struct files_struct *fsp,
-                                     struct smb_filename *smb_fname,
-                                     uint16_t *_compression_fmt)
+static NTSTATUS btrfs_fget_compression(struct vfs_handle_struct *handle,
+                                      TALLOC_CTX *mem_ctx,
+                                      struct files_struct *fsp,
+                                      uint16_t *_compression_fmt)
 {
+       struct sys_proc_fd_path_buf buf;
        int ret;
        long flags = 0;
-       int fd;
-       bool opened = false;
+       int fsp_fd = fsp_get_pathref_fd(fsp);
+       int fd = -1;
        NTSTATUS status;
-       DIR *dir = NULL;
-
-       if ((fsp != NULL) && (fsp->fh->fd != -1)) {
-               fd = fsp->fh->fd;
-       } else if (smb_fname != NULL) {
-               if (S_ISDIR(smb_fname->st.st_ex_mode)) {
-                       dir = opendir(smb_fname->base_name);
-                       if (dir == NULL) {
-                               return NT_STATUS_UNSUCCESSFUL;
-                       }
-                       opened = true;
-                       fd = dirfd(dir);
-                       if (fd < 0) {
-                               status = NT_STATUS_UNSUCCESSFUL;
-                               goto err_close;
-                       }
+
+       if (!fsp->fsp_flags.is_pathref) {
+               ret = ioctl(fsp_fd, FS_IOC_GETFLAGS, &flags);
+               if (ret < 0) {
+                       DBG_WARNING("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
+                                   strerror(errno), (long long)fd);
+                       return map_nt_error_from_unix(errno);
+               }
+               if (flags & FS_COMPR_FL) {
+                       *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
                } else {
-                       fd = open(smb_fname->base_name, O_RDONLY);
-                       if (fd < 0) {
-                               return NT_STATUS_UNSUCCESSFUL;
-                       }
-                       opened = true;
+                       *_compression_fmt = COMPRESSION_FORMAT_NONE;
                }
-       } else {
-               return NT_STATUS_INVALID_PARAMETER;
+               return NT_STATUS_OK;
+       }
+
+       if (!fsp->fsp_flags.have_proc_fds) {
+               return NT_STATUS_NOT_IMPLEMENTED;
+       }
+
+       fd = open(sys_proc_fd_path(fsp_fd, &buf), O_RDONLY);
+       if (fd == -1) {
+               DBG_DEBUG("/proc open of %s failed: %s\n",
+                         buf.buf,
+                         strerror(errno));
+               return map_nt_error_from_unix(errno);
        }
 
        ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
@@ -440,13 +513,10 @@ static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
                *_compression_fmt = COMPRESSION_FORMAT_NONE;
        }
        status = NT_STATUS_OK;
+
 err_close:
-       if (opened) {
-               if (dir != NULL) {
-                       closedir(dir);
-               } else {
-                       close(fd);
-               }
+       if (fd != -1) {
+               close(fd);
        }
 
        return status;
@@ -462,11 +532,11 @@ static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
        int fd;
        NTSTATUS status;
 
-       if ((fsp == NULL) || (fsp->fh->fd == -1)) {
+       if ((fsp == NULL) || (fsp_get_io_fd(fsp) == -1)) {
                status = NT_STATUS_INVALID_PARAMETER;
                goto err_out;
        }
-       fd = fsp->fh->fd;
+       fd = fsp_get_io_fd(fsp);
 
        ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
        if (ret < 0) {
@@ -705,7 +775,7 @@ static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
                                  char *snap_path)
 {
        char *tstr;
-       struct tm t_gmt;
+       struct tm t_gmt = {};
        DIR *dest_dir;
        int dest_fd;
        struct btrfs_ioctl_vol_args ioctl_arg;
@@ -800,16 +870,16 @@ static struct vfs_fn_pointers btrfs_fns = {
        .fs_capabilities_fn = btrfs_fs_capabilities,
        .offload_read_send_fn = btrfs_offload_read_send,
        .offload_read_recv_fn = btrfs_offload_read_recv,
-       .copy_chunk_send_fn = btrfs_copy_chunk_send,
-       .copy_chunk_recv_fn = btrfs_copy_chunk_recv,
-       .get_compression_fn = btrfs_get_compression,
+       .offload_write_send_fn = btrfs_offload_write_send,
+       .offload_write_recv_fn = btrfs_offload_write_recv,
+       .fget_compression_fn = btrfs_fget_compression,
        .set_compression_fn = btrfs_set_compression,
        .snap_check_path_fn = btrfs_snap_check_path,
        .snap_create_fn = btrfs_snap_create,
        .snap_delete_fn = btrfs_snap_delete,
 };
 
-NTSTATUS vfs_btrfs_init(TALLOC_CTX *);
+static_decl_vfs;
 NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
 {
        return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,