auth: Explain why GSS_KRB5_CRED_NO_CI_FLAGS_X is needed
[Samba.git] / source3 / modules / vfs_btrfs.c
blobbd95637c99ea62675cde727dce368273953c85c9
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 "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;
43 *_ts_res = ts_res;
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 {
55 int64_t fd;
56 uint64_t transid;
57 uint64_t flags;
58 uint64_t unused[4];
59 char name[BTRFS_SUBVOL_NAME_MAX + 1];
61 struct btrfs_ioctl_vol_args {
62 int64_t fd;
63 char name[BTRFS_PATH_NAME_MAX + 1];
66 struct btrfs_ioctl_clone_range_args {
67 int64_t src_fd;
68 uint64_t src_offset;
69 uint64_t src_length;
70 uint64_t dest_offset;
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;
83 off_t copied;
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,
89 TALLOC_CTX *mem_ctx,
90 struct tevent_context *ev,
91 struct files_struct *src_fsp,
92 off_t src_off,
93 struct files_struct *dest_fsp,
94 off_t dest_off,
95 off_t num)
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;
102 int ret;
103 NTSTATUS status;
105 req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
106 if (req == NULL) {
107 return NULL;
109 cc_state->handle = handle;
111 if (num == 0) {
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,
118 cc_state, ev,
119 src_fsp,
120 src_off,
121 dest_fsp,
122 dest_off, num);
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,
128 req);
129 return req;
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,
150 src_off,
151 num,
152 READ_LOCK,
153 &src_lck);
154 init_strict_lock_struct(dest_fsp,
155 dest_fsp->op->global->open_persistent_id,
156 dest_off,
157 num,
158 WRITE_LOCK,
159 &dest_lck);
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);
180 if (ret < 0) {
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",
188 strerror(errno),
189 (unsigned long long)cr_args.src_length,
190 (long long)cr_args.src_fd,
191 (unsigned long long)cr_args.src_offset,
192 dest_fsp->fh->fd,
193 (unsigned long long)cr_args.dest_offset));
194 cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
195 cc_state, ev,
196 src_fsp,
197 src_off,
198 dest_fsp,
199 dest_off, num);
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,
206 req);
207 return req;
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);
224 NTSTATUS status;
226 status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
227 cc_state->subreq,
228 &cc_state->copied);
229 if (tevent_req_nterror(req, status)) {
230 return;
232 tevent_req_done(req);
235 static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
236 struct tevent_req *req,
237 off_t *copied)
239 NTSTATUS status;
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",
245 nt_errstr(status)));
246 tevent_req_received(req);
247 return status;
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);
254 return NT_STATUS_OK;
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,
262 TALLOC_CTX *mem_ctx,
263 struct files_struct *fsp,
264 struct smb_filename *smb_fname,
265 uint16_t *_compression_fmt)
267 int ret;
268 long flags = 0;
269 int fd;
270 bool opened = false;
271 NTSTATUS status;
272 DIR *dir = NULL;
274 if ((fsp != NULL) && (fsp->fh->fd != -1)) {
275 fd = fsp->fh->fd;
276 } else if (smb_fname != NULL) {
277 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
278 dir = opendir(smb_fname->base_name);
279 if (dir == NULL) {
280 return NT_STATUS_UNSUCCESSFUL;
282 opened = true;
283 fd = dirfd(dir);
284 if (fd < 0) {
285 status = NT_STATUS_UNSUCCESSFUL;
286 goto err_close;
288 } else {
289 fd = open(smb_fname->base_name, O_RDONLY);
290 if (fd < 0) {
291 return NT_STATUS_UNSUCCESSFUL;
293 opened = true;
295 } else {
296 return NT_STATUS_INVALID_PARAMETER;
299 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
300 if (ret < 0) {
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);
304 goto err_close;
306 if (flags & FS_COMPR_FL) {
307 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
308 } else {
309 *_compression_fmt = COMPRESSION_FORMAT_NONE;
311 status = NT_STATUS_OK;
312 err_close:
313 if (opened) {
314 if (dir != NULL) {
315 closedir(dir);
316 } else {
317 close(fd);
321 return status;
324 static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
325 TALLOC_CTX *mem_ctx,
326 struct files_struct *fsp,
327 uint16_t compression_fmt)
329 int ret;
330 long flags = 0;
331 int fd;
332 NTSTATUS status;
334 if ((fsp == NULL) || (fsp->fh->fd == -1)) {
335 status = NT_STATUS_INVALID_PARAMETER;
336 goto err_out;
338 fd = fsp->fh->fd;
340 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
341 if (ret < 0) {
342 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
343 strerror(errno), fd));
344 status = map_nt_error_from_unix(errno);
345 goto err_out;
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;
355 } else {
356 DEBUG(1, ("invalid compression format 0x%x\n",
357 (int)compression_fmt));
358 status = NT_STATUS_INVALID_PARAMETER;
359 goto err_out;
362 ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
363 if (ret < 0) {
364 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
365 strerror(errno), fd));
366 status = map_nt_error_from_unix(errno);
367 goto err_out;
369 status = NT_STATUS_OK;
370 err_out:
371 return status;
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,
380 TALLOC_CTX *mem_ctx,
381 const char *service_path,
382 char **base_volume)
384 struct stat st;
385 char *base;
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",
400 service_path));
401 return NT_STATUS_NOT_SUPPORTED;
404 /* we "snapshot" the service path itself */
405 base = talloc_strdup(mem_ctx, service_path);
406 if (base == NULL) {
407 return NT_STATUS_NO_MEMORY;
409 *base_volume = base;
411 return NT_STATUS_OK;
414 static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
415 const char *src_path,
416 time_t *tstamp,
417 char **dest_path, char **subvolume)
419 struct tm t_gmt;
420 char time_str[50];
421 size_t tlen;
423 gmtime_r(tstamp, &t_gmt);
425 tlen = strftime(time_str, ARRAY_SIZE(time_str),
426 SHADOW_COPY_PATH_FORMAT, &t_gmt);
427 if (tlen <= 0) {
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;
437 return NT_STATUS_OK;
440 static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
441 TALLOC_CTX *mem_ctx,
442 const char *base_volume,
443 time_t *tstamp,
444 bool rw,
445 char **_base_path,
446 char **_snap_path)
448 struct btrfs_ioctl_vol_args_v2 ioctl_arg;
449 DIR *src_dir;
450 DIR *dest_dir;
451 int src_fd;
452 int dest_fd;
453 char *dest_path = NULL;
454 char *dest_subvolume = NULL;
455 int ret;
456 NTSTATUS status;
457 char *base_path;
458 char *snap_path;
459 TALLOC_CTX *tmp_ctx;
460 int saved_errno;
461 size_t len;
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,
468 _snap_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);
486 return status;
489 snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
490 dest_subvolume);
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);
502 return status;
504 src_fd = dirfd(src_dir);
505 if (src_fd < 0) {
506 status = map_nt_error_from_unix(errno);
507 closedir(src_dir);
508 talloc_free(tmp_ctx);
509 return status;
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);
517 closedir(src_dir);
518 talloc_free(tmp_ctx);
519 return status;
521 dest_fd = dirfd(dest_dir);
522 if (dest_fd < 0) {
523 status = map_nt_error_from_unix(errno);
524 closedir(src_dir);
525 closedir(dest_dir);
526 talloc_free(tmp_ctx);
527 return status;
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, ARRAY_SIZE(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"));
539 closedir(src_dir);
540 closedir(dest_dir);
541 talloc_free(tmp_ctx);
542 return NT_STATUS_INVALID_PARAMETER;
545 become_root();
546 ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
547 saved_errno = errno;
548 unbecome_root();
549 if (ret < 0) {
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);
554 closedir(src_dir);
555 closedir(dest_dir);
556 talloc_free(tmp_ctx);
557 return status;
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);
564 closedir(src_dir);
565 closedir(dest_dir);
566 talloc_free(tmp_ctx);
568 return NT_STATUS_OK;
571 static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
572 TALLOC_CTX *mem_ctx,
573 char *base_path,
574 char *snap_path)
576 char *tstr;
577 struct tm t_gmt;
578 DIR *dest_dir;
579 int dest_fd;
580 struct btrfs_ioctl_vol_args ioctl_arg;
581 int ret;
582 NTSTATUS status;
583 char *dest_path;
584 char *subvolume;
585 TALLOC_CTX *tmp_ctx;
586 int saved_errno;
587 size_t len;
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",
618 snap_path));
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);
629 return status;
631 dest_fd = dirfd(dest_dir);
632 if (dest_fd < 0) {
633 status = map_nt_error_from_unix(errno);
634 closedir(dest_dir);
635 talloc_free(tmp_ctx);
636 return status;
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"));
643 closedir(dest_dir);
644 talloc_free(tmp_ctx);
645 return NT_STATUS_INVALID_PARAMETER;
648 become_root();
649 ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
650 saved_errno = errno;
651 unbecome_root();
652 if (ret < 0) {
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);
656 closedir(dest_dir);
657 talloc_free(tmp_ctx);
658 return status;
660 DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
661 dest_path, subvolume));
663 closedir(dest_dir);
664 talloc_free(tmp_ctx);
665 return NT_STATUS_OK;
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);