1 | /*
|
---|
2 | * Module to make use of awesome Btrfs features
|
---|
3 | *
|
---|
4 | * Copyright (C) David Disseldorp 2011-2013
|
---|
5 | *
|
---|
6 | * This program is free software; you can redistribute it and/or modify
|
---|
7 | * it under the terms of the GNU General Public License as published by
|
---|
8 | * the Free Software Foundation; either version 3 of the License, or
|
---|
9 | * (at your option) any later version.
|
---|
10 | *
|
---|
11 | * This program is distributed in the hope that it will be useful,
|
---|
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
---|
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
---|
14 | * GNU General Public License for more details.
|
---|
15 | *
|
---|
16 | * You should have received a copy of the GNU General Public License
|
---|
17 | * along with this program; if not, see <http://www.gnu.org/licenses/>.
|
---|
18 | */
|
---|
19 |
|
---|
20 | #include <linux/ioctl.h>
|
---|
21 | #include <linux/fs.h>
|
---|
22 | #include <sys/ioctl.h>
|
---|
23 | #include <unistd.h>
|
---|
24 | #include <fcntl.h>
|
---|
25 | #include <dirent.h>
|
---|
26 | #include <libgen.h>
|
---|
27 | #include "system/filesys.h"
|
---|
28 | #include "includes.h"
|
---|
29 | #include "smbd/smbd.h"
|
---|
30 | #include "librpc/gen_ndr/smbXsrv.h"
|
---|
31 | #include "librpc/gen_ndr/ioctl.h"
|
---|
32 | #include "lib/util/tevent_ntstatus.h"
|
---|
33 |
|
---|
34 | static uint32_t btrfs_fs_capabilities(struct vfs_handle_struct *handle,
|
---|
35 | enum timestamp_set_resolution *_ts_res)
|
---|
36 | {
|
---|
37 | uint32_t fs_capabilities;
|
---|
38 | enum timestamp_set_resolution ts_res;
|
---|
39 |
|
---|
40 | /* inherit default capabilities, expose compression support */
|
---|
41 | fs_capabilities = SMB_VFS_NEXT_FS_CAPABILITIES(handle, &ts_res);
|
---|
42 | fs_capabilities |= FILE_FILE_COMPRESSION;
|
---|
43 | *_ts_res = ts_res;
|
---|
44 |
|
---|
45 | return fs_capabilities;
|
---|
46 | }
|
---|
47 |
|
---|
48 | #define SHADOW_COPY_PREFIX "@GMT-" /* vfs_shadow_copy format */
|
---|
49 | #define SHADOW_COPY_PATH_FORMAT "@GMT-%Y.%m.%d-%H.%M.%S"
|
---|
50 |
|
---|
51 | #define BTRFS_SUBVOL_RDONLY (1ULL << 1)
|
---|
52 | #define BTRFS_SUBVOL_NAME_MAX 4039
|
---|
53 | #define BTRFS_PATH_NAME_MAX 4087
|
---|
54 | struct btrfs_ioctl_vol_args_v2 {
|
---|
55 | int64_t fd;
|
---|
56 | uint64_t transid;
|
---|
57 | uint64_t flags;
|
---|
58 | uint64_t unused[4];
|
---|
59 | char name[BTRFS_SUBVOL_NAME_MAX + 1];
|
---|
60 | };
|
---|
61 | struct btrfs_ioctl_vol_args {
|
---|
62 | int64_t fd;
|
---|
63 | char name[BTRFS_PATH_NAME_MAX + 1];
|
---|
64 | };
|
---|
65 |
|
---|
66 | struct btrfs_ioctl_clone_range_args {
|
---|
67 | int64_t src_fd;
|
---|
68 | uint64_t src_offset;
|
---|
69 | uint64_t src_length;
|
---|
70 | uint64_t dest_offset;
|
---|
71 | };
|
---|
72 |
|
---|
73 | #define BTRFS_IOCTL_MAGIC 0x94
|
---|
74 | #define BTRFS_IOC_CLONE_RANGE _IOW(BTRFS_IOCTL_MAGIC, 13, \
|
---|
75 | struct btrfs_ioctl_clone_range_args)
|
---|
76 | #define BTRFS_IOC_SNAP_DESTROY _IOW(BTRFS_IOCTL_MAGIC, 15, \
|
---|
77 | struct btrfs_ioctl_vol_args)
|
---|
78 | #define BTRFS_IOC_SNAP_CREATE_V2 _IOW(BTRFS_IOCTL_MAGIC, 23, \
|
---|
79 | struct btrfs_ioctl_vol_args_v2)
|
---|
80 |
|
---|
81 | struct btrfs_cc_state {
|
---|
82 | struct vfs_handle_struct *handle;
|
---|
83 | off_t copied;
|
---|
84 | struct tevent_req *subreq; /* non-null if passed to next VFS fn */
|
---|
85 | };
|
---|
86 | static void btrfs_copy_chunk_done(struct tevent_req *subreq);
|
---|
87 |
|
---|
88 | static struct tevent_req *btrfs_copy_chunk_send(struct vfs_handle_struct *handle,
|
---|
89 | TALLOC_CTX *mem_ctx,
|
---|
90 | struct tevent_context *ev,
|
---|
91 | struct files_struct *src_fsp,
|
---|
92 | off_t src_off,
|
---|
93 | struct files_struct *dest_fsp,
|
---|
94 | off_t dest_off,
|
---|
95 | off_t num)
|
---|
96 | {
|
---|
97 | struct tevent_req *req;
|
---|
98 | struct btrfs_cc_state *cc_state;
|
---|
99 | struct btrfs_ioctl_clone_range_args cr_args;
|
---|
100 | struct lock_struct src_lck;
|
---|
101 | struct lock_struct dest_lck;
|
---|
102 | int ret;
|
---|
103 | NTSTATUS status;
|
---|
104 |
|
---|
105 | req = tevent_req_create(mem_ctx, &cc_state, struct btrfs_cc_state);
|
---|
106 | if (req == NULL) {
|
---|
107 | return NULL;
|
---|
108 | }
|
---|
109 | cc_state->handle = handle;
|
---|
110 |
|
---|
111 | if (num == 0) {
|
---|
112 | /*
|
---|
113 | * With a @src_length of zero, BTRFS_IOC_CLONE_RANGE clones
|
---|
114 | * all data from @src_offset->EOF! This is certainly not what
|
---|
115 | * the caller expects, and not what vfs_default does.
|
---|
116 | */
|
---|
117 | cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
|
---|
118 | cc_state, ev,
|
---|
119 | src_fsp,
|
---|
120 | src_off,
|
---|
121 | dest_fsp,
|
---|
122 | dest_off, num);
|
---|
123 | if (tevent_req_nomem(cc_state->subreq, req)) {
|
---|
124 | return tevent_req_post(req, ev);
|
---|
125 | }
|
---|
126 | tevent_req_set_callback(cc_state->subreq,
|
---|
127 | btrfs_copy_chunk_done,
|
---|
128 | req);
|
---|
129 | return req;
|
---|
130 | }
|
---|
131 |
|
---|
132 | status = vfs_stat_fsp(src_fsp);
|
---|
133 | if (tevent_req_nterror(req, status)) {
|
---|
134 | return tevent_req_post(req, ev);
|
---|
135 | }
|
---|
136 |
|
---|
137 | if (src_fsp->fsp_name->st.st_ex_size < src_off + num) {
|
---|
138 | /* [MS-SMB2] Handling a Server-Side Data Copy Request */
|
---|
139 | tevent_req_nterror(req, NT_STATUS_INVALID_VIEW_SIZE);
|
---|
140 | return tevent_req_post(req, ev);
|
---|
141 | }
|
---|
142 |
|
---|
143 | if (src_fsp->op == NULL || dest_fsp->op == NULL) {
|
---|
144 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
|
---|
145 | return tevent_req_post(req, ev);
|
---|
146 | }
|
---|
147 |
|
---|
148 | init_strict_lock_struct(src_fsp,
|
---|
149 | src_fsp->op->global->open_persistent_id,
|
---|
150 | src_off,
|
---|
151 | num,
|
---|
152 | READ_LOCK,
|
---|
153 | &src_lck);
|
---|
154 | init_strict_lock_struct(dest_fsp,
|
---|
155 | dest_fsp->op->global->open_persistent_id,
|
---|
156 | dest_off,
|
---|
157 | num,
|
---|
158 | WRITE_LOCK,
|
---|
159 | &dest_lck);
|
---|
160 |
|
---|
161 | if (!SMB_VFS_STRICT_LOCK(src_fsp->conn, src_fsp, &src_lck)) {
|
---|
162 | tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
|
---|
163 | return tevent_req_post(req, ev);
|
---|
164 | }
|
---|
165 | if (!SMB_VFS_STRICT_LOCK(dest_fsp->conn, dest_fsp, &dest_lck)) {
|
---|
166 | SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
|
---|
167 | tevent_req_nterror(req, NT_STATUS_FILE_LOCK_CONFLICT);
|
---|
168 | return tevent_req_post(req, ev);
|
---|
169 | }
|
---|
170 |
|
---|
171 | ZERO_STRUCT(cr_args);
|
---|
172 | cr_args.src_fd = src_fsp->fh->fd;
|
---|
173 | cr_args.src_offset = (uint64_t)src_off;
|
---|
174 | cr_args.dest_offset = (uint64_t)dest_off;
|
---|
175 | cr_args.src_length = (uint64_t)num;
|
---|
176 |
|
---|
177 | ret = ioctl(dest_fsp->fh->fd, BTRFS_IOC_CLONE_RANGE, &cr_args);
|
---|
178 | SMB_VFS_STRICT_UNLOCK(dest_fsp->conn, dest_fsp, &dest_lck);
|
---|
179 | SMB_VFS_STRICT_UNLOCK(src_fsp->conn, src_fsp, &src_lck);
|
---|
180 | if (ret < 0) {
|
---|
181 | /*
|
---|
182 | * BTRFS_IOC_CLONE_RANGE only supports 'sectorsize' aligned
|
---|
183 | * cloning. Which is 4096 by default, therefore fall back to
|
---|
184 | * manual read/write on failure.
|
---|
185 | */
|
---|
186 | DEBUG(5, ("BTRFS_IOC_CLONE_RANGE failed: %s, length %llu, "
|
---|
187 | "src fd: %lld off: %llu, dest fd: %d off: %llu\n",
|
---|
188 | strerror(errno),
|
---|
189 | (unsigned long long)cr_args.src_length,
|
---|
190 | (long long)cr_args.src_fd,
|
---|
191 | (unsigned long long)cr_args.src_offset,
|
---|
192 | dest_fsp->fh->fd,
|
---|
193 | (unsigned long long)cr_args.dest_offset));
|
---|
194 | cc_state->subreq = SMB_VFS_NEXT_COPY_CHUNK_SEND(handle,
|
---|
195 | cc_state, ev,
|
---|
196 | src_fsp,
|
---|
197 | src_off,
|
---|
198 | dest_fsp,
|
---|
199 | dest_off, num);
|
---|
200 | if (tevent_req_nomem(cc_state->subreq, req)) {
|
---|
201 | return tevent_req_post(req, ev);
|
---|
202 | }
|
---|
203 | /* wait for subreq completion */
|
---|
204 | tevent_req_set_callback(cc_state->subreq,
|
---|
205 | btrfs_copy_chunk_done,
|
---|
206 | req);
|
---|
207 | return req;
|
---|
208 | }
|
---|
209 |
|
---|
210 | DEBUG(5, ("BTRFS_IOC_CLONE_RANGE returned %d\n", ret));
|
---|
211 | /* BTRFS_IOC_CLONE_RANGE is all or nothing */
|
---|
212 | cc_state->copied = num;
|
---|
213 | tevent_req_done(req);
|
---|
214 | return tevent_req_post(req, ev);
|
---|
215 | }
|
---|
216 |
|
---|
217 | /* only used if the request is passed through to next VFS module */
|
---|
218 | static void btrfs_copy_chunk_done(struct tevent_req *subreq)
|
---|
219 | {
|
---|
220 | struct tevent_req *req = tevent_req_callback_data(
|
---|
221 | subreq, struct tevent_req);
|
---|
222 | struct btrfs_cc_state *cc_state = tevent_req_data(req,
|
---|
223 | struct btrfs_cc_state);
|
---|
224 | NTSTATUS status;
|
---|
225 |
|
---|
226 | status = SMB_VFS_NEXT_COPY_CHUNK_RECV(cc_state->handle,
|
---|
227 | cc_state->subreq,
|
---|
228 | &cc_state->copied);
|
---|
229 | if (tevent_req_nterror(req, status)) {
|
---|
230 | return;
|
---|
231 | }
|
---|
232 | tevent_req_done(req);
|
---|
233 | }
|
---|
234 |
|
---|
235 | static NTSTATUS btrfs_copy_chunk_recv(struct vfs_handle_struct *handle,
|
---|
236 | struct tevent_req *req,
|
---|
237 | off_t *copied)
|
---|
238 | {
|
---|
239 | NTSTATUS status;
|
---|
240 | struct btrfs_cc_state *cc_state = tevent_req_data(req,
|
---|
241 | struct btrfs_cc_state);
|
---|
242 |
|
---|
243 | if (tevent_req_is_nterror(req, &status)) {
|
---|
244 | DEBUG(4, ("server side copy chunk failed: %s\n",
|
---|
245 | nt_errstr(status)));
|
---|
246 | tevent_req_received(req);
|
---|
247 | return status;
|
---|
248 | }
|
---|
249 |
|
---|
250 | DEBUG(10, ("server side copy chunk copied %llu\n",
|
---|
251 | (unsigned long long)cc_state->copied));
|
---|
252 | *copied = cc_state->copied;
|
---|
253 | tevent_req_received(req);
|
---|
254 | return NT_STATUS_OK;
|
---|
255 | }
|
---|
256 |
|
---|
257 | /*
|
---|
258 | * caller must pass a non-null fsp or smb_fname. If fsp is null, then
|
---|
259 | * fall back to opening the corresponding file to issue the ioctl.
|
---|
260 | */
|
---|
261 | static NTSTATUS btrfs_get_compression(struct vfs_handle_struct *handle,
|
---|
262 | TALLOC_CTX *mem_ctx,
|
---|
263 | struct files_struct *fsp,
|
---|
264 | struct smb_filename *smb_fname,
|
---|
265 | uint16_t *_compression_fmt)
|
---|
266 | {
|
---|
267 | int ret;
|
---|
268 | long flags = 0;
|
---|
269 | int fd;
|
---|
270 | bool opened = false;
|
---|
271 | NTSTATUS status;
|
---|
272 | DIR *dir = NULL;
|
---|
273 |
|
---|
274 | if ((fsp != NULL) && (fsp->fh->fd != -1)) {
|
---|
275 | fd = fsp->fh->fd;
|
---|
276 | } else if (smb_fname != NULL) {
|
---|
277 | if (S_ISDIR(smb_fname->st.st_ex_mode)) {
|
---|
278 | dir = opendir(smb_fname->base_name);
|
---|
279 | if (dir == NULL) {
|
---|
280 | return NT_STATUS_UNSUCCESSFUL;
|
---|
281 | }
|
---|
282 | opened = true;
|
---|
283 | fd = dirfd(dir);
|
---|
284 | if (fd < 0) {
|
---|
285 | status = NT_STATUS_UNSUCCESSFUL;
|
---|
286 | goto err_close;
|
---|
287 | }
|
---|
288 | } else {
|
---|
289 | fd = open(smb_fname->base_name, O_RDONLY);
|
---|
290 | if (fd < 0) {
|
---|
291 | return NT_STATUS_UNSUCCESSFUL;
|
---|
292 | }
|
---|
293 | opened = true;
|
---|
294 | }
|
---|
295 | } else {
|
---|
296 | return NT_STATUS_INVALID_PARAMETER;
|
---|
297 | }
|
---|
298 |
|
---|
299 | ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
|
---|
300 | if (ret < 0) {
|
---|
301 | DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %lld\n",
|
---|
302 | strerror(errno), (long long)fd));
|
---|
303 | status = map_nt_error_from_unix(errno);
|
---|
304 | goto err_close;
|
---|
305 | }
|
---|
306 | if (flags & FS_COMPR_FL) {
|
---|
307 | *_compression_fmt = COMPRESSION_FORMAT_LZNT1;
|
---|
308 | } else {
|
---|
309 | *_compression_fmt = COMPRESSION_FORMAT_NONE;
|
---|
310 | }
|
---|
311 | status = NT_STATUS_OK;
|
---|
312 | err_close:
|
---|
313 | if (opened) {
|
---|
314 | if (dir != NULL) {
|
---|
315 | closedir(dir);
|
---|
316 | } else {
|
---|
317 | close(fd);
|
---|
318 | }
|
---|
319 | }
|
---|
320 |
|
---|
321 | return status;
|
---|
322 | }
|
---|
323 |
|
---|
324 | static NTSTATUS btrfs_set_compression(struct vfs_handle_struct *handle,
|
---|
325 | TALLOC_CTX *mem_ctx,
|
---|
326 | struct files_struct *fsp,
|
---|
327 | uint16_t compression_fmt)
|
---|
328 | {
|
---|
329 | int ret;
|
---|
330 | long flags = 0;
|
---|
331 | int fd;
|
---|
332 | NTSTATUS status;
|
---|
333 |
|
---|
334 | if ((fsp == NULL) || (fsp->fh->fd == -1)) {
|
---|
335 | status = NT_STATUS_INVALID_PARAMETER;
|
---|
336 | goto err_out;
|
---|
337 | }
|
---|
338 | fd = fsp->fh->fd;
|
---|
339 |
|
---|
340 | ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
|
---|
341 | if (ret < 0) {
|
---|
342 | DEBUG(1, ("FS_IOC_GETFLAGS failed: %s, fd %d\n",
|
---|
343 | strerror(errno), fd));
|
---|
344 | status = map_nt_error_from_unix(errno);
|
---|
345 | goto err_out;
|
---|
346 | }
|
---|
347 |
|
---|
348 | if (compression_fmt == COMPRESSION_FORMAT_NONE) {
|
---|
349 | DEBUG(5, ("setting compression\n"));
|
---|
350 | flags &= (~FS_COMPR_FL);
|
---|
351 | } else if ((compression_fmt == COMPRESSION_FORMAT_DEFAULT)
|
---|
352 | || (compression_fmt == COMPRESSION_FORMAT_LZNT1)) {
|
---|
353 | DEBUG(5, ("clearing compression\n"));
|
---|
354 | flags |= FS_COMPR_FL;
|
---|
355 | } else {
|
---|
356 | DEBUG(1, ("invalid compression format 0x%x\n",
|
---|
357 | (int)compression_fmt));
|
---|
358 | status = NT_STATUS_INVALID_PARAMETER;
|
---|
359 | goto err_out;
|
---|
360 | }
|
---|
361 |
|
---|
362 | ret = ioctl(fd, FS_IOC_SETFLAGS, &flags);
|
---|
363 | if (ret < 0) {
|
---|
364 | DEBUG(1, ("FS_IOC_SETFLAGS failed: %s, fd %d\n",
|
---|
365 | strerror(errno), fd));
|
---|
366 | status = map_nt_error_from_unix(errno);
|
---|
367 | goto err_out;
|
---|
368 | }
|
---|
369 | status = NT_STATUS_OK;
|
---|
370 | err_out:
|
---|
371 | return status;
|
---|
372 | }
|
---|
373 |
|
---|
374 | /*
|
---|
375 | * Check whether a path can be shadow copied. Return the base volume, allowing
|
---|
376 | * the caller to determine if multiple paths lie on the same base volume.
|
---|
377 | */
|
---|
378 | #define BTRFS_INODE_SUBVOL 256
|
---|
379 | static NTSTATUS btrfs_snap_check_path(struct vfs_handle_struct *handle,
|
---|
380 | TALLOC_CTX *mem_ctx,
|
---|
381 | const char *service_path,
|
---|
382 | char **base_volume)
|
---|
383 | {
|
---|
384 | struct stat st;
|
---|
385 | char *base;
|
---|
386 |
|
---|
387 | if (!lp_parm_bool(SNUM(handle->conn),
|
---|
388 | "btrfs", "manipulate snapshots", false)) {
|
---|
389 | DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
|
---|
390 | return SMB_VFS_NEXT_SNAP_CHECK_PATH(handle, mem_ctx,
|
---|
391 | service_path, base_volume);
|
---|
392 | }
|
---|
393 |
|
---|
394 | /* btrfs userspace uses this logic to confirm subvolume */
|
---|
395 | if (stat(service_path, &st) < 0) {
|
---|
396 | return NT_STATUS_NOT_SUPPORTED;
|
---|
397 | }
|
---|
398 | if ((st.st_ino != BTRFS_INODE_SUBVOL) || !S_ISDIR(st.st_mode)) {
|
---|
399 | DEBUG(0, ("%s not a btrfs subvolume, snapshots not available\n",
|
---|
400 | service_path));
|
---|
401 | return NT_STATUS_NOT_SUPPORTED;
|
---|
402 | }
|
---|
403 |
|
---|
404 | /* we "snapshot" the service path itself */
|
---|
405 | base = talloc_strdup(mem_ctx, service_path);
|
---|
406 | if (base == NULL) {
|
---|
407 | return NT_STATUS_NO_MEMORY;
|
---|
408 | }
|
---|
409 | *base_volume = base;
|
---|
410 |
|
---|
411 | return NT_STATUS_OK;
|
---|
412 | }
|
---|
413 |
|
---|
414 | static NTSTATUS btrfs_gen_snap_dest_path(TALLOC_CTX *mem_ctx,
|
---|
415 | const char *src_path,
|
---|
416 | time_t *tstamp,
|
---|
417 | char **dest_path, char **subvolume)
|
---|
418 | {
|
---|
419 | struct tm t_gmt;
|
---|
420 | char time_str[50];
|
---|
421 | size_t tlen;
|
---|
422 |
|
---|
423 | gmtime_r(tstamp, &t_gmt);
|
---|
424 |
|
---|
425 | tlen = strftime(time_str, ARRAY_SIZE(time_str),
|
---|
426 | SHADOW_COPY_PATH_FORMAT, &t_gmt);
|
---|
427 | if (tlen <= 0) {
|
---|
428 | return NT_STATUS_UNSUCCESSFUL;
|
---|
429 | }
|
---|
430 |
|
---|
431 | *dest_path = talloc_strdup(mem_ctx, src_path);
|
---|
432 | *subvolume = talloc_strdup(mem_ctx, time_str);
|
---|
433 | if ((*dest_path == NULL) || (*subvolume == NULL)) {
|
---|
434 | return NT_STATUS_NO_MEMORY;
|
---|
435 | }
|
---|
436 |
|
---|
437 | return NT_STATUS_OK;
|
---|
438 | }
|
---|
439 |
|
---|
440 | static NTSTATUS btrfs_snap_create(struct vfs_handle_struct *handle,
|
---|
441 | TALLOC_CTX *mem_ctx,
|
---|
442 | const char *base_volume,
|
---|
443 | time_t *tstamp,
|
---|
444 | bool rw,
|
---|
445 | char **_base_path,
|
---|
446 | char **_snap_path)
|
---|
447 | {
|
---|
448 | struct btrfs_ioctl_vol_args_v2 ioctl_arg;
|
---|
449 | DIR *src_dir;
|
---|
450 | DIR *dest_dir;
|
---|
451 | int src_fd;
|
---|
452 | int dest_fd;
|
---|
453 | char *dest_path = NULL;
|
---|
454 | char *dest_subvolume = NULL;
|
---|
455 | int ret;
|
---|
456 | NTSTATUS status;
|
---|
457 | char *base_path;
|
---|
458 | char *snap_path;
|
---|
459 | TALLOC_CTX *tmp_ctx;
|
---|
460 | int saved_errno;
|
---|
461 | size_t len;
|
---|
462 |
|
---|
463 | if (!lp_parm_bool(SNUM(handle->conn),
|
---|
464 | "btrfs", "manipulate snapshots", false)) {
|
---|
465 | DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
|
---|
466 | return SMB_VFS_NEXT_SNAP_CREATE(handle, mem_ctx, base_volume,
|
---|
467 | tstamp, rw, _base_path,
|
---|
468 | _snap_path);
|
---|
469 | }
|
---|
470 |
|
---|
471 | tmp_ctx = talloc_new(mem_ctx);
|
---|
472 | if (tmp_ctx == NULL) {
|
---|
473 | return NT_STATUS_NO_MEMORY;
|
---|
474 | }
|
---|
475 |
|
---|
476 | base_path = talloc_strdup(tmp_ctx, base_volume);
|
---|
477 | if (base_path == NULL) {
|
---|
478 | talloc_free(tmp_ctx);
|
---|
479 | return NT_STATUS_NO_MEMORY;
|
---|
480 | }
|
---|
481 |
|
---|
482 | status = btrfs_gen_snap_dest_path(tmp_ctx, base_volume, tstamp,
|
---|
483 | &dest_path, &dest_subvolume);
|
---|
484 | if (!NT_STATUS_IS_OK(status)) {
|
---|
485 | talloc_free(tmp_ctx);
|
---|
486 | return status;
|
---|
487 | }
|
---|
488 |
|
---|
489 | snap_path = talloc_asprintf(tmp_ctx, "%s/%s", dest_path,
|
---|
490 | dest_subvolume);
|
---|
491 | if (snap_path == NULL) {
|
---|
492 | talloc_free(tmp_ctx);
|
---|
493 | return NT_STATUS_NO_MEMORY;
|
---|
494 | }
|
---|
495 |
|
---|
496 | src_dir = opendir(base_volume);
|
---|
497 | if (src_dir == NULL) {
|
---|
498 | DEBUG(0, ("snap src %s open failed: %s\n",
|
---|
499 | base_volume, strerror(errno)));
|
---|
500 | status = map_nt_error_from_unix(errno);
|
---|
501 | talloc_free(tmp_ctx);
|
---|
502 | return status;
|
---|
503 | }
|
---|
504 | src_fd = dirfd(src_dir);
|
---|
505 | if (src_fd < 0) {
|
---|
506 | status = map_nt_error_from_unix(errno);
|
---|
507 | closedir(src_dir);
|
---|
508 | talloc_free(tmp_ctx);
|
---|
509 | return status;
|
---|
510 | }
|
---|
511 |
|
---|
512 | dest_dir = opendir(dest_path);
|
---|
513 | if (dest_dir == NULL) {
|
---|
514 | DEBUG(0, ("snap dest %s open failed: %s\n",
|
---|
515 | dest_path, strerror(errno)));
|
---|
516 | status = map_nt_error_from_unix(errno);
|
---|
517 | closedir(src_dir);
|
---|
518 | talloc_free(tmp_ctx);
|
---|
519 | return status;
|
---|
520 | }
|
---|
521 | dest_fd = dirfd(dest_dir);
|
---|
522 | if (dest_fd < 0) {
|
---|
523 | status = map_nt_error_from_unix(errno);
|
---|
524 | closedir(src_dir);
|
---|
525 | closedir(dest_dir);
|
---|
526 | talloc_free(tmp_ctx);
|
---|
527 | return status;
|
---|
528 | }
|
---|
529 |
|
---|
530 | /* avoid zeroing the entire struct here, name is 4k */
|
---|
531 | ioctl_arg.fd = src_fd;
|
---|
532 | ioctl_arg.transid = 0;
|
---|
533 | ioctl_arg.flags = (rw == false) ? BTRFS_SUBVOL_RDONLY : 0;
|
---|
534 | memset(ioctl_arg.unused, 0, ARRAY_SIZE(ioctl_arg.unused));
|
---|
535 | len = strlcpy(ioctl_arg.name, dest_subvolume,
|
---|
536 | ARRAY_SIZE(ioctl_arg.name));
|
---|
537 | if (len >= ARRAY_SIZE(ioctl_arg.name)) {
|
---|
538 | DEBUG(1, ("subvolume name too long for SNAP_CREATE ioctl\n"));
|
---|
539 | closedir(src_dir);
|
---|
540 | closedir(dest_dir);
|
---|
541 | talloc_free(tmp_ctx);
|
---|
542 | return NT_STATUS_INVALID_PARAMETER;
|
---|
543 | }
|
---|
544 |
|
---|
545 | become_root();
|
---|
546 | ret = ioctl(dest_fd, BTRFS_IOC_SNAP_CREATE_V2, &ioctl_arg);
|
---|
547 | saved_errno = errno;
|
---|
548 | unbecome_root();
|
---|
549 | if (ret < 0) {
|
---|
550 | DEBUG(0, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 failed: %s\n",
|
---|
551 | base_volume, dest_path, dest_subvolume,
|
---|
552 | strerror(saved_errno)));
|
---|
553 | status = map_nt_error_from_unix(saved_errno);
|
---|
554 | closedir(src_dir);
|
---|
555 | closedir(dest_dir);
|
---|
556 | talloc_free(tmp_ctx);
|
---|
557 | return status;
|
---|
558 | }
|
---|
559 | DEBUG(5, ("%s -> %s(%s) BTRFS_IOC_SNAP_CREATE_V2 done\n",
|
---|
560 | base_volume, dest_path, dest_subvolume));
|
---|
561 |
|
---|
562 | *_base_path = talloc_steal(mem_ctx, base_path);
|
---|
563 | *_snap_path = talloc_steal(mem_ctx, snap_path);
|
---|
564 | closedir(src_dir);
|
---|
565 | closedir(dest_dir);
|
---|
566 | talloc_free(tmp_ctx);
|
---|
567 |
|
---|
568 | return NT_STATUS_OK;
|
---|
569 | }
|
---|
570 |
|
---|
571 | static NTSTATUS btrfs_snap_delete(struct vfs_handle_struct *handle,
|
---|
572 | TALLOC_CTX *mem_ctx,
|
---|
573 | char *base_path,
|
---|
574 | char *snap_path)
|
---|
575 | {
|
---|
576 | char *tstr;
|
---|
577 | struct tm t_gmt;
|
---|
578 | DIR *dest_dir;
|
---|
579 | int dest_fd;
|
---|
580 | struct btrfs_ioctl_vol_args ioctl_arg;
|
---|
581 | int ret;
|
---|
582 | NTSTATUS status;
|
---|
583 | char *dest_path;
|
---|
584 | char *subvolume;
|
---|
585 | TALLOC_CTX *tmp_ctx;
|
---|
586 | int saved_errno;
|
---|
587 | size_t len;
|
---|
588 |
|
---|
589 | if (!lp_parm_bool(SNUM(handle->conn),
|
---|
590 | "btrfs", "manipulate snapshots", false)) {
|
---|
591 | DEBUG(2, ("Btrfs snapshot manipulation disabled, passing\n"));
|
---|
592 | return SMB_VFS_NEXT_SNAP_DELETE(handle, mem_ctx,
|
---|
593 | base_path, snap_path);
|
---|
594 | }
|
---|
595 |
|
---|
596 | tmp_ctx = talloc_new(mem_ctx);
|
---|
597 | if (tmp_ctx == NULL) {
|
---|
598 | return NT_STATUS_NO_MEMORY;
|
---|
599 | }
|
---|
600 |
|
---|
601 | dest_path = talloc_strdup(tmp_ctx, snap_path);
|
---|
602 | if (dest_path == NULL) {
|
---|
603 | talloc_free(tmp_ctx);
|
---|
604 | return NT_STATUS_NO_MEMORY;
|
---|
605 | }
|
---|
606 | subvolume = talloc_strdup(tmp_ctx, snap_path);
|
---|
607 | if (subvolume == NULL) {
|
---|
608 | talloc_free(tmp_ctx);
|
---|
609 | return NT_STATUS_NO_MEMORY;
|
---|
610 | }
|
---|
611 | dest_path = dirname(dest_path);
|
---|
612 | subvolume = basename(subvolume);
|
---|
613 |
|
---|
614 | /* confirm snap_path matches creation format */
|
---|
615 | tstr = strptime(subvolume, SHADOW_COPY_PATH_FORMAT, &t_gmt);
|
---|
616 | if ((tstr == NULL) || (*tstr != '\0')) {
|
---|
617 | DEBUG(0, ("snapshot path %s does not match creation format\n",
|
---|
618 | snap_path));
|
---|
619 | talloc_free(tmp_ctx);
|
---|
620 | return NT_STATUS_UNSUCCESSFUL;
|
---|
621 | }
|
---|
622 |
|
---|
623 | dest_dir = opendir(dest_path);
|
---|
624 | if (dest_dir == NULL) {
|
---|
625 | DEBUG(0, ("snap destroy dest %s open failed: %s\n",
|
---|
626 | dest_path, strerror(errno)));
|
---|
627 | status = map_nt_error_from_unix(errno);
|
---|
628 | talloc_free(tmp_ctx);
|
---|
629 | return status;
|
---|
630 | }
|
---|
631 | dest_fd = dirfd(dest_dir);
|
---|
632 | if (dest_fd < 0) {
|
---|
633 | status = map_nt_error_from_unix(errno);
|
---|
634 | closedir(dest_dir);
|
---|
635 | talloc_free(tmp_ctx);
|
---|
636 | return status;
|
---|
637 | }
|
---|
638 |
|
---|
639 | ioctl_arg.fd = -1; /* not needed */
|
---|
640 | len = strlcpy(ioctl_arg.name, subvolume, ARRAY_SIZE(ioctl_arg.name));
|
---|
641 | if (len >= ARRAY_SIZE(ioctl_arg.name)) {
|
---|
642 | DEBUG(1, ("subvolume name too long for SNAP_DESTROY ioctl\n"));
|
---|
643 | closedir(dest_dir);
|
---|
644 | talloc_free(tmp_ctx);
|
---|
645 | return NT_STATUS_INVALID_PARAMETER;
|
---|
646 | }
|
---|
647 |
|
---|
648 | become_root();
|
---|
649 | ret = ioctl(dest_fd, BTRFS_IOC_SNAP_DESTROY, &ioctl_arg);
|
---|
650 | saved_errno = errno;
|
---|
651 | unbecome_root();
|
---|
652 | if (ret < 0) {
|
---|
653 | DEBUG(0, ("%s(%s) BTRFS_IOC_SNAP_DESTROY failed: %s\n",
|
---|
654 | dest_path, subvolume, strerror(saved_errno)));
|
---|
655 | status = map_nt_error_from_unix(saved_errno);
|
---|
656 | closedir(dest_dir);
|
---|
657 | talloc_free(tmp_ctx);
|
---|
658 | return status;
|
---|
659 | }
|
---|
660 | DEBUG(5, ("%s(%s) BTRFS_IOC_SNAP_DESTROY done\n",
|
---|
661 | dest_path, subvolume));
|
---|
662 |
|
---|
663 | closedir(dest_dir);
|
---|
664 | talloc_free(tmp_ctx);
|
---|
665 | return NT_STATUS_OK;
|
---|
666 | }
|
---|
667 |
|
---|
668 | static struct vfs_fn_pointers btrfs_fns = {
|
---|
669 | .fs_capabilities_fn = btrfs_fs_capabilities,
|
---|
670 | .copy_chunk_send_fn = btrfs_copy_chunk_send,
|
---|
671 | .copy_chunk_recv_fn = btrfs_copy_chunk_recv,
|
---|
672 | .get_compression_fn = btrfs_get_compression,
|
---|
673 | .set_compression_fn = btrfs_set_compression,
|
---|
674 | .snap_check_path_fn = btrfs_snap_check_path,
|
---|
675 | .snap_create_fn = btrfs_snap_create,
|
---|
676 | .snap_delete_fn = btrfs_snap_delete,
|
---|
677 | };
|
---|
678 |
|
---|
679 | NTSTATUS vfs_btrfs_init(void);
|
---|
680 | NTSTATUS vfs_btrfs_init(void)
|
---|
681 | {
|
---|
682 | return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
|
---|
683 | "btrfs", &btrfs_fns);
|
---|
684 | }
|
---|