Line data Source code
1 : /*
2 : * Module for accessing CephFS snapshots as Previous Versions. This module is
3 : * separate to vfs_ceph, so that it can also be used atop a CephFS kernel backed
4 : * share with vfs_default.
5 : *
6 : * Copyright (C) David Disseldorp 2019
7 : *
8 : * This program is free software; you can redistribute it and/or modify
9 : * it under the terms of the GNU General Public License as published by
10 : * the Free Software Foundation; either version 3 of the License, or
11 : * (at your option) any later version.
12 : *
13 : * This program is distributed in the hope that it will be useful,
14 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : * GNU General Public License for more details.
17 : *
18 : * You should have received a copy of the GNU General Public License
19 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <dirent.h>
23 : #include <libgen.h>
24 : #include "includes.h"
25 : #include "include/ntioctl.h"
26 : #include "include/smb.h"
27 : #include "system/filesys.h"
28 : #include "smbd/smbd.h"
29 : #include "lib/util/tevent_ntstatus.h"
30 : #include "lib/util/smb_strtox.h"
31 :
32 : #undef DBGC_CLASS
33 : #define DBGC_CLASS DBGC_VFS
34 :
35 : /*
36 : * CephFS has a magic snapshots subdirectory in all parts of the directory tree.
37 : * This module automatically makes all snapshots in this subdir visible to SMB
38 : * clients (if permitted by corresponding access control).
39 : */
40 : #define CEPH_SNAP_SUBDIR_DEFAULT ".snap"
41 : /*
42 : * The ceph.snap.btime (virtual) extended attribute carries the snapshot
43 : * creation time in $secs.$nsecs format. It was added as part of
44 : * https://tracker.ceph.com/issues/38838. Running Samba atop old Ceph versions
45 : * which don't provide this xattr will not be able to enumerate or access
46 : * snapshots using this module. As an alternative, vfs_shadow_copy2 could be
47 : * used instead, alongside special shadow:format snapshot directory names.
48 : */
49 : #define CEPH_SNAP_BTIME_XATTR "ceph.snap.btime"
50 :
51 0 : static int ceph_snap_get_btime_fsp(struct vfs_handle_struct *handle,
52 : struct files_struct *fsp,
53 : time_t *_snap_secs)
54 : {
55 : int ret;
56 : char snap_btime[33];
57 0 : char *s = NULL;
58 0 : char *endptr = NULL;
59 : struct timespec snap_timespec;
60 : int err;
61 :
62 0 : ret = SMB_VFS_NEXT_FGETXATTR(handle,
63 : fsp,
64 : CEPH_SNAP_BTIME_XATTR,
65 : snap_btime,
66 : sizeof(snap_btime));
67 0 : if (ret < 0) {
68 0 : DBG_ERR("failed to get %s xattr: %s\n",
69 : CEPH_SNAP_BTIME_XATTR, strerror(errno));
70 0 : return -errno;
71 : }
72 :
73 0 : if (ret == 0 || ret >= sizeof(snap_btime) - 1) {
74 0 : return -EINVAL;
75 : }
76 :
77 : /* ensure zero termination */
78 0 : snap_btime[ret] = '\0';
79 :
80 : /* format is sec.nsec */
81 0 : s = strchr(snap_btime, '.');
82 0 : if (s == NULL) {
83 0 : DBG_ERR("invalid %s xattr value: %s\n",
84 : CEPH_SNAP_BTIME_XATTR, snap_btime);
85 0 : return -EINVAL;
86 : }
87 :
88 : /* First component is seconds, extract it */
89 0 : *s = '\0';
90 0 : snap_timespec.tv_sec = smb_strtoull(snap_btime,
91 : &endptr,
92 : 10,
93 : &err,
94 : SMB_STR_FULL_STR_CONV);
95 0 : if (err != 0) {
96 0 : return -err;
97 : }
98 :
99 : /* second component is nsecs */
100 0 : s++;
101 0 : snap_timespec.tv_nsec = smb_strtoul(s,
102 : &endptr,
103 : 10,
104 : &err,
105 : SMB_STR_FULL_STR_CONV);
106 0 : if (err != 0) {
107 0 : return -err;
108 : }
109 :
110 : /*
111 : * >> 30 is a rough divide by ~10**9. No need to be exact, as @GMT
112 : * tokens only offer 1-second resolution (while twrp is nsec).
113 : */
114 0 : *_snap_secs = snap_timespec.tv_sec + (snap_timespec.tv_nsec >> 30);
115 :
116 0 : return 0;
117 : }
118 :
119 : /*
120 : * XXX Ceph snapshots can be created with sub-second granularity, which means
121 : * that multiple snapshots may be mapped to the same @GMT- label.
122 : *
123 : * @this_label is a pre-zeroed buffer to be filled with a @GMT label
124 : * @return 0 if label successfully filled or -errno on error.
125 : */
126 0 : static int ceph_snap_fill_label(struct vfs_handle_struct *handle,
127 : TALLOC_CTX *tmp_ctx,
128 : struct files_struct *dirfsp,
129 : const char *subdir,
130 : SHADOW_COPY_LABEL this_label)
131 : {
132 0 : const char *parent_snapsdir = dirfsp->fsp_name->base_name;
133 : struct smb_filename *smb_fname;
134 0 : struct smb_filename *atname = NULL;
135 : time_t snap_secs;
136 : struct tm gmt_snap_time;
137 : struct tm *tm_ret;
138 : size_t str_sz;
139 : char snap_path[PATH_MAX + 1];
140 : int ret;
141 : NTSTATUS status;
142 :
143 : /*
144 : * CephFS snapshot creation times are available via a special
145 : * xattr - snapshot b/m/ctimes all match the snap source.
146 : */
147 0 : ret = snprintf(snap_path, sizeof(snap_path), "%s/%s",
148 : parent_snapsdir, subdir);
149 0 : if (ret >= sizeof(snap_path)) {
150 0 : return -EINVAL;
151 : }
152 :
153 0 : smb_fname = synthetic_smb_fname(tmp_ctx,
154 : snap_path,
155 : NULL,
156 : NULL,
157 : 0,
158 : 0);
159 0 : if (smb_fname == NULL) {
160 0 : return -ENOMEM;
161 : }
162 :
163 0 : ret = vfs_stat(handle->conn, smb_fname);
164 0 : if (ret < 0) {
165 0 : ret = -errno;
166 0 : TALLOC_FREE(smb_fname);
167 0 : return ret;
168 : }
169 :
170 0 : atname = synthetic_smb_fname(tmp_ctx,
171 : subdir,
172 : NULL,
173 0 : &smb_fname->st,
174 : 0,
175 : 0);
176 0 : if (atname == NULL) {
177 0 : TALLOC_FREE(smb_fname);
178 0 : return -ENOMEM;
179 : }
180 :
181 0 : status = openat_pathref_fsp(dirfsp, atname);
182 0 : if (!NT_STATUS_IS_OK(status)) {
183 0 : TALLOC_FREE(smb_fname);
184 0 : TALLOC_FREE(atname);
185 0 : return -map_errno_from_nt_status(status);
186 : }
187 :
188 0 : ret = ceph_snap_get_btime_fsp(handle, atname->fsp, &snap_secs);
189 0 : if (ret < 0) {
190 0 : TALLOC_FREE(smb_fname);
191 0 : TALLOC_FREE(atname);
192 0 : return ret;
193 : }
194 0 : TALLOC_FREE(smb_fname);
195 0 : TALLOC_FREE(atname);
196 :
197 0 : tm_ret = gmtime_r(&snap_secs, &gmt_snap_time);
198 0 : if (tm_ret == NULL) {
199 0 : return -EINVAL;
200 : }
201 0 : str_sz = strftime(this_label, sizeof(SHADOW_COPY_LABEL),
202 : "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
203 0 : if (str_sz == 0) {
204 0 : DBG_ERR("failed to convert tm to @GMT token\n");
205 0 : return -EINVAL;
206 : }
207 :
208 0 : DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
209 : snap_path, this_label);
210 :
211 0 : return 0;
212 : }
213 :
214 0 : static int ceph_snap_enum_snapdir(struct vfs_handle_struct *handle,
215 : struct smb_filename *snaps_dname,
216 : bool labels,
217 : struct shadow_copy_data *sc_data)
218 : {
219 0 : TALLOC_CTX *frame = talloc_stackframe();
220 0 : struct smb_Dir *dir_hnd = NULL;
221 0 : struct files_struct *dirfsp = NULL;
222 0 : const char *dname = NULL;
223 0 : char *talloced = NULL;
224 : NTSTATUS status;
225 : int ret;
226 : uint32_t slots;
227 :
228 0 : DBG_DEBUG("enumerating shadow copy dir at %s\n",
229 : snaps_dname->base_name);
230 :
231 : /*
232 : * CephFS stat(dir).size *normally* returns the number of child entries
233 : * for a given dir, but it unfortunately that's not the case for the one
234 : * place we need it (dir=.snap), so we need to dynamically determine it
235 : * via readdir.
236 : */
237 :
238 0 : status = OpenDir(frame,
239 0 : handle->conn,
240 : snaps_dname,
241 : NULL,
242 : 0,
243 : &dir_hnd);
244 0 : if (!NT_STATUS_IS_OK(status)) {
245 0 : ret = -map_errno_from_nt_status(status);
246 0 : goto err_out;
247 : }
248 :
249 : /* Check we have SEC_DIR_LIST access on this fsp. */
250 0 : dirfsp = dir_hnd_fetch_fsp(dir_hnd);
251 0 : status = smbd_check_access_rights_fsp(dirfsp->conn->cwd_fsp,
252 : dirfsp,
253 : false,
254 : SEC_DIR_LIST);
255 0 : if (!NT_STATUS_IS_OK(status)) {
256 0 : DBG_ERR("user does not have list permission "
257 : "on snapdir %s\n",
258 : fsp_str_dbg(dirfsp));
259 0 : ret = -map_errno_from_nt_status(status);
260 0 : goto err_out;
261 : }
262 :
263 0 : slots = 0;
264 0 : sc_data->num_volumes = 0;
265 0 : sc_data->labels = NULL;
266 :
267 0 : while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
268 0 : if (ISDOT(dname) || ISDOTDOT(dname)) {
269 0 : TALLOC_FREE(talloced);
270 0 : continue;
271 : }
272 0 : sc_data->num_volumes++;
273 0 : if (!labels) {
274 0 : TALLOC_FREE(talloced);
275 0 : continue;
276 : }
277 0 : if (sc_data->num_volumes > slots) {
278 0 : uint32_t new_slot_count = slots + 10;
279 0 : SMB_ASSERT(new_slot_count > slots);
280 0 : sc_data->labels = talloc_realloc(sc_data,
281 : sc_data->labels,
282 : SHADOW_COPY_LABEL,
283 : new_slot_count);
284 0 : if (sc_data->labels == NULL) {
285 0 : TALLOC_FREE(talloced);
286 0 : ret = -ENOMEM;
287 0 : goto err_closedir;
288 : }
289 0 : memset(sc_data->labels[slots], 0,
290 : sizeof(SHADOW_COPY_LABEL) * 10);
291 :
292 0 : DBG_DEBUG("%d->%d slots for enum_snaps response\n",
293 : slots, new_slot_count);
294 0 : slots = new_slot_count;
295 : }
296 0 : DBG_DEBUG("filling shadow copy label for %s/%s\n",
297 : snaps_dname->base_name, dname);
298 0 : ret = ceph_snap_fill_label(handle,
299 : snaps_dname,
300 : dirfsp,
301 : dname,
302 0 : sc_data->labels[sc_data->num_volumes - 1]);
303 0 : if (ret < 0) {
304 0 : TALLOC_FREE(talloced);
305 0 : goto err_closedir;
306 : }
307 0 : TALLOC_FREE(talloced);
308 : }
309 :
310 0 : DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
311 : snaps_dname->base_name, sc_data->num_volumes);
312 :
313 0 : TALLOC_FREE(frame);
314 0 : return 0;
315 :
316 0 : err_closedir:
317 0 : TALLOC_FREE(frame);
318 0 : err_out:
319 0 : TALLOC_FREE(sc_data->labels);
320 0 : return ret;
321 : }
322 :
323 : /*
324 : * Prior reading: The Meaning of Path Names
325 : * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
326 : *
327 : * translate paths so that we can use the parent dir for .snap access:
328 : * myfile -> parent= trimmed=myfile
329 : * /a -> parent=/ trimmed=a
330 : * dir/sub/file -> parent=dir/sub trimmed=file
331 : * /dir/sub -> parent=/dir/ trimmed=sub
332 : */
333 0 : static int ceph_snap_get_parent_path(const char *connectpath,
334 : const char *path,
335 : char *_parent_buf,
336 : size_t buflen,
337 : const char **_trimmed)
338 : {
339 : const char *p;
340 : size_t len;
341 : int ret;
342 :
343 0 : if (!strcmp(path, "/")) {
344 0 : DBG_ERR("can't go past root for %s .snap dir\n", path);
345 0 : return -EINVAL;
346 : }
347 :
348 0 : p = strrchr_m(path, '/'); /* Find final '/', if any */
349 0 : if (p == NULL) {
350 0 : DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
351 0 : ret = strlcpy(_parent_buf, "", buflen);
352 0 : if (ret >= buflen) {
353 0 : return -EINVAL;
354 : }
355 0 : if (_trimmed != NULL) {
356 0 : *_trimmed = path;
357 : }
358 0 : return 0;
359 : }
360 :
361 0 : SMB_ASSERT(p >= path);
362 0 : len = p - path;
363 :
364 0 : ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
365 0 : if (ret >= buflen) {
366 0 : return -EINVAL;
367 : }
368 :
369 : /* for absolute paths, check that we're not going outside the share */
370 0 : if ((len > 0) && (_parent_buf[0] == '/')) {
371 0 : bool connectpath_match = false;
372 0 : size_t clen = strlen(connectpath);
373 0 : DBG_DEBUG("checking absolute path %s lies within share at %s\n",
374 : _parent_buf, connectpath);
375 : /* need to check for separator, to avoid /x/abcd vs /x/ab */
376 0 : connectpath_match = (strncmp(connectpath,
377 : _parent_buf,
378 : clen) == 0);
379 0 : if (!connectpath_match
380 0 : || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
381 0 : DBG_ERR("%s parent path is outside of share at %s\n",
382 : _parent_buf, connectpath);
383 0 : return -EINVAL;
384 : }
385 : }
386 :
387 0 : if (_trimmed != NULL) {
388 : /*
389 : * point to path component which was trimmed from _parent_buf
390 : * excluding path separator.
391 : */
392 0 : *_trimmed = p + 1;
393 : }
394 :
395 0 : DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
396 : path, _parent_buf, p + 1);
397 :
398 0 : return 0;
399 : }
400 :
401 0 : static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
402 : struct files_struct *fsp,
403 : struct shadow_copy_data *sc_data,
404 : bool labels)
405 : {
406 : int ret;
407 : TALLOC_CTX *tmp_ctx;
408 0 : const char *parent_dir = NULL;
409 : char tmp[PATH_MAX + 1];
410 : char snaps_path[PATH_MAX + 1];
411 0 : struct smb_filename *snaps_dname = NULL;
412 0 : const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
413 : "ceph", "snapdir",
414 : CEPH_SNAP_SUBDIR_DEFAULT);
415 :
416 0 : DBG_DEBUG("getting shadow copy data for %s\n",
417 : fsp->fsp_name->base_name);
418 :
419 0 : tmp_ctx = talloc_new(fsp);
420 0 : if (tmp_ctx == NULL) {
421 0 : ret = -ENOMEM;
422 0 : goto err_out;
423 : }
424 :
425 0 : if (sc_data == NULL) {
426 0 : ret = -EINVAL;
427 0 : goto err_out;
428 : }
429 :
430 0 : if (fsp->fsp_flags.is_directory) {
431 0 : parent_dir = fsp->fsp_name->base_name;
432 : } else {
433 0 : ret = ceph_snap_get_parent_path(handle->conn->connectpath,
434 0 : fsp->fsp_name->base_name,
435 : tmp,
436 : sizeof(tmp),
437 : NULL); /* trimmed */
438 0 : if (ret < 0) {
439 0 : goto err_out;
440 : }
441 0 : parent_dir = tmp;
442 : }
443 :
444 0 : if (strlen(parent_dir) == 0) {
445 0 : ret = strlcpy(snaps_path, snapdir, sizeof(snaps_path));
446 : } else {
447 0 : ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
448 : parent_dir, snapdir);
449 : }
450 0 : if (ret >= sizeof(snaps_path)) {
451 0 : ret = -EINVAL;
452 0 : goto err_out;
453 : }
454 :
455 0 : snaps_dname = synthetic_smb_fname(tmp_ctx,
456 : snaps_path,
457 : NULL,
458 : NULL,
459 : 0,
460 0 : fsp->fsp_name->flags);
461 0 : if (snaps_dname == NULL) {
462 0 : ret = -ENOMEM;
463 0 : goto err_out;
464 : }
465 :
466 0 : ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
467 0 : if (ret < 0) {
468 0 : goto err_out;
469 : }
470 :
471 0 : talloc_free(tmp_ctx);
472 0 : return 0;
473 :
474 0 : err_out:
475 0 : talloc_free(tmp_ctx);
476 0 : errno = -ret;
477 0 : return -1;
478 : }
479 :
480 0 : static int ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
481 : const struct smb_filename *smb_fname,
482 : time_t *_timestamp,
483 : char *_stripped_buf,
484 : size_t buflen)
485 : {
486 : size_t len;
487 :
488 0 : if (smb_fname->twrp == 0) {
489 0 : goto no_snapshot;
490 : }
491 :
492 0 : if (_stripped_buf != NULL) {
493 0 : len = strlcpy(_stripped_buf, smb_fname->base_name, buflen);
494 0 : if (len >= buflen) {
495 0 : return -ENAMETOOLONG;
496 : }
497 : }
498 :
499 0 : *_timestamp = nt_time_to_unix(smb_fname->twrp);
500 0 : return 0;
501 0 : no_snapshot:
502 0 : *_timestamp = 0;
503 0 : return 0;
504 : }
505 :
506 0 : static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
507 : const char *name,
508 : time_t timestamp,
509 : char *_converted_buf,
510 : size_t buflen)
511 : {
512 : int ret;
513 : NTSTATUS status;
514 0 : struct smb_Dir *dir_hnd = NULL;
515 0 : struct files_struct *dirfsp = NULL;
516 0 : const char *dname = NULL;
517 0 : char *talloced = NULL;
518 0 : struct smb_filename *snaps_dname = NULL;
519 0 : const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
520 : "ceph", "snapdir",
521 : CEPH_SNAP_SUBDIR_DEFAULT);
522 0 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
523 :
524 0 : if (tmp_ctx == NULL) {
525 0 : ret = -ENOMEM;
526 0 : goto err_out;
527 : }
528 :
529 : /*
530 : * Temporally use the caller's return buffer for this.
531 : */
532 0 : if (strlen(name) == 0) {
533 0 : ret = strlcpy(_converted_buf, snapdir, buflen);
534 : } else {
535 0 : ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
536 : }
537 0 : if (ret >= buflen) {
538 0 : ret = -EINVAL;
539 0 : goto err_out;
540 : }
541 :
542 0 : snaps_dname = synthetic_smb_fname(tmp_ctx,
543 : _converted_buf,
544 : NULL,
545 : NULL,
546 : 0,
547 : 0); /* XXX check? */
548 0 : if (snaps_dname == NULL) {
549 0 : ret = -ENOMEM;
550 0 : goto err_out;
551 : }
552 :
553 : /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
554 0 : ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
555 0 : if (ret < 0) {
556 0 : ret = -errno;
557 0 : goto err_out;
558 : }
559 :
560 0 : DBG_DEBUG("enumerating shadow copy dir at %s\n",
561 : snaps_dname->base_name);
562 :
563 0 : status = OpenDir(tmp_ctx,
564 0 : handle->conn,
565 : snaps_dname,
566 : NULL,
567 : 0,
568 : &dir_hnd);
569 0 : if (!NT_STATUS_IS_OK(status)) {
570 0 : ret = -map_errno_from_nt_status(status);
571 0 : goto err_out;
572 : }
573 :
574 : /* Check we have SEC_DIR_LIST access on this fsp. */
575 0 : dirfsp = dir_hnd_fetch_fsp(dir_hnd);
576 0 : status = smbd_check_access_rights_fsp(dirfsp->conn->cwd_fsp,
577 : dirfsp,
578 : false,
579 : SEC_DIR_LIST);
580 0 : if (!NT_STATUS_IS_OK(status)) {
581 0 : DBG_ERR("user does not have list permission "
582 : "on snapdir %s\n",
583 : fsp_str_dbg(dirfsp));
584 0 : ret = -map_errno_from_nt_status(status);
585 0 : goto err_out;
586 : }
587 :
588 0 : while ((dname = ReadDirName(dir_hnd, &talloced)) != NULL) {
589 0 : struct smb_filename *smb_fname = NULL;
590 0 : struct smb_filename *atname = NULL;
591 0 : time_t snap_secs = 0;
592 :
593 0 : if (ISDOT(dname) || ISDOTDOT(dname)) {
594 0 : TALLOC_FREE(talloced);
595 0 : continue;
596 : }
597 :
598 0 : ret = snprintf(_converted_buf, buflen, "%s/%s",
599 : snaps_dname->base_name, dname);
600 0 : if (ret >= buflen) {
601 0 : ret = -EINVAL;
602 0 : goto err_out;
603 : }
604 :
605 0 : smb_fname = synthetic_smb_fname(tmp_ctx,
606 : _converted_buf,
607 : NULL,
608 : NULL,
609 : 0,
610 : 0);
611 0 : if (smb_fname == NULL) {
612 0 : ret = -ENOMEM;
613 0 : goto err_out;
614 : }
615 :
616 0 : ret = vfs_stat(handle->conn, smb_fname);
617 0 : if (ret < 0) {
618 0 : ret = -errno;
619 0 : TALLOC_FREE(smb_fname);
620 0 : goto err_out;
621 : }
622 :
623 0 : atname = synthetic_smb_fname(tmp_ctx,
624 : dname,
625 : NULL,
626 0 : &smb_fname->st,
627 : 0,
628 : 0);
629 0 : if (atname == NULL) {
630 0 : TALLOC_FREE(smb_fname);
631 0 : ret = -ENOMEM;
632 0 : goto err_out;
633 : }
634 :
635 0 : status = openat_pathref_fsp(dirfsp, atname);
636 0 : if (!NT_STATUS_IS_OK(status)) {
637 0 : TALLOC_FREE(smb_fname);
638 0 : TALLOC_FREE(atname);
639 0 : ret = -map_errno_from_nt_status(status);
640 0 : goto err_out;
641 : }
642 :
643 0 : ret = ceph_snap_get_btime_fsp(handle, atname->fsp, &snap_secs);
644 0 : if (ret < 0) {
645 0 : TALLOC_FREE(smb_fname);
646 0 : TALLOC_FREE(atname);
647 0 : goto err_out;
648 : }
649 :
650 0 : TALLOC_FREE(smb_fname);
651 0 : TALLOC_FREE(atname);
652 :
653 : /*
654 : * check gmt_snap_time matches @timestamp
655 : */
656 0 : if (timestamp == snap_secs) {
657 0 : break;
658 : }
659 0 : DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
660 : handle->conn->connectpath, name, (long long)timestamp,
661 : dname, (long long)snap_secs);
662 0 : TALLOC_FREE(talloced);
663 : }
664 :
665 0 : if (dname == NULL) {
666 0 : DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
667 : handle->conn->connectpath, name, (long long)timestamp);
668 0 : ret = -ENOENT;
669 0 : goto err_out;
670 : }
671 :
672 : /* found, _converted_buf already contains path of interest */
673 0 : DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
674 : handle->conn->connectpath, name, (long long)timestamp,
675 : _converted_buf);
676 :
677 0 : TALLOC_FREE(talloced);
678 0 : talloc_free(tmp_ctx);
679 0 : return 0;
680 :
681 0 : err_out:
682 0 : TALLOC_FREE(talloced);
683 0 : talloc_free(tmp_ctx);
684 0 : return ret;
685 : }
686 :
687 0 : static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
688 : const char *name,
689 : time_t timestamp,
690 : char *_converted_buf,
691 : size_t buflen)
692 : {
693 : int ret;
694 : char parent[PATH_MAX + 1];
695 0 : const char *trimmed = NULL;
696 : /*
697 : * CephFS Snapshots for a given dir are nested under the ./.snap subdir
698 : * *or* under ../.snap/dir (and subsequent parent dirs).
699 : * Child dirs inherit snapshots created in parent dirs if the child
700 : * exists at the time of snapshot creation.
701 : *
702 : * At this point we don't know whether @name refers to a file or dir, so
703 : * first assume it's a dir (with a corresponding .snaps subdir)
704 : */
705 0 : ret = ceph_snap_gmt_convert_dir(handle,
706 : name,
707 : timestamp,
708 : _converted_buf,
709 : buflen);
710 0 : if (ret >= 0) {
711 : /* all done: .snap subdir exists - @name is a dir */
712 0 : DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
713 0 : return ret;
714 : }
715 :
716 : /* @name/.snap access failed, attempt snapshot access via parent */
717 0 : DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
718 : name);
719 :
720 0 : ret = ceph_snap_get_parent_path(handle->conn->connectpath,
721 : name,
722 : parent,
723 : sizeof(parent),
724 : &trimmed);
725 0 : if (ret < 0) {
726 0 : return ret;
727 : }
728 :
729 0 : ret = ceph_snap_gmt_convert_dir(handle,
730 : parent,
731 : timestamp,
732 : _converted_buf,
733 : buflen);
734 0 : if (ret < 0) {
735 0 : return ret;
736 : }
737 :
738 : /*
739 : * found snapshot via parent. Append the child path component
740 : * that was trimmed... +1 for path separator + 1 for null termination.
741 : */
742 0 : if (strlen(_converted_buf) + 1 + strlen(trimmed) + 1 > buflen) {
743 0 : return -EINVAL;
744 : }
745 0 : strlcat(_converted_buf, "/", buflen);
746 0 : strlcat(_converted_buf, trimmed, buflen);
747 :
748 0 : return 0;
749 : }
750 :
751 0 : static int ceph_snap_gmt_renameat(vfs_handle_struct *handle,
752 : files_struct *srcfsp,
753 : const struct smb_filename *smb_fname_src,
754 : files_struct *dstfsp,
755 : const struct smb_filename *smb_fname_dst)
756 : {
757 : int ret;
758 : time_t timestamp_src, timestamp_dst;
759 :
760 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
761 : smb_fname_src,
762 : ×tamp_src, NULL, 0);
763 0 : if (ret < 0) {
764 0 : errno = -ret;
765 0 : return -1;
766 : }
767 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
768 : smb_fname_dst,
769 : ×tamp_dst, NULL, 0);
770 0 : if (ret < 0) {
771 0 : errno = -ret;
772 0 : return -1;
773 : }
774 0 : if (timestamp_src != 0) {
775 0 : errno = EXDEV;
776 0 : return -1;
777 : }
778 0 : if (timestamp_dst != 0) {
779 0 : errno = EROFS;
780 0 : return -1;
781 : }
782 0 : return SMB_VFS_NEXT_RENAMEAT(handle,
783 : srcfsp,
784 : smb_fname_src,
785 : dstfsp,
786 : smb_fname_dst);
787 : }
788 :
789 : /* block links from writeable shares to snapshots for now, like other modules */
790 0 : static int ceph_snap_gmt_symlinkat(vfs_handle_struct *handle,
791 : const struct smb_filename *link_contents,
792 : struct files_struct *dirfsp,
793 : const struct smb_filename *new_smb_fname)
794 : {
795 : int ret;
796 0 : time_t timestamp_old = 0;
797 0 : time_t timestamp_new = 0;
798 :
799 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
800 : link_contents,
801 : ×tamp_old,
802 : NULL, 0);
803 0 : if (ret < 0) {
804 0 : errno = -ret;
805 0 : return -1;
806 : }
807 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
808 : new_smb_fname,
809 : ×tamp_new,
810 : NULL, 0);
811 0 : if (ret < 0) {
812 0 : errno = -ret;
813 0 : return -1;
814 : }
815 0 : if ((timestamp_old != 0) || (timestamp_new != 0)) {
816 0 : errno = EROFS;
817 0 : return -1;
818 : }
819 0 : return SMB_VFS_NEXT_SYMLINKAT(handle,
820 : link_contents,
821 : dirfsp,
822 : new_smb_fname);
823 : }
824 :
825 0 : static int ceph_snap_gmt_linkat(vfs_handle_struct *handle,
826 : files_struct *srcfsp,
827 : const struct smb_filename *old_smb_fname,
828 : files_struct *dstfsp,
829 : const struct smb_filename *new_smb_fname,
830 : int flags)
831 : {
832 : int ret;
833 0 : time_t timestamp_old = 0;
834 0 : time_t timestamp_new = 0;
835 :
836 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
837 : old_smb_fname,
838 : ×tamp_old,
839 : NULL, 0);
840 0 : if (ret < 0) {
841 0 : errno = -ret;
842 0 : return -1;
843 : }
844 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
845 : new_smb_fname,
846 : ×tamp_new,
847 : NULL, 0);
848 0 : if (ret < 0) {
849 0 : errno = -ret;
850 0 : return -1;
851 : }
852 0 : if ((timestamp_old != 0) || (timestamp_new != 0)) {
853 0 : errno = EROFS;
854 0 : return -1;
855 : }
856 0 : return SMB_VFS_NEXT_LINKAT(handle,
857 : srcfsp,
858 : old_smb_fname,
859 : dstfsp,
860 : new_smb_fname,
861 : flags);
862 : }
863 :
864 0 : static int ceph_snap_gmt_stat(vfs_handle_struct *handle,
865 : struct smb_filename *smb_fname)
866 : {
867 0 : time_t timestamp = 0;
868 : char stripped[PATH_MAX + 1];
869 : char conv[PATH_MAX + 1];
870 : char *tmp;
871 : int ret;
872 :
873 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
874 : smb_fname,
875 : ×tamp, stripped, sizeof(stripped));
876 0 : if (ret < 0) {
877 0 : errno = -ret;
878 0 : return -1;
879 : }
880 0 : if (timestamp == 0) {
881 0 : return SMB_VFS_NEXT_STAT(handle, smb_fname);
882 : }
883 :
884 0 : ret = ceph_snap_gmt_convert(handle, stripped,
885 : timestamp, conv, sizeof(conv));
886 0 : if (ret < 0) {
887 0 : errno = -ret;
888 0 : return -1;
889 : }
890 0 : tmp = smb_fname->base_name;
891 0 : smb_fname->base_name = conv;
892 :
893 0 : ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
894 0 : smb_fname->base_name = tmp;
895 0 : return ret;
896 : }
897 :
898 0 : static int ceph_snap_gmt_lstat(vfs_handle_struct *handle,
899 : struct smb_filename *smb_fname)
900 : {
901 0 : time_t timestamp = 0;
902 : char stripped[PATH_MAX + 1];
903 : char conv[PATH_MAX + 1];
904 : char *tmp;
905 : int ret;
906 :
907 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
908 : smb_fname,
909 : ×tamp, stripped, sizeof(stripped));
910 0 : if (ret < 0) {
911 0 : errno = -ret;
912 0 : return -1;
913 : }
914 0 : if (timestamp == 0) {
915 0 : return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
916 : }
917 :
918 0 : ret = ceph_snap_gmt_convert(handle, stripped,
919 : timestamp, conv, sizeof(conv));
920 0 : if (ret < 0) {
921 0 : errno = -ret;
922 0 : return -1;
923 : }
924 0 : tmp = smb_fname->base_name;
925 0 : smb_fname->base_name = conv;
926 :
927 0 : ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
928 0 : smb_fname->base_name = tmp;
929 0 : return ret;
930 : }
931 :
932 0 : static int ceph_snap_gmt_openat(vfs_handle_struct *handle,
933 : const struct files_struct *dirfsp,
934 : const struct smb_filename *smb_fname_in,
935 : files_struct *fsp,
936 : const struct vfs_open_how *how)
937 : {
938 0 : time_t timestamp = 0;
939 0 : struct smb_filename *smb_fname = NULL;
940 : char stripped[PATH_MAX + 1];
941 : char conv[PATH_MAX + 1];
942 : int ret;
943 0 : int saved_errno = 0;
944 :
945 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
946 : smb_fname_in,
947 : ×tamp,
948 : stripped,
949 : sizeof(stripped));
950 0 : if (ret < 0) {
951 0 : errno = -ret;
952 0 : return -1;
953 : }
954 0 : if (timestamp == 0) {
955 0 : return SMB_VFS_NEXT_OPENAT(handle,
956 : dirfsp,
957 : smb_fname_in,
958 : fsp,
959 : how);
960 : }
961 :
962 0 : ret = ceph_snap_gmt_convert(handle,
963 : stripped,
964 : timestamp,
965 : conv,
966 : sizeof(conv));
967 0 : if (ret < 0) {
968 0 : errno = -ret;
969 0 : return -1;
970 : }
971 0 : smb_fname = cp_smb_filename(talloc_tos(), smb_fname_in);
972 0 : if (smb_fname == NULL) {
973 0 : return -1;
974 : }
975 0 : smb_fname->base_name = conv;
976 :
977 0 : ret = SMB_VFS_NEXT_OPENAT(handle,
978 : dirfsp,
979 : smb_fname,
980 : fsp,
981 : how);
982 0 : if (ret == -1) {
983 0 : saved_errno = errno;
984 : }
985 0 : TALLOC_FREE(smb_fname);
986 0 : if (saved_errno != 0) {
987 0 : errno = saved_errno;
988 : }
989 0 : return ret;
990 : }
991 :
992 0 : static int ceph_snap_gmt_unlinkat(vfs_handle_struct *handle,
993 : struct files_struct *dirfsp,
994 : const struct smb_filename *csmb_fname,
995 : int flags)
996 : {
997 0 : time_t timestamp = 0;
998 : int ret;
999 :
1000 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1001 : csmb_fname,
1002 : ×tamp, NULL, 0);
1003 0 : if (ret < 0) {
1004 0 : errno = -ret;
1005 0 : return -1;
1006 : }
1007 0 : if (timestamp != 0) {
1008 0 : errno = EROFS;
1009 0 : return -1;
1010 : }
1011 0 : return SMB_VFS_NEXT_UNLINKAT(handle,
1012 : dirfsp,
1013 : csmb_fname,
1014 : flags);
1015 : }
1016 :
1017 0 : static int ceph_snap_gmt_fchmod(vfs_handle_struct *handle,
1018 : struct files_struct *fsp,
1019 : mode_t mode)
1020 : {
1021 0 : const struct smb_filename *csmb_fname = NULL;
1022 0 : time_t timestamp = 0;
1023 : int ret;
1024 :
1025 0 : csmb_fname = fsp->fsp_name;
1026 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1027 : csmb_fname,
1028 : ×tamp, NULL, 0);
1029 0 : if (ret < 0) {
1030 0 : errno = -ret;
1031 0 : return -1;
1032 : }
1033 0 : if (timestamp != 0) {
1034 0 : errno = EROFS;
1035 0 : return -1;
1036 : }
1037 0 : return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
1038 : }
1039 :
1040 0 : static int ceph_snap_gmt_chdir(vfs_handle_struct *handle,
1041 : const struct smb_filename *csmb_fname)
1042 : {
1043 0 : time_t timestamp = 0;
1044 : char stripped[PATH_MAX + 1];
1045 : char conv[PATH_MAX + 1];
1046 : int ret;
1047 : struct smb_filename *new_fname;
1048 : int saved_errno;
1049 :
1050 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1051 : csmb_fname,
1052 : ×tamp, stripped, sizeof(stripped));
1053 0 : if (ret < 0) {
1054 0 : errno = -ret;
1055 0 : return -1;
1056 : }
1057 0 : if (timestamp == 0) {
1058 0 : return SMB_VFS_NEXT_CHDIR(handle, csmb_fname);
1059 : }
1060 :
1061 0 : ret = ceph_snap_gmt_convert_dir(handle, stripped,
1062 : timestamp, conv, sizeof(conv));
1063 0 : if (ret < 0) {
1064 0 : errno = -ret;
1065 0 : return -1;
1066 : }
1067 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1068 0 : if (new_fname == NULL) {
1069 0 : errno = ENOMEM;
1070 0 : return -1;
1071 : }
1072 0 : new_fname->base_name = conv;
1073 :
1074 0 : ret = SMB_VFS_NEXT_CHDIR(handle, new_fname);
1075 0 : saved_errno = errno;
1076 0 : TALLOC_FREE(new_fname);
1077 0 : errno = saved_errno;
1078 0 : return ret;
1079 : }
1080 :
1081 0 : static int ceph_snap_gmt_fntimes(vfs_handle_struct *handle,
1082 : files_struct *fsp,
1083 : struct smb_file_time *ft)
1084 : {
1085 0 : time_t timestamp = 0;
1086 : int ret;
1087 :
1088 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1089 0 : fsp->fsp_name,
1090 : ×tamp,
1091 : NULL,
1092 : 0);
1093 0 : if (ret < 0) {
1094 0 : errno = -ret;
1095 0 : return -1;
1096 : }
1097 0 : if (timestamp != 0) {
1098 0 : errno = EROFS;
1099 0 : return -1;
1100 : }
1101 0 : return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
1102 : }
1103 :
1104 0 : static int ceph_snap_gmt_readlinkat(vfs_handle_struct *handle,
1105 : const struct files_struct *dirfsp,
1106 : const struct smb_filename *csmb_fname,
1107 : char *buf,
1108 : size_t bufsiz)
1109 : {
1110 0 : time_t timestamp = 0;
1111 : char conv[PATH_MAX + 1];
1112 : int ret;
1113 0 : struct smb_filename *full_fname = NULL;
1114 : int saved_errno;
1115 :
1116 : /*
1117 : * Now this function only looks at csmb_fname->twrp
1118 : * we don't need to copy out the path. Just use
1119 : * csmb_fname->base_name directly.
1120 : */
1121 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1122 : csmb_fname,
1123 : ×tamp, NULL, 0);
1124 0 : if (ret < 0) {
1125 0 : errno = -ret;
1126 0 : return -1;
1127 : }
1128 0 : if (timestamp == 0) {
1129 0 : return SMB_VFS_NEXT_READLINKAT(handle,
1130 : dirfsp,
1131 : csmb_fname,
1132 : buf,
1133 : bufsiz);
1134 : }
1135 :
1136 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1137 : dirfsp,
1138 : csmb_fname);
1139 0 : if (full_fname == NULL) {
1140 0 : return -1;
1141 : }
1142 :
1143 : /* Find the snapshot path from the full pathname. */
1144 0 : ret = ceph_snap_gmt_convert(handle,
1145 0 : full_fname->base_name,
1146 : timestamp,
1147 : conv,
1148 : sizeof(conv));
1149 0 : if (ret < 0) {
1150 0 : TALLOC_FREE(full_fname);
1151 0 : errno = -ret;
1152 0 : return -1;
1153 : }
1154 0 : full_fname->base_name = conv;
1155 :
1156 0 : ret = SMB_VFS_NEXT_READLINKAT(handle,
1157 : handle->conn->cwd_fsp,
1158 : full_fname,
1159 : buf,
1160 : bufsiz);
1161 0 : saved_errno = errno;
1162 0 : TALLOC_FREE(full_fname);
1163 0 : errno = saved_errno;
1164 0 : return ret;
1165 : }
1166 :
1167 0 : static int ceph_snap_gmt_mknodat(vfs_handle_struct *handle,
1168 : files_struct *dirfsp,
1169 : const struct smb_filename *csmb_fname,
1170 : mode_t mode,
1171 : SMB_DEV_T dev)
1172 : {
1173 0 : time_t timestamp = 0;
1174 : int ret;
1175 :
1176 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1177 : csmb_fname,
1178 : ×tamp, NULL, 0);
1179 0 : if (ret < 0) {
1180 0 : errno = -ret;
1181 0 : return -1;
1182 : }
1183 0 : if (timestamp != 0) {
1184 0 : errno = EROFS;
1185 0 : return -1;
1186 : }
1187 0 : return SMB_VFS_NEXT_MKNODAT(handle,
1188 : dirfsp,
1189 : csmb_fname,
1190 : mode,
1191 : dev);
1192 : }
1193 :
1194 0 : static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1195 : TALLOC_CTX *ctx,
1196 : const struct smb_filename *csmb_fname)
1197 : {
1198 0 : time_t timestamp = 0;
1199 : char stripped[PATH_MAX + 1];
1200 : char conv[PATH_MAX + 1];
1201 : struct smb_filename *result_fname;
1202 : int ret;
1203 : struct smb_filename *new_fname;
1204 : int saved_errno;
1205 :
1206 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1207 : csmb_fname,
1208 : ×tamp, stripped, sizeof(stripped));
1209 0 : if (ret < 0) {
1210 0 : errno = -ret;
1211 0 : return NULL;
1212 : }
1213 0 : if (timestamp == 0) {
1214 0 : return SMB_VFS_NEXT_REALPATH(handle, ctx, csmb_fname);
1215 : }
1216 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1217 : timestamp, conv, sizeof(conv));
1218 0 : if (ret < 0) {
1219 0 : errno = -ret;
1220 0 : return NULL;
1221 : }
1222 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1223 0 : if (new_fname == NULL) {
1224 0 : errno = ENOMEM;
1225 0 : return NULL;
1226 : }
1227 0 : new_fname->base_name = conv;
1228 :
1229 0 : result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, new_fname);
1230 0 : saved_errno = errno;
1231 0 : TALLOC_FREE(new_fname);
1232 0 : errno = saved_errno;
1233 0 : return result_fname;
1234 : }
1235 :
1236 0 : static int ceph_snap_gmt_mkdirat(vfs_handle_struct *handle,
1237 : struct files_struct *dirfsp,
1238 : const struct smb_filename *csmb_fname,
1239 : mode_t mode)
1240 : {
1241 0 : time_t timestamp = 0;
1242 : int ret;
1243 :
1244 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1245 : csmb_fname,
1246 : ×tamp, NULL, 0);
1247 0 : if (ret < 0) {
1248 0 : errno = -ret;
1249 0 : return -1;
1250 : }
1251 0 : if (timestamp != 0) {
1252 0 : errno = EROFS;
1253 0 : return -1;
1254 : }
1255 0 : return SMB_VFS_NEXT_MKDIRAT(handle,
1256 : dirfsp,
1257 : csmb_fname,
1258 : mode);
1259 : }
1260 :
1261 0 : static int ceph_snap_gmt_fchflags(vfs_handle_struct *handle,
1262 : struct files_struct *fsp,
1263 : unsigned int flags)
1264 : {
1265 0 : time_t timestamp = 0;
1266 : int ret;
1267 :
1268 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1269 0 : fsp->fsp_name,
1270 : ×tamp, NULL, 0);
1271 0 : if (ret < 0) {
1272 0 : errno = -ret;
1273 0 : return -1;
1274 : }
1275 0 : if (timestamp != 0) {
1276 0 : errno = EROFS;
1277 0 : return -1;
1278 : }
1279 0 : return SMB_VFS_NEXT_FCHFLAGS(handle, fsp, flags);
1280 : }
1281 :
1282 0 : static int ceph_snap_gmt_fsetxattr(struct vfs_handle_struct *handle,
1283 : struct files_struct *fsp,
1284 : const char *aname, const void *value,
1285 : size_t size, int flags)
1286 : {
1287 0 : const struct smb_filename *csmb_fname = NULL;
1288 0 : time_t timestamp = 0;
1289 : int ret;
1290 :
1291 0 : csmb_fname = fsp->fsp_name;
1292 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1293 : csmb_fname,
1294 : ×tamp, NULL, 0);
1295 0 : if (ret < 0) {
1296 0 : errno = -ret;
1297 0 : return -1;
1298 : }
1299 0 : if (timestamp != 0) {
1300 0 : errno = EROFS;
1301 0 : return -1;
1302 : }
1303 0 : return SMB_VFS_NEXT_FSETXATTR(handle, fsp,
1304 : aname, value, size, flags);
1305 : }
1306 :
1307 0 : static NTSTATUS ceph_snap_gmt_get_real_filename_at(
1308 : struct vfs_handle_struct *handle,
1309 : struct files_struct *dirfsp,
1310 : const char *name,
1311 : TALLOC_CTX *mem_ctx,
1312 : char **found_name)
1313 : {
1314 0 : time_t timestamp = 0;
1315 : char stripped[PATH_MAX + 1];
1316 : char conv[PATH_MAX + 1];
1317 0 : struct smb_filename *conv_fname = NULL;
1318 : int ret;
1319 : NTSTATUS status;
1320 :
1321 0 : ret = ceph_snap_gmt_strip_snapshot(
1322 : handle,
1323 0 : dirfsp->fsp_name,
1324 : ×tamp,
1325 : stripped,
1326 : sizeof(stripped));
1327 0 : if (ret < 0) {
1328 0 : return map_nt_error_from_unix(-ret);
1329 : }
1330 0 : if (timestamp == 0) {
1331 0 : return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1332 : handle, dirfsp, name, mem_ctx, found_name);
1333 : }
1334 0 : ret = ceph_snap_gmt_convert_dir(handle, stripped,
1335 : timestamp, conv, sizeof(conv));
1336 0 : if (ret < 0) {
1337 0 : return map_nt_error_from_unix(-ret);
1338 : }
1339 :
1340 0 : status = synthetic_pathref(
1341 : talloc_tos(),
1342 0 : dirfsp->conn->cwd_fsp,
1343 : conv,
1344 : NULL,
1345 : NULL,
1346 : 0,
1347 : 0,
1348 : &conv_fname);
1349 0 : if (!NT_STATUS_IS_OK(status)) {
1350 0 : return status;
1351 : }
1352 :
1353 0 : status = SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1354 : handle, conv_fname->fsp, name, mem_ctx, found_name);
1355 0 : TALLOC_FREE(conv_fname);
1356 0 : return status;
1357 : }
1358 :
1359 0 : static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1360 : const struct smb_filename *csmb_fname,
1361 : uint64_t *bsize,
1362 : uint64_t *dfree,
1363 : uint64_t *dsize)
1364 : {
1365 0 : time_t timestamp = 0;
1366 : char stripped[PATH_MAX + 1];
1367 : char conv[PATH_MAX + 1];
1368 : int ret;
1369 : struct smb_filename *new_fname;
1370 : int saved_errno;
1371 :
1372 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1373 : csmb_fname,
1374 : ×tamp, stripped, sizeof(stripped));
1375 0 : if (ret < 0) {
1376 0 : errno = -ret;
1377 0 : return -1;
1378 : }
1379 0 : if (timestamp == 0) {
1380 0 : return SMB_VFS_NEXT_DISK_FREE(handle, csmb_fname,
1381 : bsize, dfree, dsize);
1382 : }
1383 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1384 : timestamp, conv, sizeof(conv));
1385 0 : if (ret < 0) {
1386 0 : errno = -ret;
1387 0 : return -1;
1388 : }
1389 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1390 0 : if (new_fname == NULL) {
1391 0 : errno = ENOMEM;
1392 0 : return -1;
1393 : }
1394 0 : new_fname->base_name = conv;
1395 :
1396 0 : ret = SMB_VFS_NEXT_DISK_FREE(handle, new_fname,
1397 : bsize, dfree, dsize);
1398 0 : saved_errno = errno;
1399 0 : TALLOC_FREE(new_fname);
1400 0 : errno = saved_errno;
1401 0 : return ret;
1402 : }
1403 :
1404 0 : static int ceph_snap_gmt_get_quota(vfs_handle_struct *handle,
1405 : const struct smb_filename *csmb_fname,
1406 : enum SMB_QUOTA_TYPE qtype,
1407 : unid_t id,
1408 : SMB_DISK_QUOTA *dq)
1409 : {
1410 0 : time_t timestamp = 0;
1411 : char stripped[PATH_MAX + 1];
1412 : char conv[PATH_MAX + 1];
1413 : int ret;
1414 : struct smb_filename *new_fname;
1415 : int saved_errno;
1416 :
1417 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1418 : csmb_fname,
1419 : ×tamp, stripped, sizeof(stripped));
1420 0 : if (ret < 0) {
1421 0 : errno = -ret;
1422 0 : return -1;
1423 : }
1424 0 : if (timestamp == 0) {
1425 0 : return SMB_VFS_NEXT_GET_QUOTA(handle, csmb_fname, qtype, id, dq);
1426 : }
1427 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1428 : timestamp, conv, sizeof(conv));
1429 0 : if (ret < 0) {
1430 0 : errno = -ret;
1431 0 : return -1;
1432 : }
1433 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1434 0 : if (new_fname == NULL) {
1435 0 : errno = ENOMEM;
1436 0 : return -1;
1437 : }
1438 0 : new_fname->base_name = conv;
1439 :
1440 0 : ret = SMB_VFS_NEXT_GET_QUOTA(handle, new_fname, qtype, id, dq);
1441 0 : saved_errno = errno;
1442 0 : TALLOC_FREE(new_fname);
1443 0 : errno = saved_errno;
1444 0 : return ret;
1445 : }
1446 :
1447 : static struct vfs_fn_pointers ceph_snap_fns = {
1448 : .get_shadow_copy_data_fn = ceph_snap_get_shadow_copy_data,
1449 : .disk_free_fn = ceph_snap_gmt_disk_free,
1450 : .get_quota_fn = ceph_snap_gmt_get_quota,
1451 : .renameat_fn = ceph_snap_gmt_renameat,
1452 : .linkat_fn = ceph_snap_gmt_linkat,
1453 : .symlinkat_fn = ceph_snap_gmt_symlinkat,
1454 : .stat_fn = ceph_snap_gmt_stat,
1455 : .lstat_fn = ceph_snap_gmt_lstat,
1456 : .openat_fn = ceph_snap_gmt_openat,
1457 : .unlinkat_fn = ceph_snap_gmt_unlinkat,
1458 : .fchmod_fn = ceph_snap_gmt_fchmod,
1459 : .chdir_fn = ceph_snap_gmt_chdir,
1460 : .fntimes_fn = ceph_snap_gmt_fntimes,
1461 : .readlinkat_fn = ceph_snap_gmt_readlinkat,
1462 : .mknodat_fn = ceph_snap_gmt_mknodat,
1463 : .realpath_fn = ceph_snap_gmt_realpath,
1464 : .mkdirat_fn = ceph_snap_gmt_mkdirat,
1465 : .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1466 : .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1467 : .fsetxattr_fn = ceph_snap_gmt_fsetxattr,
1468 : .fchflags_fn = ceph_snap_gmt_fchflags,
1469 : .get_real_filename_at_fn = ceph_snap_gmt_get_real_filename_at,
1470 : };
1471 :
1472 : static_decl_vfs;
1473 27 : NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1474 : {
1475 27 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1476 : "ceph_snapshots", &ceph_snap_fns);
1477 : }
|