Line data Source code
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 "includes.h"
21 : #include "system/filesys.h"
22 : #include <linux/ioctl.h>
23 : #include <linux/fs.h>
24 : #include <sys/ioctl.h>
25 : #include <unistd.h>
26 : #include <fcntl.h>
27 : #include <dirent.h>
28 : #include <libgen.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 0 : 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 0 : fs_capabilities = SMB_VFS_NEXT_FS_CAPABILITIES(handle, &ts_res);
44 0 : fs_capabilities |= (FILE_FILE_COMPRESSION
45 : | FILE_SUPPORTS_BLOCK_REFCOUNTING);
46 0 : *_ts_res = ts_res;
47 :
48 0 : 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 : uint32_t flags;
90 : uint64_t xferlen;
91 : DATA_BLOB token;
92 : };
93 :
94 : static void btrfs_offload_read_done(struct tevent_req *subreq);
95 :
96 0 : static struct tevent_req *btrfs_offload_read_send(
97 : TALLOC_CTX *mem_ctx,
98 : struct tevent_context *ev,
99 : struct vfs_handle_struct *handle,
100 : files_struct *fsp,
101 : uint32_t fsctl,
102 : uint32_t ttl,
103 : off_t offset,
104 : size_t to_copy)
105 : {
106 0 : struct tevent_req *req = NULL;
107 0 : struct tevent_req *subreq = NULL;
108 0 : struct btrfs_offload_read_state *state = NULL;
109 : NTSTATUS status;
110 :
111 0 : req = tevent_req_create(mem_ctx, &state,
112 : struct btrfs_offload_read_state);
113 0 : if (req == NULL) {
114 0 : return NULL;
115 : }
116 0 : *state = (struct btrfs_offload_read_state) {
117 : .handle = handle,
118 : .fsp = fsp,
119 : };
120 :
121 0 : status = vfs_offload_token_ctx_init(fsp->conn->sconn->client,
122 : &btrfs_offload_ctx);
123 0 : if (tevent_req_nterror(req, status)) {
124 0 : return tevent_req_post(req, ev);
125 : }
126 :
127 0 : if (fsctl == FSCTL_DUP_EXTENTS_TO_FILE) {
128 0 : status = vfs_offload_token_create_blob(state, fsp, fsctl,
129 0 : &state->token);
130 0 : if (tevent_req_nterror(req, status)) {
131 0 : return tevent_req_post(req, ev);
132 : }
133 :
134 0 : status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx, fsp,
135 0 : &state->token);
136 0 : if (tevent_req_nterror(req, status)) {
137 0 : return tevent_req_post(req, ev);
138 : }
139 0 : tevent_req_done(req);
140 0 : return tevent_req_post(req, ev);
141 : }
142 :
143 0 : subreq = SMB_VFS_NEXT_OFFLOAD_READ_SEND(mem_ctx, ev, handle, fsp,
144 : fsctl, ttl, offset, to_copy);
145 0 : if (tevent_req_nomem(subreq, req)) {
146 0 : return tevent_req_post(req, ev);
147 : }
148 0 : tevent_req_set_callback(subreq, btrfs_offload_read_done, req);
149 0 : return req;
150 : }
151 :
152 0 : static void btrfs_offload_read_done(struct tevent_req *subreq)
153 : {
154 0 : struct tevent_req *req = tevent_req_callback_data(
155 : subreq, struct tevent_req);
156 0 : struct btrfs_offload_read_state *state = tevent_req_data(
157 : req, struct btrfs_offload_read_state);
158 : NTSTATUS status;
159 :
160 0 : status = SMB_VFS_NEXT_OFFLOAD_READ_RECV(subreq,
161 : state->handle,
162 : state,
163 : &state->flags,
164 : &state->xferlen,
165 : &state->token);
166 0 : TALLOC_FREE(subreq);
167 0 : if (tevent_req_nterror(req, status)) {
168 0 : return;
169 : }
170 :
171 0 : status = vfs_offload_token_db_store_fsp(btrfs_offload_ctx,
172 0 : state->fsp,
173 0 : &state->token);
174 0 : if (tevent_req_nterror(req, status)) {
175 0 : return;
176 : }
177 :
178 0 : tevent_req_done(req);
179 0 : return;
180 : }
181 :
182 0 : static NTSTATUS btrfs_offload_read_recv(struct tevent_req *req,
183 : struct vfs_handle_struct *handle,
184 : TALLOC_CTX *mem_ctx,
185 : uint32_t *flags,
186 : uint64_t *xferlen,
187 : DATA_BLOB *token)
188 : {
189 0 : struct btrfs_offload_read_state *state = tevent_req_data(
190 : req, struct btrfs_offload_read_state);
191 : NTSTATUS status;
192 :
193 0 : if (tevent_req_is_nterror(req, &status)) {
194 0 : tevent_req_received(req);
195 0 : return status;
196 : }
197 :
198 0 : *flags = state->flags;
199 0 : *xferlen = state->xferlen;
200 0 : token->length = state->token.length;
201 0 : token->data = talloc_move(mem_ctx, &state->token.data);
202 :
203 0 : tevent_req_received(req);
204 0 : return NT_STATUS_OK;
205 : }
206 :
207 : struct btrfs_offload_write_state {
208 : struct vfs_handle_struct *handle;
209 : off_t copied;
210 : bool need_unbecome_user;
211 : };
212 :
213 0 : static void btrfs_offload_write_cleanup(struct tevent_req *req,
214 : enum tevent_req_state req_state)
215 : {
216 : struct btrfs_offload_write_state *state =
217 0 : tevent_req_data(req,
218 : struct btrfs_offload_write_state);
219 : bool ok;
220 :
221 0 : if (!state->need_unbecome_user) {
222 0 : return;
223 : }
224 :
225 0 : ok = unbecome_user_without_service();
226 0 : SMB_ASSERT(ok);
227 0 : state->need_unbecome_user = false;
228 : }
229 :
230 : static void btrfs_offload_write_done(struct tevent_req *subreq);
231 :
232 0 : static struct tevent_req *btrfs_offload_write_send(struct vfs_handle_struct *handle,
233 : TALLOC_CTX *mem_ctx,
234 : struct tevent_context *ev,
235 : uint32_t fsctl,
236 : DATA_BLOB *token,
237 : off_t transfer_offset,
238 : struct files_struct *dest_fsp,
239 : off_t dest_off,
240 : off_t num)
241 : {
242 0 : struct tevent_req *req = NULL;
243 0 : struct btrfs_offload_write_state *state = NULL;
244 0 : struct tevent_req *subreq = NULL;
245 : struct btrfs_ioctl_clone_range_args cr_args;
246 : struct lock_struct src_lck;
247 : struct lock_struct dest_lck;
248 0 : off_t src_off = transfer_offset;
249 0 : files_struct *src_fsp = NULL;
250 : int ret;
251 0 : bool handle_offload_write = true;
252 0 : bool do_locking = false;
253 : NTSTATUS status;
254 : bool ok;
255 :
256 0 : req = tevent_req_create(mem_ctx, &state,
257 : struct btrfs_offload_write_state);
258 0 : if (req == NULL) {
259 0 : return NULL;
260 : }
261 :
262 0 : state->handle = handle;
263 :
264 0 : tevent_req_set_cleanup_fn(req, btrfs_offload_write_cleanup);
265 :
266 0 : status = vfs_offload_token_db_fetch_fsp(btrfs_offload_ctx,
267 : token, &src_fsp);
268 0 : if (tevent_req_nterror(req, status)) {
269 0 : return tevent_req_post(req, ev);
270 : }
271 :
272 0 : switch (fsctl) {
273 0 : case FSCTL_SRV_COPYCHUNK:
274 : case FSCTL_SRV_COPYCHUNK_WRITE:
275 0 : do_locking = true;
276 0 : break;
277 :
278 0 : case FSCTL_DUP_EXTENTS_TO_FILE:
279 : /* dup extents does not use locking */
280 0 : break;
281 :
282 0 : default:
283 0 : handle_offload_write = false;
284 0 : break;
285 : }
286 :
287 0 : if (num == 0) {
288 : /*
289 : * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
290 : * all data from @src_offset->EOF! This is certainly not what
291 : * the caller expects, and not what vfs_default does.
292 : */
293 0 : handle_offload_write = false;
294 : }
295 :
296 0 : if (!handle_offload_write) {
297 0 : subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
298 : state,
299 : ev,
300 : fsctl,
301 : token,
302 : transfer_offset,
303 : dest_fsp,
304 : dest_off,
305 : num);
306 0 : if (tevent_req_nomem(subreq, req)) {
307 0 : return tevent_req_post(req, ev);
308 : }
309 0 : tevent_req_set_callback(subreq,
310 : btrfs_offload_write_done,
311 : req);
312 0 : return req;
313 : }
314 :
315 0 : status = vfs_offload_token_check_handles(
316 : fsctl, src_fsp, dest_fsp);
317 0 : if (!NT_STATUS_IS_OK(status)) {
318 0 : tevent_req_nterror(req, status);
319 0 : return tevent_req_post(req, ev);
320 : }
321 :
322 0 : ok = become_user_without_service_by_fsp(src_fsp);
323 0 : if (!ok) {
324 0 : tevent_req_nterror(req, NT_STATUS_ACCESS_DENIED);
325 0 : return tevent_req_post(req, ev);
326 : }
327 0 : state->need_unbecome_user = true;
328 :
329 0 : status = vfs_stat_fsp(src_fsp);
330 0 : if (tevent_req_nterror(req, status)) {
331 0 : return tevent_req_post(req, ev);
332 : }
333 :
334 0 : if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
335 : /* [MS-SMB2] Handling a Server-Side Data Copy Request */
336 0 : tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
337 0 : return tevent_req_post(req, ev);
338 : }
339 :
340 0 : if (do_locking) {
341 0 : init_strict_lock_struct(src_fsp,
342 0 : src_fsp->op->global->open_persistent_id,
343 : src_off,
344 : num,
345 : READ_LOCK,
346 : lp_posix_cifsu_locktype(src_fsp),
347 : &src_lck);
348 0 : if (!SMB_VFS_STRICT_LOCK_CHECK(src_fsp->conn, src_fsp, &src_lck)) {
349 0 : tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
350 0 : return tevent_req_post(req, ev);
351 : }
352 : }
353 :
354 0 : ok = unbecome_user_without_service();
355 0 : SMB_ASSERT(ok);
356 0 : state->need_unbecome_user = false;
357 :
358 0 : if (do_locking) {
359 0 : init_strict_lock_struct(dest_fsp,
360 0 : dest_fsp->op->global->open_persistent_id,
361 : dest_off,
362 : num,
363 : WRITE_LOCK,
364 : lp_posix_cifsu_locktype(dest_fsp),
365 : &dest_lck);
366 :
367 0 : if (!SMB_VFS_STRICT_LOCK_CHECK(dest_fsp->conn, dest_fsp, &dest_lck)) {
368 0 : tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
369 0 : return tevent_req_post(req, ev);
370 : }
371 : }
372 :
373 0 : ZERO_STRUCT(cr_args);
374 0 : cr_args.src_fd = fsp_get_io_fd(src_fsp);
375 0 : cr_args.src_offset = (uint64_t)src_off;
376 0 : cr_args.dest_offset = (uint64_t)dest_off;
377 0 : cr_args.src_length = (uint64_t)num;
378 :
379 0 : ret = ioctl(fsp_get_io_fd(dest_fsp), BTRFS_IOC_CLONE_RANGE, &cr_args);
380 0 : if (ret < 0) {
381 : /*
382 : * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
383 : * cloning. Which is 4096 by default, therefore fall back to
384 : * manual read/write on failure.
385 : */
386 0 : DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
387 : "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
388 : strerror(errno),
389 : (unsigned long long)cr_args.src_length,
390 : (long long)cr_args.src_fd,
391 : (unsigned long long)cr_args.src_offset,
392 : fsp_get_io_fd(dest_fsp),
393 : (unsigned long long)cr_args.dest_offset));
394 0 : subreq = SMB_VFS_NEXT_OFFLOAD_WRITE_SEND(handle,
395 : state,
396 : ev,
397 : fsctl,
398 : token,
399 : transfer_offset,
400 : dest_fsp,
401 : dest_off,
402 : num);
403 0 : if (tevent_req_nomem(subreq, req)) {
404 0 : return tevent_req_post(req, ev);
405 : }
406 : /* wait for subreq completion */
407 0 : tevent_req_set_callback(subreq,
408 : btrfs_offload_write_done,
409 : req);
410 0 : return req;
411 : }
412 :
413 0 : DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
414 : /* BTRFS_IOC_CLONE_RANGE is all or nothing */
415 0 : state->copied = num;
416 0 : tevent_req_done(req);
417 0 : return tevent_req_post(req, ev);
418 : }
419 :
420 : /* only used if the request is passed through to next VFS module */
421 0 : static void btrfs_offload_write_done(struct tevent_req *subreq)
422 : {
423 : struct tevent_req *req =
424 0 : tevent_req_callback_data(subreq,
425 : struct tevent_req);
426 : struct btrfs_offload_write_state *state =
427 0 : tevent_req_data(req,
428 : struct btrfs_offload_write_state);
429 : NTSTATUS status;
430 :
431 0 : status = SMB_VFS_NEXT_OFFLOAD_WRITE_RECV(state->handle,
432 : subreq,
433 : &state->copied);
434 0 : TALLOC_FREE(subreq);
435 0 : if (tevent_req_nterror(req, status)) {
436 0 : return;
437 : }
438 0 : tevent_req_done(req);
439 : }
440 :
441 0 : static NTSTATUS btrfs_offload_write_recv(struct vfs_handle_struct *handle,
442 : struct tevent_req *req,
443 : off_t *copied)
444 : {
445 : struct btrfs_offload_write_state *state =
446 0 : tevent_req_data(req,
447 : struct btrfs_offload_write_state);
448 : NTSTATUS status;
449 :
450 0 : if (tevent_req_is_nterror(req, &status)) {
451 0 : DEBUG(4, ("server side copy chunk failed: %s\n",
452 : nt_errstr(status)));
453 0 : tevent_req_received(req);
454 0 : return status;
455 : }
456 :
457 0 : DEBUG(10, ("server side copy chunk copied %llu\n",
458 : (unsigned long long)state->copied));
459 0 : *copied = state->copied;
460 0 : tevent_req_received(req);
461 0 : return NT_STATUS_OK;
462 : }
463 :
464 0 : static NTSTATUS btrfs_fget_compression(struct vfs_handle_struct *handle,
465 : TALLOC_CTX *mem_ctx,
466 : struct files_struct *fsp,
467 : uint16_t *_compression_fmt)
468 : {
469 : struct sys_proc_fd_path_buf buf;
470 : int ret;
471 0 : long flags = 0;
472 0 : int fsp_fd = fsp_get_pathref_fd(fsp);
473 0 : int fd = -1;
474 : NTSTATUS status;
475 :
476 0 : if (!fsp->fsp_flags.is_pathref) {
477 0 : ret = ioctl(fsp_fd, FS_IOC_GETFLAGS, &flags);
478 0 : if (ret < 0) {
479 0 : DBG_WARNING("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
480 : strerror(errno), (long long)fd);
481 0 : return map_nt_error_from_unix(errno);
482 : }
483 0 : if (flags & FS_COMPR_FL) {
484 0 : *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
485 : } else {
486 0 : *_compression_fmt = COMPRESSION_FORMAT_NONE;
487 : }
488 0 : return NT_STATUS_OK;
489 : }
490 :
491 0 : if (!fsp->fsp_flags.have_proc_fds) {
492 0 : return NT_STATUS_NOT_IMPLEMENTED;
493 : }
494 :
495 0 : fd = open(sys_proc_fd_path(fsp_fd, &buf), O_RDONLY);
496 0 : if (fd == -1) {
497 0 : DBG_DEBUG("/proc open of %s failed: %s\n",
498 : buf.buf,
499 : strerror(errno));
500 0 : return map_nt_error_from_unix(errno);
501 : }
502 :
503 0 : ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
504 0 : if (ret < 0) {
505 0 : DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
506 : strerror(errno), (long long)fd));
507 0 : status = map_nt_error_from_unix(errno);
508 0 : goto err_close;
509 : }
510 0 : if (flags & FS_COMPR_FL) {
511 0 : *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
512 : } else {
513 0 : *_compression_fmt = COMPRESSION_FORMAT_NONE;
514 : }
515 0 : status = NT_STATUS_OK;
516 :
517 0 : err_close:
518 0 : if (fd != -1) {
519 0 : close(fd);
520 : }
521 :
522 0 : return status;
523 : }
524 :
525 0 : static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
526 : TALLOC_CTX *mem_ctx,
527 : struct files_struct *fsp,
528 : uint16_t compression_fmt)
529 : {
530 : int ret;
531 0 : long flags = 0;
532 : int fd;
533 : NTSTATUS status;
534 :
535 0 : if ((fsp == NULL) || (fsp_get_io_fd(fsp) == -1)) {
536 0 : status = NT_STATUS_INVALID_PARAMETER;
537 0 : goto err_out;
538 : }
539 0 : fd = fsp_get_io_fd(fsp);
540 :
541 0 : ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
542 0 : if (ret < 0) {
543 0 : DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
544 : strerror(errno), fd));
545 0 : status = map_nt_error_from_unix(errno);
546 0 : goto err_out;
547 : }
548 :
549 0 : if (compression_fmt == COMPRESSION_FORMAT_NONE) {
550 0 : DEBUG(5, ("setting compression\n"));
551 0 : flags &= (~FS_COMPR_FL);
552 0 : } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
553 0 : || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
554 0 : DEBUG(5, ("clearing compression\n"));
555 0 : flags |= FS_COMPR_FL;
556 : } else {
557 0 : DEBUG(1, ("invalid compression format 0x%x\n",
558 : (int)compression_fmt));
559 0 : status = NT_STATUS_INVALID_PARAMETER;
560 0 : goto err_out;
561 : }
562 :
563 0 : ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
564 0 : if (ret < 0) {
565 0 : DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
566 : strerror(errno), fd));
567 0 : status = map_nt_error_from_unix(errno);
568 0 : goto err_out;
569 : }
570 0 : status = NT_STATUS_OK;
571 0 : err_out:
572 0 : return status;
573 : }
574 :
575 : /*
576 : * Check whether a path can be shadow copied. Return the base volume, allowing
577 : * the caller to determine if multiple paths lie on the same base volume.
578 : */
579 : #define BTRFS_INODE_SUBVOL 256
580 0 : static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
581 : TALLOC_CTX *mem_ctx,
582 : const char *service_path,
583 : char **base_volume)
584 : {
585 : struct stat st;
586 : char *base;
587 :
588 0 : if (!lp_parm_bool(SNUM(handle->conn),
589 : "btrfs", "manipulate snapshots", false)) {
590 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
591 0 : return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
592 : service_path, base_volume);
593 : }
594 :
595 : /* btrfs userspace uses this logic to confirm subvolume */
596 0 : if (stat(service_path, &st) < 0) {
597 0 : return NT_STATUS_NOT_SUPPORTED;
598 : }
599 0 : if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
600 0 : DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
601 : service_path));
602 0 : return NT_STATUS_NOT_SUPPORTED;
603 : }
604 :
605 : /* we "snapshot" the service path itself */
606 0 : base = talloc_strdup(mem_ctx, service_path);
607 0 : if (base == NULL) {
608 0 : return NT_STATUS_NO_MEMORY;
609 : }
610 0 : *base_volume = base;
611 :
612 0 : return NT_STATUS_OK;
613 : }
614 :
615 0 : static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
616 : const char *src_path,
617 : time_t *tstamp,
618 : char **dest_path, char **subvolume)
619 : {
620 : struct tm t_gmt;
621 : char time_str[50];
622 : size_t tlen;
623 :
624 0 : gmtime_r(tstamp, &t_gmt);
625 :
626 0 : tlen = strftime(time_str, ARRAY_SIZE(time_str),
627 : SHADOW_COPY_PATH_FORMAT, &t_gmt);
628 0 : if (tlen <= 0) {
629 0 : return NT_STATUS_UNSUCCESSFUL;
630 : }
631 :
632 0 : *dest_path = talloc_strdup(mem_ctx, src_path);
633 0 : *subvolume = talloc_strdup(mem_ctx, time_str);
634 0 : if ((*dest_path == NULL) || (*subvolume == NULL)) {
635 0 : return NT_STATUS_NO_MEMORY;
636 : }
637 :
638 0 : return NT_STATUS_OK;
639 : }
640 :
641 0 : static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
642 : TALLOC_CTX *mem_ctx,
643 : const char *base_volume,
644 : time_t *tstamp,
645 : bool rw,
646 : char **_base_path,
647 : char **_snap_path)
648 : {
649 : struct btrfs_ioctl_vol_args_v2 ioctl_arg;
650 : DIR *src_dir;
651 : DIR *dest_dir;
652 : int src_fd;
653 : int dest_fd;
654 0 : char *dest_path = NULL;
655 0 : char *dest_subvolume = NULL;
656 : int ret;
657 : NTSTATUS status;
658 : char *base_path;
659 : char *snap_path;
660 : TALLOC_CTX *tmp_ctx;
661 : int saved_errno;
662 : size_t len;
663 :
664 0 : if (!lp_parm_bool(SNUM(handle->conn),
665 : "btrfs", "manipulate snapshots", false)) {
666 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
667 0 : return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
668 : tstamp, rw, _base_path,
669 : _snap_path);
670 : }
671 :
672 0 : tmp_ctx = talloc_new(mem_ctx);
673 0 : if (tmp_ctx == NULL) {
674 0 : return NT_STATUS_NO_MEMORY;
675 : }
676 :
677 0 : base_path = talloc_strdup(tmp_ctx, base_volume);
678 0 : if (base_path == NULL) {
679 0 : talloc_free(tmp_ctx);
680 0 : return NT_STATUS_NO_MEMORY;
681 : }
682 :
683 0 : status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
684 : &dest_path, &dest_subvolume);
685 0 : if (!NT_STATUS_IS_OK(status)) {
686 0 : talloc_free(tmp_ctx);
687 0 : return status;
688 : }
689 :
690 0 : snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
691 : dest_subvolume);
692 0 : if (snap_path == NULL) {
693 0 : talloc_free(tmp_ctx);
694 0 : return NT_STATUS_NO_MEMORY;
695 : }
696 :
697 0 : src_dir = opendir(base_volume);
698 0 : if (src_dir == NULL) {
699 0 : DEBUG(0, ("snap src %s open failed: %s\n",
700 : base_volume, strerror(errno)));
701 0 : status = map_nt_error_from_unix(errno);
702 0 : talloc_free(tmp_ctx);
703 0 : return status;
704 : }
705 0 : src_fd = dirfd(src_dir);
706 0 : if (src_fd < 0) {
707 0 : status = map_nt_error_from_unix(errno);
708 0 : closedir(src_dir);
709 0 : talloc_free(tmp_ctx);
710 0 : return status;
711 : }
712 :
713 0 : dest_dir = opendir(dest_path);
714 0 : if (dest_dir == NULL) {
715 0 : DEBUG(0, ("snap dest %s open failed: %s\n",
716 : dest_path, strerror(errno)));
717 0 : status = map_nt_error_from_unix(errno);
718 0 : closedir(src_dir);
719 0 : talloc_free(tmp_ctx);
720 0 : return status;
721 : }
722 0 : dest_fd = dirfd(dest_dir);
723 0 : if (dest_fd < 0) {
724 0 : status = map_nt_error_from_unix(errno);
725 0 : closedir(src_dir);
726 0 : closedir(dest_dir);
727 0 : talloc_free(tmp_ctx);
728 0 : return status;
729 : }
730 :
731 : /* avoid zeroing the entire struct here, name is 4k */
732 0 : ioctl_arg.fd = src_fd;
733 0 : ioctl_arg.transid = 0;
734 0 : ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
735 0 : memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
736 0 : len = strlcpy(ioctl_arg.name, dest_subvolume,
737 : ARRAY_SIZE(ioctl_arg.name));
738 0 : if (len >= ARRAY_SIZE(ioctl_arg.name)) {
739 0 : DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
740 0 : closedir(src_dir);
741 0 : closedir(dest_dir);
742 0 : talloc_free(tmp_ctx);
743 0 : return NT_STATUS_INVALID_PARAMETER;
744 : }
745 :
746 0 : become_root();
747 0 : ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
748 0 : saved_errno = errno;
749 0 : unbecome_root();
750 0 : if (ret < 0) {
751 0 : DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
752 : base_volume, dest_path, dest_subvolume,
753 : strerror(saved_errno)));
754 0 : status = map_nt_error_from_unix(saved_errno);
755 0 : closedir(src_dir);
756 0 : closedir(dest_dir);
757 0 : talloc_free(tmp_ctx);
758 0 : return status;
759 : }
760 0 : DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
761 : base_volume, dest_path, dest_subvolume));
762 :
763 0 : *_base_path = talloc_steal(mem_ctx, base_path);
764 0 : *_snap_path = talloc_steal(mem_ctx, snap_path);
765 0 : closedir(src_dir);
766 0 : closedir(dest_dir);
767 0 : talloc_free(tmp_ctx);
768 :
769 0 : return NT_STATUS_OK;
770 : }
771 :
772 0 : static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
773 : TALLOC_CTX *mem_ctx,
774 : char *base_path,
775 : char *snap_path)
776 : {
777 : char *tstr;
778 0 : struct tm t_gmt = {};
779 : DIR *dest_dir;
780 : int dest_fd;
781 : struct btrfs_ioctl_vol_args ioctl_arg;
782 : int ret;
783 : NTSTATUS status;
784 : char *dest_path;
785 : char *subvolume;
786 : TALLOC_CTX *tmp_ctx;
787 : int saved_errno;
788 : size_t len;
789 :
790 0 : if (!lp_parm_bool(SNUM(handle->conn),
791 : "btrfs", "manipulate snapshots", false)) {
792 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
793 0 : return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
794 : base_path, snap_path);
795 : }
796 :
797 0 : tmp_ctx = talloc_new(mem_ctx);
798 0 : if (tmp_ctx == NULL) {
799 0 : return NT_STATUS_NO_MEMORY;
800 : }
801 :
802 0 : dest_path = talloc_strdup(tmp_ctx, snap_path);
803 0 : if (dest_path == NULL) {
804 0 : talloc_free(tmp_ctx);
805 0 : return NT_STATUS_NO_MEMORY;
806 : }
807 0 : subvolume = talloc_strdup(tmp_ctx, snap_path);
808 0 : if (subvolume == NULL) {
809 0 : talloc_free(tmp_ctx);
810 0 : return NT_STATUS_NO_MEMORY;
811 : }
812 0 : dest_path = dirname(dest_path);
813 0 : subvolume = basename(subvolume);
814 :
815 : /* confirm snap_path matches creation format */
816 0 : tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
817 0 : if ((tstr == NULL) || (*tstr != '\0')) {
818 0 : DEBUG(0, ("snapshot path %s does not match creation format\n",
819 : snap_path));
820 0 : talloc_free(tmp_ctx);
821 0 : return NT_STATUS_UNSUCCESSFUL;
822 : }
823 :
824 0 : dest_dir = opendir(dest_path);
825 0 : if (dest_dir == NULL) {
826 0 : DEBUG(0, ("snap destroy dest %s open failed: %s\n",
827 : dest_path, strerror(errno)));
828 0 : status = map_nt_error_from_unix(errno);
829 0 : talloc_free(tmp_ctx);
830 0 : return status;
831 : }
832 0 : dest_fd = dirfd(dest_dir);
833 0 : if (dest_fd < 0) {
834 0 : status = map_nt_error_from_unix(errno);
835 0 : closedir(dest_dir);
836 0 : talloc_free(tmp_ctx);
837 0 : return status;
838 : }
839 :
840 0 : ioctl_arg.fd = -1; /* not needed */
841 0 : len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
842 0 : if (len >= ARRAY_SIZE(ioctl_arg.name)) {
843 0 : DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
844 0 : closedir(dest_dir);
845 0 : talloc_free(tmp_ctx);
846 0 : return NT_STATUS_INVALID_PARAMETER;
847 : }
848 :
849 0 : become_root();
850 0 : ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
851 0 : saved_errno = errno;
852 0 : unbecome_root();
853 0 : if (ret < 0) {
854 0 : DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
855 : dest_path, subvolume, strerror(saved_errno)));
856 0 : status = map_nt_error_from_unix(saved_errno);
857 0 : closedir(dest_dir);
858 0 : talloc_free(tmp_ctx);
859 0 : return status;
860 : }
861 0 : DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
862 : dest_path, subvolume));
863 :
864 0 : closedir(dest_dir);
865 0 : talloc_free(tmp_ctx);
866 0 : return NT_STATUS_OK;
867 : }
868 :
869 : static struct vfs_fn_pointers btrfs_fns = {
870 : .fs_capabilities_fn = btrfs_fs_capabilities,
871 : .offload_read_send_fn = btrfs_offload_read_send,
872 : .offload_read_recv_fn = btrfs_offload_read_recv,
873 : .offload_write_send_fn = btrfs_offload_write_send,
874 : .offload_write_recv_fn = btrfs_offload_write_recv,
875 : .fget_compression_fn = btrfs_fget_compression,
876 : .set_compression_fn = btrfs_set_compression,
877 : .snap_check_path_fn = btrfs_snap_check_path,
878 : .snap_create_fn = btrfs_snap_create,
879 : .snap_delete_fn = btrfs_snap_delete,
880 : };
881 :
882 : static_decl_vfs;
883 27 : NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
884 : {
885 27 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
886 : "btrfs", &btrfs_fns);
887 : }
|