libcli/smb: Allow smb2cli_validate_negotiate_info_done() to ignore NT_STATUS_INVALID_...
[Samba.git] / source3 / modules / vfs_btrfs.c
blobf478d3fac684acbbcd57b12c43399963453fe57a
1 /*
2 * Module to make use of awesome Btrfs features
4 * Copyright (C) David Disseldorp 2011-2013
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include <linux/ioctl.h>
21 #include <linux/fs.h>
22 #include <sys/ioctl.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <dirent.h>
26 #include <libgen.h>
27 #include "system/filesys.h"
28 #include "includes.h"
29 #include "smbd/smbd.h"
30 #include "smbd/globals.h"
31 #include "librpc/gen_ndr/smbXsrv.h"
32 #include "librpc/gen_ndr/ioctl.h"
33 #include "lib/util/tevent_ntstatus.h"
34 #include "offload_token.h"
36 static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
37 enum timestamp_set_resolution *_ts_res)
39 uint32_t fs_capabilities;
40 enum timestamp_set_resolution ts_res;
42 /* inherit default capabilities, expose compression support */
43 fs_capabilities = SMB_VFS_NEXT_FS_CAPABILITIES(handle, &ts_res);
44 fs_capabilities |= (FILE_FILE_COMPRESSION
45 | FILE_SUPPORTS_BLOCK_REFCOUNTING);
46 *_ts_res = ts_res;
48 return fs_capabilities;
51 #define SHADOW_COPY_PREFIX "@GMT-" /* vfs_shadow_copy format */
52 #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
54 #define BTRFS_SUBVOL_RDONLY (1ULL << 1)
55 #define BTRFS_SUBVOL_NAME_MAX 4039
56 #define BTRFS_PATH_NAME_MAX 4087
57 struct btrfs_ioctl_vol_args_v2 {
58 int64_t fd;
59 uint64_t transid;
60 uint64_t flags;
61 uint64_t unused[4];
62 char name[BTRFS_SUBVOL_NAME_MAX + 1];
64 struct btrfs_ioctl_vol_args {
65 int64_t fd;
66 char name[BTRFS_PATH_NAME_MAX + 1];
69 struct btrfs_ioctl_clone_range_args {
70 int64_t src_fd;
71 uint64_t src_offset;
72 uint64_t src_length;
73 uint64_t dest_offset;
76 #define BTRFS_IOCTL_MAGIC 0x94
77 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
78 struct btrfs_ioctl_clone_range_args)
79 #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
80 struct btrfs_ioctl_vol_args)
81 #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
82 struct btrfs_ioctl_vol_args_v2)
84 static struct vfs_offload_ctx *btrfs_offload_ctx;
86 struct btrfs_offload_read_state {
87 struct vfs_handle_struct *handle;
88 files_struct *fsp;
89 DATA_BLOB token;
92 static void btrfs_offload_read_done(struct tevent_req *subreq);
94 static struct tevent_req *btrfs_offload_read_send(
95 TALLOC_CTX *mem_ctx,
96 struct tevent_context *ev,
97 struct vfs_handle_struct *handle,
98 files_struct *fsp,
99 uint32_t fsctl,
100 uint32_t ttl,
101 off_t offset,
102 size_t to_copy)
104 struct tevent_req *req = NULL;
105 struct tevent_req *subreq = NULL;
106 struct btrfs_offload_read_state *state = NULL;
107 NTSTATUS status;
109 req = tevent_req_create(mem_ctx, &state,
110 struct btrfs_offload_read_state);
111 if (req == NULL) {
112 return NULL;
114 *state = (struct btrfs_offload_read_state) {
115 .handle = handle,
116 .fsp = fsp,
119 status = vfs_offload_token_ctx_init(fsp->conn->sconn->client,
120 &btrfs_offload_ctx);
121 if (tevent_req_nterror(req, status)) {
122 return tevent_req_post(req, ev);
125 if (fsctl == FSCTL_DUP_EXTENTS_TO_FILE) {
126 status = vfs_offload_token_create_blob(state, fsp, fsctl,
127 &state->token);
128 if (tevent_req_nterror(req, status)) {
129 return tevent_req_post(req, ev);
132 status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx, fsp,
133 &state->token);
134 if (tevent_req_nterror(req, status)) {
135 return tevent_req_post(req, ev);
137 tevent_req_done(req);
138 return tevent_req_post(req, ev);
141 subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
142 fsctl, ttl, offset, to_copy);
143 if (tevent_req_nomem(subreq, req)) {
144 return tevent_req_post(req, ev);
146 tevent_req_set_callback(subreq, btrfs_offload_read_done, req);
147 return req;
150 static void btrfs_offload_read_done(struct tevent_req *subreq)
152 struct tevent_req *req = tevent_req_callback_data(
153 subreq, struct tevent_req);
154 struct btrfs_offload_read_state *state = tevent_req_data(
155 req, struct btrfs_offload_read_state);
156 NTSTATUS status;
158 status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
159 state->handle,
160 state,
161 &state->token);
162 TALLOC_FREE(subreq);
163 if (tevent_req_nterror(req, status)) {
164 return;
167 status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx,
168 state->fsp,
169 &state->token);
170 if (tevent_req_nterror(req, status)) {
171 return;
174 tevent_req_done(req);
175 return;
178 static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
179 struct vfs_handle_struct *handle,
180 TALLOC_CTX *mem_ctx,
181 DATA_BLOB *token)
183 struct btrfs_offload_read_state *state = tevent_req_data(
184 req, struct btrfs_offload_read_state);
185 NTSTATUS status;
187 if (tevent_req_is_nterror(req, &status)) {
188 tevent_req_received(req);
189 return status;
192 token->length = state->token.length;
193 token->data = talloc_move(mem_ctx, &state->token.data);
195 tevent_req_received(req);
196 return NT_STATUS_OK;
199 struct btrfs_offload_write_state {
200 struct vfs_handle_struct *handle;
201 off_t copied;
202 bool need_unbecome_user;
205 static void btrfs_offload_write_cleanup(struct tevent_req *req,
206 enum tevent_req_state req_state)
208 struct btrfs_offload_write_state *state =
209 tevent_req_data(req,
210 struct btrfs_offload_write_state);
211 bool ok;
213 if (!state->need_unbecome_user) {
214 return;
217 ok = unbecome_user_without_service();
218 SMB_ASSERT(ok);
219 state->need_unbecome_user = false;
222 static void btrfs_offload_write_done(struct tevent_req *subreq);
224 static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
225 TALLOC_CTX *mem_ctx,
226 struct tevent_context *ev,
227 uint32_t fsctl,
228 DATA_BLOB *token,
229 off_t transfer_offset,
230 struct files_struct *dest_fsp,
231 off_t dest_off,
232 off_t num)
234 struct tevent_req *req = NULL;
235 struct btrfs_offload_write_state *state = NULL;
236 struct tevent_req *subreq = NULL;
237 struct btrfs_ioctl_clone_range_args cr_args;
238 struct lock_struct src_lck;
239 struct lock_struct dest_lck;
240 off_t src_off = transfer_offset;
241 files_struct *src_fsp = NULL;
242 int ret;
243 bool handle_offload_write = true;
244 bool do_locking = false;
245 NTSTATUS status;
246 bool ok;
248 req = tevent_req_create(mem_ctx, &state,
249 struct btrfs_offload_write_state);
250 if (req == NULL) {
251 return NULL;
254 state->handle = handle;
256 tevent_req_set_cleanup_fn(req, btrfs_offload_write_cleanup);
258 status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
259 token, &src_fsp);
260 if (tevent_req_nterror(req, status)) {
261 return tevent_req_post(req, ev);
264 switch (fsctl) {
265 case FSCTL_SRV_COPYCHUNK:
266 case FSCTL_SRV_COPYCHUNK_WRITE:
267 do_locking = true;
268 break;
270 case FSCTL_DUP_EXTENTS_TO_FILE:
271 /* dup extents does not use locking */
272 break;
274 default:
275 handle_offload_write = false;
276 break;
279 if (num == 0) {
281 * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
282 * all data from @src_offset->EOF! This is certainly not what
283 * the caller expects, and not what vfs_default does.
285 handle_offload_write = false;
288 if (!handle_offload_write) {
289 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
290 state,
292 fsctl,
293 token,
294 transfer_offset,
295 dest_fsp,
296 dest_off,
297 num);
298 if (tevent_req_nomem(subreq, req)) {
299 return tevent_req_post(req, ev);
301 tevent_req_set_callback(subreq,
302 btrfs_offload_write_done,
303 req);
304 return req;
307 status = vfs_offload_token_check_handles(
308 fsctl, src_fsp, dest_fsp);
309 if (!NT_STATUS_IS_OK(status)) {
310 tevent_req_nterror(req, status);
311 return tevent_req_post(req, ev);
314 ok = become_user_without_service_by_fsp(src_fsp);
315 if (!ok) {
316 tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
317 return tevent_req_post(req, ev);
319 state->need_unbecome_user = true;
321 status = vfs_stat_fsp(src_fsp);
322 if (tevent_req_nterror(req, status)) {
323 return tevent_req_post(req, ev);
326 if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
327 /* [MS-SMB2] Handling a Server-Side Data Copy Request */
328 tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
329 return tevent_req_post(req, ev);
332 if (do_locking) {
333 init_strict_lock_struct(src_fsp,
334 src_fsp->op->global->open_persistent_id,
335 src_off,
336 num,
337 READ_LOCK,
338 &src_lck);
339 if (!SMB_VFS_STRICT_LOCK_CHECK(src_fsp->conn, src_fsp, &src_lck)) {
340 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
341 return tevent_req_post(req, ev);
345 ok = unbecome_user_without_service();
346 SMB_ASSERT(ok);
347 state->need_unbecome_user = false;
349 if (do_locking) {
350 init_strict_lock_struct(dest_fsp,
351 dest_fsp->op->global->open_persistent_id,
352 dest_off,
353 num,
354 WRITE_LOCK,
355 &dest_lck);
357 if (!SMB_VFS_STRICT_LOCK_CHECK(dest_fsp->conn, dest_fsp, &dest_lck)) {
358 tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
359 return tevent_req_post(req, ev);
363 ZERO_STRUCT(cr_args);
364 cr_args.src_fd = src_fsp->fh->fd;
365 cr_args.src_offset = (uint64_t)src_off;
366 cr_args.dest_offset = (uint64_t)dest_off;
367 cr_args.src_length = (uint64_t)num;
369 ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
370 if (ret < 0) {
372 * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
373 * cloning. Which is 4096 by default, therefore fall back to
374 * manual read/write on failure.
376 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
377 "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
378 strerror(errno),
379 (unsigned long long)cr_args.src_length,
380 (long long)cr_args.src_fd,
381 (unsigned long long)cr_args.src_offset,
382 dest_fsp->fh->fd,
383 (unsigned long long)cr_args.dest_offset));
384 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
385 state,
387 fsctl,
388 token,
389 transfer_offset,
390 dest_fsp,
391 dest_off,
392 num);
393 if (tevent_req_nomem(subreq, req)) {
394 return tevent_req_post(req, ev);
396 /* wait for subreq completion */
397 tevent_req_set_callback(subreq,
398 btrfs_offload_write_done,
399 req);
400 return req;
403 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
404 /* BTRFS_IOC_CLONE_RANGE is all or nothing */
405 state->copied = num;
406 tevent_req_done(req);
407 return tevent_req_post(req, ev);
410 /* only used if the request is passed through to next VFS module */
411 static void btrfs_offload_write_done(struct tevent_req *subreq)
413 struct tevent_req *req =
414 tevent_req_callback_data(subreq,
415 struct tevent_req);
416 struct btrfs_offload_write_state *state =
417 tevent_req_data(req,
418 struct btrfs_offload_write_state);
419 NTSTATUS status;
421 status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
422 subreq,
423 &state->copied);
424 TALLOC_FREE(subreq);
425 if (tevent_req_nterror(req, status)) {
426 return;
428 tevent_req_done(req);
431 static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
432 struct tevent_req *req,
433 off_t *copied)
435 struct btrfs_offload_write_state *state =
436 tevent_req_data(req,
437 struct btrfs_offload_write_state);
438 NTSTATUS status;
440 if (tevent_req_is_nterror(req, &status)) {
441 DEBUG(4, ("server side copy chunk failed: %s\n",
442 nt_errstr(status)));
443 tevent_req_received(req);
444 return status;
447 DEBUG(10, ("server side copy chunk copied %llu\n",
448 (unsigned long long)state->copied));
449 *copied = state->copied;
450 tevent_req_received(req);
451 return NT_STATUS_OK;
455 * caller must pass a non-null fsp or smb_fname. If fsp is null, then
456 * fall back to opening the corresponding file to issue the ioctl.
458 static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
459 TALLOC_CTX *mem_ctx,
460 struct files_struct *fsp,
461 struct smb_filename *smb_fname,
462 uint16_t *_compression_fmt)
464 int ret;
465 long flags = 0;
466 int fd;
467 bool opened = false;
468 NTSTATUS status;
469 DIR *dir = NULL;
471 if ((fsp != NULL) && (fsp->fh->fd != -1)) {
472 fd = fsp->fh->fd;
473 } else if (smb_fname != NULL) {
474 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
475 dir = opendir(smb_fname->base_name);
476 if (dir == NULL) {
477 return NT_STATUS_UNSUCCESSFUL;
479 opened = true;
480 fd = dirfd(dir);
481 if (fd < 0) {
482 status = NT_STATUS_UNSUCCESSFUL;
483 goto err_close;
485 } else {
486 fd = open(smb_fname->base_name, O_RDONLY);
487 if (fd < 0) {
488 return NT_STATUS_UNSUCCESSFUL;
490 opened = true;
492 } else {
493 return NT_STATUS_INVALID_PARAMETER;
496 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
497 if (ret < 0) {
498 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
499 strerror(errno), (long long)fd));
500 status = map_nt_error_from_unix(errno);
501 goto err_close;
503 if (flags & FS_COMPR_FL) {
504 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
505 } else {
506 *_compression_fmt = COMPRESSION_FORMAT_NONE;
508 status = NT_STATUS_OK;
509 err_close:
510 if (opened) {
511 if (dir != NULL) {
512 closedir(dir);
513 } else {
514 close(fd);
518 return status;
521 static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
522 TALLOC_CTX *mem_ctx,
523 struct files_struct *fsp,
524 uint16_t compression_fmt)
526 int ret;
527 long flags = 0;
528 int fd;
529 NTSTATUS status;
531 if ((fsp == NULL) || (fsp->fh->fd == -1)) {
532 status = NT_STATUS_INVALID_PARAMETER;
533 goto err_out;
535 fd = fsp->fh->fd;
537 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
538 if (ret < 0) {
539 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
540 strerror(errno), fd));
541 status = map_nt_error_from_unix(errno);
542 goto err_out;
545 if (compression_fmt == COMPRESSION_FORMAT_NONE) {
546 DEBUG(5, ("setting compression\n"));
547 flags &= (~FS_COMPR_FL);
548 } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
549 || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
550 DEBUG(5, ("clearing compression\n"));
551 flags |= FS_COMPR_FL;
552 } else {
553 DEBUG(1, ("invalid compression format 0x%x\n",
554 (int)compression_fmt));
555 status = NT_STATUS_INVALID_PARAMETER;
556 goto err_out;
559 ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
560 if (ret < 0) {
561 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
562 strerror(errno), fd));
563 status = map_nt_error_from_unix(errno);
564 goto err_out;
566 status = NT_STATUS_OK;
567 err_out:
568 return status;
572 * Check whether a path can be shadow copied. Return the base volume, allowing
573 * the caller to determine if multiple paths lie on the same base volume.
575 #define BTRFS_INODE_SUBVOL 256
576 static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
577 TALLOC_CTX *mem_ctx,
578 const char *service_path,
579 char **base_volume)
581 struct stat st;
582 char *base;
584 if (!lp_parm_bool(SNUM(handle->conn),
585 "btrfs", "manipulate snapshots", false)) {
586 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
587 return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
588 service_path, base_volume);
591 /* btrfs userspace uses this logic to confirm subvolume */
592 if (stat(service_path, &st) < 0) {
593 return NT_STATUS_NOT_SUPPORTED;
595 if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
596 DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
597 service_path));
598 return NT_STATUS_NOT_SUPPORTED;
601 /* we "snapshot" the service path itself */
602 base = talloc_strdup(mem_ctx, service_path);
603 if (base == NULL) {
604 return NT_STATUS_NO_MEMORY;
606 *base_volume = base;
608 return NT_STATUS_OK;
611 static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
612 const char *src_path,
613 time_t *tstamp,
614 char **dest_path, char **subvolume)
616 struct tm t_gmt;
617 char time_str[50];
618 size_t tlen;
620 gmtime_r(tstamp, &t_gmt);
622 tlen = strftime(time_str, ARRAY_SIZE(time_str),
623 SHADOW_COPY_PATH_FORMAT, &t_gmt);
624 if (tlen <= 0) {
625 return NT_STATUS_UNSUCCESSFUL;
628 *dest_path = talloc_strdup(mem_ctx, src_path);
629 *subvolume = talloc_strdup(mem_ctx, time_str);
630 if ((*dest_path == NULL) || (*subvolume == NULL)) {
631 return NT_STATUS_NO_MEMORY;
634 return NT_STATUS_OK;
637 static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
638 TALLOC_CTX *mem_ctx,
639 const char *base_volume,
640 time_t *tstamp,
641 bool rw,
642 char **_base_path,
643 char **_snap_path)
645 struct btrfs_ioctl_vol_args_v2 ioctl_arg;
646 DIR *src_dir;
647 DIR *dest_dir;
648 int src_fd;
649 int dest_fd;
650 char *dest_path = NULL;
651 char *dest_subvolume = NULL;
652 int ret;
653 NTSTATUS status;
654 char *base_path;
655 char *snap_path;
656 TALLOC_CTX *tmp_ctx;
657 int saved_errno;
658 size_t len;
660 if (!lp_parm_bool(SNUM(handle->conn),
661 "btrfs", "manipulate snapshots", false)) {
662 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
663 return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
664 tstamp, rw, _base_path,
665 _snap_path);
668 tmp_ctx = talloc_new(mem_ctx);
669 if (tmp_ctx == NULL) {
670 return NT_STATUS_NO_MEMORY;
673 base_path = talloc_strdup(tmp_ctx, base_volume);
674 if (base_path == NULL) {
675 talloc_free(tmp_ctx);
676 return NT_STATUS_NO_MEMORY;
679 status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
680 &dest_path, &dest_subvolume);
681 if (!NT_STATUS_IS_OK(status)) {
682 talloc_free(tmp_ctx);
683 return status;
686 snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
687 dest_subvolume);
688 if (snap_path == NULL) {
689 talloc_free(tmp_ctx);
690 return NT_STATUS_NO_MEMORY;
693 src_dir = opendir(base_volume);
694 if (src_dir == NULL) {
695 DEBUG(0, ("snap src %s open failed: %s\n",
696 base_volume, strerror(errno)));
697 status = map_nt_error_from_unix(errno);
698 talloc_free(tmp_ctx);
699 return status;
701 src_fd = dirfd(src_dir);
702 if (src_fd < 0) {
703 status = map_nt_error_from_unix(errno);
704 closedir(src_dir);
705 talloc_free(tmp_ctx);
706 return status;
709 dest_dir = opendir(dest_path);
710 if (dest_dir == NULL) {
711 DEBUG(0, ("snap dest %s open failed: %s\n",
712 dest_path, strerror(errno)));
713 status = map_nt_error_from_unix(errno);
714 closedir(src_dir);
715 talloc_free(tmp_ctx);
716 return status;
718 dest_fd = dirfd(dest_dir);
719 if (dest_fd < 0) {
720 status = map_nt_error_from_unix(errno);
721 closedir(src_dir);
722 closedir(dest_dir);
723 talloc_free(tmp_ctx);
724 return status;
727 /* avoid zeroing the entire struct here, name is 4k */
728 ioctl_arg.fd = src_fd;
729 ioctl_arg.transid = 0;
730 ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
731 memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
732 len = strlcpy(ioctl_arg.name, dest_subvolume,
733 ARRAY_SIZE(ioctl_arg.name));
734 if (len >= ARRAY_SIZE(ioctl_arg.name)) {
735 DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
736 closedir(src_dir);
737 closedir(dest_dir);
738 talloc_free(tmp_ctx);
739 return NT_STATUS_INVALID_PARAMETER;
742 become_root();
743 ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
744 saved_errno = errno;
745 unbecome_root();
746 if (ret < 0) {
747 DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
748 base_volume, dest_path, dest_subvolume,
749 strerror(saved_errno)));
750 status = map_nt_error_from_unix(saved_errno);
751 closedir(src_dir);
752 closedir(dest_dir);
753 talloc_free(tmp_ctx);
754 return status;
756 DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
757 base_volume, dest_path, dest_subvolume));
759 *_base_path = talloc_steal(mem_ctx, base_path);
760 *_snap_path = talloc_steal(mem_ctx, snap_path);
761 closedir(src_dir);
762 closedir(dest_dir);
763 talloc_free(tmp_ctx);
765 return NT_STATUS_OK;
768 static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
769 TALLOC_CTX *mem_ctx,
770 char *base_path,
771 char *snap_path)
773 char *tstr;
774 struct tm t_gmt;
775 DIR *dest_dir;
776 int dest_fd;
777 struct btrfs_ioctl_vol_args ioctl_arg;
778 int ret;
779 NTSTATUS status;
780 char *dest_path;
781 char *subvolume;
782 TALLOC_CTX *tmp_ctx;
783 int saved_errno;
784 size_t len;
786 if (!lp_parm_bool(SNUM(handle->conn),
787 "btrfs", "manipulate snapshots", false)) {
788 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
789 return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
790 base_path, snap_path);
793 tmp_ctx = talloc_new(mem_ctx);
794 if (tmp_ctx == NULL) {
795 return NT_STATUS_NO_MEMORY;
798 dest_path = talloc_strdup(tmp_ctx, snap_path);
799 if (dest_path == NULL) {
800 talloc_free(tmp_ctx);
801 return NT_STATUS_NO_MEMORY;
803 subvolume = talloc_strdup(tmp_ctx, snap_path);
804 if (subvolume == NULL) {
805 talloc_free(tmp_ctx);
806 return NT_STATUS_NO_MEMORY;
808 dest_path = dirname(dest_path);
809 subvolume = basename(subvolume);
811 /* confirm snap_path matches creation format */
812 tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
813 if ((tstr == NULL) || (*tstr != '\0')) {
814 DEBUG(0, ("snapshot path %s does not match creation format\n",
815 snap_path));
816 talloc_free(tmp_ctx);
817 return NT_STATUS_UNSUCCESSFUL;
820 dest_dir = opendir(dest_path);
821 if (dest_dir == NULL) {
822 DEBUG(0, ("snap destroy dest %s open failed: %s\n",
823 dest_path, strerror(errno)));
824 status = map_nt_error_from_unix(errno);
825 talloc_free(tmp_ctx);
826 return status;
828 dest_fd = dirfd(dest_dir);
829 if (dest_fd < 0) {
830 status = map_nt_error_from_unix(errno);
831 closedir(dest_dir);
832 talloc_free(tmp_ctx);
833 return status;
836 ioctl_arg.fd = -1; /* not needed */
837 len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
838 if (len >= ARRAY_SIZE(ioctl_arg.name)) {
839 DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
840 closedir(dest_dir);
841 talloc_free(tmp_ctx);
842 return NT_STATUS_INVALID_PARAMETER;
845 become_root();
846 ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
847 saved_errno = errno;
848 unbecome_root();
849 if (ret < 0) {
850 DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
851 dest_path, subvolume, strerror(saved_errno)));
852 status = map_nt_error_from_unix(saved_errno);
853 closedir(dest_dir);
854 talloc_free(tmp_ctx);
855 return status;
857 DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
858 dest_path, subvolume));
860 closedir(dest_dir);
861 talloc_free(tmp_ctx);
862 return NT_STATUS_OK;
865 static struct vfs_fn_pointers btrfs_fns = {
866 .fs_capabilities_fn = btrfs_fs_capabilities,
867 .offload_read_send_fn = btrfs_offload_read_send,
868 .offload_read_recv_fn = btrfs_offload_read_recv,
869 .offload_write_send_fn = btrfs_offload_write_send,
870 .offload_write_recv_fn = btrfs_offload_write_recv,
871 .get_compression_fn = btrfs_get_compression,
872 .set_compression_fn = btrfs_set_compression,
873 .snap_check_path_fn = btrfs_snap_check_path,
874 .snap_create_fn = btrfs_snap_create,
875 .snap_delete_fn = btrfs_snap_delete,
878 static_decl_vfs;
879 NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
881 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
882 "btrfs", &btrfs_fns);