* 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>
#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"
struct btrfs_offload_read_state {
struct vfs_handle_struct *handle;
files_struct *fsp;
+ uint32_t flags;
+ uint64_t xferlen;
DATA_BLOB token;
};
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)) {
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(
return status;
}
+ *flags = state->flags;
+ *xferlen = state->xferlen;
token->length = state->token.length;
token->data = talloc_move(mem_ctx, &state->token.data);
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) {
/*
* 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);
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
(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",
}
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);
*_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;
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) {
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;
.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,