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