X-Git-Url: http://git.samba.org/?a=blobdiff_plain;f=source3%2Fmodules%2Fvfs_btrfs.c;h=903125242876febad9c86042210ae5683402f336;hb=HEAD;hp=b175a8437ce9908cfd99c55c49d5dcb4655f84dd;hpb=af6cbc7a441e05f71ae4e97c7d82c27868633e53;p=samba.git diff --git a/source3/modules/vfs_btrfs.c b/source3/modules/vfs_btrfs.c index b175a8437ce..90312524287 100644 --- a/source3/modules/vfs_btrfs.c +++ b/source3/modules/vfs_btrfs.c @@ -17,6 +17,8 @@ * along with this program; if not, see . */ +#include "includes.h" +#include "system/filesys.h" #include #include #include @@ -24,8 +26,6 @@ #include #include #include -#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,