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/>.
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"
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,
58 struct timespec snap_timespec;
61 ret = SMB_VFS_NEXT_GETXATTR(handle, smb_fname, CEPH_SNAP_BTIME_XATTR,
62 snap_btime, sizeof(snap_btime));
64 DBG_ERR("failed to get %s xattr: %s\n",
65 CEPH_SNAP_BTIME_XATTR, strerror(errno));
69 if (ret == 0 || ret >= sizeof(snap_btime) - 1) {
73 /* ensure zero termination */
74 snap_btime[ret] = '\0';
76 /* format is sec.nsec */
77 s = strchr(snap_btime, '.');
79 DBG_ERR("invalid %s xattr value: %s\n",
80 CEPH_SNAP_BTIME_XATTR, snap_btime);
84 /* First component is seconds, extract it */
86 snap_timespec.tv_sec = smb_strtoull(snap_btime,
90 SMB_STR_FULL_STR_CONV);
95 /* second component is nsecs */
97 snap_timespec.tv_nsec = smb_strtoul(s,
101 SMB_STR_FULL_STR_CONV);
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);
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,
124 const char *parent_snapsdir,
126 SHADOW_COPY_LABEL this_label)
128 struct smb_filename *smb_fname;
130 struct tm gmt_snap_time;
133 char snap_path[PATH_MAX + 1];
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)) {
146 smb_fname = synthetic_smb_fname(tmp_ctx,
152 if (smb_fname == NULL) {
156 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
161 tm_ret = gmtime_r(&snap_secs, &gmt_snap_time);
162 if (tm_ret == NULL) {
165 str_sz = strftime(this_label, sizeof(SHADOW_COPY_LABEL),
166 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
168 DBG_ERR("failed to convert tm to @GMT token\n");
172 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
173 snap_path, this_label);
178 static int ceph_snap_enum_snapdir(struct vfs_handle_struct *handle,
179 struct smb_filename *snaps_dname,
181 struct shadow_copy_data *sc_data)
183 TALLOC_CTX *frame = talloc_stackframe();
184 struct smb_Dir *dir_hnd = NULL;
185 const char *dname = NULL;
186 char *talloced = NULL;
192 status = smbd_check_access_rights(handle->conn,
193 handle->conn->cwd_fsp,
197 if (!NT_STATUS_IS_OK(status)) {
198 DEBUG(0,("user does not have list permission "
200 snaps_dname->base_name));
201 ret = -map_errno_from_nt_status(status);
205 DBG_DEBUG("enumerating shadow copy dir at %s\n",
206 snaps_dname->base_name);
209 * CephFS stat(dir).size *normally* returns the number of child entries
210 * for a given dir, but it unfortunately that's not the case for the one
211 * place we need it (dir=.snap), so we need to dynamically determine it
215 dir_hnd = OpenDir(frame, handle->conn, snaps_dname, NULL, 0);
216 if (dir_hnd == NULL) {
222 sc_data->num_volumes = 0;
223 sc_data->labels = NULL;
225 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
228 if (ISDOT(dname) || ISDOTDOT(dname)) {
229 TALLOC_FREE(talloced);
232 sc_data->num_volumes++;
234 TALLOC_FREE(talloced);
237 if (sc_data->num_volumes > slots) {
238 uint32_t new_slot_count = slots + 10;
239 SMB_ASSERT(new_slot_count > slots);
240 sc_data->labels = talloc_realloc(sc_data,
244 if (sc_data->labels == NULL) {
245 TALLOC_FREE(talloced);
249 memset(sc_data->labels[slots], 0,
250 sizeof(SHADOW_COPY_LABEL) * 10);
252 DBG_DEBUG("%d->%d slots for enum_snaps response\n",
253 slots, new_slot_count);
254 slots = new_slot_count;
256 DBG_DEBUG("filling shadow copy label for %s/%s\n",
257 snaps_dname->base_name, dname);
258 ret = ceph_snap_fill_label(handle, snaps_dname,
259 snaps_dname->base_name, dname,
260 sc_data->labels[sc_data->num_volumes - 1]);
262 TALLOC_FREE(talloced);
265 TALLOC_FREE(talloced);
268 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
269 snaps_dname->base_name, sc_data->num_volumes);
277 TALLOC_FREE(sc_data->labels);
282 * Prior reading: The Meaning of Path Names
283 * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
285 * translate paths so that we can use the parent dir for .snap access:
286 * myfile -> parent= trimmed=myfile
287 * /a -> parent=/ trimmed=a
288 * dir/sub/file -> parent=dir/sub trimmed=file
289 * /dir/sub -> parent=/dir/ trimmed=sub
291 static int ceph_snap_get_parent_path(const char *connectpath,
295 const char **_trimmed)
301 if (!strcmp(path, "/")) {
302 DBG_ERR("can't go past root for %s .snap dir\n", path);
306 p = strrchr_m(path, '/'); /* Find final '/', if any */
308 DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
309 ret = strlcpy(_parent_buf, "", buflen);
313 if (_trimmed != NULL) {
319 SMB_ASSERT(p >= path);
322 ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
327 /* for absolute paths, check that we're not going outside the share */
328 if ((len > 0) && (_parent_buf[0] == '/')) {
329 bool connectpath_match = false;
330 size_t clen = strlen(connectpath);
331 DBG_DEBUG("checking absolute path %s lies within share at %s\n",
332 _parent_buf, connectpath);
333 /* need to check for separator, to avoid /x/abcd vs /x/ab */
334 connectpath_match = (strncmp(connectpath,
337 if (!connectpath_match
338 || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
339 DBG_ERR("%s parent path is outside of share at %s\n",
340 _parent_buf, connectpath);
345 if (_trimmed != NULL) {
347 * point to path component which was trimmed from _parent_buf
348 * excluding path separator.
353 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
354 path, _parent_buf, p + 1);
359 static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
360 struct files_struct *fsp,
361 struct shadow_copy_data *sc_data,
366 const char *parent_dir = NULL;
367 char tmp[PATH_MAX + 1];
368 char snaps_path[PATH_MAX + 1];
369 struct smb_filename *snaps_dname = NULL;
370 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
372 CEPH_SNAP_SUBDIR_DEFAULT);
374 DBG_DEBUG("getting shadow copy data for %s\n",
375 fsp->fsp_name->base_name);
377 tmp_ctx = talloc_new(fsp);
378 if (tmp_ctx == NULL) {
383 if (sc_data == NULL) {
388 if (fsp->fsp_flags.is_directory) {
389 parent_dir = fsp->fsp_name->base_name;
391 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
392 fsp->fsp_name->base_name,
402 if (strlen(parent_dir) == 0) {
403 ret = strlcpy(snaps_path, snapdir, sizeof(snaps_path));
405 ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
406 parent_dir, snapdir);
408 if (ret >= sizeof(snaps_path)) {
413 snaps_dname = synthetic_smb_fname(tmp_ctx,
418 fsp->fsp_name->flags);
419 if (snaps_dname == NULL) {
424 ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
429 talloc_free(tmp_ctx);
433 talloc_free(tmp_ctx);
438 static bool ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
448 size_t rest_len, dst_len;
449 ptrdiff_t len_before_gmt;
451 p = strstr_m(name, "@GMT-");
455 if ((p > name) && (p[-1] != '/')) {
458 len_before_gmt = p - name;
459 q = strptime(p, GMT_FORMAT, &tm);
464 timestamp = timegm(&tm);
465 if (timestamp == (time_t)-1) {
470 * The name consists of only the GMT token or the GMT
471 * token is at the end of the path.
473 if (_stripped_buf != NULL) {
474 if (len_before_gmt >= buflen) {
477 if (len_before_gmt > 0) {
479 * There is a slash before the @GMT-. Remove it
480 * and copy the result.
483 strlcpy(_stripped_buf, name, len_before_gmt);
485 _stripped_buf[0] = '\0'; /* token only */
487 DBG_DEBUG("GMT token in %s stripped to %s\n",
488 name, _stripped_buf);
490 *_timestamp = timestamp;
495 * It is not a complete path component, i.e. the path
496 * component continues after the gmt-token.
502 rest_len = strlen(q);
503 dst_len = len_before_gmt + rest_len;
504 SMB_ASSERT(dst_len >= rest_len);
506 if (_stripped_buf != NULL) {
507 if (dst_len >= buflen) {
511 memcpy(_stripped_buf, name, len_before_gmt);
514 memcpy(_stripped_buf + len_before_gmt, q, rest_len);
516 _stripped_buf[dst_len] = '\0';
517 DBG_DEBUG("GMT token in %s stripped to %s\n",
518 name, _stripped_buf);
520 *_timestamp = timestamp;
527 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
530 char *_converted_buf,
535 struct smb_Dir *dir_hnd = NULL;
536 const char *dname = NULL;
537 char *talloced = NULL;
539 struct smb_filename *snaps_dname = NULL;
540 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
542 CEPH_SNAP_SUBDIR_DEFAULT);
543 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
545 if (tmp_ctx == NULL) {
551 * Temporally use the caller's return buffer for this.
553 if (strlen(name) == 0) {
554 ret = strlcpy(_converted_buf, snapdir, buflen);
556 ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
563 snaps_dname = synthetic_smb_fname(tmp_ctx,
569 if (snaps_dname == NULL) {
574 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
575 ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
581 status = smbd_check_access_rights(handle->conn,
582 handle->conn->cwd_fsp,
586 if (!NT_STATUS_IS_OK(status)) {
587 DEBUG(0,("user does not have list permission "
589 snaps_dname->base_name));
590 ret = -map_errno_from_nt_status(status);
594 DBG_DEBUG("enumerating shadow copy dir at %s\n",
595 snaps_dname->base_name);
597 dir_hnd = OpenDir(tmp_ctx, handle->conn, snaps_dname, NULL, 0);
598 if (dir_hnd == NULL) {
603 while ((dname = ReadDirName(dir_hnd, &offset, NULL, &talloced))
606 struct smb_filename *smb_fname;
609 if (ISDOT(dname) || ISDOTDOT(dname)) {
610 TALLOC_FREE(talloced);
614 ret = snprintf(_converted_buf, buflen, "%s/%s",
615 snaps_dname->base_name, dname);
621 smb_fname = synthetic_smb_fname(tmp_ctx,
627 if (smb_fname == NULL) {
632 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
638 * check gmt_snap_time matches @timestamp
640 if (timestamp == snap_secs) {
643 DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
644 handle->conn->connectpath, name, (long long)timestamp,
645 dname, (long long)snap_secs);
646 TALLOC_FREE(talloced);
650 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
651 handle->conn->connectpath, name, (long long)timestamp);
656 /* found, _converted_buf already contains path of interest */
657 DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
658 handle->conn->connectpath, name, (long long)timestamp,
661 TALLOC_FREE(talloced);
662 talloc_free(tmp_ctx);
666 TALLOC_FREE(talloced);
667 talloc_free(tmp_ctx);
671 static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
674 char *_converted_buf,
678 char parent[PATH_MAX + 1];
679 const char *trimmed = NULL;
681 * CephFS Snapshots for a given dir are nested under the ./.snap subdir
682 * *or* under ../.snap/dir (and subsequent parent dirs).
683 * Child dirs inherit snapshots created in parent dirs if the child
684 * exists at the time of snapshot creation.
686 * At this point we don't know whether @name refers to a file or dir, so
687 * first assume it's a dir (with a corresponding .snaps subdir)
689 ret = ceph_snap_gmt_convert_dir(handle,
695 /* all done: .snap subdir exists - @name is a dir */
696 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
700 /* @name/.snap access failed, attempt snapshot access via parent */
701 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
704 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
713 ret = ceph_snap_gmt_convert_dir(handle,
723 * found snapshot via parent. Append the child path component
724 * that was trimmed... +1 for path separator + 1 for null termination.
726 if (strlen(_converted_buf) + 1 + strlen(trimmed) + 1 > buflen) {
729 strlcat(_converted_buf, "/", buflen);
730 strlcat(_converted_buf, trimmed, buflen);
735 static int ceph_snap_gmt_renameat(vfs_handle_struct *handle,
736 files_struct *srcfsp,
737 const struct smb_filename *smb_fname_src,
738 files_struct *dstfsp,
739 const struct smb_filename *smb_fname_dst)
742 time_t timestamp_src, timestamp_dst;
744 ret = ceph_snap_gmt_strip_snapshot(handle,
745 smb_fname_src->base_name,
746 ×tamp_src, NULL, 0);
751 ret = ceph_snap_gmt_strip_snapshot(handle,
752 smb_fname_dst->base_name,
753 ×tamp_dst, NULL, 0);
758 if (timestamp_src != 0) {
762 if (timestamp_dst != 0) {
766 return SMB_VFS_NEXT_RENAMEAT(handle,
773 /* block links from writeable shares to snapshots for now, like other modules */
774 static int ceph_snap_gmt_symlinkat(vfs_handle_struct *handle,
775 const char *link_contents,
776 struct files_struct *dirfsp,
777 const struct smb_filename *new_smb_fname)
780 time_t timestamp_old = 0;
781 time_t timestamp_new = 0;
783 ret = ceph_snap_gmt_strip_snapshot(handle,
791 ret = ceph_snap_gmt_strip_snapshot(handle,
792 new_smb_fname->base_name,
799 if ((timestamp_old != 0) || (timestamp_new != 0)) {
803 return SMB_VFS_NEXT_SYMLINKAT(handle,
809 static int ceph_snap_gmt_linkat(vfs_handle_struct *handle,
810 files_struct *srcfsp,
811 const struct smb_filename *old_smb_fname,
812 files_struct *dstfsp,
813 const struct smb_filename *new_smb_fname,
817 time_t timestamp_old = 0;
818 time_t timestamp_new = 0;
820 ret = ceph_snap_gmt_strip_snapshot(handle,
821 old_smb_fname->base_name,
828 ret = ceph_snap_gmt_strip_snapshot(handle,
829 new_smb_fname->base_name,
836 if ((timestamp_old != 0) || (timestamp_new != 0)) {
840 return SMB_VFS_NEXT_LINKAT(handle,
848 static int ceph_snap_gmt_stat(vfs_handle_struct *handle,
849 struct smb_filename *smb_fname)
851 time_t timestamp = 0;
852 char stripped[PATH_MAX + 1];
853 char conv[PATH_MAX + 1];
857 ret = ceph_snap_gmt_strip_snapshot(handle,
858 smb_fname->base_name,
859 ×tamp, stripped, sizeof(stripped));
864 if (timestamp == 0) {
865 return SMB_VFS_NEXT_STAT(handle, smb_fname);
868 ret = ceph_snap_gmt_convert(handle, stripped,
869 timestamp, conv, sizeof(conv));
874 tmp = smb_fname->base_name;
875 smb_fname->base_name = conv;
877 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
878 smb_fname->base_name = tmp;
882 static int ceph_snap_gmt_lstat(vfs_handle_struct *handle,
883 struct smb_filename *smb_fname)
885 time_t timestamp = 0;
886 char stripped[PATH_MAX + 1];
887 char conv[PATH_MAX + 1];
891 ret = ceph_snap_gmt_strip_snapshot(handle,
892 smb_fname->base_name,
893 ×tamp, stripped, sizeof(stripped));
898 if (timestamp == 0) {
899 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
902 ret = ceph_snap_gmt_convert(handle, stripped,
903 timestamp, conv, sizeof(conv));
908 tmp = smb_fname->base_name;
909 smb_fname->base_name = conv;
911 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
912 smb_fname->base_name = tmp;
916 static int ceph_snap_gmt_open(vfs_handle_struct *handle,
917 struct smb_filename *smb_fname, files_struct *fsp,
918 int flags, mode_t mode)
920 time_t timestamp = 0;
921 char stripped[PATH_MAX + 1];
922 char conv[PATH_MAX + 1];
926 ret = ceph_snap_gmt_strip_snapshot(handle,
927 smb_fname->base_name,
928 ×tamp, stripped, sizeof(stripped));
933 if (timestamp == 0) {
934 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
937 ret = ceph_snap_gmt_convert(handle, stripped,
938 timestamp, conv, sizeof(conv));
943 tmp = smb_fname->base_name;
944 smb_fname->base_name = conv;
946 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
947 smb_fname->base_name = tmp;
951 static int ceph_snap_gmt_unlinkat(vfs_handle_struct *handle,
952 struct files_struct *dirfsp,
953 const struct smb_filename *csmb_fname,
956 time_t timestamp = 0;
959 ret = ceph_snap_gmt_strip_snapshot(handle,
960 csmb_fname->base_name,
961 ×tamp, NULL, 0);
966 if (timestamp != 0) {
970 return SMB_VFS_NEXT_UNLINKAT(handle,
976 static int ceph_snap_gmt_chmod(vfs_handle_struct *handle,
977 const struct smb_filename *csmb_fname,
980 time_t timestamp = 0;
983 ret = ceph_snap_gmt_strip_snapshot(handle,
984 csmb_fname->base_name,
985 ×tamp, NULL, 0);
990 if (timestamp != 0) {
994 return SMB_VFS_NEXT_CHMOD(handle, csmb_fname, mode);
997 static int ceph_snap_gmt_chdir(vfs_handle_struct *handle,
998 const struct smb_filename *csmb_fname)
1000 time_t timestamp = 0;
1001 char stripped[PATH_MAX + 1];
1002 char conv[PATH_MAX + 1];
1004 struct smb_filename *new_fname;
1007 ret = ceph_snap_gmt_strip_snapshot(handle,
1008 csmb_fname->base_name,
1009 ×tamp, stripped, sizeof(stripped));
1014 if (timestamp == 0) {
1015 return SMB_VFS_NEXT_CHDIR(handle, csmb_fname);
1018 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1019 timestamp, conv, sizeof(conv));
1024 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1025 if (new_fname == NULL) {
1029 new_fname->base_name = conv;
1031 ret = SMB_VFS_NEXT_CHDIR(handle, new_fname);
1032 saved_errno = errno;
1033 TALLOC_FREE(new_fname);
1034 errno = saved_errno;
1038 static int ceph_snap_gmt_ntimes(vfs_handle_struct *handle,
1039 const struct smb_filename *csmb_fname,
1040 struct smb_file_time *ft)
1042 time_t timestamp = 0;
1045 ret = ceph_snap_gmt_strip_snapshot(handle,
1046 csmb_fname->base_name,
1047 ×tamp, NULL, 0);
1052 if (timestamp != 0) {
1056 return SMB_VFS_NEXT_NTIMES(handle, csmb_fname, ft);
1059 static int ceph_snap_gmt_readlinkat(vfs_handle_struct *handle,
1060 files_struct *dirfsp,
1061 const struct smb_filename *csmb_fname,
1065 time_t timestamp = 0;
1066 char stripped[PATH_MAX + 1];
1067 char conv[PATH_MAX + 1];
1069 struct smb_filename *new_fname;
1072 ret = ceph_snap_gmt_strip_snapshot(handle,
1073 csmb_fname->base_name,
1074 ×tamp, stripped, sizeof(stripped));
1079 if (timestamp == 0) {
1080 return SMB_VFS_NEXT_READLINKAT(handle,
1086 ret = ceph_snap_gmt_convert(handle, stripped,
1087 timestamp, conv, sizeof(conv));
1092 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1093 if (new_fname == NULL) {
1097 new_fname->base_name = conv;
1099 ret = SMB_VFS_NEXT_READLINKAT(handle,
1104 saved_errno = errno;
1105 TALLOC_FREE(new_fname);
1106 errno = saved_errno;
1110 static int ceph_snap_gmt_mknodat(vfs_handle_struct *handle,
1111 files_struct *dirfsp,
1112 const struct smb_filename *csmb_fname,
1116 time_t timestamp = 0;
1119 ret = ceph_snap_gmt_strip_snapshot(handle,
1120 csmb_fname->base_name,
1121 ×tamp, NULL, 0);
1126 if (timestamp != 0) {
1130 return SMB_VFS_NEXT_MKNODAT(handle,
1137 static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1139 const struct smb_filename *csmb_fname)
1141 time_t timestamp = 0;
1142 char stripped[PATH_MAX + 1];
1143 char conv[PATH_MAX + 1];
1144 struct smb_filename *result_fname;
1146 struct smb_filename *new_fname;
1149 ret = ceph_snap_gmt_strip_snapshot(handle,
1150 csmb_fname->base_name,
1151 ×tamp, stripped, sizeof(stripped));
1156 if (timestamp == 0) {
1157 return SMB_VFS_NEXT_REALPATH(handle, ctx, csmb_fname);
1159 ret = ceph_snap_gmt_convert(handle, stripped,
1160 timestamp, conv, sizeof(conv));
1165 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1166 if (new_fname == NULL) {
1170 new_fname->base_name = conv;
1172 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, new_fname);
1173 saved_errno = errno;
1174 TALLOC_FREE(new_fname);
1175 errno = saved_errno;
1176 return result_fname;
1179 static NTSTATUS ceph_snap_gmt_get_nt_acl(vfs_handle_struct *handle,
1180 const struct smb_filename *csmb_fname,
1181 uint32_t security_info,
1182 TALLOC_CTX *mem_ctx,
1183 struct security_descriptor **ppdesc)
1185 time_t timestamp = 0;
1186 char stripped[PATH_MAX + 1];
1187 char conv[PATH_MAX + 1];
1190 struct smb_filename *new_fname;
1193 ret = ceph_snap_gmt_strip_snapshot(handle,
1194 csmb_fname->base_name,
1195 ×tamp, stripped, sizeof(stripped));
1197 return map_nt_error_from_unix(-ret);
1199 if (timestamp == 0) {
1200 return SMB_VFS_NEXT_GET_NT_ACL(handle, csmb_fname, security_info,
1203 ret = ceph_snap_gmt_convert(handle, stripped,
1204 timestamp, conv, sizeof(conv));
1206 return map_nt_error_from_unix(-ret);
1208 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1209 if (new_fname == NULL) {
1210 return NT_STATUS_NO_MEMORY;
1212 new_fname->base_name = conv;
1214 status = SMB_VFS_NEXT_GET_NT_ACL(handle, new_fname, security_info,
1216 saved_errno = errno;
1217 TALLOC_FREE(new_fname);
1218 errno = saved_errno;
1222 static int ceph_snap_gmt_mkdirat(vfs_handle_struct *handle,
1223 struct files_struct *dirfsp,
1224 const struct smb_filename *csmb_fname,
1227 time_t timestamp = 0;
1230 ret = ceph_snap_gmt_strip_snapshot(handle,
1231 csmb_fname->base_name,
1232 ×tamp, NULL, 0);
1237 if (timestamp != 0) {
1241 return SMB_VFS_NEXT_MKDIRAT(handle,
1247 static int ceph_snap_gmt_chflags(vfs_handle_struct *handle,
1248 const struct smb_filename *csmb_fname,
1251 time_t timestamp = 0;
1254 ret = ceph_snap_gmt_strip_snapshot(handle,
1255 csmb_fname->base_name,
1256 ×tamp, NULL, 0);
1261 if (timestamp != 0) {
1265 return SMB_VFS_NEXT_CHFLAGS(handle, csmb_fname, flags);
1268 static ssize_t ceph_snap_gmt_getxattr(vfs_handle_struct *handle,
1269 const struct smb_filename *csmb_fname,
1274 time_t timestamp = 0;
1275 char stripped[PATH_MAX + 1];
1276 char conv[PATH_MAX + 1];
1278 struct smb_filename *new_fname;
1281 ret = ceph_snap_gmt_strip_snapshot(handle,
1282 csmb_fname->base_name,
1283 ×tamp, stripped, sizeof(stripped));
1288 if (timestamp == 0) {
1289 return SMB_VFS_NEXT_GETXATTR(handle, csmb_fname, aname, value,
1292 ret = ceph_snap_gmt_convert(handle, stripped,
1293 timestamp, conv, sizeof(conv));
1298 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1299 if (new_fname == NULL) {
1303 new_fname->base_name = conv;
1305 ret = SMB_VFS_NEXT_GETXATTR(handle, new_fname, aname, value, size);
1306 saved_errno = errno;
1307 TALLOC_FREE(new_fname);
1308 errno = saved_errno;
1312 static ssize_t ceph_snap_gmt_listxattr(struct vfs_handle_struct *handle,
1313 const struct smb_filename *csmb_fname,
1314 char *list, size_t size)
1316 time_t timestamp = 0;
1317 char stripped[PATH_MAX + 1];
1318 char conv[PATH_MAX + 1];
1320 struct smb_filename *new_fname;
1323 ret = ceph_snap_gmt_strip_snapshot(handle,
1324 csmb_fname->base_name,
1325 ×tamp, stripped, sizeof(stripped));
1330 if (timestamp == 0) {
1331 return SMB_VFS_NEXT_LISTXATTR(handle, csmb_fname, list, size);
1333 ret = ceph_snap_gmt_convert(handle, stripped,
1334 timestamp, conv, sizeof(conv));
1339 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1340 if (new_fname == NULL) {
1344 new_fname->base_name = conv;
1346 ret = SMB_VFS_NEXT_LISTXATTR(handle, new_fname, list, size);
1347 saved_errno = errno;
1348 TALLOC_FREE(new_fname);
1349 errno = saved_errno;
1353 static int ceph_snap_gmt_removexattr(vfs_handle_struct *handle,
1354 const struct smb_filename *csmb_fname,
1357 time_t timestamp = 0;
1360 ret = ceph_snap_gmt_strip_snapshot(handle,
1361 csmb_fname->base_name,
1362 ×tamp, NULL, 0);
1367 if (timestamp != 0) {
1371 return SMB_VFS_NEXT_REMOVEXATTR(handle, csmb_fname, aname);
1374 static int ceph_snap_gmt_setxattr(struct vfs_handle_struct *handle,
1375 const struct smb_filename *csmb_fname,
1376 const char *aname, const void *value,
1377 size_t size, int flags)
1379 time_t timestamp = 0;
1382 ret = ceph_snap_gmt_strip_snapshot(handle,
1383 csmb_fname->base_name,
1384 ×tamp, NULL, 0);
1389 if (timestamp != 0) {
1393 return SMB_VFS_NEXT_SETXATTR(handle, csmb_fname,
1394 aname, value, size, flags);
1397 static int ceph_snap_gmt_get_real_filename(struct vfs_handle_struct *handle,
1400 TALLOC_CTX *mem_ctx,
1403 time_t timestamp = 0;
1404 char stripped[PATH_MAX + 1];
1405 char conv[PATH_MAX + 1];
1408 ret = ceph_snap_gmt_strip_snapshot(handle, path,
1409 ×tamp, stripped, sizeof(stripped));
1414 if (timestamp == 0) {
1415 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1416 mem_ctx, found_name);
1418 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1419 timestamp, conv, sizeof(conv));
1424 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1425 mem_ctx, found_name);
1429 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1430 const struct smb_filename *csmb_fname,
1435 time_t timestamp = 0;
1436 char stripped[PATH_MAX + 1];
1437 char conv[PATH_MAX + 1];
1439 struct smb_filename *new_fname;
1442 ret = ceph_snap_gmt_strip_snapshot(handle,
1443 csmb_fname->base_name,
1444 ×tamp, stripped, sizeof(stripped));
1449 if (timestamp == 0) {
1450 return SMB_VFS_NEXT_DISK_FREE(handle, csmb_fname,
1451 bsize, dfree, dsize);
1453 ret = ceph_snap_gmt_convert(handle, stripped,
1454 timestamp, conv, sizeof(conv));
1459 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1460 if (new_fname == NULL) {
1464 new_fname->base_name = conv;
1466 ret = SMB_VFS_NEXT_DISK_FREE(handle, new_fname,
1467 bsize, dfree, dsize);
1468 saved_errno = errno;
1469 TALLOC_FREE(new_fname);
1470 errno = saved_errno;
1474 static int ceph_snap_gmt_get_quota(vfs_handle_struct *handle,
1475 const struct smb_filename *csmb_fname,
1476 enum SMB_QUOTA_TYPE qtype,
1480 time_t timestamp = 0;
1481 char stripped[PATH_MAX + 1];
1482 char conv[PATH_MAX + 1];
1484 struct smb_filename *new_fname;
1487 ret = ceph_snap_gmt_strip_snapshot(handle,
1488 csmb_fname->base_name,
1489 ×tamp, stripped, sizeof(stripped));
1494 if (timestamp == 0) {
1495 return SMB_VFS_NEXT_GET_QUOTA(handle, csmb_fname, qtype, id, dq);
1497 ret = ceph_snap_gmt_convert(handle, stripped,
1498 timestamp, conv, sizeof(conv));
1503 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1504 if (new_fname == NULL) {
1508 new_fname->base_name = conv;
1510 ret = SMB_VFS_NEXT_GET_QUOTA(handle, new_fname, qtype, id, dq);
1511 saved_errno = errno;
1512 TALLOC_FREE(new_fname);
1513 errno = saved_errno;
1517 static struct vfs_fn_pointers ceph_snap_fns = {
1518 .get_shadow_copy_data_fn = ceph_snap_get_shadow_copy_data,
1519 .disk_free_fn = ceph_snap_gmt_disk_free,
1520 .get_quota_fn = ceph_snap_gmt_get_quota,
1521 .renameat_fn = ceph_snap_gmt_renameat,
1522 .linkat_fn = ceph_snap_gmt_linkat,
1523 .symlinkat_fn = ceph_snap_gmt_symlinkat,
1524 .stat_fn = ceph_snap_gmt_stat,
1525 .lstat_fn = ceph_snap_gmt_lstat,
1526 .open_fn = ceph_snap_gmt_open,
1527 .unlinkat_fn = ceph_snap_gmt_unlinkat,
1528 .chmod_fn = ceph_snap_gmt_chmod,
1529 .chdir_fn = ceph_snap_gmt_chdir,
1530 .ntimes_fn = ceph_snap_gmt_ntimes,
1531 .readlinkat_fn = ceph_snap_gmt_readlinkat,
1532 .mknodat_fn = ceph_snap_gmt_mknodat,
1533 .realpath_fn = ceph_snap_gmt_realpath,
1534 .get_nt_acl_fn = ceph_snap_gmt_get_nt_acl,
1535 .mkdirat_fn = ceph_snap_gmt_mkdirat,
1536 .getxattr_fn = ceph_snap_gmt_getxattr,
1537 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1538 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1539 .listxattr_fn = ceph_snap_gmt_listxattr,
1540 .removexattr_fn = ceph_snap_gmt_removexattr,
1541 .setxattr_fn = ceph_snap_gmt_setxattr,
1542 .chflags_fn = ceph_snap_gmt_chflags,
1543 .get_real_filename_fn = ceph_snap_gmt_get_real_filename,
1547 NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1549 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1550 "ceph_snapshots", &ceph_snap_fns);