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 0 : 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 0 : struct tevent_req *req =
424 0 : tevent_req_callback_data(subreq,
425 : struct tevent_req);
426 0 : 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 0 : 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 : char buf[PATH_MAX];
470 0 : const char *p = NULL;
471 : int ret;
472 0 : long flags = 0;
473 0 : int fsp_fd = fsp_get_pathref_fd(fsp);
474 0 : int fd = -1;
475 : NTSTATUS status;
476 :
477 0 : if (!fsp->fsp_flags.is_pathref) {
478 0 : ret = ioctl(fsp_fd, FS_IOC_GETFLAGS, &flags);
479 0 : if (ret < 0) {
480 0 : DBG_WARNING("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
481 : strerror(errno), (long long)fd);
482 0 : return map_nt_error_from_unix(errno);
483 : }
484 0 : if (flags & FS_COMPR_FL) {
485 0 : *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
486 : } else {
487 0 : *_compression_fmt = COMPRESSION_FORMAT_NONE;
488 : }
489 0 : return NT_STATUS_OK;
490 : }
491 :
492 0 : if (!fsp->fsp_flags.have_proc_fds) {
493 0 : return NT_STATUS_NOT_IMPLEMENTED;
494 : }
495 :
496 0 : p = sys_proc_fd_path(fsp_fd, buf, sizeof(buf));
497 0 : if (p == NULL) {
498 0 : return NT_STATUS_NO_MEMORY;
499 : }
500 :
501 0 : fd = open(p, O_RDONLY);
502 0 : if (fd == -1) {
503 0 : DBG_DEBUG("/proc open of %s failed: %s\n", p, strerror(errno));
504 0 : return map_nt_error_from_unix(errno);
505 : }
506 :
507 0 : ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
508 0 : if (ret < 0) {
509 0 : DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
510 : strerror(errno), (long long)fd));
511 0 : status = map_nt_error_from_unix(errno);
512 0 : goto err_close;
513 : }
514 0 : if (flags & FS_COMPR_FL) {
515 0 : *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
516 : } else {
517 0 : *_compression_fmt = COMPRESSION_FORMAT_NONE;
518 : }
519 0 : status = NT_STATUS_OK;
520 :
521 0 : err_close:
522 0 : if (fd != -1) {
523 0 : close(fd);
524 : }
525 :
526 0 : return status;
527 : }
528 :
529 0 : static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
530 : TALLOC_CTX *mem_ctx,
531 : struct files_struct *fsp,
532 : uint16_t compression_fmt)
533 : {
534 : int ret;
535 0 : long flags = 0;
536 : int fd;
537 : NTSTATUS status;
538 :
539 0 : if ((fsp == NULL) || (fsp_get_io_fd(fsp) == -1)) {
540 0 : status = NT_STATUS_INVALID_PARAMETER;
541 0 : goto err_out;
542 : }
543 0 : fd = fsp_get_io_fd(fsp);
544 :
545 0 : ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
546 0 : if (ret < 0) {
547 0 : DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
548 : strerror(errno), fd));
549 0 : status = map_nt_error_from_unix(errno);
550 0 : goto err_out;
551 : }
552 :
553 0 : if (compression_fmt == COMPRESSION_FORMAT_NONE) {
554 0 : DEBUG(5, ("setting compression\n"));
555 0 : flags &= (~FS_COMPR_FL);
556 0 : } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
557 0 : || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
558 0 : DEBUG(5, ("clearing compression\n"));
559 0 : flags |= FS_COMPR_FL;
560 : } else {
561 0 : DEBUG(1, ("invalid compression format 0x%x\n",
562 : (int)compression_fmt));
563 0 : status = NT_STATUS_INVALID_PARAMETER;
564 0 : goto err_out;
565 : }
566 :
567 0 : ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
568 0 : if (ret < 0) {
569 0 : DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
570 : strerror(errno), fd));
571 0 : status = map_nt_error_from_unix(errno);
572 0 : goto err_out;
573 : }
574 0 : status = NT_STATUS_OK;
575 0 : err_out:
576 0 : return status;
577 : }
578 :
579 : /*
580 : * Check whether a path can be shadow copied. Return the base volume, allowing
581 : * the caller to determine if multiple paths lie on the same base volume.
582 : */
583 : #define BTRFS_INODE_SUBVOL 256
584 0 : static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
585 : TALLOC_CTX *mem_ctx,
586 : const char *service_path,
587 : char **base_volume)
588 : {
589 : struct stat st;
590 : char *base;
591 :
592 0 : if (!lp_parm_bool(SNUM(handle->conn),
593 : "btrfs", "manipulate snapshots", false)) {
594 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
595 0 : return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
596 : service_path, base_volume);
597 : }
598 :
599 : /* btrfs userspace uses this logic to confirm subvolume */
600 0 : if (stat(service_path, &st) < 0) {
601 0 : return NT_STATUS_NOT_SUPPORTED;
602 : }
603 0 : if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
604 0 : DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
605 : service_path));
606 0 : return NT_STATUS_NOT_SUPPORTED;
607 : }
608 :
609 : /* we "snapshot" the service path itself */
610 0 : base = talloc_strdup(mem_ctx, service_path);
611 0 : if (base == NULL) {
612 0 : return NT_STATUS_NO_MEMORY;
613 : }
614 0 : *base_volume = base;
615 :
616 0 : return NT_STATUS_OK;
617 : }
618 :
619 0 : static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
620 : const char *src_path,
621 : time_t *tstamp,
622 : char **dest_path, char **subvolume)
623 : {
624 : struct tm t_gmt;
625 : char time_str[50];
626 : size_t tlen;
627 :
628 0 : gmtime_r(tstamp, &t_gmt);
629 :
630 0 : tlen = strftime(time_str, ARRAY_SIZE(time_str),
631 : SHADOW_COPY_PATH_FORMAT, &t_gmt);
632 0 : if (tlen <= 0) {
633 0 : return NT_STATUS_UNSUCCESSFUL;
634 : }
635 :
636 0 : *dest_path = talloc_strdup(mem_ctx, src_path);
637 0 : *subvolume = talloc_strdup(mem_ctx, time_str);
638 0 : if ((*dest_path == NULL) || (*subvolume == NULL)) {
639 0 : return NT_STATUS_NO_MEMORY;
640 : }
641 :
642 0 : return NT_STATUS_OK;
643 : }
644 :
645 0 : static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
646 : TALLOC_CTX *mem_ctx,
647 : const char *base_volume,
648 : time_t *tstamp,
649 : bool rw,
650 : char **_base_path,
651 : char **_snap_path)
652 : {
653 : struct btrfs_ioctl_vol_args_v2 ioctl_arg;
654 : DIR *src_dir;
655 : DIR *dest_dir;
656 : int src_fd;
657 : int dest_fd;
658 0 : char *dest_path = NULL;
659 0 : char *dest_subvolume = NULL;
660 : int ret;
661 : NTSTATUS status;
662 : char *base_path;
663 : char *snap_path;
664 : TALLOC_CTX *tmp_ctx;
665 : int saved_errno;
666 : size_t len;
667 :
668 0 : if (!lp_parm_bool(SNUM(handle->conn),
669 : "btrfs", "manipulate snapshots", false)) {
670 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
671 0 : return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
672 : tstamp, rw, _base_path,
673 : _snap_path);
674 : }
675 :
676 0 : tmp_ctx = talloc_new(mem_ctx);
677 0 : if (tmp_ctx == NULL) {
678 0 : return NT_STATUS_NO_MEMORY;
679 : }
680 :
681 0 : base_path = talloc_strdup(tmp_ctx, base_volume);
682 0 : if (base_path == NULL) {
683 0 : talloc_free(tmp_ctx);
684 0 : return NT_STATUS_NO_MEMORY;
685 : }
686 :
687 0 : status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
688 : &dest_path, &dest_subvolume);
689 0 : if (!NT_STATUS_IS_OK(status)) {
690 0 : talloc_free(tmp_ctx);
691 0 : return status;
692 : }
693 :
694 0 : snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
695 : dest_subvolume);
696 0 : if (snap_path == NULL) {
697 0 : talloc_free(tmp_ctx);
698 0 : return NT_STATUS_NO_MEMORY;
699 : }
700 :
701 0 : src_dir = opendir(base_volume);
702 0 : if (src_dir == NULL) {
703 0 : DEBUG(0, ("snap src %s open failed: %s\n",
704 : base_volume, strerror(errno)));
705 0 : status = map_nt_error_from_unix(errno);
706 0 : talloc_free(tmp_ctx);
707 0 : return status;
708 : }
709 0 : src_fd = dirfd(src_dir);
710 0 : if (src_fd < 0) {
711 0 : status = map_nt_error_from_unix(errno);
712 0 : closedir(src_dir);
713 0 : talloc_free(tmp_ctx);
714 0 : return status;
715 : }
716 :
717 0 : dest_dir = opendir(dest_path);
718 0 : if (dest_dir == NULL) {
719 0 : DEBUG(0, ("snap dest %s open failed: %s\n",
720 : dest_path, strerror(errno)));
721 0 : status = map_nt_error_from_unix(errno);
722 0 : closedir(src_dir);
723 0 : talloc_free(tmp_ctx);
724 0 : return status;
725 : }
726 0 : dest_fd = dirfd(dest_dir);
727 0 : if (dest_fd < 0) {
728 0 : status = map_nt_error_from_unix(errno);
729 0 : closedir(src_dir);
730 0 : closedir(dest_dir);
731 0 : talloc_free(tmp_ctx);
732 0 : return status;
733 : }
734 :
735 : /* avoid zeroing the entire struct here, name is 4k */
736 0 : ioctl_arg.fd = src_fd;
737 0 : ioctl_arg.transid = 0;
738 0 : ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
739 0 : memset(ioctl_arg.unused, 0, sizeof(ioctl_arg.unused));
740 0 : len = strlcpy(ioctl_arg.name, dest_subvolume,
741 : ARRAY_SIZE(ioctl_arg.name));
742 0 : if (len >= ARRAY_SIZE(ioctl_arg.name)) {
743 0 : DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
744 0 : closedir(src_dir);
745 0 : closedir(dest_dir);
746 0 : talloc_free(tmp_ctx);
747 0 : return NT_STATUS_INVALID_PARAMETER;
748 : }
749 :
750 0 : become_root();
751 0 : ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
752 0 : saved_errno = errno;
753 0 : unbecome_root();
754 0 : if (ret < 0) {
755 0 : DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
756 : base_volume, dest_path, dest_subvolume,
757 : strerror(saved_errno)));
758 0 : status = map_nt_error_from_unix(saved_errno);
759 0 : closedir(src_dir);
760 0 : closedir(dest_dir);
761 0 : talloc_free(tmp_ctx);
762 0 : return status;
763 : }
764 0 : DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
765 : base_volume, dest_path, dest_subvolume));
766 :
767 0 : *_base_path = talloc_steal(mem_ctx, base_path);
768 0 : *_snap_path = talloc_steal(mem_ctx, snap_path);
769 0 : closedir(src_dir);
770 0 : closedir(dest_dir);
771 0 : talloc_free(tmp_ctx);
772 :
773 0 : return NT_STATUS_OK;
774 : }
775 :
776 0 : static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
777 : TALLOC_CTX *mem_ctx,
778 : char *base_path,
779 : char *snap_path)
780 : {
781 : char *tstr;
782 : struct tm t_gmt;
783 : DIR *dest_dir;
784 : int dest_fd;
785 : struct btrfs_ioctl_vol_args ioctl_arg;
786 : int ret;
787 : NTSTATUS status;
788 : char *dest_path;
789 : char *subvolume;
790 : TALLOC_CTX *tmp_ctx;
791 : int saved_errno;
792 : size_t len;
793 :
794 0 : if (!lp_parm_bool(SNUM(handle->conn),
795 : "btrfs", "manipulate snapshots", false)) {
796 0 : DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
797 0 : return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
798 : base_path, snap_path);
799 : }
800 :
801 0 : tmp_ctx = talloc_new(mem_ctx);
802 0 : if (tmp_ctx == NULL) {
803 0 : return NT_STATUS_NO_MEMORY;
804 : }
805 :
806 0 : dest_path = talloc_strdup(tmp_ctx, snap_path);
807 0 : if (dest_path == NULL) {
808 0 : talloc_free(tmp_ctx);
809 0 : return NT_STATUS_NO_MEMORY;
810 : }
811 0 : subvolume = talloc_strdup(tmp_ctx, snap_path);
812 0 : if (subvolume == NULL) {
813 0 : talloc_free(tmp_ctx);
814 0 : return NT_STATUS_NO_MEMORY;
815 : }
816 0 : dest_path = dirname(dest_path);
817 0 : subvolume = basename(subvolume);
818 :
819 : /* confirm snap_path matches creation format */
820 0 : tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
821 0 : if ((tstr == NULL) || (*tstr != '\0')) {
822 0 : DEBUG(0, ("snapshot path %s does not match creation format\n",
823 : snap_path));
824 0 : talloc_free(tmp_ctx);
825 0 : return NT_STATUS_UNSUCCESSFUL;
826 : }
827 :
828 0 : dest_dir = opendir(dest_path);
829 0 : if (dest_dir == NULL) {
830 0 : DEBUG(0, ("snap destroy dest %s open failed: %s\n",
831 : dest_path, strerror(errno)));
832 0 : status = map_nt_error_from_unix(errno);
833 0 : talloc_free(tmp_ctx);
834 0 : return status;
835 : }
836 0 : dest_fd = dirfd(dest_dir);
837 0 : if (dest_fd < 0) {
838 0 : status = map_nt_error_from_unix(errno);
839 0 : closedir(dest_dir);
840 0 : talloc_free(tmp_ctx);
841 0 : return status;
842 : }
843 :
844 0 : ioctl_arg.fd = -1; /* not needed */
845 0 : len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
846 0 : if (len >= ARRAY_SIZE(ioctl_arg.name)) {
847 0 : DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
848 0 : closedir(dest_dir);
849 0 : talloc_free(tmp_ctx);
850 0 : return NT_STATUS_INVALID_PARAMETER;
851 : }
852 :
853 0 : become_root();
854 0 : ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
855 0 : saved_errno = errno;
856 0 : unbecome_root();
857 0 : if (ret < 0) {
858 0 : DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
859 : dest_path, subvolume, strerror(saved_errno)));
860 0 : status = map_nt_error_from_unix(saved_errno);
861 0 : closedir(dest_dir);
862 0 : talloc_free(tmp_ctx);
863 0 : return status;
864 : }
865 0 : DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
866 : dest_path, subvolume));
867 :
868 0 : closedir(dest_dir);
869 0 : talloc_free(tmp_ctx);
870 0 : return NT_STATUS_OK;
871 : }
872 :
873 : static struct vfs_fn_pointers btrfs_fns = {
874 : .fs_capabilities_fn = btrfs_fs_capabilities,
875 : .offload_read_send_fn = btrfs_offload_read_send,
876 : .offload_read_recv_fn = btrfs_offload_read_recv,
877 : .offload_write_send_fn = btrfs_offload_write_send,
878 : .offload_write_recv_fn = btrfs_offload_write_recv,
879 : .fget_compression_fn = btrfs_fget_compression,
880 : .set_compression_fn = btrfs_set_compression,
881 : .snap_check_path_fn = btrfs_snap_check_path,
882 : .snap_create_fn = btrfs_snap_create,
883 : .snap_delete_fn = btrfs_snap_delete,
884 : };
885 :
886 : static_decl_vfs;
887 26 : NTSTATUS vfs_btrfs_init(TALLOC_CTX *ctx)
888 : {
889 26 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
890 : "btrfs", &btrfs_fns);
891 : }
|