s3: VFS: vfs_ceph_snapshots: Make mkdir return errno = EROFS on a shadow copy path.
[Samba.git] / source3 / modules / vfs_ceph_snapshots.c
blob832d0ab3fd9fde06d7e3e471f7d857201c9eb675
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.
6 * Copyright (C) David Disseldorp 2019
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.
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.
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/>.
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"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_VFS
35 * CephFS has a magic snapshots subdirectory in all parts of the directory tree.
36 * This module automatically makes all snapshots in this subdir visible to SMB
37 * clients (if permitted by corresponding access control).
39 #define CEPH_SNAP_SUBDIR_DEFAULT ".snap"
41 * The ceph.snap.btime (virtual) extended attribute carries the snapshot
42 * creation time in $secs.$nsecs format. It was added as part of
43 * https://tracker.ceph.com/issues/38838. Running Samba atop old Ceph versions
44 * which don't provide this xattr will not be able to enumerate or access
45 * snapshots using this module. As an alternative, vfs_shadow_copy2 could be
46 * used instead, alongside special shadow:format snapshot directory names.
48 #define CEPH_SNAP_BTIME_XATTR "ceph.snap.btime"
50 static int ceph_snap_get_btime(struct vfs_handle_struct *handle,
51 struct smb_filename *smb_fname,
52 time_t *_snap_secs)
54 int ret;
55 char snap_btime[33];
56 char *s = NULL;
57 char *endptr = NULL;
58 struct timespec snap_timespec;
59 int err;
61 ret = SMB_VFS_NEXT_GETXATTR(handle, smb_fname, CEPH_SNAP_BTIME_XATTR,
62 snap_btime, sizeof(snap_btime));
63 if (ret < 0) {
64 DBG_ERR("failed to get %s xattr: %s\n",
65 CEPH_SNAP_BTIME_XATTR, strerror(errno));
66 return -errno;
69 if (ret == 0 || ret >= sizeof(snap_btime) - 1) {
70 return -EINVAL;
73 /* ensure zero termination */
74 snap_btime[ret] = '\0';
76 /* format is sec.nsec */
77 s = strchr(snap_btime, '.');
78 if (s == NULL) {
79 DBG_ERR("invalid %s xattr value: %s\n",
80 CEPH_SNAP_BTIME_XATTR, snap_btime);
81 return -EINVAL;
84 /* First component is seconds, extract it */
85 *s = '\0';
86 snap_timespec.tv_sec = smb_strtoull(snap_btime,
87 &endptr,
88 10,
89 &err,
90 SMB_STR_FULL_STR_CONV);
91 if (err != 0) {
92 return -err;
95 /* second component is nsecs */
96 s++;
97 snap_timespec.tv_nsec = smb_strtoul(s,
98 &endptr,
99 10,
100 &err,
101 SMB_STR_FULL_STR_CONV);
102 if (err != 0) {
103 return -err;
107 * >> 30 is a rough divide by ~10**9. No need to be exact, as @GMT
108 * tokens only offer 1-second resolution (while twrp is nsec).
110 *_snap_secs = snap_timespec.tv_sec + (snap_timespec.tv_nsec >> 30);
112 return 0;
116 * XXX Ceph snapshots can be created with sub-second granularity, which means
117 * that multiple snapshots may be mapped to the same @GMT- label.
119 * @this_label is a pre-zeroed buffer to be filled with a @GMT label
120 * @return 0 if label successfully filled or -errno on error.
122 static int ceph_snap_fill_label(struct vfs_handle_struct *handle,
123 TALLOC_CTX *tmp_ctx,
124 const char *parent_snapsdir,
125 const char *subdir,
126 SHADOW_COPY_LABEL this_label)
128 struct smb_filename *smb_fname;
129 time_t snap_secs;
130 struct tm gmt_snap_time;
131 struct tm *tm_ret;
132 size_t str_sz;
133 char snap_path[PATH_MAX + 1];
134 int ret;
137 * CephFS snapshot creation times are available via a special
138 * xattr - snapshot b/m/ctimes all match the snap source.
140 ret = snprintf(snap_path, sizeof(snap_path), "%s/%s",
141 parent_snapsdir, subdir);
142 if (ret >= sizeof(snap_path)) {
143 return -EINVAL;
146 smb_fname = synthetic_smb_fname(tmp_ctx, snap_path,
147 NULL, NULL, 0);
148 if (smb_fname == NULL) {
149 return -ENOMEM;
152 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
153 if (ret < 0) {
154 return ret;
157 tm_ret = gmtime_r(&snap_secs, &gmt_snap_time);
158 if (tm_ret == NULL) {
159 return -EINVAL;
161 str_sz = strftime(this_label, sizeof(SHADOW_COPY_LABEL),
162 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
163 if (str_sz == 0) {
164 DBG_ERR("failed to convert tm to @GMT token\n");
165 return -EINVAL;
168 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
169 snap_path, this_label);
171 return 0;
174 static int ceph_snap_enum_snapdir(struct vfs_handle_struct *handle,
175 struct smb_filename *snaps_dname,
176 bool labels,
177 struct shadow_copy_data *sc_data)
179 NTSTATUS status;
180 int ret;
181 DIR *d = NULL;
182 struct dirent *e = NULL;
183 uint32_t slots;
185 status = smbd_check_access_rights(handle->conn,
186 snaps_dname,
187 false,
188 SEC_DIR_LIST);
189 if (!NT_STATUS_IS_OK(status)) {
190 DEBUG(0,("user does not have list permission "
191 "on snapdir %s\n",
192 snaps_dname->base_name));
193 ret = -map_errno_from_nt_status(status);
194 goto err_out;
197 DBG_DEBUG("enumerating shadow copy dir at %s\n",
198 snaps_dname->base_name);
201 * CephFS stat(dir).size *normally* returns the number of child entries
202 * for a given dir, but it unfortunately that's not the case for the one
203 * place we need it (dir=.snap), so we need to dynamically determine it
204 * via readdir.
206 d = SMB_VFS_NEXT_OPENDIR(handle, snaps_dname, NULL, 0);
207 if (d == NULL) {
208 ret = -errno;
209 goto err_out;
212 slots = 0;
213 sc_data->num_volumes = 0;
214 sc_data->labels = NULL;
216 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
217 e != NULL;
218 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
219 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
220 continue;
222 sc_data->num_volumes++;
223 if (!labels) {
224 continue;
226 if (sc_data->num_volumes > slots) {
227 uint32_t new_slot_count = slots + 10;
228 SMB_ASSERT(new_slot_count > slots);
229 sc_data->labels = talloc_realloc(sc_data,
230 sc_data->labels,
231 SHADOW_COPY_LABEL,
232 new_slot_count);
233 if (sc_data->labels == NULL) {
234 ret = -ENOMEM;
235 goto err_closedir;
237 memset(sc_data->labels[slots], 0,
238 sizeof(SHADOW_COPY_LABEL) * 10);
240 DBG_DEBUG("%d->%d slots for enum_snaps response\n",
241 slots, new_slot_count);
242 slots = new_slot_count;
244 DBG_DEBUG("filling shadow copy label for %s/%s\n",
245 snaps_dname->base_name, e->d_name);
246 ret = ceph_snap_fill_label(handle, snaps_dname,
247 snaps_dname->base_name, e->d_name,
248 sc_data->labels[sc_data->num_volumes - 1]);
249 if (ret < 0) {
250 goto err_closedir;
254 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
255 if (ret != 0) {
256 ret = -errno;
257 goto err_out;
260 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
261 snaps_dname->base_name, sc_data->num_volumes);
263 return 0;
265 err_closedir:
266 SMB_VFS_NEXT_CLOSEDIR(handle, d);
267 err_out:
268 TALLOC_FREE(sc_data->labels);
269 return ret;
273 * Prior reading: The Meaning of Path Names
274 * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
276 * translate paths so that we can use the parent dir for .snap access:
277 * myfile -> parent= trimmed=myfile
278 * /a -> parent=/ trimmed=a
279 * dir/sub/file -> parent=dir/sub trimmed=file
280 * /dir/sub -> parent=/dir/ trimmed=sub
282 static int ceph_snap_get_parent_path(const char *connectpath,
283 const char *path,
284 char *_parent_buf,
285 size_t buflen,
286 const char **_trimmed)
288 const char *p;
289 size_t len;
290 int ret;
292 if (!strcmp(path, "/")) {
293 DBG_ERR("can't go past root for %s .snap dir\n", path);
294 return -EINVAL;
297 p = strrchr_m(path, '/'); /* Find final '/', if any */
298 if (p == NULL) {
299 DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
300 ret = strlcpy(_parent_buf, "", buflen);
301 if (ret >= buflen) {
302 return -EINVAL;
304 if (_trimmed != NULL) {
305 *_trimmed = path;
307 return 0;
310 SMB_ASSERT(p >= path);
311 len = p - path;
313 ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
314 if (ret >= buflen) {
315 return -EINVAL;
318 /* for absolute paths, check that we're not going outside the share */
319 if ((len > 0) && (_parent_buf[0] == '/')) {
320 bool connectpath_match = false;
321 size_t clen = strlen(connectpath);
322 DBG_DEBUG("checking absolute path %s lies within share at %s\n",
323 _parent_buf, connectpath);
324 /* need to check for separator, to avoid /x/abcd vs /x/ab */
325 connectpath_match = (strncmp(connectpath,
326 _parent_buf,
327 clen) == 0);
328 if (!connectpath_match
329 || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
330 DBG_ERR("%s parent path is outside of share at %s\n",
331 _parent_buf, connectpath);
332 return -EINVAL;
336 if (_trimmed != NULL) {
338 * point to path component which was trimmed from _parent_buf
339 * excluding path separator.
341 *_trimmed = p + 1;
344 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
345 path, _parent_buf, p + 1);
347 return 0;
350 static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
351 struct files_struct *fsp,
352 struct shadow_copy_data *sc_data,
353 bool labels)
355 int ret;
356 TALLOC_CTX *tmp_ctx;
357 const char *parent_dir = NULL;
358 char tmp[PATH_MAX + 1];
359 char snaps_path[PATH_MAX + 1];
360 struct smb_filename *snaps_dname = NULL;
361 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
362 "ceph", "snapdir",
363 CEPH_SNAP_SUBDIR_DEFAULT);
365 DBG_DEBUG("getting shadow copy data for %s\n",
366 fsp->fsp_name->base_name);
368 tmp_ctx = talloc_new(fsp);
369 if (tmp_ctx == NULL) {
370 ret = -ENOMEM;
371 goto err_out;
374 if (sc_data == NULL) {
375 ret = -EINVAL;
376 goto err_out;
379 if (fsp->is_directory) {
380 parent_dir = fsp->fsp_name->base_name;
381 } else {
382 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
383 fsp->fsp_name->base_name,
384 tmp,
385 sizeof(tmp),
386 NULL); /* trimmed */
387 if (ret < 0) {
388 goto err_out;
390 parent_dir = tmp;
393 ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
394 parent_dir, snapdir);
395 if (ret >= sizeof(snaps_path)) {
396 ret = -EINVAL;
397 goto err_out;
400 snaps_dname = synthetic_smb_fname(tmp_ctx,
401 snaps_path,
402 NULL,
403 NULL,
404 fsp->fsp_name->flags);
405 if (snaps_dname == NULL) {
406 ret = -ENOMEM;
407 goto err_out;
410 ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
411 if (ret < 0) {
412 goto err_out;
415 talloc_free(tmp_ctx);
416 return 0;
418 err_out:
419 talloc_free(tmp_ctx);
420 errno = -ret;
421 return -1;
424 static bool ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
425 const char *name,
426 time_t *_timestamp,
427 char *_stripped_buf,
428 size_t buflen)
430 struct tm tm;
431 time_t timestamp;
432 const char *p;
433 char *q;
434 size_t rest_len, dst_len;
435 ptrdiff_t len_before_gmt;
437 p = strstr_m(name, "@GMT-");
438 if (p == NULL) {
439 goto no_snapshot;
441 if ((p > name) && (p[-1] != '/')) {
442 goto no_snapshot;
444 len_before_gmt = p - name;
445 q = strptime(p, GMT_FORMAT, &tm);
446 if (q == NULL) {
447 goto no_snapshot;
449 tm.tm_isdst = -1;
450 timestamp = timegm(&tm);
451 if (timestamp == (time_t)-1) {
452 goto no_snapshot;
454 if (q[0] == '\0') {
456 * The name consists of only the GMT token or the GMT
457 * token is at the end of the path.
459 if (_stripped_buf != NULL) {
460 if (len_before_gmt >= buflen) {
461 return -EINVAL;
463 if (len_before_gmt > 0) {
465 * There is a slash before the @GMT-. Remove it
466 * and copy the result.
468 len_before_gmt -= 1;
469 strlcpy(_stripped_buf, name, len_before_gmt);
470 } else {
471 _stripped_buf[0] = '\0'; /* token only */
473 DBG_DEBUG("GMT token in %s stripped to %s\n",
474 name, _stripped_buf);
476 *_timestamp = timestamp;
477 return 0;
479 if (q[0] != '/') {
481 * It is not a complete path component, i.e. the path
482 * component continues after the gmt-token.
484 goto no_snapshot;
486 q += 1;
488 rest_len = strlen(q);
489 dst_len = len_before_gmt + rest_len;
490 SMB_ASSERT(dst_len >= rest_len);
492 if (_stripped_buf != NULL) {
493 if (dst_len >= buflen) {
494 return -EINVAL;
496 if (p > name) {
497 memcpy(_stripped_buf, name, len_before_gmt);
499 if (rest_len > 0) {
500 memcpy(_stripped_buf + len_before_gmt, q, rest_len);
502 _stripped_buf[dst_len] = '\0';
503 DBG_DEBUG("GMT token in %s stripped to %s\n",
504 name, _stripped_buf);
506 *_timestamp = timestamp;
507 return 0;
508 no_snapshot:
509 *_timestamp = 0;
510 return 0;
513 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
514 const char *name,
515 time_t timestamp,
516 char *_converted_buf,
517 size_t buflen)
519 int ret;
520 NTSTATUS status;
521 DIR *d = NULL;
522 struct dirent *e = NULL;
523 struct smb_filename *snaps_dname = NULL;
524 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
525 "ceph", "snapdir",
526 CEPH_SNAP_SUBDIR_DEFAULT);
527 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
529 if (tmp_ctx == NULL) {
530 ret = -ENOMEM;
531 goto err_out;
535 * Temporally use the caller's return buffer for this.
537 ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
538 if (ret >= buflen) {
539 ret = -EINVAL;
540 goto err_out;
543 snaps_dname = synthetic_smb_fname(tmp_ctx,
544 _converted_buf,
545 NULL,
546 NULL,
547 0); /* XXX check? */
548 if (snaps_dname == NULL) {
549 ret = -ENOMEM;
550 goto err_out;
553 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
554 ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
555 if (ret < 0) {
556 ret = -errno;
557 goto err_out;
560 status = smbd_check_access_rights(handle->conn,
561 snaps_dname,
562 false,
563 SEC_DIR_LIST);
564 if (!NT_STATUS_IS_OK(status)) {
565 DEBUG(0,("user does not have list permission "
566 "on snapdir %s\n",
567 snaps_dname->base_name));
568 ret = -map_errno_from_nt_status(status);
569 goto err_out;
572 DBG_DEBUG("enumerating shadow copy dir at %s\n",
573 snaps_dname->base_name);
575 d = SMB_VFS_NEXT_OPENDIR(handle, snaps_dname, NULL, 0);
576 if (d == NULL) {
577 ret = -errno;
578 goto err_out;
581 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
582 e != NULL;
583 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
584 struct smb_filename *smb_fname;
585 time_t snap_secs;
587 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
588 continue;
591 ret = snprintf(_converted_buf, buflen, "%s/%s",
592 snaps_dname->base_name, e->d_name);
593 if (ret >= buflen) {
594 ret = -EINVAL;
595 goto err_closedir;
598 smb_fname = synthetic_smb_fname(tmp_ctx, _converted_buf,
599 NULL, NULL, 0);
600 if (smb_fname == NULL) {
601 ret = -ENOMEM;
602 goto err_closedir;
605 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
606 if (ret < 0) {
607 goto err_closedir;
611 * check gmt_snap_time matches @timestamp
613 if (timestamp == snap_secs) {
614 break;
616 DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
617 handle->conn->connectpath, name, (long long)timestamp,
618 e->d_name, (long long)snap_secs);
621 if (e == NULL) {
622 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
623 handle->conn->connectpath, name, (long long)timestamp);
624 ret = -ENOENT;
625 goto err_closedir;
628 /* found, _converted_buf already contains path of interest */
629 DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
630 handle->conn->connectpath, name, (long long)timestamp,
631 _converted_buf);
633 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
634 if (ret != 0) {
635 ret = -errno;
636 goto err_out;
638 talloc_free(tmp_ctx);
639 return 0;
641 err_closedir:
642 SMB_VFS_NEXT_CLOSEDIR(handle, d);
643 err_out:
644 talloc_free(tmp_ctx);
645 return ret;
648 static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
649 const char *name,
650 time_t timestamp,
651 char *_converted_buf,
652 size_t buflen)
654 int ret;
655 char parent[PATH_MAX + 1];
656 const char *trimmed = NULL;
658 * CephFS Snapshots for a given dir are nested under the ./.snap subdir
659 * *or* under ../.snap/dir (and subsequent parent dirs).
660 * Child dirs inherit snapshots created in parent dirs if the child
661 * exists at the time of snapshot creation.
663 * At this point we don't know whether @name refers to a file or dir, so
664 * first assume it's a dir (with a corresponding .snaps subdir)
666 ret = ceph_snap_gmt_convert_dir(handle,
667 name,
668 timestamp,
669 _converted_buf,
670 buflen);
671 if (ret >= 0) {
672 /* all done: .snap subdir exists - @name is a dir */
673 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
674 return ret;
677 /* @name/.snap access failed, attempt snapshot access via parent */
678 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
679 name);
681 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
682 name,
683 parent,
684 sizeof(parent),
685 &trimmed);
686 if (ret < 0) {
687 return ret;
690 ret = ceph_snap_gmt_convert_dir(handle,
691 parent,
692 timestamp,
693 _converted_buf,
694 buflen);
695 if (ret < 0) {
696 return ret;
700 * found snapshot via parent. Append the child path component
701 * that was trimmed... +1 for path separator + 1 for null termination.
703 if (strlen(_converted_buf) + 1 + strlen(trimmed) + 1 > buflen) {
704 return -EINVAL;
706 strlcat(_converted_buf, "/", buflen);
707 strlcat(_converted_buf, trimmed, buflen);
709 return 0;
712 static DIR *ceph_snap_gmt_opendir(vfs_handle_struct *handle,
713 const struct smb_filename *csmb_fname,
714 const char *mask,
715 uint32_t attr)
717 time_t timestamp = 0;
718 char stripped[PATH_MAX + 1];
719 int ret;
720 DIR *dir;
721 int saved_errno;
722 struct smb_filename *conv_smb_fname = NULL;
723 char conv[PATH_MAX + 1];
725 ret = ceph_snap_gmt_strip_snapshot(handle,
726 csmb_fname->base_name,
727 &timestamp,
728 stripped, sizeof(stripped));
729 if (ret < 0) {
730 errno = -ret;
731 return NULL;
733 if (timestamp == 0) {
734 return SMB_VFS_NEXT_OPENDIR(handle, csmb_fname, mask, attr);
736 ret = ceph_snap_gmt_convert_dir(handle, stripped,
737 timestamp, conv, sizeof(conv));
738 if (ret < 0) {
739 errno = -ret;
740 return NULL;
742 conv_smb_fname = synthetic_smb_fname(talloc_tos(),
743 conv,
744 NULL,
745 NULL,
746 csmb_fname->flags);
747 if (conv_smb_fname == NULL) {
748 errno = ENOMEM;
749 return NULL;
752 dir = SMB_VFS_NEXT_OPENDIR(handle, conv_smb_fname, mask, attr);
753 saved_errno = errno;
754 TALLOC_FREE(conv_smb_fname);
755 errno = saved_errno;
756 return dir;
759 static int ceph_snap_gmt_rename(vfs_handle_struct *handle,
760 const struct smb_filename *smb_fname_src,
761 const struct smb_filename *smb_fname_dst)
763 int ret;
764 time_t timestamp_src, timestamp_dst;
766 ret = ceph_snap_gmt_strip_snapshot(handle,
767 smb_fname_src->base_name,
768 &timestamp_src, NULL, 0);
769 if (ret < 0) {
770 errno = -ret;
771 return -1;
773 ret = ceph_snap_gmt_strip_snapshot(handle,
774 smb_fname_dst->base_name,
775 &timestamp_dst, NULL, 0);
776 if (ret < 0) {
777 errno = -ret;
778 return -1;
780 if (timestamp_src != 0) {
781 errno = EXDEV;
782 return -1;
784 if (timestamp_dst != 0) {
785 errno = EROFS;
786 return -1;
788 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
791 /* block links from writeable shares to snapshots for now, like other modules */
792 static int ceph_snap_gmt_symlink(vfs_handle_struct *handle,
793 const char *link_contents,
794 const struct smb_filename *new_smb_fname)
796 int ret;
797 time_t timestamp_old = 0;
798 time_t timestamp_new = 0;
800 ret = ceph_snap_gmt_strip_snapshot(handle,
801 link_contents,
802 &timestamp_old,
803 NULL, 0);
804 if (ret < 0) {
805 errno = -ret;
806 return -1;
808 ret = ceph_snap_gmt_strip_snapshot(handle,
809 new_smb_fname->base_name,
810 &timestamp_new,
811 NULL, 0);
812 if (ret < 0) {
813 errno = -ret;
814 return -1;
816 if ((timestamp_old != 0) || (timestamp_new != 0)) {
817 errno = EROFS;
818 return -1;
820 return SMB_VFS_NEXT_SYMLINK(handle, link_contents, new_smb_fname);
823 static int ceph_snap_gmt_link(vfs_handle_struct *handle,
824 const struct smb_filename *old_smb_fname,
825 const struct smb_filename *new_smb_fname)
827 int ret;
828 time_t timestamp_old = 0;
829 time_t timestamp_new = 0;
831 ret = ceph_snap_gmt_strip_snapshot(handle,
832 old_smb_fname->base_name,
833 &timestamp_old,
834 NULL, 0);
835 if (ret < 0) {
836 errno = -ret;
837 return -1;
839 ret = ceph_snap_gmt_strip_snapshot(handle,
840 new_smb_fname->base_name,
841 &timestamp_new,
842 NULL, 0);
843 if (ret < 0) {
844 errno = -ret;
845 return -1;
847 if ((timestamp_old != 0) || (timestamp_new != 0)) {
848 errno = EROFS;
849 return -1;
851 return SMB_VFS_NEXT_LINK(handle, old_smb_fname, new_smb_fname);
854 static int ceph_snap_gmt_stat(vfs_handle_struct *handle,
855 struct smb_filename *smb_fname)
857 time_t timestamp = 0;
858 char stripped[PATH_MAX + 1];
859 char conv[PATH_MAX + 1];
860 char *tmp;
861 int ret;
863 ret = ceph_snap_gmt_strip_snapshot(handle,
864 smb_fname->base_name,
865 &timestamp, stripped, sizeof(stripped));
866 if (ret < 0) {
867 errno = -ret;
868 return -1;
870 if (timestamp == 0) {
871 return SMB_VFS_NEXT_STAT(handle, smb_fname);
874 ret = ceph_snap_gmt_convert(handle, stripped,
875 timestamp, conv, sizeof(conv));
876 if (ret < 0) {
877 errno = -ret;
878 return -1;
880 tmp = smb_fname->base_name;
881 smb_fname->base_name = conv;
883 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
884 smb_fname->base_name = tmp;
885 return ret;
888 static int ceph_snap_gmt_lstat(vfs_handle_struct *handle,
889 struct smb_filename *smb_fname)
891 time_t timestamp = 0;
892 char stripped[PATH_MAX + 1];
893 char conv[PATH_MAX + 1];
894 char *tmp;
895 int ret;
897 ret = ceph_snap_gmt_strip_snapshot(handle,
898 smb_fname->base_name,
899 &timestamp, stripped, sizeof(stripped));
900 if (ret < 0) {
901 errno = -ret;
902 return -1;
904 if (timestamp == 0) {
905 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
908 ret = ceph_snap_gmt_convert(handle, stripped,
909 timestamp, conv, sizeof(conv));
910 if (ret < 0) {
911 errno = -ret;
912 return -1;
914 tmp = smb_fname->base_name;
915 smb_fname->base_name = conv;
917 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
918 smb_fname->base_name = tmp;
919 return ret;
922 static int ceph_snap_gmt_open(vfs_handle_struct *handle,
923 struct smb_filename *smb_fname, files_struct *fsp,
924 int flags, mode_t mode)
926 time_t timestamp = 0;
927 char stripped[PATH_MAX + 1];
928 char conv[PATH_MAX + 1];
929 char *tmp;
930 int ret;
932 ret = ceph_snap_gmt_strip_snapshot(handle,
933 smb_fname->base_name,
934 &timestamp, stripped, sizeof(stripped));
935 if (ret < 0) {
936 errno = -ret;
937 return -1;
939 if (timestamp == 0) {
940 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
943 ret = ceph_snap_gmt_convert(handle, stripped,
944 timestamp, conv, sizeof(conv));
945 if (ret < 0) {
946 errno = -ret;
947 return -1;
949 tmp = smb_fname->base_name;
950 smb_fname->base_name = conv;
952 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
953 smb_fname->base_name = tmp;
954 return ret;
957 static int ceph_snap_gmt_unlink(vfs_handle_struct *handle,
958 const struct smb_filename *csmb_fname)
960 time_t timestamp = 0;
961 int ret;
963 ret = ceph_snap_gmt_strip_snapshot(handle,
964 csmb_fname->base_name,
965 &timestamp, NULL, 0);
966 if (ret < 0) {
967 errno = -ret;
968 return -1;
970 if (timestamp != 0) {
971 errno = EROFS;
972 return -1;
974 return SMB_VFS_NEXT_UNLINK(handle, csmb_fname);
977 static int ceph_snap_gmt_chmod(vfs_handle_struct *handle,
978 const struct smb_filename *csmb_fname,
979 mode_t mode)
981 time_t timestamp = 0;
982 int ret;
984 ret = ceph_snap_gmt_strip_snapshot(handle,
985 csmb_fname->base_name,
986 &timestamp, NULL, 0);
987 if (ret < 0) {
988 errno = -ret;
989 return -1;
991 if (timestamp != 0) {
992 errno = EROFS;
993 return -1;
995 return SMB_VFS_NEXT_CHMOD(handle, csmb_fname, mode);
998 static int ceph_snap_gmt_chown(vfs_handle_struct *handle,
999 const struct smb_filename *csmb_fname,
1000 uid_t uid,
1001 gid_t gid)
1003 time_t timestamp = 0;
1004 int ret;
1006 ret = ceph_snap_gmt_strip_snapshot(handle,
1007 csmb_fname->base_name,
1008 &timestamp, NULL, 0);
1009 if (ret < 0) {
1010 errno = -ret;
1011 return -1;
1013 if (timestamp != 0) {
1014 errno = EROFS;
1015 return -1;
1017 return SMB_VFS_NEXT_CHOWN(handle, csmb_fname, uid, gid);
1020 static int ceph_snap_gmt_chdir(vfs_handle_struct *handle,
1021 const struct smb_filename *csmb_fname)
1023 time_t timestamp = 0;
1024 char stripped[PATH_MAX + 1];
1025 char conv[PATH_MAX + 1];
1026 int ret;
1027 struct smb_filename *new_fname;
1028 int saved_errno;
1030 ret = ceph_snap_gmt_strip_snapshot(handle,
1031 csmb_fname->base_name,
1032 &timestamp, stripped, sizeof(stripped));
1033 if (ret < 0) {
1034 errno = -ret;
1035 return -1;
1037 if (timestamp == 0) {
1038 return SMB_VFS_NEXT_CHDIR(handle, csmb_fname);
1041 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1042 timestamp, conv, sizeof(conv));
1043 if (ret < 0) {
1044 errno = -ret;
1045 return -1;
1047 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1048 if (new_fname == NULL) {
1049 errno = ENOMEM;
1050 return -1;
1052 new_fname->base_name = conv;
1054 ret = SMB_VFS_NEXT_CHDIR(handle, new_fname);
1055 saved_errno = errno;
1056 TALLOC_FREE(new_fname);
1057 errno = saved_errno;
1058 return ret;
1061 static int ceph_snap_gmt_ntimes(vfs_handle_struct *handle,
1062 const struct smb_filename *csmb_fname,
1063 struct smb_file_time *ft)
1065 time_t timestamp = 0;
1066 int ret;
1068 ret = ceph_snap_gmt_strip_snapshot(handle,
1069 csmb_fname->base_name,
1070 &timestamp, NULL, 0);
1071 if (ret < 0) {
1072 errno = -ret;
1073 return -1;
1075 if (timestamp != 0) {
1076 errno = EROFS;
1077 return -1;
1079 return SMB_VFS_NEXT_NTIMES(handle, csmb_fname, ft);
1082 static int ceph_snap_gmt_readlink(vfs_handle_struct *handle,
1083 const struct smb_filename *csmb_fname,
1084 char *buf,
1085 size_t bufsiz)
1087 time_t timestamp = 0;
1088 char stripped[PATH_MAX + 1];
1089 char conv[PATH_MAX + 1];
1090 int ret;
1091 struct smb_filename *new_fname;
1092 int saved_errno;
1094 ret = ceph_snap_gmt_strip_snapshot(handle,
1095 csmb_fname->base_name,
1096 &timestamp, stripped, sizeof(stripped));
1097 if (ret < 0) {
1098 errno = -ret;
1099 return -1;
1101 if (timestamp == 0) {
1102 return SMB_VFS_NEXT_READLINK(handle, csmb_fname, buf, bufsiz);
1104 ret = ceph_snap_gmt_convert(handle, stripped,
1105 timestamp, conv, sizeof(conv));
1106 if (ret < 0) {
1107 errno = -ret;
1108 return -1;
1110 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1111 if (new_fname == NULL) {
1112 errno = ENOMEM;
1113 return -1;
1115 new_fname->base_name = conv;
1117 ret = SMB_VFS_NEXT_READLINK(handle, new_fname, buf, bufsiz);
1118 saved_errno = errno;
1119 TALLOC_FREE(new_fname);
1120 errno = saved_errno;
1121 return ret;
1124 static int ceph_snap_gmt_mknod(vfs_handle_struct *handle,
1125 const struct smb_filename *csmb_fname,
1126 mode_t mode,
1127 SMB_DEV_T dev)
1129 time_t timestamp = 0;
1130 int ret;
1132 ret = ceph_snap_gmt_strip_snapshot(handle,
1133 csmb_fname->base_name,
1134 &timestamp, NULL, 0);
1135 if (ret < 0) {
1136 errno = -ret;
1137 return -1;
1139 if (timestamp != 0) {
1140 errno = EROFS;
1141 return -1;
1143 return SMB_VFS_NEXT_MKNOD(handle, csmb_fname, mode, dev);
1146 static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1147 TALLOC_CTX *ctx,
1148 const struct smb_filename *csmb_fname)
1150 time_t timestamp = 0;
1151 char stripped[PATH_MAX + 1];
1152 char conv[PATH_MAX + 1];
1153 struct smb_filename *result_fname;
1154 int ret;
1155 struct smb_filename *new_fname;
1156 int saved_errno;
1158 ret = ceph_snap_gmt_strip_snapshot(handle,
1159 csmb_fname->base_name,
1160 &timestamp, stripped, sizeof(stripped));
1161 if (ret < 0) {
1162 errno = -ret;
1163 return NULL;
1165 if (timestamp == 0) {
1166 return SMB_VFS_NEXT_REALPATH(handle, ctx, csmb_fname);
1168 ret = ceph_snap_gmt_convert(handle, stripped,
1169 timestamp, conv, sizeof(conv));
1170 if (ret < 0) {
1171 errno = -ret;
1172 return NULL;
1174 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1175 if (new_fname == NULL) {
1176 errno = ENOMEM;
1177 return NULL;
1179 new_fname->base_name = conv;
1181 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, new_fname);
1182 saved_errno = errno;
1183 TALLOC_FREE(new_fname);
1184 errno = saved_errno;
1185 return result_fname;
1189 * XXX this should have gone through open() conversion, so why do we need
1190 * a handler here? posix_fget_nt_acl() falls back to posix_get_nt_acl() for
1191 * dirs (or fd == -1).
1193 static NTSTATUS ceph_snap_gmt_fget_nt_acl(vfs_handle_struct *handle,
1194 struct files_struct *fsp,
1195 uint32_t security_info,
1196 TALLOC_CTX *mem_ctx,
1197 struct security_descriptor **ppdesc)
1199 time_t timestamp = 0;
1200 char stripped[PATH_MAX + 1];
1201 char conv[PATH_MAX + 1];
1202 struct smb_filename *smb_fname;
1203 int ret;
1204 NTSTATUS status;
1206 ret = ceph_snap_gmt_strip_snapshot(handle,
1207 fsp->fsp_name->base_name,
1208 &timestamp, stripped, sizeof(stripped));
1209 if (ret < 0) {
1210 return map_nt_error_from_unix(-ret);
1212 if (timestamp == 0) {
1213 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1214 mem_ctx,
1215 ppdesc);
1217 ret = ceph_snap_gmt_convert(handle, stripped,
1218 timestamp, conv, sizeof(conv));
1219 if (ret < 0) {
1220 return map_nt_error_from_unix(-ret);
1223 smb_fname = synthetic_smb_fname(mem_ctx,
1224 conv,
1225 NULL,
1226 NULL,
1227 fsp->fsp_name->flags);
1228 if (smb_fname == NULL) {
1229 return NT_STATUS_NO_MEMORY;
1232 status = SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname, security_info,
1233 mem_ctx, ppdesc);
1234 TALLOC_FREE(smb_fname);
1235 return status;
1238 static NTSTATUS ceph_snap_gmt_get_nt_acl(vfs_handle_struct *handle,
1239 const struct smb_filename *csmb_fname,
1240 uint32_t security_info,
1241 TALLOC_CTX *mem_ctx,
1242 struct security_descriptor **ppdesc)
1244 time_t timestamp = 0;
1245 char stripped[PATH_MAX + 1];
1246 char conv[PATH_MAX + 1];
1247 int ret;
1248 NTSTATUS status;
1249 struct smb_filename *new_fname;
1250 int saved_errno;
1252 ret = ceph_snap_gmt_strip_snapshot(handle,
1253 csmb_fname->base_name,
1254 &timestamp, stripped, sizeof(stripped));
1255 if (ret < 0) {
1256 return map_nt_error_from_unix(-ret);
1258 if (timestamp == 0) {
1259 return SMB_VFS_NEXT_GET_NT_ACL(handle, csmb_fname, security_info,
1260 mem_ctx, ppdesc);
1262 ret = ceph_snap_gmt_convert(handle, stripped,
1263 timestamp, conv, sizeof(conv));
1264 if (ret < 0) {
1265 return map_nt_error_from_unix(-ret);
1267 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1268 if (new_fname == NULL) {
1269 return NT_STATUS_NO_MEMORY;
1271 new_fname->base_name = conv;
1273 status = SMB_VFS_NEXT_GET_NT_ACL(handle, new_fname, security_info,
1274 mem_ctx, ppdesc);
1275 saved_errno = errno;
1276 TALLOC_FREE(new_fname);
1277 errno = saved_errno;
1278 return status;
1281 static int ceph_snap_gmt_mkdir(vfs_handle_struct *handle,
1282 const struct smb_filename *csmb_fname,
1283 mode_t mode)
1285 time_t timestamp = 0;
1286 int ret;
1288 ret = ceph_snap_gmt_strip_snapshot(handle,
1289 csmb_fname->base_name,
1290 &timestamp, NULL, 0);
1291 if (ret < 0) {
1292 errno = -ret;
1293 return -1;
1295 if (timestamp != 0) {
1296 errno = EROFS;
1297 return -1;
1299 return SMB_VFS_NEXT_MKDIR(handle, csmb_fname, mode);
1302 static int ceph_snap_gmt_rmdir(vfs_handle_struct *handle,
1303 const struct smb_filename *csmb_fname)
1305 time_t timestamp = 0;
1306 char stripped[PATH_MAX + 1];
1307 char conv[PATH_MAX + 1];
1308 int ret;
1309 struct smb_filename *new_fname;
1310 int saved_errno;
1312 ret = ceph_snap_gmt_strip_snapshot(handle,
1313 csmb_fname->base_name,
1314 &timestamp, stripped, sizeof(stripped));
1315 if (ret < 0) {
1316 errno = -ret;
1317 return -1;
1319 if (timestamp == 0) {
1320 return SMB_VFS_NEXT_RMDIR(handle, csmb_fname);
1322 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1323 timestamp, conv, sizeof(conv));
1324 if (ret < 0) {
1325 errno = -ret;
1326 return -1;
1328 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1329 if (new_fname == NULL) {
1330 errno = ENOMEM;
1331 return -1;
1333 new_fname->base_name = conv;
1335 ret = SMB_VFS_NEXT_RMDIR(handle, new_fname);
1336 saved_errno = errno;
1337 TALLOC_FREE(new_fname);
1338 errno = saved_errno;
1339 return ret;
1342 static int ceph_snap_gmt_chflags(vfs_handle_struct *handle,
1343 const struct smb_filename *csmb_fname,
1344 unsigned int flags)
1346 time_t timestamp = 0;
1347 char stripped[PATH_MAX + 1];
1348 char conv[PATH_MAX + 1];
1349 int ret;
1350 struct smb_filename *new_fname;
1351 int saved_errno;
1353 ret = ceph_snap_gmt_strip_snapshot(handle,
1354 csmb_fname->base_name,
1355 &timestamp, stripped, sizeof(stripped));
1356 if (ret < 0) {
1357 errno = -ret;
1358 return -1;
1360 if (timestamp == 0) {
1361 return SMB_VFS_NEXT_CHFLAGS(handle, csmb_fname, flags);
1363 ret = ceph_snap_gmt_convert(handle, stripped,
1364 timestamp, conv, sizeof(conv));
1365 if (ret < 0) {
1366 errno = -ret;
1367 return -1;
1369 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1370 if (new_fname == NULL) {
1371 errno = ENOMEM;
1372 return -1;
1374 new_fname->base_name = conv;
1376 ret = SMB_VFS_NEXT_CHFLAGS(handle, new_fname, flags);
1377 saved_errno = errno;
1378 TALLOC_FREE(new_fname);
1379 errno = saved_errno;
1380 return ret;
1383 static ssize_t ceph_snap_gmt_getxattr(vfs_handle_struct *handle,
1384 const struct smb_filename *csmb_fname,
1385 const char *aname,
1386 void *value,
1387 size_t size)
1389 time_t timestamp = 0;
1390 char stripped[PATH_MAX + 1];
1391 char conv[PATH_MAX + 1];
1392 int ret;
1393 struct smb_filename *new_fname;
1394 int saved_errno;
1396 ret = ceph_snap_gmt_strip_snapshot(handle,
1397 csmb_fname->base_name,
1398 &timestamp, stripped, sizeof(stripped));
1399 if (ret < 0) {
1400 errno = -ret;
1401 return -1;
1403 if (timestamp == 0) {
1404 return SMB_VFS_NEXT_GETXATTR(handle, csmb_fname, aname, value,
1405 size);
1407 ret = ceph_snap_gmt_convert(handle, stripped,
1408 timestamp, conv, sizeof(conv));
1409 if (ret < 0) {
1410 errno = -ret;
1411 return -1;
1413 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1414 if (new_fname == NULL) {
1415 errno = ENOMEM;
1416 return -1;
1418 new_fname->base_name = conv;
1420 ret = SMB_VFS_NEXT_GETXATTR(handle, new_fname, aname, value, size);
1421 saved_errno = errno;
1422 TALLOC_FREE(new_fname);
1423 errno = saved_errno;
1424 return ret;
1427 static ssize_t ceph_snap_gmt_listxattr(struct vfs_handle_struct *handle,
1428 const struct smb_filename *csmb_fname,
1429 char *list, size_t size)
1431 time_t timestamp = 0;
1432 char stripped[PATH_MAX + 1];
1433 char conv[PATH_MAX + 1];
1434 int ret;
1435 struct smb_filename *new_fname;
1436 int saved_errno;
1438 ret = ceph_snap_gmt_strip_snapshot(handle,
1439 csmb_fname->base_name,
1440 &timestamp, stripped, sizeof(stripped));
1441 if (ret < 0) {
1442 errno = -ret;
1443 return -1;
1445 if (timestamp == 0) {
1446 return SMB_VFS_NEXT_LISTXATTR(handle, csmb_fname, list, size);
1448 ret = ceph_snap_gmt_convert(handle, stripped,
1449 timestamp, conv, sizeof(conv));
1450 if (ret < 0) {
1451 errno = -ret;
1452 return -1;
1454 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1455 if (new_fname == NULL) {
1456 errno = ENOMEM;
1457 return -1;
1459 new_fname->base_name = conv;
1461 ret = SMB_VFS_NEXT_LISTXATTR(handle, new_fname, list, size);
1462 saved_errno = errno;
1463 TALLOC_FREE(new_fname);
1464 errno = saved_errno;
1465 return ret;
1468 static int ceph_snap_gmt_removexattr(vfs_handle_struct *handle,
1469 const struct smb_filename *csmb_fname,
1470 const char *aname)
1472 time_t timestamp = 0;
1473 char stripped[PATH_MAX + 1];
1474 char conv[PATH_MAX + 1];
1475 int ret;
1476 struct smb_filename *new_fname;
1477 int saved_errno;
1479 ret = ceph_snap_gmt_strip_snapshot(handle,
1480 csmb_fname->base_name,
1481 &timestamp, stripped, sizeof(stripped));
1482 if (ret < 0) {
1483 errno = -ret;
1484 return -1;
1486 if (timestamp == 0) {
1487 return SMB_VFS_NEXT_REMOVEXATTR(handle, csmb_fname, aname);
1489 ret = ceph_snap_gmt_convert(handle, stripped,
1490 timestamp, conv, sizeof(conv));
1491 if (ret < 0) {
1492 errno = -ret;
1493 return -1;
1495 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1496 if (new_fname == NULL) {
1497 errno = ENOMEM;
1498 return -1;
1500 new_fname->base_name = conv;
1502 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, new_fname, aname);
1503 saved_errno = errno;
1504 TALLOC_FREE(new_fname);
1505 errno = saved_errno;
1506 return ret;
1509 static int ceph_snap_gmt_setxattr(struct vfs_handle_struct *handle,
1510 const struct smb_filename *csmb_fname,
1511 const char *aname, const void *value,
1512 size_t size, int flags)
1514 time_t timestamp = 0;
1515 char stripped[PATH_MAX + 1];
1516 char conv[PATH_MAX + 1];
1517 int ret;
1518 struct smb_filename *new_fname;
1519 int saved_errno;
1521 ret = ceph_snap_gmt_strip_snapshot(handle,
1522 csmb_fname->base_name,
1523 &timestamp, stripped, sizeof(stripped));
1524 if (ret < 0) {
1525 errno = -ret;
1526 return -1;
1528 if (timestamp == 0) {
1529 return SMB_VFS_NEXT_SETXATTR(handle, csmb_fname,
1530 aname, value, size, flags);
1532 ret = ceph_snap_gmt_convert(handle, stripped,
1533 timestamp, conv, sizeof(conv));
1534 if (ret < 0) {
1535 errno = -ret;
1536 return -1;
1538 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1539 if (new_fname == NULL) {
1540 errno = ENOMEM;
1541 return -1;
1543 new_fname->base_name = conv;
1545 ret = SMB_VFS_NEXT_SETXATTR(handle, new_fname,
1546 aname, value, size, flags);
1547 saved_errno = errno;
1548 TALLOC_FREE(new_fname);
1549 errno = saved_errno;
1550 return ret;
1553 static int ceph_snap_gmt_get_real_filename(struct vfs_handle_struct *handle,
1554 const char *path,
1555 const char *name,
1556 TALLOC_CTX *mem_ctx,
1557 char **found_name)
1559 time_t timestamp = 0;
1560 char stripped[PATH_MAX + 1];
1561 char conv[PATH_MAX + 1];
1562 int ret;
1564 ret = ceph_snap_gmt_strip_snapshot(handle, path,
1565 &timestamp, stripped, sizeof(stripped));
1566 if (ret < 0) {
1567 errno = -ret;
1568 return -1;
1570 if (timestamp == 0) {
1571 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1572 mem_ctx, found_name);
1574 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1575 timestamp, conv, sizeof(conv));
1576 if (ret < 0) {
1577 errno = -ret;
1578 return -1;
1580 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1581 mem_ctx, found_name);
1582 return ret;
1585 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1586 const struct smb_filename *csmb_fname,
1587 uint64_t *bsize,
1588 uint64_t *dfree,
1589 uint64_t *dsize)
1591 time_t timestamp = 0;
1592 char stripped[PATH_MAX + 1];
1593 char conv[PATH_MAX + 1];
1594 int ret;
1595 struct smb_filename *new_fname;
1596 int saved_errno;
1598 ret = ceph_snap_gmt_strip_snapshot(handle,
1599 csmb_fname->base_name,
1600 &timestamp, stripped, sizeof(stripped));
1601 if (ret < 0) {
1602 errno = -ret;
1603 return -1;
1605 if (timestamp == 0) {
1606 return SMB_VFS_NEXT_DISK_FREE(handle, csmb_fname,
1607 bsize, dfree, dsize);
1609 ret = ceph_snap_gmt_convert(handle, stripped,
1610 timestamp, conv, sizeof(conv));
1611 if (ret < 0) {
1612 errno = -ret;
1613 return -1;
1615 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1616 if (new_fname == NULL) {
1617 errno = ENOMEM;
1618 return -1;
1620 new_fname->base_name = conv;
1622 ret = SMB_VFS_NEXT_DISK_FREE(handle, new_fname,
1623 bsize, dfree, dsize);
1624 saved_errno = errno;
1625 TALLOC_FREE(new_fname);
1626 errno = saved_errno;
1627 return ret;
1630 static int ceph_snap_gmt_get_quota(vfs_handle_struct *handle,
1631 const struct smb_filename *csmb_fname,
1632 enum SMB_QUOTA_TYPE qtype,
1633 unid_t id,
1634 SMB_DISK_QUOTA *dq)
1636 time_t timestamp = 0;
1637 char stripped[PATH_MAX + 1];
1638 char conv[PATH_MAX + 1];
1639 int ret;
1640 struct smb_filename *new_fname;
1641 int saved_errno;
1643 ret = ceph_snap_gmt_strip_snapshot(handle,
1644 csmb_fname->base_name,
1645 &timestamp, stripped, sizeof(stripped));
1646 if (ret < 0) {
1647 errno = -ret;
1648 return -1;
1650 if (timestamp == 0) {
1651 return SMB_VFS_NEXT_GET_QUOTA(handle, csmb_fname, qtype, id, dq);
1653 ret = ceph_snap_gmt_convert(handle, stripped,
1654 timestamp, conv, sizeof(conv));
1655 if (ret < 0) {
1656 errno = -ret;
1657 return -1;
1659 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1660 if (new_fname == NULL) {
1661 errno = ENOMEM;
1662 return -1;
1664 new_fname->base_name = conv;
1666 ret = SMB_VFS_NEXT_GET_QUOTA(handle, new_fname, qtype, id, dq);
1667 saved_errno = errno;
1668 TALLOC_FREE(new_fname);
1669 errno = saved_errno;
1670 return ret;
1673 static struct vfs_fn_pointers ceph_snap_fns = {
1674 .get_shadow_copy_data_fn = ceph_snap_get_shadow_copy_data,
1675 .opendir_fn = ceph_snap_gmt_opendir,
1676 .disk_free_fn = ceph_snap_gmt_disk_free,
1677 .get_quota_fn = ceph_snap_gmt_get_quota,
1678 .rename_fn = ceph_snap_gmt_rename,
1679 .link_fn = ceph_snap_gmt_link,
1680 .symlink_fn = ceph_snap_gmt_symlink,
1681 .stat_fn = ceph_snap_gmt_stat,
1682 .lstat_fn = ceph_snap_gmt_lstat,
1683 .open_fn = ceph_snap_gmt_open,
1684 .unlink_fn = ceph_snap_gmt_unlink,
1685 .chmod_fn = ceph_snap_gmt_chmod,
1686 .chown_fn = ceph_snap_gmt_chown,
1687 .chdir_fn = ceph_snap_gmt_chdir,
1688 .ntimes_fn = ceph_snap_gmt_ntimes,
1689 .readlink_fn = ceph_snap_gmt_readlink,
1690 .mknod_fn = ceph_snap_gmt_mknod,
1691 .realpath_fn = ceph_snap_gmt_realpath,
1692 .get_nt_acl_fn = ceph_snap_gmt_get_nt_acl,
1693 .fget_nt_acl_fn = ceph_snap_gmt_fget_nt_acl,
1694 .get_nt_acl_fn = ceph_snap_gmt_get_nt_acl,
1695 .mkdir_fn = ceph_snap_gmt_mkdir,
1696 .rmdir_fn = ceph_snap_gmt_rmdir,
1697 .getxattr_fn = ceph_snap_gmt_getxattr,
1698 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1699 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1700 .listxattr_fn = ceph_snap_gmt_listxattr,
1701 .removexattr_fn = ceph_snap_gmt_removexattr,
1702 .setxattr_fn = ceph_snap_gmt_setxattr,
1703 .chflags_fn = ceph_snap_gmt_chflags,
1704 .get_real_filename_fn = ceph_snap_gmt_get_real_filename,
1707 static_decl_vfs;
1708 NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1710 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1711 "ceph_snapshots", &ceph_snap_fns);