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 0 : long offset = 0;
225 : NTSTATUS status;
226 : int ret;
227 : uint32_t slots;
228 :
229 0 : DBG_DEBUG("enumerating shadow copy dir at %s\n",
230 : snaps_dname->base_name);
231 :
232 : /*
233 : * CephFS stat(dir).size *normally* returns the number of child entries
234 : * for a given dir, but it unfortunately that's not the case for the one
235 : * place we need it (dir=.snap), so we need to dynamically determine it
236 : * via readdir.
237 : */
238 :
239 0 : status = OpenDir(frame,
240 0 : handle->conn,
241 : snaps_dname,
242 : NULL,
243 : 0,
244 : &dir_hnd);
245 0 : if (!NT_STATUS_IS_OK(status)) {
246 0 : ret = -map_errno_from_nt_status(status);
247 0 : goto err_out;
248 : }
249 :
250 : /* Check we have SEC_DIR_LIST access on this fsp. */
251 0 : dirfsp = dir_hnd_fetch_fsp(dir_hnd);
252 0 : status = smbd_check_access_rights_fsp(dirfsp->conn->cwd_fsp,
253 : dirfsp,
254 : false,
255 : SEC_DIR_LIST);
256 0 : if (!NT_STATUS_IS_OK(status)) {
257 0 : DBG_ERR("user does not have list permission "
258 : "on snapdir %s\n",
259 : fsp_str_dbg(dirfsp));
260 0 : ret = -map_errno_from_nt_status(status);
261 0 : goto err_out;
262 : }
263 :
264 0 : slots = 0;
265 0 : sc_data->num_volumes = 0;
266 0 : sc_data->labels = NULL;
267 :
268 0 : while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
269 : != NULL)
270 : {
271 0 : if (ISDOT(dname) || ISDOTDOT(dname)) {
272 0 : TALLOC_FREE(talloced);
273 0 : continue;
274 : }
275 0 : sc_data->num_volumes++;
276 0 : if (!labels) {
277 0 : TALLOC_FREE(talloced);
278 0 : continue;
279 : }
280 0 : if (sc_data->num_volumes > slots) {
281 0 : uint32_t new_slot_count = slots + 10;
282 0 : SMB_ASSERT(new_slot_count > slots);
283 0 : sc_data->labels = talloc_realloc(sc_data,
284 : sc_data->labels,
285 : SHADOW_COPY_LABEL,
286 : new_slot_count);
287 0 : if (sc_data->labels == NULL) {
288 0 : TALLOC_FREE(talloced);
289 0 : ret = -ENOMEM;
290 0 : goto err_closedir;
291 : }
292 0 : memset(sc_data->labels[slots], 0,
293 : sizeof(SHADOW_COPY_LABEL) * 10);
294 :
295 0 : DBG_DEBUG("%d->%d slots for enum_snaps response\n",
296 : slots, new_slot_count);
297 0 : slots = new_slot_count;
298 : }
299 0 : DBG_DEBUG("filling shadow copy label for %s/%s\n",
300 : snaps_dname->base_name, dname);
301 0 : ret = ceph_snap_fill_label(handle,
302 : snaps_dname,
303 : dirfsp,
304 : dname,
305 0 : sc_data->labels[sc_data->num_volumes - 1]);
306 0 : if (ret < 0) {
307 0 : TALLOC_FREE(talloced);
308 0 : goto err_closedir;
309 : }
310 0 : TALLOC_FREE(talloced);
311 : }
312 :
313 0 : DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
314 : snaps_dname->base_name, sc_data->num_volumes);
315 :
316 0 : TALLOC_FREE(frame);
317 0 : return 0;
318 :
319 0 : err_closedir:
320 0 : TALLOC_FREE(frame);
321 0 : err_out:
322 0 : TALLOC_FREE(sc_data->labels);
323 0 : return ret;
324 : }
325 :
326 : /*
327 : * Prior reading: The Meaning of Path Names
328 : * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
329 : *
330 : * translate paths so that we can use the parent dir for .snap access:
331 : * myfile -> parent= trimmed=myfile
332 : * /a -> parent=/ trimmed=a
333 : * dir/sub/file -> parent=dir/sub trimmed=file
334 : * /dir/sub -> parent=/dir/ trimmed=sub
335 : */
336 0 : static int ceph_snap_get_parent_path(const char *connectpath,
337 : const char *path,
338 : char *_parent_buf,
339 : size_t buflen,
340 : const char **_trimmed)
341 : {
342 : const char *p;
343 : size_t len;
344 : int ret;
345 :
346 0 : if (!strcmp(path, "/")) {
347 0 : DBG_ERR("can't go past root for %s .snap dir\n", path);
348 0 : return -EINVAL;
349 : }
350 :
351 0 : p = strrchr_m(path, '/'); /* Find final '/', if any */
352 0 : if (p == NULL) {
353 0 : DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
354 0 : ret = strlcpy(_parent_buf, "", buflen);
355 0 : if (ret >= buflen) {
356 0 : return -EINVAL;
357 : }
358 0 : if (_trimmed != NULL) {
359 0 : *_trimmed = path;
360 : }
361 0 : return 0;
362 : }
363 :
364 0 : SMB_ASSERT(p >= path);
365 0 : len = p - path;
366 :
367 0 : ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
368 0 : if (ret >= buflen) {
369 0 : return -EINVAL;
370 : }
371 :
372 : /* for absolute paths, check that we're not going outside the share */
373 0 : if ((len > 0) && (_parent_buf[0] == '/')) {
374 0 : bool connectpath_match = false;
375 0 : size_t clen = strlen(connectpath);
376 0 : DBG_DEBUG("checking absolute path %s lies within share at %s\n",
377 : _parent_buf, connectpath);
378 : /* need to check for separator, to avoid /x/abcd vs /x/ab */
379 0 : connectpath_match = (strncmp(connectpath,
380 : _parent_buf,
381 : clen) == 0);
382 0 : if (!connectpath_match
383 0 : || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
384 0 : DBG_ERR("%s parent path is outside of share at %s\n",
385 : _parent_buf, connectpath);
386 0 : return -EINVAL;
387 : }
388 : }
389 :
390 0 : if (_trimmed != NULL) {
391 : /*
392 : * point to path component which was trimmed from _parent_buf
393 : * excluding path separator.
394 : */
395 0 : *_trimmed = p + 1;
396 : }
397 :
398 0 : DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
399 : path, _parent_buf, p + 1);
400 :
401 0 : return 0;
402 : }
403 :
404 0 : static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
405 : struct files_struct *fsp,
406 : struct shadow_copy_data *sc_data,
407 : bool labels)
408 : {
409 : int ret;
410 : TALLOC_CTX *tmp_ctx;
411 0 : const char *parent_dir = NULL;
412 : char tmp[PATH_MAX + 1];
413 : char snaps_path[PATH_MAX + 1];
414 0 : struct smb_filename *snaps_dname = NULL;
415 0 : const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
416 : "ceph", "snapdir",
417 : CEPH_SNAP_SUBDIR_DEFAULT);
418 :
419 0 : DBG_DEBUG("getting shadow copy data for %s\n",
420 : fsp->fsp_name->base_name);
421 :
422 0 : tmp_ctx = talloc_new(fsp);
423 0 : if (tmp_ctx == NULL) {
424 0 : ret = -ENOMEM;
425 0 : goto err_out;
426 : }
427 :
428 0 : if (sc_data == NULL) {
429 0 : ret = -EINVAL;
430 0 : goto err_out;
431 : }
432 :
433 0 : if (fsp->fsp_flags.is_directory) {
434 0 : parent_dir = fsp->fsp_name->base_name;
435 : } else {
436 0 : ret = ceph_snap_get_parent_path(handle->conn->connectpath,
437 0 : fsp->fsp_name->base_name,
438 : tmp,
439 : sizeof(tmp),
440 : NULL); /* trimmed */
441 0 : if (ret < 0) {
442 0 : goto err_out;
443 : }
444 0 : parent_dir = tmp;
445 : }
446 :
447 0 : if (strlen(parent_dir) == 0) {
448 0 : ret = strlcpy(snaps_path, snapdir, sizeof(snaps_path));
449 : } else {
450 0 : ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
451 : parent_dir, snapdir);
452 : }
453 0 : if (ret >= sizeof(snaps_path)) {
454 0 : ret = -EINVAL;
455 0 : goto err_out;
456 : }
457 :
458 0 : snaps_dname = synthetic_smb_fname(tmp_ctx,
459 : snaps_path,
460 : NULL,
461 : NULL,
462 : 0,
463 0 : fsp->fsp_name->flags);
464 0 : if (snaps_dname == NULL) {
465 0 : ret = -ENOMEM;
466 0 : goto err_out;
467 : }
468 :
469 0 : ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
470 0 : if (ret < 0) {
471 0 : goto err_out;
472 : }
473 :
474 0 : talloc_free(tmp_ctx);
475 0 : return 0;
476 :
477 0 : err_out:
478 0 : talloc_free(tmp_ctx);
479 0 : errno = -ret;
480 0 : return -1;
481 : }
482 :
483 0 : static int ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
484 : const struct smb_filename *smb_fname,
485 : time_t *_timestamp,
486 : char *_stripped_buf,
487 : size_t buflen)
488 : {
489 : size_t len;
490 :
491 0 : if (smb_fname->twrp == 0) {
492 0 : goto no_snapshot;
493 : }
494 :
495 0 : if (_stripped_buf != NULL) {
496 0 : len = strlcpy(_stripped_buf, smb_fname->base_name, buflen);
497 0 : if (len >= buflen) {
498 0 : return -ENAMETOOLONG;
499 : }
500 : }
501 :
502 0 : *_timestamp = nt_time_to_unix(smb_fname->twrp);
503 0 : return 0;
504 0 : no_snapshot:
505 0 : *_timestamp = 0;
506 0 : return 0;
507 : }
508 :
509 0 : static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
510 : const char *name,
511 : time_t timestamp,
512 : char *_converted_buf,
513 : size_t buflen)
514 : {
515 : int ret;
516 : NTSTATUS status;
517 0 : struct smb_Dir *dir_hnd = NULL;
518 0 : struct files_struct *dirfsp = NULL;
519 0 : const char *dname = NULL;
520 0 : char *talloced = NULL;
521 0 : long offset = 0;
522 0 : struct smb_filename *snaps_dname = NULL;
523 0 : const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
524 : "ceph", "snapdir",
525 : CEPH_SNAP_SUBDIR_DEFAULT);
526 0 : TALLOC_CTX *tmp_ctx = talloc_new(NULL);
527 :
528 0 : if (tmp_ctx == NULL) {
529 0 : ret = -ENOMEM;
530 0 : goto err_out;
531 : }
532 :
533 : /*
534 : * Temporally use the caller's return buffer for this.
535 : */
536 0 : if (strlen(name) == 0) {
537 0 : ret = strlcpy(_converted_buf, snapdir, buflen);
538 : } else {
539 0 : ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
540 : }
541 0 : if (ret >= buflen) {
542 0 : ret = -EINVAL;
543 0 : goto err_out;
544 : }
545 :
546 0 : snaps_dname = synthetic_smb_fname(tmp_ctx,
547 : _converted_buf,
548 : NULL,
549 : NULL,
550 : 0,
551 : 0); /* XXX check? */
552 0 : if (snaps_dname == NULL) {
553 0 : ret = -ENOMEM;
554 0 : goto err_out;
555 : }
556 :
557 : /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
558 0 : ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
559 0 : if (ret < 0) {
560 0 : ret = -errno;
561 0 : goto err_out;
562 : }
563 :
564 0 : DBG_DEBUG("enumerating shadow copy dir at %s\n",
565 : snaps_dname->base_name);
566 :
567 0 : status = OpenDir(tmp_ctx,
568 0 : handle->conn,
569 : snaps_dname,
570 : NULL,
571 : 0,
572 : &dir_hnd);
573 0 : if (!NT_STATUS_IS_OK(status)) {
574 0 : ret = -map_errno_from_nt_status(status);
575 0 : goto err_out;
576 : }
577 :
578 : /* Check we have SEC_DIR_LIST access on this fsp. */
579 0 : dirfsp = dir_hnd_fetch_fsp(dir_hnd);
580 0 : status = smbd_check_access_rights_fsp(dirfsp->conn->cwd_fsp,
581 : dirfsp,
582 : false,
583 : SEC_DIR_LIST);
584 0 : if (!NT_STATUS_IS_OK(status)) {
585 0 : DBG_ERR("user does not have list permission "
586 : "on snapdir %s\n",
587 : fsp_str_dbg(dirfsp));
588 0 : ret = -map_errno_from_nt_status(status);
589 0 : goto err_out;
590 : }
591 :
592 0 : while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
593 : != NULL)
594 : {
595 0 : struct smb_filename *smb_fname = NULL;
596 0 : struct smb_filename *atname = NULL;
597 0 : time_t snap_secs = 0;
598 :
599 0 : if (ISDOT(dname) || ISDOTDOT(dname)) {
600 0 : TALLOC_FREE(talloced);
601 0 : continue;
602 : }
603 :
604 0 : ret = snprintf(_converted_buf, buflen, "%s/%s",
605 : snaps_dname->base_name, dname);
606 0 : if (ret >= buflen) {
607 0 : ret = -EINVAL;
608 0 : goto err_out;
609 : }
610 :
611 0 : smb_fname = synthetic_smb_fname(tmp_ctx,
612 : _converted_buf,
613 : NULL,
614 : NULL,
615 : 0,
616 : 0);
617 0 : if (smb_fname == NULL) {
618 0 : ret = -ENOMEM;
619 0 : goto err_out;
620 : }
621 :
622 0 : ret = vfs_stat(handle->conn, smb_fname);
623 0 : if (ret < 0) {
624 0 : ret = -errno;
625 0 : TALLOC_FREE(smb_fname);
626 0 : goto err_out;
627 : }
628 :
629 0 : atname = synthetic_smb_fname(tmp_ctx,
630 : dname,
631 : NULL,
632 0 : &smb_fname->st,
633 : 0,
634 : 0);
635 0 : if (atname == NULL) {
636 0 : TALLOC_FREE(smb_fname);
637 0 : ret = -ENOMEM;
638 0 : goto err_out;
639 : }
640 :
641 0 : status = openat_pathref_fsp(dirfsp, atname);
642 0 : if (!NT_STATUS_IS_OK(status)) {
643 0 : TALLOC_FREE(smb_fname);
644 0 : TALLOC_FREE(atname);
645 0 : ret = -map_errno_from_nt_status(status);
646 0 : goto err_out;
647 : }
648 :
649 0 : ret = ceph_snap_get_btime_fsp(handle, atname->fsp, &snap_secs);
650 0 : if (ret < 0) {
651 0 : TALLOC_FREE(smb_fname);
652 0 : TALLOC_FREE(atname);
653 0 : goto err_out;
654 : }
655 :
656 0 : TALLOC_FREE(smb_fname);
657 0 : TALLOC_FREE(atname);
658 :
659 : /*
660 : * check gmt_snap_time matches @timestamp
661 : */
662 0 : if (timestamp == snap_secs) {
663 0 : break;
664 : }
665 0 : DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
666 : handle->conn->connectpath, name, (long long)timestamp,
667 : dname, (long long)snap_secs);
668 0 : TALLOC_FREE(talloced);
669 : }
670 :
671 0 : if (dname == NULL) {
672 0 : DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
673 : handle->conn->connectpath, name, (long long)timestamp);
674 0 : ret = -ENOENT;
675 0 : goto err_out;
676 : }
677 :
678 : /* found, _converted_buf already contains path of interest */
679 0 : DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
680 : handle->conn->connectpath, name, (long long)timestamp,
681 : _converted_buf);
682 :
683 0 : TALLOC_FREE(talloced);
684 0 : talloc_free(tmp_ctx);
685 0 : return 0;
686 :
687 0 : err_out:
688 0 : TALLOC_FREE(talloced);
689 0 : talloc_free(tmp_ctx);
690 0 : return ret;
691 : }
692 :
693 0 : static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
694 : const char *name,
695 : time_t timestamp,
696 : char *_converted_buf,
697 : size_t buflen)
698 : {
699 : int ret;
700 : char parent[PATH_MAX + 1];
701 0 : const char *trimmed = NULL;
702 : /*
703 : * CephFS Snapshots for a given dir are nested under the ./.snap subdir
704 : * *or* under ../.snap/dir (and subsequent parent dirs).
705 : * Child dirs inherit snapshots created in parent dirs if the child
706 : * exists at the time of snapshot creation.
707 : *
708 : * At this point we don't know whether @name refers to a file or dir, so
709 : * first assume it's a dir (with a corresponding .snaps subdir)
710 : */
711 0 : ret = ceph_snap_gmt_convert_dir(handle,
712 : name,
713 : timestamp,
714 : _converted_buf,
715 : buflen);
716 0 : if (ret >= 0) {
717 : /* all done: .snap subdir exists - @name is a dir */
718 0 : DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
719 0 : return ret;
720 : }
721 :
722 : /* @name/.snap access failed, attempt snapshot access via parent */
723 0 : DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
724 : name);
725 :
726 0 : ret = ceph_snap_get_parent_path(handle->conn->connectpath,
727 : name,
728 : parent,
729 : sizeof(parent),
730 : &trimmed);
731 0 : if (ret < 0) {
732 0 : return ret;
733 : }
734 :
735 0 : ret = ceph_snap_gmt_convert_dir(handle,
736 : parent,
737 : timestamp,
738 : _converted_buf,
739 : buflen);
740 0 : if (ret < 0) {
741 0 : return ret;
742 : }
743 :
744 : /*
745 : * found snapshot via parent. Append the child path component
746 : * that was trimmed... +1 for path separator + 1 for null termination.
747 : */
748 0 : if (strlen(_converted_buf) + 1 + strlen(trimmed) + 1 > buflen) {
749 0 : return -EINVAL;
750 : }
751 0 : strlcat(_converted_buf, "/", buflen);
752 0 : strlcat(_converted_buf, trimmed, buflen);
753 :
754 0 : return 0;
755 : }
756 :
757 0 : static int ceph_snap_gmt_renameat(vfs_handle_struct *handle,
758 : files_struct *srcfsp,
759 : const struct smb_filename *smb_fname_src,
760 : files_struct *dstfsp,
761 : const struct smb_filename *smb_fname_dst)
762 : {
763 : int ret;
764 : time_t timestamp_src, timestamp_dst;
765 :
766 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
767 : smb_fname_src,
768 : ×tamp_src, NULL, 0);
769 0 : if (ret < 0) {
770 0 : errno = -ret;
771 0 : return -1;
772 : }
773 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
774 : smb_fname_dst,
775 : ×tamp_dst, NULL, 0);
776 0 : if (ret < 0) {
777 0 : errno = -ret;
778 0 : return -1;
779 : }
780 0 : if (timestamp_src != 0) {
781 0 : errno = EXDEV;
782 0 : return -1;
783 : }
784 0 : if (timestamp_dst != 0) {
785 0 : errno = EROFS;
786 0 : return -1;
787 : }
788 0 : return SMB_VFS_NEXT_RENAMEAT(handle,
789 : srcfsp,
790 : smb_fname_src,
791 : dstfsp,
792 : smb_fname_dst);
793 : }
794 :
795 : /* block links from writeable shares to snapshots for now, like other modules */
796 0 : static int ceph_snap_gmt_symlinkat(vfs_handle_struct *handle,
797 : const struct smb_filename *link_contents,
798 : struct files_struct *dirfsp,
799 : const struct smb_filename *new_smb_fname)
800 : {
801 : int ret;
802 0 : time_t timestamp_old = 0;
803 0 : time_t timestamp_new = 0;
804 :
805 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
806 : link_contents,
807 : ×tamp_old,
808 : NULL, 0);
809 0 : if (ret < 0) {
810 0 : errno = -ret;
811 0 : return -1;
812 : }
813 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
814 : new_smb_fname,
815 : ×tamp_new,
816 : NULL, 0);
817 0 : if (ret < 0) {
818 0 : errno = -ret;
819 0 : return -1;
820 : }
821 0 : if ((timestamp_old != 0) || (timestamp_new != 0)) {
822 0 : errno = EROFS;
823 0 : return -1;
824 : }
825 0 : return SMB_VFS_NEXT_SYMLINKAT(handle,
826 : link_contents,
827 : dirfsp,
828 : new_smb_fname);
829 : }
830 :
831 0 : static int ceph_snap_gmt_linkat(vfs_handle_struct *handle,
832 : files_struct *srcfsp,
833 : const struct smb_filename *old_smb_fname,
834 : files_struct *dstfsp,
835 : const struct smb_filename *new_smb_fname,
836 : int flags)
837 : {
838 : int ret;
839 0 : time_t timestamp_old = 0;
840 0 : time_t timestamp_new = 0;
841 :
842 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
843 : old_smb_fname,
844 : ×tamp_old,
845 : NULL, 0);
846 0 : if (ret < 0) {
847 0 : errno = -ret;
848 0 : return -1;
849 : }
850 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
851 : new_smb_fname,
852 : ×tamp_new,
853 : NULL, 0);
854 0 : if (ret < 0) {
855 0 : errno = -ret;
856 0 : return -1;
857 : }
858 0 : if ((timestamp_old != 0) || (timestamp_new != 0)) {
859 0 : errno = EROFS;
860 0 : return -1;
861 : }
862 0 : return SMB_VFS_NEXT_LINKAT(handle,
863 : srcfsp,
864 : old_smb_fname,
865 : dstfsp,
866 : new_smb_fname,
867 : flags);
868 : }
869 :
870 0 : static int ceph_snap_gmt_stat(vfs_handle_struct *handle,
871 : struct smb_filename *smb_fname)
872 : {
873 0 : time_t timestamp = 0;
874 : char stripped[PATH_MAX + 1];
875 : char conv[PATH_MAX + 1];
876 : char *tmp;
877 : int ret;
878 :
879 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
880 : smb_fname,
881 : ×tamp, stripped, sizeof(stripped));
882 0 : if (ret < 0) {
883 0 : errno = -ret;
884 0 : return -1;
885 : }
886 0 : if (timestamp == 0) {
887 0 : return SMB_VFS_NEXT_STAT(handle, smb_fname);
888 : }
889 :
890 0 : ret = ceph_snap_gmt_convert(handle, stripped,
891 : timestamp, conv, sizeof(conv));
892 0 : if (ret < 0) {
893 0 : errno = -ret;
894 0 : return -1;
895 : }
896 0 : tmp = smb_fname->base_name;
897 0 : smb_fname->base_name = conv;
898 :
899 0 : ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
900 0 : smb_fname->base_name = tmp;
901 0 : return ret;
902 : }
903 :
904 0 : static int ceph_snap_gmt_lstat(vfs_handle_struct *handle,
905 : struct smb_filename *smb_fname)
906 : {
907 0 : time_t timestamp = 0;
908 : char stripped[PATH_MAX + 1];
909 : char conv[PATH_MAX + 1];
910 : char *tmp;
911 : int ret;
912 :
913 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
914 : smb_fname,
915 : ×tamp, stripped, sizeof(stripped));
916 0 : if (ret < 0) {
917 0 : errno = -ret;
918 0 : return -1;
919 : }
920 0 : if (timestamp == 0) {
921 0 : return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
922 : }
923 :
924 0 : ret = ceph_snap_gmt_convert(handle, stripped,
925 : timestamp, conv, sizeof(conv));
926 0 : if (ret < 0) {
927 0 : errno = -ret;
928 0 : return -1;
929 : }
930 0 : tmp = smb_fname->base_name;
931 0 : smb_fname->base_name = conv;
932 :
933 0 : ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
934 0 : smb_fname->base_name = tmp;
935 0 : return ret;
936 : }
937 :
938 0 : static int ceph_snap_gmt_openat(vfs_handle_struct *handle,
939 : const struct files_struct *dirfsp,
940 : const struct smb_filename *smb_fname_in,
941 : files_struct *fsp,
942 : const struct vfs_open_how *how)
943 : {
944 0 : time_t timestamp = 0;
945 0 : struct smb_filename *smb_fname = NULL;
946 : char stripped[PATH_MAX + 1];
947 : char conv[PATH_MAX + 1];
948 : int ret;
949 0 : int saved_errno = 0;
950 :
951 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
952 : smb_fname_in,
953 : ×tamp,
954 : stripped,
955 : sizeof(stripped));
956 0 : if (ret < 0) {
957 0 : errno = -ret;
958 0 : return -1;
959 : }
960 0 : if (timestamp == 0) {
961 0 : return SMB_VFS_NEXT_OPENAT(handle,
962 : dirfsp,
963 : smb_fname_in,
964 : fsp,
965 : how);
966 : }
967 :
968 0 : ret = ceph_snap_gmt_convert(handle,
969 : stripped,
970 : timestamp,
971 : conv,
972 : sizeof(conv));
973 0 : if (ret < 0) {
974 0 : errno = -ret;
975 0 : return -1;
976 : }
977 0 : smb_fname = cp_smb_filename(talloc_tos(), smb_fname_in);
978 0 : if (smb_fname == NULL) {
979 0 : return -1;
980 : }
981 0 : smb_fname->base_name = conv;
982 :
983 0 : ret = SMB_VFS_NEXT_OPENAT(handle,
984 : dirfsp,
985 : smb_fname,
986 : fsp,
987 : how);
988 0 : if (ret == -1) {
989 0 : saved_errno = errno;
990 : }
991 0 : TALLOC_FREE(smb_fname);
992 0 : if (saved_errno != 0) {
993 0 : errno = saved_errno;
994 : }
995 0 : return ret;
996 : }
997 :
998 0 : static int ceph_snap_gmt_unlinkat(vfs_handle_struct *handle,
999 : struct files_struct *dirfsp,
1000 : const struct smb_filename *csmb_fname,
1001 : int flags)
1002 : {
1003 0 : time_t timestamp = 0;
1004 : int ret;
1005 :
1006 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1007 : csmb_fname,
1008 : ×tamp, NULL, 0);
1009 0 : if (ret < 0) {
1010 0 : errno = -ret;
1011 0 : return -1;
1012 : }
1013 0 : if (timestamp != 0) {
1014 0 : errno = EROFS;
1015 0 : return -1;
1016 : }
1017 0 : return SMB_VFS_NEXT_UNLINKAT(handle,
1018 : dirfsp,
1019 : csmb_fname,
1020 : flags);
1021 : }
1022 :
1023 0 : static int ceph_snap_gmt_fchmod(vfs_handle_struct *handle,
1024 : struct files_struct *fsp,
1025 : mode_t mode)
1026 : {
1027 0 : const struct smb_filename *csmb_fname = NULL;
1028 0 : time_t timestamp = 0;
1029 : int ret;
1030 :
1031 0 : csmb_fname = fsp->fsp_name;
1032 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1033 : csmb_fname,
1034 : ×tamp, NULL, 0);
1035 0 : if (ret < 0) {
1036 0 : errno = -ret;
1037 0 : return -1;
1038 : }
1039 0 : if (timestamp != 0) {
1040 0 : errno = EROFS;
1041 0 : return -1;
1042 : }
1043 0 : return SMB_VFS_NEXT_FCHMOD(handle, fsp, mode);
1044 : }
1045 :
1046 0 : static int ceph_snap_gmt_chdir(vfs_handle_struct *handle,
1047 : const struct smb_filename *csmb_fname)
1048 : {
1049 0 : time_t timestamp = 0;
1050 : char stripped[PATH_MAX + 1];
1051 : char conv[PATH_MAX + 1];
1052 : int ret;
1053 : struct smb_filename *new_fname;
1054 : int saved_errno;
1055 :
1056 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1057 : csmb_fname,
1058 : ×tamp, stripped, sizeof(stripped));
1059 0 : if (ret < 0) {
1060 0 : errno = -ret;
1061 0 : return -1;
1062 : }
1063 0 : if (timestamp == 0) {
1064 0 : return SMB_VFS_NEXT_CHDIR(handle, csmb_fname);
1065 : }
1066 :
1067 0 : ret = ceph_snap_gmt_convert_dir(handle, stripped,
1068 : timestamp, conv, sizeof(conv));
1069 0 : if (ret < 0) {
1070 0 : errno = -ret;
1071 0 : return -1;
1072 : }
1073 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1074 0 : if (new_fname == NULL) {
1075 0 : errno = ENOMEM;
1076 0 : return -1;
1077 : }
1078 0 : new_fname->base_name = conv;
1079 :
1080 0 : ret = SMB_VFS_NEXT_CHDIR(handle, new_fname);
1081 0 : saved_errno = errno;
1082 0 : TALLOC_FREE(new_fname);
1083 0 : errno = saved_errno;
1084 0 : return ret;
1085 : }
1086 :
1087 0 : static int ceph_snap_gmt_fntimes(vfs_handle_struct *handle,
1088 : files_struct *fsp,
1089 : struct smb_file_time *ft)
1090 : {
1091 0 : time_t timestamp = 0;
1092 : int ret;
1093 :
1094 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1095 0 : fsp->fsp_name,
1096 : ×tamp,
1097 : NULL,
1098 : 0);
1099 0 : if (ret < 0) {
1100 0 : errno = -ret;
1101 0 : return -1;
1102 : }
1103 0 : if (timestamp != 0) {
1104 0 : errno = EROFS;
1105 0 : return -1;
1106 : }
1107 0 : return SMB_VFS_NEXT_FNTIMES(handle, fsp, ft);
1108 : }
1109 :
1110 0 : static int ceph_snap_gmt_readlinkat(vfs_handle_struct *handle,
1111 : const struct files_struct *dirfsp,
1112 : const struct smb_filename *csmb_fname,
1113 : char *buf,
1114 : size_t bufsiz)
1115 : {
1116 0 : time_t timestamp = 0;
1117 : char conv[PATH_MAX + 1];
1118 : int ret;
1119 0 : struct smb_filename *full_fname = NULL;
1120 : int saved_errno;
1121 :
1122 : /*
1123 : * Now this function only looks at csmb_fname->twrp
1124 : * we don't need to copy out the path. Just use
1125 : * csmb_fname->base_name directly.
1126 : */
1127 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1128 : csmb_fname,
1129 : ×tamp, NULL, 0);
1130 0 : if (ret < 0) {
1131 0 : errno = -ret;
1132 0 : return -1;
1133 : }
1134 0 : if (timestamp == 0) {
1135 0 : return SMB_VFS_NEXT_READLINKAT(handle,
1136 : dirfsp,
1137 : csmb_fname,
1138 : buf,
1139 : bufsiz);
1140 : }
1141 :
1142 0 : full_fname = full_path_from_dirfsp_atname(talloc_tos(),
1143 : dirfsp,
1144 : csmb_fname);
1145 0 : if (full_fname == NULL) {
1146 0 : return -1;
1147 : }
1148 :
1149 : /* Find the snapshot path from the full pathname. */
1150 0 : ret = ceph_snap_gmt_convert(handle,
1151 0 : full_fname->base_name,
1152 : timestamp,
1153 : conv,
1154 : sizeof(conv));
1155 0 : if (ret < 0) {
1156 0 : TALLOC_FREE(full_fname);
1157 0 : errno = -ret;
1158 0 : return -1;
1159 : }
1160 0 : full_fname->base_name = conv;
1161 :
1162 0 : ret = SMB_VFS_NEXT_READLINKAT(handle,
1163 : handle->conn->cwd_fsp,
1164 : full_fname,
1165 : buf,
1166 : bufsiz);
1167 0 : saved_errno = errno;
1168 0 : TALLOC_FREE(full_fname);
1169 0 : errno = saved_errno;
1170 0 : return ret;
1171 : }
1172 :
1173 0 : static int ceph_snap_gmt_mknodat(vfs_handle_struct *handle,
1174 : files_struct *dirfsp,
1175 : const struct smb_filename *csmb_fname,
1176 : mode_t mode,
1177 : SMB_DEV_T dev)
1178 : {
1179 0 : time_t timestamp = 0;
1180 : int ret;
1181 :
1182 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1183 : csmb_fname,
1184 : ×tamp, NULL, 0);
1185 0 : if (ret < 0) {
1186 0 : errno = -ret;
1187 0 : return -1;
1188 : }
1189 0 : if (timestamp != 0) {
1190 0 : errno = EROFS;
1191 0 : return -1;
1192 : }
1193 0 : return SMB_VFS_NEXT_MKNODAT(handle,
1194 : dirfsp,
1195 : csmb_fname,
1196 : mode,
1197 : dev);
1198 : }
1199 :
1200 0 : static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1201 : TALLOC_CTX *ctx,
1202 : const struct smb_filename *csmb_fname)
1203 : {
1204 0 : time_t timestamp = 0;
1205 : char stripped[PATH_MAX + 1];
1206 : char conv[PATH_MAX + 1];
1207 : struct smb_filename *result_fname;
1208 : int ret;
1209 : struct smb_filename *new_fname;
1210 : int saved_errno;
1211 :
1212 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1213 : csmb_fname,
1214 : ×tamp, stripped, sizeof(stripped));
1215 0 : if (ret < 0) {
1216 0 : errno = -ret;
1217 0 : return NULL;
1218 : }
1219 0 : if (timestamp == 0) {
1220 0 : return SMB_VFS_NEXT_REALPATH(handle, ctx, csmb_fname);
1221 : }
1222 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1223 : timestamp, conv, sizeof(conv));
1224 0 : if (ret < 0) {
1225 0 : errno = -ret;
1226 0 : return NULL;
1227 : }
1228 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1229 0 : if (new_fname == NULL) {
1230 0 : errno = ENOMEM;
1231 0 : return NULL;
1232 : }
1233 0 : new_fname->base_name = conv;
1234 :
1235 0 : result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, new_fname);
1236 0 : saved_errno = errno;
1237 0 : TALLOC_FREE(new_fname);
1238 0 : errno = saved_errno;
1239 0 : return result_fname;
1240 : }
1241 :
1242 0 : static int ceph_snap_gmt_mkdirat(vfs_handle_struct *handle,
1243 : struct files_struct *dirfsp,
1244 : const struct smb_filename *csmb_fname,
1245 : mode_t mode)
1246 : {
1247 0 : time_t timestamp = 0;
1248 : int ret;
1249 :
1250 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1251 : csmb_fname,
1252 : ×tamp, NULL, 0);
1253 0 : if (ret < 0) {
1254 0 : errno = -ret;
1255 0 : return -1;
1256 : }
1257 0 : if (timestamp != 0) {
1258 0 : errno = EROFS;
1259 0 : return -1;
1260 : }
1261 0 : return SMB_VFS_NEXT_MKDIRAT(handle,
1262 : dirfsp,
1263 : csmb_fname,
1264 : mode);
1265 : }
1266 :
1267 0 : static int ceph_snap_gmt_fchflags(vfs_handle_struct *handle,
1268 : struct files_struct *fsp,
1269 : unsigned int flags)
1270 : {
1271 0 : time_t timestamp = 0;
1272 : int ret;
1273 :
1274 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1275 0 : fsp->fsp_name,
1276 : ×tamp, NULL, 0);
1277 0 : if (ret < 0) {
1278 0 : errno = -ret;
1279 0 : return -1;
1280 : }
1281 0 : if (timestamp != 0) {
1282 0 : errno = EROFS;
1283 0 : return -1;
1284 : }
1285 0 : return SMB_VFS_NEXT_FCHFLAGS(handle, fsp, flags);
1286 : }
1287 :
1288 0 : static int ceph_snap_gmt_fsetxattr(struct vfs_handle_struct *handle,
1289 : struct files_struct *fsp,
1290 : const char *aname, const void *value,
1291 : size_t size, int flags)
1292 : {
1293 0 : const struct smb_filename *csmb_fname = NULL;
1294 0 : time_t timestamp = 0;
1295 : int ret;
1296 :
1297 0 : csmb_fname = fsp->fsp_name;
1298 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1299 : csmb_fname,
1300 : ×tamp, NULL, 0);
1301 0 : if (ret < 0) {
1302 0 : errno = -ret;
1303 0 : return -1;
1304 : }
1305 0 : if (timestamp != 0) {
1306 0 : errno = EROFS;
1307 0 : return -1;
1308 : }
1309 0 : return SMB_VFS_NEXT_FSETXATTR(handle, fsp,
1310 : aname, value, size, flags);
1311 : }
1312 :
1313 0 : static NTSTATUS ceph_snap_gmt_get_real_filename_at(
1314 : struct vfs_handle_struct *handle,
1315 : struct files_struct *dirfsp,
1316 : const char *name,
1317 : TALLOC_CTX *mem_ctx,
1318 : char **found_name)
1319 : {
1320 0 : time_t timestamp = 0;
1321 : char stripped[PATH_MAX + 1];
1322 : char conv[PATH_MAX + 1];
1323 0 : struct smb_filename *conv_fname = NULL;
1324 : int ret;
1325 : NTSTATUS status;
1326 :
1327 0 : ret = ceph_snap_gmt_strip_snapshot(
1328 : handle,
1329 0 : dirfsp->fsp_name,
1330 : ×tamp,
1331 : stripped,
1332 : sizeof(stripped));
1333 0 : if (ret < 0) {
1334 0 : return map_nt_error_from_unix(-ret);
1335 : }
1336 0 : if (timestamp == 0) {
1337 0 : return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1338 : handle, dirfsp, name, mem_ctx, found_name);
1339 : }
1340 0 : ret = ceph_snap_gmt_convert_dir(handle, stripped,
1341 : timestamp, conv, sizeof(conv));
1342 0 : if (ret < 0) {
1343 0 : return map_nt_error_from_unix(-ret);
1344 : }
1345 :
1346 0 : status = synthetic_pathref(
1347 : talloc_tos(),
1348 0 : dirfsp->conn->cwd_fsp,
1349 : conv,
1350 : NULL,
1351 : NULL,
1352 : 0,
1353 : 0,
1354 : &conv_fname);
1355 0 : if (!NT_STATUS_IS_OK(status)) {
1356 0 : return status;
1357 : }
1358 :
1359 0 : status = SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1360 : handle, conv_fname->fsp, name, mem_ctx, found_name);
1361 0 : TALLOC_FREE(conv_fname);
1362 0 : return status;
1363 : }
1364 :
1365 0 : static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1366 : const struct smb_filename *csmb_fname,
1367 : uint64_t *bsize,
1368 : uint64_t *dfree,
1369 : uint64_t *dsize)
1370 : {
1371 0 : time_t timestamp = 0;
1372 : char stripped[PATH_MAX + 1];
1373 : char conv[PATH_MAX + 1];
1374 : int ret;
1375 : struct smb_filename *new_fname;
1376 : int saved_errno;
1377 :
1378 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1379 : csmb_fname,
1380 : ×tamp, stripped, sizeof(stripped));
1381 0 : if (ret < 0) {
1382 0 : errno = -ret;
1383 0 : return -1;
1384 : }
1385 0 : if (timestamp == 0) {
1386 0 : return SMB_VFS_NEXT_DISK_FREE(handle, csmb_fname,
1387 : bsize, dfree, dsize);
1388 : }
1389 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1390 : timestamp, conv, sizeof(conv));
1391 0 : if (ret < 0) {
1392 0 : errno = -ret;
1393 0 : return -1;
1394 : }
1395 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1396 0 : if (new_fname == NULL) {
1397 0 : errno = ENOMEM;
1398 0 : return -1;
1399 : }
1400 0 : new_fname->base_name = conv;
1401 :
1402 0 : ret = SMB_VFS_NEXT_DISK_FREE(handle, new_fname,
1403 : bsize, dfree, dsize);
1404 0 : saved_errno = errno;
1405 0 : TALLOC_FREE(new_fname);
1406 0 : errno = saved_errno;
1407 0 : return ret;
1408 : }
1409 :
1410 0 : static int ceph_snap_gmt_get_quota(vfs_handle_struct *handle,
1411 : const struct smb_filename *csmb_fname,
1412 : enum SMB_QUOTA_TYPE qtype,
1413 : unid_t id,
1414 : SMB_DISK_QUOTA *dq)
1415 : {
1416 0 : time_t timestamp = 0;
1417 : char stripped[PATH_MAX + 1];
1418 : char conv[PATH_MAX + 1];
1419 : int ret;
1420 : struct smb_filename *new_fname;
1421 : int saved_errno;
1422 :
1423 0 : ret = ceph_snap_gmt_strip_snapshot(handle,
1424 : csmb_fname,
1425 : ×tamp, stripped, sizeof(stripped));
1426 0 : if (ret < 0) {
1427 0 : errno = -ret;
1428 0 : return -1;
1429 : }
1430 0 : if (timestamp == 0) {
1431 0 : return SMB_VFS_NEXT_GET_QUOTA(handle, csmb_fname, qtype, id, dq);
1432 : }
1433 0 : ret = ceph_snap_gmt_convert(handle, stripped,
1434 : timestamp, conv, sizeof(conv));
1435 0 : if (ret < 0) {
1436 0 : errno = -ret;
1437 0 : return -1;
1438 : }
1439 0 : new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1440 0 : if (new_fname == NULL) {
1441 0 : errno = ENOMEM;
1442 0 : return -1;
1443 : }
1444 0 : new_fname->base_name = conv;
1445 :
1446 0 : ret = SMB_VFS_NEXT_GET_QUOTA(handle, new_fname, qtype, id, dq);
1447 0 : saved_errno = errno;
1448 0 : TALLOC_FREE(new_fname);
1449 0 : errno = saved_errno;
1450 0 : return ret;
1451 : }
1452 :
1453 : static struct vfs_fn_pointers ceph_snap_fns = {
1454 : .get_shadow_copy_data_fn = ceph_snap_get_shadow_copy_data,
1455 : .disk_free_fn = ceph_snap_gmt_disk_free,
1456 : .get_quota_fn = ceph_snap_gmt_get_quota,
1457 : .renameat_fn = ceph_snap_gmt_renameat,
1458 : .linkat_fn = ceph_snap_gmt_linkat,
1459 : .symlinkat_fn = ceph_snap_gmt_symlinkat,
1460 : .stat_fn = ceph_snap_gmt_stat,
1461 : .lstat_fn = ceph_snap_gmt_lstat,
1462 : .openat_fn = ceph_snap_gmt_openat,
1463 : .unlinkat_fn = ceph_snap_gmt_unlinkat,
1464 : .fchmod_fn = ceph_snap_gmt_fchmod,
1465 : .chdir_fn = ceph_snap_gmt_chdir,
1466 : .fntimes_fn = ceph_snap_gmt_fntimes,
1467 : .readlinkat_fn = ceph_snap_gmt_readlinkat,
1468 : .mknodat_fn = ceph_snap_gmt_mknodat,
1469 : .realpath_fn = ceph_snap_gmt_realpath,
1470 : .mkdirat_fn = ceph_snap_gmt_mkdirat,
1471 : .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1472 : .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1473 : .fsetxattr_fn = ceph_snap_gmt_fsetxattr,
1474 : .fchflags_fn = ceph_snap_gmt_fchflags,
1475 : .get_real_filename_at_fn = ceph_snap_gmt_get_real_filename_at,
1476 : };
1477 :
1478 : static_decl_vfs;
1479 26 : NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1480 : {
1481 26 : return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1482 : "ceph_snapshots", &ceph_snap_fns);
1483 : }
|