ab4fd99ba2c3e64bd46b8e303e43ba4a52062835
[samba.git] / source3 / modules / vfs_btrfs.c
1 /*
2  * Module to make use of awesome Btrfs features
3  *
4  * Copyright (C) David Disseldorp 2011-2013
5  *
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.
10  *
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.
15  *
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/>.
18  */
19
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"
33
34 static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
35                                       enum timestamp_set_resolution *_ts_res)
36 {
37         uint32_t fs_capabilities;
38         enum timestamp_set_resolution ts_res;
39
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;
44
45         return fs_capabilities;
46 }
47
48 #define SHADOW_COPY_PREFIX "@GMT-"      /* vfs_shadow_copy format */
49 #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
50
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];
60 };
61 struct btrfs_ioctl_vol_args {
62         int64_t fd;
63         char name[BTRFS_PATH_NAME_MAX + 1];
64 };
65
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;
71 };
72
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)
80
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 */
85 };
86 static void btrfs_copy_chunk_done(struct tevent_req *subreq);
87
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)
96 {
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;
104
105         req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
106         if (req == NULL) {
107                 return NULL;
108         }
109         cc_state->handle = handle;
110
111         if (num == 0) {
112                 /*
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.
116                  */
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);
125                 }
126                 tevent_req_set_callback(cc_state->subreq,
127                                         btrfs_copy_chunk_done,
128                                         req);
129                 return req;
130         }
131
132         status = vfs_stat_fsp(src_fsp);
133         if (tevent_req_nterror(req, status)) {
134                 return tevent_req_post(req, ev);
135         }
136
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);
141         }
142
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);
146         }
147
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);
160
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);
164         }
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);
169         }
170
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;
176
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) {
181                 /*
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.
185                  */
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);
202                 }
203                 /* wait for subreq completion */
204                 tevent_req_set_callback(cc_state->subreq,
205                                         btrfs_copy_chunk_done,
206                                         req);
207                 return req;
208         }
209
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);
215 }
216
217 /* only used if the request is passed through to next VFS module */
218 static void btrfs_copy_chunk_done(struct tevent_req *subreq)
219 {
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;
225
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;
231         }
232         tevent_req_done(req);
233 }
234
235 static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
236                                       struct tevent_req *req,
237                                       off_t *copied)
238 {
239         NTSTATUS status;
240         struct btrfs_cc_state *cc_state = tevent_req_data(req,
241                                                         struct btrfs_cc_state);
242
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;
248         }
249
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;
255 }
256
257 /*
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.
260  */
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)
266 {
267         int ret;
268         long flags = 0;
269         int fd;
270         bool opened = false;
271         NTSTATUS status;
272         DIR *dir = NULL;
273
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;
281                         }
282                         opened = true;
283                         fd = dirfd(dir);
284                         if (fd < 0) {
285                                 status = NT_STATUS_UNSUCCESSFUL;
286                                 goto err_close;
287                         }
288                 } else {
289                         fd = open(smb_fname->base_name, O_RDONLY);
290                         if (fd < 0) {
291                                 return NT_STATUS_UNSUCCESSFUL;
292                         }
293                         opened = true;
294                 }
295         } else {
296                 return NT_STATUS_INVALID_PARAMETER;
297         }
298
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;
305         }
306         if (flags & FS_COMPR_FL) {
307                 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
308         } else {
309                 *_compression_fmt = COMPRESSION_FORMAT_NONE;
310         }
311         status = NT_STATUS_OK;
312 err_close:
313         if (opened) {
314                 if (dir != NULL) {
315                         closedir(dir);
316                 } else {
317                         close(fd);
318                 }
319         }
320
321         return status;
322 }
323
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)
328 {
329         int ret;
330         long flags = 0;
331         int fd;
332         NTSTATUS status;
333
334         if ((fsp == NULL) || (fsp->fh->fd == -1)) {
335                 status = NT_STATUS_INVALID_PARAMETER;
336                 goto err_out;
337         }
338         fd = fsp->fh->fd;
339
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;
346         }
347
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;
360         }
361
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;
368         }
369         status = NT_STATUS_OK;
370 err_out:
371         return status;
372 }
373
374 /*
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.
377  */
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)
383 {
384         struct stat st;
385         char *base;
386
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);
392         }
393
394         /* btrfs userspace uses this logic to confirm subvolume */
395         if (stat(service_path, &st) < 0) {
396                 return NT_STATUS_NOT_SUPPORTED;
397         }
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;
402         }
403
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;
408         }
409         *base_volume = base;
410
411         return NT_STATUS_OK;
412 }
413
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)
418 {
419         struct tm t_gmt;
420         char time_str[50];
421         size_t tlen;
422
423         gmtime_r(tstamp, &t_gmt);
424
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;
429         }
430
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;
435         }
436
437         return NT_STATUS_OK;
438 }
439
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)
447 {
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;
462
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);
469         }
470
471         tmp_ctx = talloc_new(mem_ctx);
472         if (tmp_ctx == NULL) {
473                 return NT_STATUS_NO_MEMORY;
474         }
475
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;
480         }
481
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;
487         }
488
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;
494         }
495
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;
503         }
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;
510         }
511
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;
520         }
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;
528         }
529
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"));
539                 closedir(src_dir);
540                 closedir(dest_dir);
541                 talloc_free(tmp_ctx);
542                 return NT_STATUS_INVALID_PARAMETER;
543         }
544
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;
558         }
559         DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
560                   base_volume, dest_path, dest_subvolume));
561
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);
567
568         return NT_STATUS_OK;
569 }
570
571 static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
572                                   TALLOC_CTX *mem_ctx,
573                                   char *base_path,
574                                   char *snap_path)
575 {
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;
588
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);
594         }
595
596         tmp_ctx = talloc_new(mem_ctx);
597         if (tmp_ctx == NULL) {
598                 return NT_STATUS_NO_MEMORY;
599         }
600
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;
605         }
606         subvolume = talloc_strdup(tmp_ctx, snap_path);
607         if (subvolume == NULL) {
608                 talloc_free(tmp_ctx);
609                 return NT_STATUS_NO_MEMORY;
610         }
611         dest_path = dirname(dest_path);
612         subvolume = basename(subvolume);
613
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;
621         }
622
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;
630         }
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;
637         }
638
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;
646         }
647
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;
659         }
660         DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
661                   dest_path, subvolume));
662
663         closedir(dest_dir);
664         talloc_free(tmp_ctx);
665         return NT_STATUS_OK;
666 }
667
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,
677 };
678
679 NTSTATUS vfs_btrfs_init(TALLOC_CTX *);
680 NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
681 {
682         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
683                                 "btrfs", &btrfs_fns);
684 }