vfs: SMB_VFS_GET_COMPRESSION() -> SMB_VFS_FGET_COMPRESSION()
[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 "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"
35
36 static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
37                                       enum timestamp_set_resolution *_ts_res)
38 {
39         uint32_t fs_capabilities;
40         enum timestamp_set_resolution ts_res;
41
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;
47
48         return fs_capabilities;
49 }
50
51 #define SHADOW_COPY_PREFIX "@GMT-"      /* vfs_shadow_copy format */
52 #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
53
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];
63 };
64 struct btrfs_ioctl_vol_args {
65         int64_t fd;
66         char name[BTRFS_PATH_NAME_MAX + 1];
67 };
68
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;
74 };
75
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)
83
84 static struct vfs_offload_ctx *btrfs_offload_ctx;
85
86 struct btrfs_offload_read_state {
87         struct vfs_handle_struct *handle;
88         files_struct *fsp;
89         DATA_BLOB token;
90 };
91
92 static void btrfs_offload_read_done(struct tevent_req *subreq);
93
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)
103 {
104         struct tevent_req *req = NULL;
105         struct tevent_req *subreq = NULL;
106         struct btrfs_offload_read_state *state = NULL;
107         NTSTATUS status;
108
109         req = tevent_req_create(mem_ctx, &state,
110                                 struct btrfs_offload_read_state);
111         if (req == NULL) {
112                 return NULL;
113         }
114         *state = (struct btrfs_offload_read_state) {
115                 .handle = handle,
116                 .fsp = fsp,
117         };
118
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);
123         }
124
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);
130                 }
131
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);
136                 }
137                 tevent_req_done(req);
138                 return tevent_req_post(req, ev);
139         }
140
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);
145         }
146         tevent_req_set_callback(subreq, btrfs_offload_read_done, req);
147         return req;
148 }
149
150 static void btrfs_offload_read_done(struct tevent_req *subreq)
151 {
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;
157
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;
165         }
166
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;
172         }
173
174         tevent_req_done(req);
175         return;
176 }
177
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)
182 {
183         struct btrfs_offload_read_state *state = tevent_req_data(
184                 req, struct btrfs_offload_read_state);
185         NTSTATUS status;
186
187         if (tevent_req_is_nterror(req, &status)) {
188                 tevent_req_received(req);
189                 return status;
190         }
191
192         token->length = state->token.length;
193         token->data = talloc_move(mem_ctx, &state->token.data);
194
195         tevent_req_received(req);
196         return NT_STATUS_OK;
197 }
198
199 struct btrfs_offload_write_state {
200         struct vfs_handle_struct *handle;
201         off_t copied;
202         bool need_unbecome_user;
203 };
204
205 static void btrfs_offload_write_cleanup(struct tevent_req *req,
206                                         enum tevent_req_state req_state)
207 {
208         struct btrfs_offload_write_state *state =
209                 tevent_req_data(req,
210                 struct btrfs_offload_write_state);
211         bool ok;
212
213         if (!state->need_unbecome_user) {
214                 return;
215         }
216
217         ok = unbecome_user_without_service();
218         SMB_ASSERT(ok);
219         state->need_unbecome_user = false;
220 }
221
222 static void btrfs_offload_write_done(struct tevent_req *subreq);
223
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)
233 {
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;
247
248         req = tevent_req_create(mem_ctx, &state,
249                                 struct btrfs_offload_write_state);
250         if (req == NULL) {
251                 return NULL;
252         }
253
254         state->handle = handle;
255
256         tevent_req_set_cleanup_fn(req, btrfs_offload_write_cleanup);
257
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);
262         }
263
264         switch (fsctl) {
265         case FSCTL_SRV_COPYCHUNK:
266         case FSCTL_SRV_COPYCHUNK_WRITE:
267                 do_locking = true;
268                 break;
269
270         case FSCTL_DUP_EXTENTS_TO_FILE:
271                 /* dup extents does not use locking */
272                 break;
273
274         default:
275                 handle_offload_write = false;
276                 break;
277         }
278
279         if (num == 0) {
280                 /*
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.
284                  */
285                 handle_offload_write = false;
286         }
287
288         if (!handle_offload_write) {
289                 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
290                                                          state,
291                                                          ev,
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);
300                 }
301                 tevent_req_set_callback(subreq,
302                                         btrfs_offload_write_done,
303                                         req);
304                 return req;
305         }
306
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);
312         }
313
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);
318         }
319         state->need_unbecome_user = true;
320
321         status = vfs_stat_fsp(src_fsp);
322         if (tevent_req_nterror(req, status)) {
323                 return tevent_req_post(req, ev);
324         }
325
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);
330         }
331
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);
342                 }
343         }
344
345         ok = unbecome_user_without_service();
346         SMB_ASSERT(ok);
347         state->need_unbecome_user = false;
348
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);
356
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);
360                 }
361         }
362
363         ZERO_STRUCT(cr_args);
364         cr_args.src_fd = fsp_get_io_fd(src_fsp);
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;
368
369         ret = ioctl(fsp_get_io_fd(dest_fsp), BTRFS_IOC_CLONE_RANGE, &cr_args);
370         if (ret < 0) {
371                 /*
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.
375                  */
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                           fsp_get_io_fd(dest_fsp),
383                           (unsigned long long)cr_args.dest_offset));
384                 subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
385                                                          state,
386                                                          ev,
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);
395                 }
396                 /* wait for subreq completion */
397                 tevent_req_set_callback(subreq,
398                                         btrfs_offload_write_done,
399                                         req);
400                 return req;
401         }
402
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);
408 }
409
410 /* only used if the request is passed through to next VFS module */
411 static void btrfs_offload_write_done(struct tevent_req *subreq)
412 {
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;
420
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;
427         }
428         tevent_req_done(req);
429 }
430
431 static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
432                                          struct tevent_req *req,
433                                          off_t *copied)
434 {
435         struct btrfs_offload_write_state *state =
436                 tevent_req_data(req,
437                 struct btrfs_offload_write_state);
438         NTSTATUS status;
439
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;
445         }
446
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;
452 }
453
454 static NTSTATUS btrfs_fget_compression(struct vfs_handle_struct *handle,
455                                        TALLOC_CTX *mem_ctx,
456                                        struct files_struct *fsp,
457                                        uint16_t *_compression_fmt)
458 {
459         char buf[PATH_MAX];
460         const char *p = NULL;
461         int ret;
462         long flags = 0;
463         int fd = -1;
464         NTSTATUS status;
465
466         if (!fsp->fsp_flags.is_pathref) {
467                 ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
468                 if (ret < 0) {
469                         DBG_WARNING("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
470                                     strerror(errno), (long long)fd);
471                         return map_nt_error_from_unix(errno);
472                 }
473                 if (flags & FS_COMPR_FL) {
474                         *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
475                 } else {
476                         *_compression_fmt = COMPRESSION_FORMAT_NONE;
477                 }
478                 return NT_STATUS_OK;
479         }
480
481         if (!fsp->fsp_flags.have_proc_fds) {
482                 return NT_STATUS_NOT_IMPLEMENTED;
483         }
484
485         fd = fsp_get_pathref_fd(fsp);
486
487         p = sys_proc_fd_path(fd, buf, sizeof(buf));
488         if (p == NULL) {
489                 return NT_STATUS_NO_MEMORY;
490         }
491
492         fd = open(p, O_RDONLY);
493         if (fd == -1) {
494                 DBG_ERR("/proc open of %s failed: %s\n", p, strerror(errno));
495                 return map_nt_error_from_unix(errno);
496         }
497
498         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
499         if (ret < 0) {
500                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
501                           strerror(errno), (long long)fd));
502                 status = map_nt_error_from_unix(errno);
503                 goto err_close;
504         }
505         if (flags & FS_COMPR_FL) {
506                 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
507         } else {
508                 *_compression_fmt = COMPRESSION_FORMAT_NONE;
509         }
510         status = NT_STATUS_OK;
511
512 err_close:
513         if (fd != -1) {
514                 close(fd);
515         }
516
517         return status;
518 }
519
520 static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
521                                       TALLOC_CTX *mem_ctx,
522                                       struct files_struct *fsp,
523                                       uint16_t compression_fmt)
524 {
525         int ret;
526         long flags = 0;
527         int fd;
528         NTSTATUS status;
529
530         if ((fsp == NULL) || (fsp_get_io_fd(fsp) == -1)) {
531                 status = NT_STATUS_INVALID_PARAMETER;
532                 goto err_out;
533         }
534         fd = fsp_get_io_fd(fsp);
535
536         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
537         if (ret < 0) {
538                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
539                           strerror(errno), fd));
540                 status = map_nt_error_from_unix(errno);
541                 goto err_out;
542         }
543
544         if (compression_fmt == COMPRESSION_FORMAT_NONE) {
545                 DEBUG(5, ("setting compression\n"));
546                 flags &= (~FS_COMPR_FL);
547         } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
548                 || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
549                 DEBUG(5, ("clearing compression\n"));
550                 flags |= FS_COMPR_FL;
551         } else {
552                 DEBUG(1, ("invalid compression format 0x%x\n",
553                           (int)compression_fmt));
554                 status = NT_STATUS_INVALID_PARAMETER;
555                 goto err_out;
556         }
557
558         ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
559         if (ret < 0) {
560                 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
561                           strerror(errno), fd));
562                 status = map_nt_error_from_unix(errno);
563                 goto err_out;
564         }
565         status = NT_STATUS_OK;
566 err_out:
567         return status;
568 }
569
570 /*
571  * Check whether a path can be shadow copied. Return the base volume, allowing
572  * the caller to determine if multiple paths lie on the same base volume.
573  */
574 #define BTRFS_INODE_SUBVOL 256
575 static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
576                                       TALLOC_CTX *mem_ctx,
577                                       const char *service_path,
578                                       char **base_volume)
579 {
580         struct stat st;
581         char *base;
582
583         if (!lp_parm_bool(SNUM(handle->conn),
584                          "btrfs", "manipulate snapshots", false)) {
585                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
586                 return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
587                                                     service_path, base_volume);
588         }
589
590         /* btrfs userspace uses this logic to confirm subvolume */
591         if (stat(service_path, &st) < 0) {
592                 return NT_STATUS_NOT_SUPPORTED;
593         }
594         if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
595                 DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
596                           service_path));
597                 return NT_STATUS_NOT_SUPPORTED;
598         }
599
600         /* we "snapshot" the service path itself */
601         base = talloc_strdup(mem_ctx, service_path);
602         if (base == NULL) {
603                 return NT_STATUS_NO_MEMORY;
604         }
605         *base_volume = base;
606
607         return NT_STATUS_OK;
608 }
609
610 static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
611                                          const char *src_path,
612                                          time_t *tstamp,
613                                          char **dest_path, char **subvolume)
614 {
615         struct tm t_gmt;
616         char time_str[50];
617         size_t tlen;
618
619         gmtime_r(tstamp, &t_gmt);
620
621         tlen = strftime(time_str, ARRAY_SIZE(time_str),
622                         SHADOW_COPY_PATH_FORMAT, &t_gmt);
623         if (tlen <= 0) {
624                 return NT_STATUS_UNSUCCESSFUL;
625         }
626
627         *dest_path = talloc_strdup(mem_ctx, src_path);
628         *subvolume = talloc_strdup(mem_ctx, time_str);
629         if ((*dest_path == NULL) || (*subvolume == NULL)) {
630                 return NT_STATUS_NO_MEMORY;
631         }
632
633         return NT_STATUS_OK;
634 }
635
636 static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
637                                   TALLOC_CTX *mem_ctx,
638                                   const char *base_volume,
639                                   time_t *tstamp,
640                                   bool rw,
641                                   char **_base_path,
642                                   char **_snap_path)
643 {
644         struct btrfs_ioctl_vol_args_v2 ioctl_arg;
645         DIR *src_dir;
646         DIR *dest_dir;
647         int src_fd;
648         int dest_fd;
649         char *dest_path = NULL;
650         char *dest_subvolume = NULL;
651         int ret;
652         NTSTATUS status;
653         char *base_path;
654         char *snap_path;
655         TALLOC_CTX *tmp_ctx;
656         int saved_errno;
657         size_t len;
658
659         if (!lp_parm_bool(SNUM(handle->conn),
660                           "btrfs", "manipulate snapshots", false)) {
661                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
662                 return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
663                                                 tstamp, rw, _base_path,
664                                                 _snap_path);
665         }
666
667         tmp_ctx = talloc_new(mem_ctx);
668         if (tmp_ctx == NULL) {
669                 return NT_STATUS_NO_MEMORY;
670         }
671
672         base_path = talloc_strdup(tmp_ctx, base_volume);
673         if (base_path == NULL) {
674                 talloc_free(tmp_ctx);
675                 return NT_STATUS_NO_MEMORY;
676         }
677
678         status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
679                                           &dest_path, &dest_subvolume);
680         if (!NT_STATUS_IS_OK(status)) {
681                 talloc_free(tmp_ctx);
682                 return status;
683         }
684
685         snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
686                                     dest_subvolume);
687         if (snap_path == NULL) {
688                 talloc_free(tmp_ctx);
689                 return NT_STATUS_NO_MEMORY;
690         }
691
692         src_dir = opendir(base_volume);
693         if (src_dir == NULL) {
694                 DEBUG(0, ("snap src %s open failed: %s\n",
695                           base_volume, strerror(errno)));
696                 status = map_nt_error_from_unix(errno);
697                 talloc_free(tmp_ctx);
698                 return status;
699         }
700         src_fd = dirfd(src_dir);
701         if (src_fd < 0) {
702                 status = map_nt_error_from_unix(errno);
703                 closedir(src_dir);
704                 talloc_free(tmp_ctx);
705                 return status;
706         }
707
708         dest_dir = opendir(dest_path);
709         if (dest_dir == NULL) {
710                 DEBUG(0, ("snap dest %s open failed: %s\n",
711                           dest_path, strerror(errno)));
712                 status = map_nt_error_from_unix(errno);
713                 closedir(src_dir);
714                 talloc_free(tmp_ctx);
715                 return status;
716         }
717         dest_fd = dirfd(dest_dir);
718         if (dest_fd < 0) {
719                 status = map_nt_error_from_unix(errno);
720                 closedir(src_dir);
721                 closedir(dest_dir);
722                 talloc_free(tmp_ctx);
723                 return status;
724         }
725
726         /* avoid zeroing the entire struct here, name is 4k */
727         ioctl_arg.fd = src_fd;
728         ioctl_arg.transid = 0;
729         ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
730         memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
731         len = strlcpy(ioctl_arg.name, dest_subvolume,
732                       ARRAY_SIZE(ioctl_arg.name));
733         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
734                 DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
735                 closedir(src_dir);
736                 closedir(dest_dir);
737                 talloc_free(tmp_ctx);
738                 return NT_STATUS_INVALID_PARAMETER;
739         }
740
741         become_root();
742         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
743         saved_errno = errno;
744         unbecome_root();
745         if (ret < 0) {
746                 DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
747                           base_volume, dest_path, dest_subvolume,
748                           strerror(saved_errno)));
749                 status = map_nt_error_from_unix(saved_errno);
750                 closedir(src_dir);
751                 closedir(dest_dir);
752                 talloc_free(tmp_ctx);
753                 return status;
754         }
755         DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
756                   base_volume, dest_path, dest_subvolume));
757
758         *_base_path = talloc_steal(mem_ctx, base_path);
759         *_snap_path = talloc_steal(mem_ctx, snap_path);
760         closedir(src_dir);
761         closedir(dest_dir);
762         talloc_free(tmp_ctx);
763
764         return NT_STATUS_OK;
765 }
766
767 static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
768                                   TALLOC_CTX *mem_ctx,
769                                   char *base_path,
770                                   char *snap_path)
771 {
772         char *tstr;
773         struct tm t_gmt;
774         DIR *dest_dir;
775         int dest_fd;
776         struct btrfs_ioctl_vol_args ioctl_arg;
777         int ret;
778         NTSTATUS status;
779         char *dest_path;
780         char *subvolume;
781         TALLOC_CTX *tmp_ctx;
782         int saved_errno;
783         size_t len;
784
785         if (!lp_parm_bool(SNUM(handle->conn),
786                           "btrfs", "manipulate snapshots", false)) {
787                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
788                 return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
789                                                 base_path, snap_path);
790         }
791
792         tmp_ctx = talloc_new(mem_ctx);
793         if (tmp_ctx == NULL) {
794                 return NT_STATUS_NO_MEMORY;
795         }
796
797         dest_path = talloc_strdup(tmp_ctx, snap_path);
798         if (dest_path == NULL) {
799                 talloc_free(tmp_ctx);
800                 return NT_STATUS_NO_MEMORY;
801         }
802         subvolume = talloc_strdup(tmp_ctx, snap_path);
803         if (subvolume == NULL) {
804                 talloc_free(tmp_ctx);
805                 return NT_STATUS_NO_MEMORY;
806         }
807         dest_path = dirname(dest_path);
808         subvolume = basename(subvolume);
809
810         /* confirm snap_path matches creation format */
811         tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
812         if ((tstr == NULL) || (*tstr != '\0')) {
813                 DEBUG(0, ("snapshot path %s does not match creation format\n",
814                           snap_path));
815                 talloc_free(tmp_ctx);
816                 return NT_STATUS_UNSUCCESSFUL;
817         }
818
819         dest_dir = opendir(dest_path);
820         if (dest_dir == NULL) {
821                 DEBUG(0, ("snap destroy dest %s open failed: %s\n",
822                           dest_path, strerror(errno)));
823                 status = map_nt_error_from_unix(errno);
824                 talloc_free(tmp_ctx);
825                 return status;
826         }
827         dest_fd = dirfd(dest_dir);
828         if (dest_fd < 0) {
829                 status = map_nt_error_from_unix(errno);
830                 closedir(dest_dir);
831                 talloc_free(tmp_ctx);
832                 return status;
833         }
834
835         ioctl_arg.fd = -1;      /* not needed */
836         len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
837         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
838                 DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
839                 closedir(dest_dir);
840                 talloc_free(tmp_ctx);
841                 return NT_STATUS_INVALID_PARAMETER;
842         }
843
844         become_root();
845         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
846         saved_errno = errno;
847         unbecome_root();
848         if (ret < 0) {
849                 DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
850                           dest_path, subvolume, strerror(saved_errno)));
851                 status = map_nt_error_from_unix(saved_errno);
852                 closedir(dest_dir);
853                 talloc_free(tmp_ctx);
854                 return status;
855         }
856         DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
857                   dest_path, subvolume));
858
859         closedir(dest_dir);
860         talloc_free(tmp_ctx);
861         return NT_STATUS_OK;
862 }
863
864 static struct vfs_fn_pointers btrfs_fns = {
865         .fs_capabilities_fn = btrfs_fs_capabilities,
866         .offload_read_send_fn = btrfs_offload_read_send,
867         .offload_read_recv_fn = btrfs_offload_read_recv,
868         .offload_write_send_fn = btrfs_offload_write_send,
869         .offload_write_recv_fn = btrfs_offload_write_recv,
870         .fget_compression_fn = btrfs_fget_compression,
871         .set_compression_fn = btrfs_set_compression,
872         .snap_check_path_fn = btrfs_snap_check_path,
873         .snap_create_fn = btrfs_snap_create,
874         .snap_delete_fn = btrfs_snap_delete,
875 };
876
877 static_decl_vfs;
878 NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
879 {
880         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
881                                 "btrfs", &btrfs_fns);
882 }