13fd5e34c19db1b2c5406bdb2e0e619f52c2e041
[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_cc_state {
200         struct vfs_handle_struct *handle;
201         off_t copied;
202         struct tevent_req *subreq;      /* non-null if passed to next VFS fn */
203 };
204 static void btrfs_offload_write_done(struct tevent_req *subreq);
205
206 static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
207                                                 TALLOC_CTX *mem_ctx,
208                                                 struct tevent_context *ev,
209                                                 uint32_t fsctl,
210                                                 DATA_BLOB *token,
211                                                 off_t transfer_offset,
212                                                 struct files_struct *dest_fsp,
213                                                 off_t dest_off,
214                                                 off_t num)
215 {
216         struct tevent_req *req;
217         struct btrfs_cc_state *cc_state;
218         struct btrfs_ioctl_clone_range_args cr_args;
219         struct lock_struct src_lck;
220         struct lock_struct dest_lck;
221         off_t src_off = transfer_offset;
222         files_struct *src_fsp = NULL;
223         int ret;
224         bool handle_offload_write = true;
225         bool do_locking = false;
226         NTSTATUS status;
227
228         req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
229         if (req == NULL) {
230                 return NULL;
231         }
232
233         cc_state->handle = handle;
234
235         status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
236                                                 token, &src_fsp);
237         if (tevent_req_nterror(req, status)) {
238                 return tevent_req_post(req, ev);
239         }
240
241         switch (fsctl) {
242         case FSCTL_SRV_COPYCHUNK:
243         case FSCTL_SRV_COPYCHUNK_WRITE:
244                 do_locking = true;
245                 break;
246
247         case FSCTL_DUP_EXTENTS_TO_FILE:
248                 /* dup extents does not use locking */
249                 break;
250
251         default:
252                 handle_offload_write = false;
253                 break;
254         }
255
256         if (num == 0) {
257                 /*
258                  * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
259                  * all data from @src_offset->EOF! This is certainly not what
260                  * the caller expects, and not what vfs_default does.
261                  */
262                 handle_offload_write = false;
263         }
264
265         if (!handle_offload_write) {
266                 cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
267                                                                 cc_state, ev,
268                                                                 fsctl,
269                                                                 token,
270                                                                 transfer_offset,
271                                                                 dest_fsp,
272                                                                 dest_off,
273                                                                 num);
274                 if (tevent_req_nomem(cc_state->subreq, req)) {
275                         return tevent_req_post(req, ev);
276                 }
277                 tevent_req_set_callback(cc_state->subreq,
278                                         btrfs_offload_write_done,
279                                         req);
280                 return req;
281         }
282
283         status = vfs_offload_token_check_handles(
284                 fsctl, src_fsp, dest_fsp);
285         if (!NT_STATUS_IS_OK(status)) {
286                 tevent_req_nterror(req, status);
287                 return tevent_req_post(req, ev);
288         }
289
290         status = vfs_stat_fsp(src_fsp);
291         if (tevent_req_nterror(req, status)) {
292                 return tevent_req_post(req, ev);
293         }
294
295         if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
296                 /* [MS-SMB2] Handling a Server-Side Data Copy Request */
297                 tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
298                 return tevent_req_post(req, ev);
299         }
300
301         if (src_fsp->op == NULL || dest_fsp->op == NULL) {
302                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
303                 return tevent_req_post(req, ev);
304         }
305
306         if (do_locking) {
307                 init_strict_lock_struct(src_fsp,
308                                         src_fsp->op->global->open_persistent_id,
309                                         src_off,
310                                         num,
311                                         READ_LOCK,
312                                         &src_lck);
313                 init_strict_lock_struct(dest_fsp,
314                                        dest_fsp->op->global->open_persistent_id,
315                                         dest_off,
316                                         num,
317                                         WRITE_LOCK,
318                                         &dest_lck);
319
320                 if (!SMB_VFS_STRICT_LOCK_CHECK(src_fsp->conn, src_fsp, &src_lck)) {
321                         tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
322                         return tevent_req_post(req, ev);
323                 }
324                 if (!SMB_VFS_STRICT_LOCK_CHECK(dest_fsp->conn, dest_fsp, &dest_lck)) {
325                         tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
326                         return tevent_req_post(req, ev);
327                 }
328         }
329
330         ZERO_STRUCT(cr_args);
331         cr_args.src_fd = src_fsp->fh->fd;
332         cr_args.src_offset = (uint64_t)src_off;
333         cr_args.dest_offset = (uint64_t)dest_off;
334         cr_args.src_length = (uint64_t)num;
335
336         ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
337         if (ret < 0) {
338                 /*
339                  * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
340                  * cloning. Which is 4096 by default, therefore fall back to
341                  * manual read/write on failure.
342                  */
343                 DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
344                           "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
345                           strerror(errno),
346                           (unsigned long long)cr_args.src_length,
347                           (long long)cr_args.src_fd,
348                           (unsigned long long)cr_args.src_offset,
349                           dest_fsp->fh->fd,
350                           (unsigned long long)cr_args.dest_offset));
351                 cc_state->subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
352                                                                 cc_state, ev,
353                                                                 fsctl,
354                                                                 token,
355                                                                 transfer_offset,
356                                                                 dest_fsp,
357                                                                 dest_off,
358                                                                 num);
359                 if (tevent_req_nomem(cc_state->subreq, req)) {
360                         return tevent_req_post(req, ev);
361                 }
362                 /* wait for subreq completion */
363                 tevent_req_set_callback(cc_state->subreq,
364                                         btrfs_offload_write_done,
365                                         req);
366                 return req;
367         }
368
369         DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
370         /* BTRFS_IOC_CLONE_RANGE is all or nothing */
371         cc_state->copied = num;
372         tevent_req_done(req);
373         return tevent_req_post(req, ev);
374 }
375
376 /* only used if the request is passed through to next VFS module */
377 static void btrfs_offload_write_done(struct tevent_req *subreq)
378 {
379         struct tevent_req *req = tevent_req_callback_data(
380                 subreq, struct tevent_req);
381         struct btrfs_cc_state *cc_state = tevent_req_data(req,
382                                                         struct btrfs_cc_state);
383         NTSTATUS status;
384
385         status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(cc_state->handle,
386                                               cc_state->subreq,
387                                               &cc_state->copied);
388         if (tevent_req_nterror(req, status)) {
389                 return;
390         }
391         tevent_req_done(req);
392 }
393
394 static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
395                                       struct tevent_req *req,
396                                       off_t *copied)
397 {
398         NTSTATUS status;
399         struct btrfs_cc_state *cc_state = tevent_req_data(req,
400                                                         struct btrfs_cc_state);
401
402         if (tevent_req_is_nterror(req, &status)) {
403                 DEBUG(4, ("server side copy chunk failed: %s\n",
404                           nt_errstr(status)));
405                 tevent_req_received(req);
406                 return status;
407         }
408
409         DEBUG(10, ("server side copy chunk copied %llu\n",
410                    (unsigned long long)cc_state->copied));
411         *copied = cc_state->copied;
412         tevent_req_received(req);
413         return NT_STATUS_OK;
414 }
415
416 /*
417  * caller must pass a non-null fsp or smb_fname. If fsp is null, then
418  * fall back to opening the corresponding file to issue the ioctl.
419  */
420 static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
421                                       TALLOC_CTX *mem_ctx,
422                                       struct files_struct *fsp,
423                                       struct smb_filename *smb_fname,
424                                       uint16_t *_compression_fmt)
425 {
426         int ret;
427         long flags = 0;
428         int fd;
429         bool opened = false;
430         NTSTATUS status;
431         DIR *dir = NULL;
432
433         if ((fsp != NULL) && (fsp->fh->fd != -1)) {
434                 fd = fsp->fh->fd;
435         } else if (smb_fname != NULL) {
436                 if (S_ISDIR(smb_fname->st.st_ex_mode)) {
437                         dir = opendir(smb_fname->base_name);
438                         if (dir == NULL) {
439                                 return NT_STATUS_UNSUCCESSFUL;
440                         }
441                         opened = true;
442                         fd = dirfd(dir);
443                         if (fd < 0) {
444                                 status = NT_STATUS_UNSUCCESSFUL;
445                                 goto err_close;
446                         }
447                 } else {
448                         fd = open(smb_fname->base_name, O_RDONLY);
449                         if (fd < 0) {
450                                 return NT_STATUS_UNSUCCESSFUL;
451                         }
452                         opened = true;
453                 }
454         } else {
455                 return NT_STATUS_INVALID_PARAMETER;
456         }
457
458         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
459         if (ret < 0) {
460                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
461                           strerror(errno), (long long)fd));
462                 status = map_nt_error_from_unix(errno);
463                 goto err_close;
464         }
465         if (flags & FS_COMPR_FL) {
466                 *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
467         } else {
468                 *_compression_fmt = COMPRESSION_FORMAT_NONE;
469         }
470         status = NT_STATUS_OK;
471 err_close:
472         if (opened) {
473                 if (dir != NULL) {
474                         closedir(dir);
475                 } else {
476                         close(fd);
477                 }
478         }
479
480         return status;
481 }
482
483 static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
484                                       TALLOC_CTX *mem_ctx,
485                                       struct files_struct *fsp,
486                                       uint16_t compression_fmt)
487 {
488         int ret;
489         long flags = 0;
490         int fd;
491         NTSTATUS status;
492
493         if ((fsp == NULL) || (fsp->fh->fd == -1)) {
494                 status = NT_STATUS_INVALID_PARAMETER;
495                 goto err_out;
496         }
497         fd = fsp->fh->fd;
498
499         ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
500         if (ret < 0) {
501                 DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
502                           strerror(errno), fd));
503                 status = map_nt_error_from_unix(errno);
504                 goto err_out;
505         }
506
507         if (compression_fmt == COMPRESSION_FORMAT_NONE) {
508                 DEBUG(5, ("setting compression\n"));
509                 flags &= (~FS_COMPR_FL);
510         } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
511                 || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
512                 DEBUG(5, ("clearing compression\n"));
513                 flags |= FS_COMPR_FL;
514         } else {
515                 DEBUG(1, ("invalid compression format 0x%x\n",
516                           (int)compression_fmt));
517                 status = NT_STATUS_INVALID_PARAMETER;
518                 goto err_out;
519         }
520
521         ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
522         if (ret < 0) {
523                 DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
524                           strerror(errno), fd));
525                 status = map_nt_error_from_unix(errno);
526                 goto err_out;
527         }
528         status = NT_STATUS_OK;
529 err_out:
530         return status;
531 }
532
533 /*
534  * Check whether a path can be shadow copied. Return the base volume, allowing
535  * the caller to determine if multiple paths lie on the same base volume.
536  */
537 #define BTRFS_INODE_SUBVOL 256
538 static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
539                                       TALLOC_CTX *mem_ctx,
540                                       const char *service_path,
541                                       char **base_volume)
542 {
543         struct stat st;
544         char *base;
545
546         if (!lp_parm_bool(SNUM(handle->conn),
547                          "btrfs", "manipulate snapshots", false)) {
548                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
549                 return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
550                                                     service_path, base_volume);
551         }
552
553         /* btrfs userspace uses this logic to confirm subvolume */
554         if (stat(service_path, &st) < 0) {
555                 return NT_STATUS_NOT_SUPPORTED;
556         }
557         if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
558                 DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
559                           service_path));
560                 return NT_STATUS_NOT_SUPPORTED;
561         }
562
563         /* we "snapshot" the service path itself */
564         base = talloc_strdup(mem_ctx, service_path);
565         if (base == NULL) {
566                 return NT_STATUS_NO_MEMORY;
567         }
568         *base_volume = base;
569
570         return NT_STATUS_OK;
571 }
572
573 static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
574                                          const char *src_path,
575                                          time_t *tstamp,
576                                          char **dest_path, char **subvolume)
577 {
578         struct tm t_gmt;
579         char time_str[50];
580         size_t tlen;
581
582         gmtime_r(tstamp, &t_gmt);
583
584         tlen = strftime(time_str, ARRAY_SIZE(time_str),
585                         SHADOW_COPY_PATH_FORMAT, &t_gmt);
586         if (tlen <= 0) {
587                 return NT_STATUS_UNSUCCESSFUL;
588         }
589
590         *dest_path = talloc_strdup(mem_ctx, src_path);
591         *subvolume = talloc_strdup(mem_ctx, time_str);
592         if ((*dest_path == NULL) || (*subvolume == NULL)) {
593                 return NT_STATUS_NO_MEMORY;
594         }
595
596         return NT_STATUS_OK;
597 }
598
599 static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
600                                   TALLOC_CTX *mem_ctx,
601                                   const char *base_volume,
602                                   time_t *tstamp,
603                                   bool rw,
604                                   char **_base_path,
605                                   char **_snap_path)
606 {
607         struct btrfs_ioctl_vol_args_v2 ioctl_arg;
608         DIR *src_dir;
609         DIR *dest_dir;
610         int src_fd;
611         int dest_fd;
612         char *dest_path = NULL;
613         char *dest_subvolume = NULL;
614         int ret;
615         NTSTATUS status;
616         char *base_path;
617         char *snap_path;
618         TALLOC_CTX *tmp_ctx;
619         int saved_errno;
620         size_t len;
621
622         if (!lp_parm_bool(SNUM(handle->conn),
623                           "btrfs", "manipulate snapshots", false)) {
624                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
625                 return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
626                                                 tstamp, rw, _base_path,
627                                                 _snap_path);
628         }
629
630         tmp_ctx = talloc_new(mem_ctx);
631         if (tmp_ctx == NULL) {
632                 return NT_STATUS_NO_MEMORY;
633         }
634
635         base_path = talloc_strdup(tmp_ctx, base_volume);
636         if (base_path == NULL) {
637                 talloc_free(tmp_ctx);
638                 return NT_STATUS_NO_MEMORY;
639         }
640
641         status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
642                                           &dest_path, &dest_subvolume);
643         if (!NT_STATUS_IS_OK(status)) {
644                 talloc_free(tmp_ctx);
645                 return status;
646         }
647
648         snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
649                                     dest_subvolume);
650         if (snap_path == NULL) {
651                 talloc_free(tmp_ctx);
652                 return NT_STATUS_NO_MEMORY;
653         }
654
655         src_dir = opendir(base_volume);
656         if (src_dir == NULL) {
657                 DEBUG(0, ("snap src %s open failed: %s\n",
658                           base_volume, strerror(errno)));
659                 status = map_nt_error_from_unix(errno);
660                 talloc_free(tmp_ctx);
661                 return status;
662         }
663         src_fd = dirfd(src_dir);
664         if (src_fd < 0) {
665                 status = map_nt_error_from_unix(errno);
666                 closedir(src_dir);
667                 talloc_free(tmp_ctx);
668                 return status;
669         }
670
671         dest_dir = opendir(dest_path);
672         if (dest_dir == NULL) {
673                 DEBUG(0, ("snap dest %s open failed: %s\n",
674                           dest_path, strerror(errno)));
675                 status = map_nt_error_from_unix(errno);
676                 closedir(src_dir);
677                 talloc_free(tmp_ctx);
678                 return status;
679         }
680         dest_fd = dirfd(dest_dir);
681         if (dest_fd < 0) {
682                 status = map_nt_error_from_unix(errno);
683                 closedir(src_dir);
684                 closedir(dest_dir);
685                 talloc_free(tmp_ctx);
686                 return status;
687         }
688
689         /* avoid zeroing the entire struct here, name is 4k */
690         ioctl_arg.fd = src_fd;
691         ioctl_arg.transid = 0;
692         ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
693         memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
694         len = strlcpy(ioctl_arg.name, dest_subvolume,
695                       ARRAY_SIZE(ioctl_arg.name));
696         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
697                 DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
698                 closedir(src_dir);
699                 closedir(dest_dir);
700                 talloc_free(tmp_ctx);
701                 return NT_STATUS_INVALID_PARAMETER;
702         }
703
704         become_root();
705         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
706         saved_errno = errno;
707         unbecome_root();
708         if (ret < 0) {
709                 DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
710                           base_volume, dest_path, dest_subvolume,
711                           strerror(saved_errno)));
712                 status = map_nt_error_from_unix(saved_errno);
713                 closedir(src_dir);
714                 closedir(dest_dir);
715                 talloc_free(tmp_ctx);
716                 return status;
717         }
718         DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
719                   base_volume, dest_path, dest_subvolume));
720
721         *_base_path = talloc_steal(mem_ctx, base_path);
722         *_snap_path = talloc_steal(mem_ctx, snap_path);
723         closedir(src_dir);
724         closedir(dest_dir);
725         talloc_free(tmp_ctx);
726
727         return NT_STATUS_OK;
728 }
729
730 static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
731                                   TALLOC_CTX *mem_ctx,
732                                   char *base_path,
733                                   char *snap_path)
734 {
735         char *tstr;
736         struct tm t_gmt;
737         DIR *dest_dir;
738         int dest_fd;
739         struct btrfs_ioctl_vol_args ioctl_arg;
740         int ret;
741         NTSTATUS status;
742         char *dest_path;
743         char *subvolume;
744         TALLOC_CTX *tmp_ctx;
745         int saved_errno;
746         size_t len;
747
748         if (!lp_parm_bool(SNUM(handle->conn),
749                           "btrfs", "manipulate snapshots", false)) {
750                 DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
751                 return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
752                                                 base_path, snap_path);
753         }
754
755         tmp_ctx = talloc_new(mem_ctx);
756         if (tmp_ctx == NULL) {
757                 return NT_STATUS_NO_MEMORY;
758         }
759
760         dest_path = talloc_strdup(tmp_ctx, snap_path);
761         if (dest_path == NULL) {
762                 talloc_free(tmp_ctx);
763                 return NT_STATUS_NO_MEMORY;
764         }
765         subvolume = talloc_strdup(tmp_ctx, snap_path);
766         if (subvolume == NULL) {
767                 talloc_free(tmp_ctx);
768                 return NT_STATUS_NO_MEMORY;
769         }
770         dest_path = dirname(dest_path);
771         subvolume = basename(subvolume);
772
773         /* confirm snap_path matches creation format */
774         tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
775         if ((tstr == NULL) || (*tstr != '\0')) {
776                 DEBUG(0, ("snapshot path %s does not match creation format\n",
777                           snap_path));
778                 talloc_free(tmp_ctx);
779                 return NT_STATUS_UNSUCCESSFUL;
780         }
781
782         dest_dir = opendir(dest_path);
783         if (dest_dir == NULL) {
784                 DEBUG(0, ("snap destroy dest %s open failed: %s\n",
785                           dest_path, strerror(errno)));
786                 status = map_nt_error_from_unix(errno);
787                 talloc_free(tmp_ctx);
788                 return status;
789         }
790         dest_fd = dirfd(dest_dir);
791         if (dest_fd < 0) {
792                 status = map_nt_error_from_unix(errno);
793                 closedir(dest_dir);
794                 talloc_free(tmp_ctx);
795                 return status;
796         }
797
798         ioctl_arg.fd = -1;      /* not needed */
799         len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
800         if (len >= ARRAY_SIZE(ioctl_arg.name)) {
801                 DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
802                 closedir(dest_dir);
803                 talloc_free(tmp_ctx);
804                 return NT_STATUS_INVALID_PARAMETER;
805         }
806
807         become_root();
808         ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
809         saved_errno = errno;
810         unbecome_root();
811         if (ret < 0) {
812                 DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
813                           dest_path, subvolume, strerror(saved_errno)));
814                 status = map_nt_error_from_unix(saved_errno);
815                 closedir(dest_dir);
816                 talloc_free(tmp_ctx);
817                 return status;
818         }
819         DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
820                   dest_path, subvolume));
821
822         closedir(dest_dir);
823         talloc_free(tmp_ctx);
824         return NT_STATUS_OK;
825 }
826
827 static struct vfs_fn_pointers btrfs_fns = {
828         .fs_capabilities_fn = btrfs_fs_capabilities,
829         .offload_read_send_fn = btrfs_offload_read_send,
830         .offload_read_recv_fn = btrfs_offload_read_recv,
831         .offload_write_send_fn = btrfs_offload_write_send,
832         .offload_write_recv_fn = btrfs_offload_write_recv,
833         .get_compression_fn = btrfs_get_compression,
834         .set_compression_fn = btrfs_set_compression,
835         .snap_check_path_fn = btrfs_snap_check_path,
836         .snap_create_fn = btrfs_snap_create,
837         .snap_delete_fn = btrfs_snap_delete,
838 };
839
840 NTSTATUS vfs_btrfs_init(TALLOC_CTX *);
841 NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
842 {
843         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
844                                 "btrfs", &btrfs_fns);
845 }