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>
22 #include <sys/ioctl.h>
27 #include "system/filesys.h"
29 #include "smbd/smbd.h"
30 #include "librpc/gen_ndr/smbXsrv.h"
31 #include "librpc/gen_ndr/ioctl.h"
32 #include "lib/util/tevent_ntstatus.h"
34 static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct
*handle
,
35 enum timestamp_set_resolution
*_ts_res
)
37 uint32_t fs_capabilities
;
38 enum timestamp_set_resolution ts_res
;
40 /* inherit default capabilities, expose compression support */
41 fs_capabilities
= SMB_VFS_NEXT_FS_CAPABILITIES(handle
, &ts_res
);
42 fs_capabilities
|= FILE_FILE_COMPRESSION
;
45 return fs_capabilities
;
48 #define SHADOW_COPY_PREFIX "@GMT-" /* vfs_shadow_copy format */
49 #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
51 #define BTRFS_SUBVOL_RDONLY (1ULL << 1)
52 #define BTRFS_SUBVOL_NAME_MAX 4039
53 #define BTRFS_PATH_NAME_MAX 4087
54 struct btrfs_ioctl_vol_args_v2
{
59 char name
[BTRFS_SUBVOL_NAME_MAX
+ 1];
61 struct btrfs_ioctl_vol_args
{
63 char name
[BTRFS_PATH_NAME_MAX
+ 1];
66 struct btrfs_ioctl_clone_range_args
{
73 #define BTRFS_IOCTL_MAGIC 0x94
74 #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
75 struct btrfs_ioctl_clone_range_args)
76 #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
77 struct btrfs_ioctl_vol_args)
78 #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
79 struct btrfs_ioctl_vol_args_v2)
81 struct btrfs_cc_state
{
82 struct vfs_handle_struct
*handle
;
84 struct tevent_req
*subreq
; /* non-null if passed to next VFS fn */
86 static void btrfs_copy_chunk_done(struct tevent_req
*subreq
);
88 static struct tevent_req
*btrfs_copy_chunk_send(struct vfs_handle_struct
*handle
,
90 struct tevent_context
*ev
,
91 struct files_struct
*src_fsp
,
93 struct files_struct
*dest_fsp
,
97 struct tevent_req
*req
;
98 struct btrfs_cc_state
*cc_state
;
99 struct btrfs_ioctl_clone_range_args cr_args
;
100 struct lock_struct src_lck
;
101 struct lock_struct dest_lck
;
105 req
= tevent_req_create(mem_ctx
, &cc_state
, struct btrfs_cc_state
);
109 cc_state
->handle
= handle
;
113 * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
114 * all data from @src_offset->EOF! This is certainly not what
115 * the caller expects, and not what vfs_default does.
117 cc_state
->subreq
= SMB_VFS_NEXT_COPY_CHUNK_SEND(handle
,
123 if (tevent_req_nomem(cc_state
->subreq
, req
)) {
124 return tevent_req_post(req
, ev
);
126 tevent_req_set_callback(cc_state
->subreq
,
127 btrfs_copy_chunk_done
,
132 status
= vfs_stat_fsp(src_fsp
);
133 if (tevent_req_nterror(req
, status
)) {
134 return tevent_req_post(req
, ev
);
137 if (src_fsp
->fsp_name
->st
.st_ex_size
< src_off
+ num
) {
138 /* [MS-SMB2] Handling a Server-Side Data Copy Request */
139 tevent_req_nterror(req
, NT_STATUS_INVALID_VIEW_SIZE
);
140 return tevent_req_post(req
, ev
);
143 if (src_fsp
->op
== NULL
|| dest_fsp
->op
== NULL
) {
144 tevent_req_nterror(req
, NT_STATUS_INTERNAL_ERROR
);
145 return tevent_req_post(req
, ev
);
148 init_strict_lock_struct(src_fsp
,
149 src_fsp
->op
->global
->open_persistent_id
,
154 init_strict_lock_struct(dest_fsp
,
155 dest_fsp
->op
->global
->open_persistent_id
,
161 if (!SMB_VFS_STRICT_LOCK(src_fsp
->conn
, src_fsp
, &src_lck
)) {
162 tevent_req_nterror(req
, NT_STATUS_FILE_LOCK_CONFLICT
);
163 return tevent_req_post(req
, ev
);
165 if (!SMB_VFS_STRICT_LOCK(dest_fsp
->conn
, dest_fsp
, &dest_lck
)) {
166 SMB_VFS_STRICT_UNLOCK(src_fsp
->conn
, src_fsp
, &src_lck
);
167 tevent_req_nterror(req
, NT_STATUS_FILE_LOCK_CONFLICT
);
168 return tevent_req_post(req
, ev
);
171 ZERO_STRUCT(cr_args
);
172 cr_args
.src_fd
= src_fsp
->fh
->fd
;
173 cr_args
.src_offset
= (uint64_t)src_off
;
174 cr_args
.dest_offset
= (uint64_t)dest_off
;
175 cr_args
.src_length
= (uint64_t)num
;
177 ret
= ioctl(dest_fsp
->fh
->fd
, BTRFS_IOC_CLONE_RANGE
, &cr_args
);
178 SMB_VFS_STRICT_UNLOCK(dest_fsp
->conn
, dest_fsp
, &dest_lck
);
179 SMB_VFS_STRICT_UNLOCK(src_fsp
->conn
, src_fsp
, &src_lck
);
182 * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
183 * cloning. Which is 4096 by default, therefore fall back to
184 * manual read/write on failure.
186 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
187 "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
189 (unsigned long long)cr_args
.src_length
,
190 (long long)cr_args
.src_fd
,
191 (unsigned long long)cr_args
.src_offset
,
193 (unsigned long long)cr_args
.dest_offset
));
194 cc_state
->subreq
= SMB_VFS_NEXT_COPY_CHUNK_SEND(handle
,
200 if (tevent_req_nomem(cc_state
->subreq
, req
)) {
201 return tevent_req_post(req
, ev
);
203 /* wait for subreq completion */
204 tevent_req_set_callback(cc_state
->subreq
,
205 btrfs_copy_chunk_done
,
210 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret
));
211 /* BTRFS_IOC_CLONE_RANGE is all or nothing */
212 cc_state
->copied
= num
;
213 tevent_req_done(req
);
214 return tevent_req_post(req
, ev
);
217 /* only used if the request is passed through to next VFS module */
218 static void btrfs_copy_chunk_done(struct tevent_req
*subreq
)
220 struct tevent_req
*req
= tevent_req_callback_data(
221 subreq
, struct tevent_req
);
222 struct btrfs_cc_state
*cc_state
= tevent_req_data(req
,
223 struct btrfs_cc_state
);
226 status
= SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state
->handle
,
229 if (tevent_req_nterror(req
, status
)) {
232 tevent_req_done(req
);
235 static NTSTATUS
btrfs_copy_chunk_recv(struct vfs_handle_struct
*handle
,
236 struct tevent_req
*req
,
240 struct btrfs_cc_state
*cc_state
= tevent_req_data(req
,
241 struct btrfs_cc_state
);
243 if (tevent_req_is_nterror(req
, &status
)) {
244 DEBUG(4, ("server side copy chunk failed: %s\n",
246 tevent_req_received(req
);
250 DEBUG(10, ("server side copy chunk copied %llu\n",
251 (unsigned long long)cc_state
->copied
));
252 *copied
= cc_state
->copied
;
253 tevent_req_received(req
);
258 * caller must pass a non-null fsp or smb_fname. If fsp is null, then
259 * fall back to opening the corresponding file to issue the ioctl.
261 static NTSTATUS
btrfs_get_compression(struct vfs_handle_struct
*handle
,
263 struct files_struct
*fsp
,
264 struct smb_filename
*smb_fname
,
265 uint16_t *_compression_fmt
)
274 if ((fsp
!= NULL
) && (fsp
->fh
->fd
!= -1)) {
276 } else if (smb_fname
!= NULL
) {
277 if (S_ISDIR(smb_fname
->st
.st_ex_mode
)) {
278 dir
= opendir(smb_fname
->base_name
);
280 return NT_STATUS_UNSUCCESSFUL
;
285 status
= NT_STATUS_UNSUCCESSFUL
;
289 fd
= open(smb_fname
->base_name
, O_RDONLY
);
291 return NT_STATUS_UNSUCCESSFUL
;
296 return NT_STATUS_INVALID_PARAMETER
;
299 ret
= ioctl(fd
, FS_IOC_GETFLAGS
, &flags
);
301 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
302 strerror(errno
), (long long)fd
));
303 status
= map_nt_error_from_unix(errno
);
306 if (flags
& FS_COMPR_FL
) {
307 *_compression_fmt
= COMPRESSION_FORMAT_LZNT1
;
309 *_compression_fmt
= COMPRESSION_FORMAT_NONE
;
311 status
= NT_STATUS_OK
;
324 static NTSTATUS
btrfs_set_compression(struct vfs_handle_struct
*handle
,
326 struct files_struct
*fsp
,
327 uint16_t compression_fmt
)
334 if ((fsp
== NULL
) || (fsp
->fh
->fd
== -1)) {
335 status
= NT_STATUS_INVALID_PARAMETER
;
340 ret
= ioctl(fd
, FS_IOC_GETFLAGS
, &flags
);
342 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
343 strerror(errno
), fd
));
344 status
= map_nt_error_from_unix(errno
);
348 if (compression_fmt
== COMPRESSION_FORMAT_NONE
) {
349 DEBUG(5, ("setting compression\n"));
350 flags
&= (~FS_COMPR_FL
);
351 } else if ((compression_fmt
== COMPRESSION_FORMAT_DEFAULT
)
352 || (compression_fmt
== COMPRESSION_FORMAT_LZNT1
)) {
353 DEBUG(5, ("clearing compression\n"));
354 flags
|= FS_COMPR_FL
;
356 DEBUG(1, ("invalid compression format 0x%x\n",
357 (int)compression_fmt
));
358 status
= NT_STATUS_INVALID_PARAMETER
;
362 ret
= ioctl(fd
, FS_IOC_SETFLAGS
, &flags
);
364 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
365 strerror(errno
), fd
));
366 status
= map_nt_error_from_unix(errno
);
369 status
= NT_STATUS_OK
;
375 * Check whether a path can be shadow copied. Return the base volume, allowing
376 * the caller to determine if multiple paths lie on the same base volume.
378 #define BTRFS_INODE_SUBVOL 256
379 static NTSTATUS
btrfs_snap_check_path(struct vfs_handle_struct
*handle
,
381 const char *service_path
,
387 if (!lp_parm_bool(SNUM(handle
->conn
),
388 "btrfs", "manipulate snapshots", false)) {
389 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
390 return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle
, mem_ctx
,
391 service_path
, base_volume
);
394 /* btrfs userspace uses this logic to confirm subvolume */
395 if (stat(service_path
, &st
) < 0) {
396 return NT_STATUS_NOT_SUPPORTED
;
398 if ((st
.st_ino
!= BTRFS_INODE_SUBVOL
) || !S_ISDIR(st
.st_mode
)) {
399 DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
401 return NT_STATUS_NOT_SUPPORTED
;
404 /* we "snapshot" the service path itself */
405 base
= talloc_strdup(mem_ctx
, service_path
);
407 return NT_STATUS_NO_MEMORY
;
414 static NTSTATUS
btrfs_gen_snap_dest_path(TALLOC_CTX
*mem_ctx
,
415 const char *src_path
,
417 char **dest_path
, char **subvolume
)
423 gmtime_r(tstamp
, &t_gmt
);
425 tlen
= strftime(time_str
, ARRAY_SIZE(time_str
),
426 SHADOW_COPY_PATH_FORMAT
, &t_gmt
);
428 return NT_STATUS_UNSUCCESSFUL
;
431 *dest_path
= talloc_strdup(mem_ctx
, src_path
);
432 *subvolume
= talloc_strdup(mem_ctx
, time_str
);
433 if ((*dest_path
== NULL
) || (*subvolume
== NULL
)) {
434 return NT_STATUS_NO_MEMORY
;
440 static NTSTATUS
btrfs_snap_create(struct vfs_handle_struct
*handle
,
442 const char *base_volume
,
448 struct btrfs_ioctl_vol_args_v2 ioctl_arg
;
453 char *dest_path
= NULL
;
454 char *dest_subvolume
= NULL
;
463 if (!lp_parm_bool(SNUM(handle
->conn
),
464 "btrfs", "manipulate snapshots", false)) {
465 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
466 return SMB_VFS_NEXT_SNAP_CREATE(handle
, mem_ctx
, base_volume
,
467 tstamp
, rw
, _base_path
,
471 tmp_ctx
= talloc_new(mem_ctx
);
472 if (tmp_ctx
== NULL
) {
473 return NT_STATUS_NO_MEMORY
;
476 base_path
= talloc_strdup(tmp_ctx
, base_volume
);
477 if (base_path
== NULL
) {
478 talloc_free(tmp_ctx
);
479 return NT_STATUS_NO_MEMORY
;
482 status
= btrfs_gen_snap_dest_path(tmp_ctx
, base_volume
, tstamp
,
483 &dest_path
, &dest_subvolume
);
484 if (!NT_STATUS_IS_OK(status
)) {
485 talloc_free(tmp_ctx
);
489 snap_path
= talloc_asprintf(tmp_ctx
, "%s/%s", dest_path
,
491 if (snap_path
== NULL
) {
492 talloc_free(tmp_ctx
);
493 return NT_STATUS_NO_MEMORY
;
496 src_dir
= opendir(base_volume
);
497 if (src_dir
== NULL
) {
498 DEBUG(0, ("snap src %s open failed: %s\n",
499 base_volume
, strerror(errno
)));
500 status
= map_nt_error_from_unix(errno
);
501 talloc_free(tmp_ctx
);
504 src_fd
= dirfd(src_dir
);
506 status
= map_nt_error_from_unix(errno
);
508 talloc_free(tmp_ctx
);
512 dest_dir
= opendir(dest_path
);
513 if (dest_dir
== NULL
) {
514 DEBUG(0, ("snap dest %s open failed: %s\n",
515 dest_path
, strerror(errno
)));
516 status
= map_nt_error_from_unix(errno
);
518 talloc_free(tmp_ctx
);
521 dest_fd
= dirfd(dest_dir
);
523 status
= map_nt_error_from_unix(errno
);
526 talloc_free(tmp_ctx
);
530 /* avoid zeroing the entire struct here, name is 4k */
531 ioctl_arg
.fd
= src_fd
;
532 ioctl_arg
.transid
= 0;
533 ioctl_arg
.flags
= (rw
== false) ? BTRFS_SUBVOL_RDONLY
: 0;
534 memset(ioctl_arg
.unused
, 0, sizeof(ioctl_arg
.unused
));
535 len
= strlcpy(ioctl_arg
.name
, dest_subvolume
,
536 ARRAY_SIZE(ioctl_arg
.name
));
537 if (len
>= ARRAY_SIZE(ioctl_arg
.name
)) {
538 DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
541 talloc_free(tmp_ctx
);
542 return NT_STATUS_INVALID_PARAMETER
;
546 ret
= ioctl(dest_fd
, BTRFS_IOC_SNAP_CREATE_V2
, &ioctl_arg
);
550 DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
551 base_volume
, dest_path
, dest_subvolume
,
552 strerror(saved_errno
)));
553 status
= map_nt_error_from_unix(saved_errno
);
556 talloc_free(tmp_ctx
);
559 DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
560 base_volume
, dest_path
, dest_subvolume
));
562 *_base_path
= talloc_steal(mem_ctx
, base_path
);
563 *_snap_path
= talloc_steal(mem_ctx
, snap_path
);
566 talloc_free(tmp_ctx
);
571 static NTSTATUS
btrfs_snap_delete(struct vfs_handle_struct
*handle
,
580 struct btrfs_ioctl_vol_args ioctl_arg
;
589 if (!lp_parm_bool(SNUM(handle
->conn
),
590 "btrfs", "manipulate snapshots", false)) {
591 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
592 return SMB_VFS_NEXT_SNAP_DELETE(handle
, mem_ctx
,
593 base_path
, snap_path
);
596 tmp_ctx
= talloc_new(mem_ctx
);
597 if (tmp_ctx
== NULL
) {
598 return NT_STATUS_NO_MEMORY
;
601 dest_path
= talloc_strdup(tmp_ctx
, snap_path
);
602 if (dest_path
== NULL
) {
603 talloc_free(tmp_ctx
);
604 return NT_STATUS_NO_MEMORY
;
606 subvolume
= talloc_strdup(tmp_ctx
, snap_path
);
607 if (subvolume
== NULL
) {
608 talloc_free(tmp_ctx
);
609 return NT_STATUS_NO_MEMORY
;
611 dest_path
= dirname(dest_path
);
612 subvolume
= basename(subvolume
);
614 /* confirm snap_path matches creation format */
615 tstr
= strptime(subvolume
, SHADOW_COPY_PATH_FORMAT
, &t_gmt
);
616 if ((tstr
== NULL
) || (*tstr
!= '\0')) {
617 DEBUG(0, ("snapshot path %s does not match creation format\n",
619 talloc_free(tmp_ctx
);
620 return NT_STATUS_UNSUCCESSFUL
;
623 dest_dir
= opendir(dest_path
);
624 if (dest_dir
== NULL
) {
625 DEBUG(0, ("snap destroy dest %s open failed: %s\n",
626 dest_path
, strerror(errno
)));
627 status
= map_nt_error_from_unix(errno
);
628 talloc_free(tmp_ctx
);
631 dest_fd
= dirfd(dest_dir
);
633 status
= map_nt_error_from_unix(errno
);
635 talloc_free(tmp_ctx
);
639 ioctl_arg
.fd
= -1; /* not needed */
640 len
= strlcpy(ioctl_arg
.name
, subvolume
, ARRAY_SIZE(ioctl_arg
.name
));
641 if (len
>= ARRAY_SIZE(ioctl_arg
.name
)) {
642 DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
644 talloc_free(tmp_ctx
);
645 return NT_STATUS_INVALID_PARAMETER
;
649 ret
= ioctl(dest_fd
, BTRFS_IOC_SNAP_DESTROY
, &ioctl_arg
);
653 DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
654 dest_path
, subvolume
, strerror(saved_errno
)));
655 status
= map_nt_error_from_unix(saved_errno
);
657 talloc_free(tmp_ctx
);
660 DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
661 dest_path
, subvolume
));
664 talloc_free(tmp_ctx
);
668 static struct vfs_fn_pointers btrfs_fns
= {
669 .fs_capabilities_fn
= btrfs_fs_capabilities
,
670 .copy_chunk_send_fn
= btrfs_copy_chunk_send
,
671 .copy_chunk_recv_fn
= btrfs_copy_chunk_recv
,
672 .get_compression_fn
= btrfs_get_compression
,
673 .set_compression_fn
= btrfs_set_compression
,
674 .snap_check_path_fn
= btrfs_snap_check_path
,
675 .snap_create_fn
= btrfs_snap_create
,
676 .snap_delete_fn
= btrfs_snap_delete
,
679 NTSTATUS
vfs_btrfs_init(void);
680 NTSTATUS
vfs_btrfs_init(void)
682 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
683 "btrfs", &btrfs_fns
);