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"
30 #include "lib/util/smb_strtox.h"
31 #include "source3/smbd/dir.h"
34 #define DBGC_CLASS DBGC_VFS
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).
41 #define CEPH_SNAP_SUBDIR_DEFAULT ".snap"
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.
50 #define CEPH_SNAP_BTIME_XATTR "ceph.snap.btime"
52 static int ceph_snap_get_btime_fsp(struct vfs_handle_struct
*handle
,
53 struct files_struct
*fsp
,
60 struct timespec snap_timespec
;
63 ret
= SMB_VFS_NEXT_FGETXATTR(handle
,
65 CEPH_SNAP_BTIME_XATTR
,
69 DBG_ERR("failed to get %s xattr: %s\n",
70 CEPH_SNAP_BTIME_XATTR
, strerror(errno
));
74 if (ret
== 0 || ret
>= sizeof(snap_btime
) - 1) {
78 /* ensure zero termination */
79 snap_btime
[ret
] = '\0';
81 /* format is sec.nsec */
82 s
= strchr(snap_btime
, '.');
84 DBG_ERR("invalid %s xattr value: %s\n",
85 CEPH_SNAP_BTIME_XATTR
, snap_btime
);
89 /* First component is seconds, extract it */
91 snap_timespec
.tv_sec
= smb_strtoull(snap_btime
,
95 SMB_STR_FULL_STR_CONV
);
100 /* second component is nsecs */
102 snap_timespec
.tv_nsec
= smb_strtoul(s
,
106 SMB_STR_FULL_STR_CONV
);
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).
115 *_snap_secs
= snap_timespec
.tv_sec
+ (snap_timespec
.tv_nsec
>> 30);
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.
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.
127 static int ceph_snap_fill_label(struct vfs_handle_struct
*handle
,
129 struct files_struct
*dirfsp
,
131 SHADOW_COPY_LABEL this_label
)
133 const char *parent_snapsdir
= dirfsp
->fsp_name
->base_name
;
134 struct smb_filename
*smb_fname
;
135 struct smb_filename
*atname
= NULL
;
137 struct tm gmt_snap_time
;
140 char snap_path
[PATH_MAX
+ 1];
145 * CephFS snapshot creation times are available via a special
146 * xattr - snapshot b/m/ctimes all match the snap source.
148 ret
= snprintf(snap_path
, sizeof(snap_path
), "%s/%s",
149 parent_snapsdir
, subdir
);
150 if (ret
>= sizeof(snap_path
)) {
154 smb_fname
= synthetic_smb_fname(tmp_ctx
,
160 if (smb_fname
== NULL
) {
164 ret
= vfs_stat(handle
->conn
, smb_fname
);
167 TALLOC_FREE(smb_fname
);
171 atname
= synthetic_smb_fname(tmp_ctx
,
177 if (atname
== NULL
) {
178 TALLOC_FREE(smb_fname
);
182 status
= openat_pathref_fsp(dirfsp
, atname
);
183 if (!NT_STATUS_IS_OK(status
)) {
184 TALLOC_FREE(smb_fname
);
186 return -map_errno_from_nt_status(status
);
189 ret
= ceph_snap_get_btime_fsp(handle
, atname
->fsp
, &snap_secs
);
191 TALLOC_FREE(smb_fname
);
195 TALLOC_FREE(smb_fname
);
198 tm_ret
= gmtime_r(&snap_secs
, &gmt_snap_time
);
199 if (tm_ret
== NULL
) {
202 str_sz
= strftime(this_label
, sizeof(SHADOW_COPY_LABEL
),
203 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
205 DBG_ERR("failed to convert tm to @GMT token\n");
209 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
210 snap_path
, this_label
);
215 static int ceph_snap_enum_snapdir(struct vfs_handle_struct
*handle
,
216 struct smb_filename
*snaps_dname
,
218 struct shadow_copy_data
*sc_data
)
220 TALLOC_CTX
*frame
= talloc_stackframe();
221 struct smb_Dir
*dir_hnd
= NULL
;
222 struct files_struct
*dirfsp
= NULL
;
223 const char *dname
= NULL
;
224 char *talloced
= NULL
;
229 DBG_DEBUG("enumerating shadow copy dir at %s\n",
230 snaps_dname
->base_name
);
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
239 status
= OpenDir(frame
,
245 if (!NT_STATUS_IS_OK(status
)) {
246 ret
= -map_errno_from_nt_status(status
);
250 /* Check we have SEC_DIR_LIST access on this fsp. */
251 dirfsp
= dir_hnd_fetch_fsp(dir_hnd
);
252 status
= smbd_check_access_rights_fsp(dirfsp
->conn
->cwd_fsp
,
256 if (!NT_STATUS_IS_OK(status
)) {
257 DBG_ERR("user does not have list permission "
259 fsp_str_dbg(dirfsp
));
260 ret
= -map_errno_from_nt_status(status
);
265 sc_data
->num_volumes
= 0;
266 sc_data
->labels
= NULL
;
268 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
269 if (ISDOT(dname
) || ISDOTDOT(dname
)) {
270 TALLOC_FREE(talloced
);
273 sc_data
->num_volumes
++;
275 TALLOC_FREE(talloced
);
278 if (sc_data
->num_volumes
> slots
) {
279 uint32_t new_slot_count
= slots
+ 10;
280 SMB_ASSERT(new_slot_count
> slots
);
281 sc_data
->labels
= talloc_realloc(sc_data
,
285 if (sc_data
->labels
== NULL
) {
286 TALLOC_FREE(talloced
);
290 memset(sc_data
->labels
[slots
], 0,
291 sizeof(SHADOW_COPY_LABEL
) * 10);
293 DBG_DEBUG("%d->%d slots for enum_snaps response\n",
294 slots
, new_slot_count
);
295 slots
= new_slot_count
;
297 DBG_DEBUG("filling shadow copy label for %s/%s\n",
298 snaps_dname
->base_name
, dname
);
299 ret
= ceph_snap_fill_label(handle
,
303 sc_data
->labels
[sc_data
->num_volumes
- 1]);
305 TALLOC_FREE(talloced
);
308 TALLOC_FREE(talloced
);
311 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
312 snaps_dname
->base_name
, sc_data
->num_volumes
);
320 TALLOC_FREE(sc_data
->labels
);
325 * Prior reading: The Meaning of Path Names
326 * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
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
334 static int ceph_snap_get_parent_path(const char *connectpath
,
338 const char **_trimmed
)
344 if (!strcmp(path
, "/")) {
345 DBG_ERR("can't go past root for %s .snap dir\n", path
);
349 p
= strrchr_m(path
, '/'); /* Find final '/', if any */
351 DBG_DEBUG("parent .snap dir for %s is cwd\n", path
);
352 ret
= strlcpy(_parent_buf
, "", buflen
);
356 if (_trimmed
!= NULL
) {
362 SMB_ASSERT(p
>= path
);
365 ret
= snprintf(_parent_buf
, buflen
, "%.*s", (int)len
, path
);
370 /* for absolute paths, check that we're not going outside the share */
371 if ((len
> 0) && (_parent_buf
[0] == '/')) {
372 bool connectpath_match
= false;
373 size_t clen
= strlen(connectpath
);
374 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 connectpath_match
= (strncmp(connectpath
,
380 if (!connectpath_match
381 || ((_parent_buf
[clen
] != '/') && (_parent_buf
[clen
] != '\0'))) {
382 DBG_ERR("%s parent path is outside of share at %s\n",
383 _parent_buf
, connectpath
);
388 if (_trimmed
!= NULL
) {
390 * point to path component which was trimmed from _parent_buf
391 * excluding path separator.
396 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
397 path
, _parent_buf
, p
+ 1);
402 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
,
409 const char *parent_dir
= NULL
;
410 char tmp
[PATH_MAX
+ 1];
411 char snaps_path
[PATH_MAX
+ 1];
412 struct smb_filename
*snaps_dname
= NULL
;
413 const char *snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
415 CEPH_SNAP_SUBDIR_DEFAULT
);
417 DBG_DEBUG("getting shadow copy data for %s\n",
418 fsp
->fsp_name
->base_name
);
420 tmp_ctx
= talloc_new(fsp
);
421 if (tmp_ctx
== NULL
) {
426 if (sc_data
== NULL
) {
431 if (fsp
->fsp_flags
.is_directory
) {
432 parent_dir
= fsp
->fsp_name
->base_name
;
434 ret
= ceph_snap_get_parent_path(handle
->conn
->connectpath
,
435 fsp
->fsp_name
->base_name
,
445 if (strlen(parent_dir
) == 0) {
446 ret
= strlcpy(snaps_path
, snapdir
, sizeof(snaps_path
));
448 ret
= snprintf(snaps_path
, sizeof(snaps_path
), "%s/%s",
449 parent_dir
, snapdir
);
451 if (ret
>= sizeof(snaps_path
)) {
456 snaps_dname
= synthetic_smb_fname(tmp_ctx
,
461 fsp
->fsp_name
->flags
);
462 if (snaps_dname
== NULL
) {
467 ret
= ceph_snap_enum_snapdir(handle
, snaps_dname
, labels
, sc_data
);
472 talloc_free(tmp_ctx
);
476 talloc_free(tmp_ctx
);
481 static int ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct
*handle
,
482 const struct smb_filename
*smb_fname
,
489 if (smb_fname
->twrp
== 0) {
493 if (_stripped_buf
!= NULL
) {
494 len
= strlcpy(_stripped_buf
, smb_fname
->base_name
, buflen
);
496 return -ENAMETOOLONG
;
500 *_timestamp
= nt_time_to_unix(smb_fname
->twrp
);
507 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct
*handle
,
510 char *_converted_buf
,
515 struct smb_Dir
*dir_hnd
= NULL
;
516 struct files_struct
*dirfsp
= NULL
;
517 const char *dname
= NULL
;
518 char *talloced
= NULL
;
519 struct smb_filename
*snaps_dname
= NULL
;
520 const char *snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
522 CEPH_SNAP_SUBDIR_DEFAULT
);
523 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
525 if (tmp_ctx
== NULL
) {
531 * Temporally use the caller's return buffer for this.
533 if (strlen(name
) == 0) {
534 ret
= strlcpy(_converted_buf
, snapdir
, buflen
);
536 ret
= snprintf(_converted_buf
, buflen
, "%s/%s", name
, snapdir
);
543 snaps_dname
= synthetic_smb_fname(tmp_ctx
,
549 if (snaps_dname
== NULL
) {
554 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
555 ret
= SMB_VFS_NEXT_STAT(handle
, snaps_dname
);
561 DBG_DEBUG("enumerating shadow copy dir at %s\n",
562 snaps_dname
->base_name
);
564 status
= OpenDir(tmp_ctx
,
570 if (!NT_STATUS_IS_OK(status
)) {
571 ret
= -map_errno_from_nt_status(status
);
575 /* Check we have SEC_DIR_LIST access on this fsp. */
576 dirfsp
= dir_hnd_fetch_fsp(dir_hnd
);
577 status
= smbd_check_access_rights_fsp(dirfsp
->conn
->cwd_fsp
,
581 if (!NT_STATUS_IS_OK(status
)) {
582 DBG_ERR("user does not have list permission "
584 fsp_str_dbg(dirfsp
));
585 ret
= -map_errno_from_nt_status(status
);
589 while ((dname
= ReadDirName(dir_hnd
, &talloced
)) != NULL
) {
590 struct smb_filename
*smb_fname
= NULL
;
591 struct smb_filename
*atname
= NULL
;
592 time_t snap_secs
= 0;
594 if (ISDOT(dname
) || ISDOTDOT(dname
)) {
595 TALLOC_FREE(talloced
);
599 ret
= snprintf(_converted_buf
, buflen
, "%s/%s",
600 snaps_dname
->base_name
, dname
);
606 smb_fname
= synthetic_smb_fname(tmp_ctx
,
612 if (smb_fname
== NULL
) {
617 ret
= vfs_stat(handle
->conn
, smb_fname
);
620 TALLOC_FREE(smb_fname
);
624 atname
= synthetic_smb_fname(tmp_ctx
,
630 if (atname
== NULL
) {
631 TALLOC_FREE(smb_fname
);
636 status
= openat_pathref_fsp(dirfsp
, atname
);
637 if (!NT_STATUS_IS_OK(status
)) {
638 TALLOC_FREE(smb_fname
);
640 ret
= -map_errno_from_nt_status(status
);
644 ret
= ceph_snap_get_btime_fsp(handle
, atname
->fsp
, &snap_secs
);
646 TALLOC_FREE(smb_fname
);
651 TALLOC_FREE(smb_fname
);
655 * check gmt_snap_time matches @timestamp
657 if (timestamp
== snap_secs
) {
660 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 TALLOC_FREE(talloced
);
667 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
668 handle
->conn
->connectpath
, name
, (long long)timestamp
);
673 /* found, _converted_buf already contains path of interest */
674 DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
675 handle
->conn
->connectpath
, name
, (long long)timestamp
,
678 TALLOC_FREE(talloced
);
679 talloc_free(tmp_ctx
);
683 TALLOC_FREE(talloced
);
684 talloc_free(tmp_ctx
);
688 static int ceph_snap_gmt_convert(struct vfs_handle_struct
*handle
,
691 char *_converted_buf
,
695 char parent
[PATH_MAX
+ 1];
696 const char *trimmed
= NULL
;
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.
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)
706 ret
= ceph_snap_gmt_convert_dir(handle
,
712 /* all done: .snap subdir exists - @name is a dir */
713 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name
);
717 /* @name/.snap access failed, attempt snapshot access via parent */
718 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
721 ret
= ceph_snap_get_parent_path(handle
->conn
->connectpath
,
730 ret
= ceph_snap_gmt_convert_dir(handle
,
740 * found snapshot via parent. Append the child path component
741 * that was trimmed... +1 for path separator + 1 for null termination.
743 if (strlen(_converted_buf
) + 1 + strlen(trimmed
) + 1 > buflen
) {
746 strlcat(_converted_buf
, "/", buflen
);
747 strlcat(_converted_buf
, trimmed
, buflen
);
752 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 const struct vfs_rename_how
*how
)
760 time_t timestamp_src
, timestamp_dst
;
762 ret
= ceph_snap_gmt_strip_snapshot(handle
,
764 ×tamp_src
, NULL
, 0);
769 ret
= ceph_snap_gmt_strip_snapshot(handle
,
771 ×tamp_dst
, NULL
, 0);
776 if (timestamp_src
!= 0) {
780 if (timestamp_dst
!= 0) {
784 return SMB_VFS_NEXT_RENAMEAT(handle
,
792 /* block links from writeable shares to snapshots for now, like other modules */
793 static int ceph_snap_gmt_symlinkat(vfs_handle_struct
*handle
,
794 const struct smb_filename
*link_contents
,
795 struct files_struct
*dirfsp
,
796 const struct smb_filename
*new_smb_fname
)
799 time_t timestamp_old
= 0;
800 time_t timestamp_new
= 0;
802 ret
= ceph_snap_gmt_strip_snapshot(handle
,
810 ret
= ceph_snap_gmt_strip_snapshot(handle
,
818 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
822 return SMB_VFS_NEXT_SYMLINKAT(handle
,
828 static int ceph_snap_gmt_linkat(vfs_handle_struct
*handle
,
829 files_struct
*srcfsp
,
830 const struct smb_filename
*old_smb_fname
,
831 files_struct
*dstfsp
,
832 const struct smb_filename
*new_smb_fname
,
836 time_t timestamp_old
= 0;
837 time_t timestamp_new
= 0;
839 ret
= ceph_snap_gmt_strip_snapshot(handle
,
847 ret
= ceph_snap_gmt_strip_snapshot(handle
,
855 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
859 return SMB_VFS_NEXT_LINKAT(handle
,
867 static int ceph_snap_gmt_stat(vfs_handle_struct
*handle
,
868 struct smb_filename
*smb_fname
)
870 time_t timestamp
= 0;
871 char stripped
[PATH_MAX
+ 1];
872 char conv
[PATH_MAX
+ 1];
876 ret
= ceph_snap_gmt_strip_snapshot(handle
,
878 ×tamp
, stripped
, sizeof(stripped
));
883 if (timestamp
== 0) {
884 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
887 ret
= ceph_snap_gmt_convert(handle
, stripped
,
888 timestamp
, conv
, sizeof(conv
));
893 tmp
= smb_fname
->base_name
;
894 smb_fname
->base_name
= conv
;
896 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
897 smb_fname
->base_name
= tmp
;
901 static int ceph_snap_gmt_lstat(vfs_handle_struct
*handle
,
902 struct smb_filename
*smb_fname
)
904 time_t timestamp
= 0;
905 char stripped
[PATH_MAX
+ 1];
906 char conv
[PATH_MAX
+ 1];
910 ret
= ceph_snap_gmt_strip_snapshot(handle
,
912 ×tamp
, stripped
, sizeof(stripped
));
917 if (timestamp
== 0) {
918 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
921 ret
= ceph_snap_gmt_convert(handle
, stripped
,
922 timestamp
, conv
, sizeof(conv
));
927 tmp
= smb_fname
->base_name
;
928 smb_fname
->base_name
= conv
;
930 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
931 smb_fname
->base_name
= tmp
;
935 static int ceph_snap_gmt_openat(vfs_handle_struct
*handle
,
936 const struct files_struct
*dirfsp
,
937 const struct smb_filename
*smb_fname_in
,
939 const struct vfs_open_how
*how
)
941 time_t timestamp
= 0;
942 struct smb_filename
*smb_fname
= NULL
;
943 char stripped
[PATH_MAX
+ 1];
944 char conv
[PATH_MAX
+ 1];
948 ret
= ceph_snap_gmt_strip_snapshot(handle
,
957 if (timestamp
== 0) {
958 return SMB_VFS_NEXT_OPENAT(handle
,
965 ret
= ceph_snap_gmt_convert(handle
,
974 smb_fname
= cp_smb_filename(talloc_tos(), smb_fname_in
);
975 if (smb_fname
== NULL
) {
978 smb_fname
->base_name
= conv
;
980 ret
= SMB_VFS_NEXT_OPENAT(handle
,
988 TALLOC_FREE(smb_fname
);
989 if (saved_errno
!= 0) {
995 static int ceph_snap_gmt_unlinkat(vfs_handle_struct
*handle
,
996 struct files_struct
*dirfsp
,
997 const struct smb_filename
*csmb_fname
,
1000 time_t timestamp
= 0;
1003 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1005 ×tamp
, NULL
, 0);
1010 if (timestamp
!= 0) {
1014 return SMB_VFS_NEXT_UNLINKAT(handle
,
1020 static int ceph_snap_gmt_fchmod(vfs_handle_struct
*handle
,
1021 struct files_struct
*fsp
,
1024 const struct smb_filename
*csmb_fname
= NULL
;
1025 time_t timestamp
= 0;
1028 csmb_fname
= fsp
->fsp_name
;
1029 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1031 ×tamp
, NULL
, 0);
1036 if (timestamp
!= 0) {
1040 return SMB_VFS_NEXT_FCHMOD(handle
, fsp
, mode
);
1043 static int ceph_snap_gmt_chdir(vfs_handle_struct
*handle
,
1044 const struct smb_filename
*csmb_fname
)
1046 time_t timestamp
= 0;
1047 char stripped
[PATH_MAX
+ 1];
1048 char conv
[PATH_MAX
+ 1];
1050 struct smb_filename
*new_fname
;
1053 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1055 ×tamp
, stripped
, sizeof(stripped
));
1060 if (timestamp
== 0) {
1061 return SMB_VFS_NEXT_CHDIR(handle
, csmb_fname
);
1064 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1065 timestamp
, conv
, sizeof(conv
));
1070 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1071 if (new_fname
== NULL
) {
1075 new_fname
->base_name
= conv
;
1077 ret
= SMB_VFS_NEXT_CHDIR(handle
, new_fname
);
1078 saved_errno
= errno
;
1079 TALLOC_FREE(new_fname
);
1080 errno
= saved_errno
;
1084 static int ceph_snap_gmt_fntimes(vfs_handle_struct
*handle
,
1086 struct smb_file_time
*ft
)
1088 time_t timestamp
= 0;
1091 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1100 if (timestamp
!= 0) {
1104 return SMB_VFS_NEXT_FNTIMES(handle
, fsp
, ft
);
1107 static int ceph_snap_gmt_readlinkat(vfs_handle_struct
*handle
,
1108 const struct files_struct
*dirfsp
,
1109 const struct smb_filename
*csmb_fname
,
1113 time_t timestamp
= 0;
1114 char conv
[PATH_MAX
+ 1];
1116 struct smb_filename
*full_fname
= NULL
;
1120 * Now this function only looks at csmb_fname->twrp
1121 * we don't need to copy out the path. Just use
1122 * csmb_fname->base_name directly.
1124 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1126 ×tamp
, NULL
, 0);
1131 if (timestamp
== 0) {
1132 return SMB_VFS_NEXT_READLINKAT(handle
,
1139 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
1142 if (full_fname
== NULL
) {
1146 /* Find the snapshot path from the full pathname. */
1147 ret
= ceph_snap_gmt_convert(handle
,
1148 full_fname
->base_name
,
1153 TALLOC_FREE(full_fname
);
1157 full_fname
->base_name
= conv
;
1159 ret
= SMB_VFS_NEXT_READLINKAT(handle
,
1160 handle
->conn
->cwd_fsp
,
1164 saved_errno
= errno
;
1165 TALLOC_FREE(full_fname
);
1166 errno
= saved_errno
;
1170 static int ceph_snap_gmt_mknodat(vfs_handle_struct
*handle
,
1171 files_struct
*dirfsp
,
1172 const struct smb_filename
*csmb_fname
,
1176 time_t timestamp
= 0;
1179 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1181 ×tamp
, NULL
, 0);
1186 if (timestamp
!= 0) {
1190 return SMB_VFS_NEXT_MKNODAT(handle
,
1197 static struct smb_filename
*ceph_snap_gmt_realpath(vfs_handle_struct
*handle
,
1199 const struct smb_filename
*csmb_fname
)
1201 time_t timestamp
= 0;
1202 char stripped
[PATH_MAX
+ 1];
1203 char conv
[PATH_MAX
+ 1];
1204 struct smb_filename
*result_fname
;
1206 struct smb_filename
*new_fname
;
1209 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1211 ×tamp
, stripped
, sizeof(stripped
));
1216 if (timestamp
== 0) {
1217 return SMB_VFS_NEXT_REALPATH(handle
, ctx
, csmb_fname
);
1219 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1220 timestamp
, conv
, sizeof(conv
));
1225 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1226 if (new_fname
== NULL
) {
1230 new_fname
->base_name
= conv
;
1232 result_fname
= SMB_VFS_NEXT_REALPATH(handle
, ctx
, new_fname
);
1233 saved_errno
= errno
;
1234 TALLOC_FREE(new_fname
);
1235 errno
= saved_errno
;
1236 return result_fname
;
1239 static int ceph_snap_gmt_mkdirat(vfs_handle_struct
*handle
,
1240 struct files_struct
*dirfsp
,
1241 const struct smb_filename
*csmb_fname
,
1244 time_t timestamp
= 0;
1247 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1249 ×tamp
, NULL
, 0);
1254 if (timestamp
!= 0) {
1258 return SMB_VFS_NEXT_MKDIRAT(handle
,
1264 static int ceph_snap_gmt_fchflags(vfs_handle_struct
*handle
,
1265 struct files_struct
*fsp
,
1268 time_t timestamp
= 0;
1271 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1273 ×tamp
, NULL
, 0);
1278 if (timestamp
!= 0) {
1282 return SMB_VFS_NEXT_FCHFLAGS(handle
, fsp
, flags
);
1285 static int ceph_snap_gmt_fsetxattr(struct vfs_handle_struct
*handle
,
1286 struct files_struct
*fsp
,
1287 const char *aname
, const void *value
,
1288 size_t size
, int flags
)
1290 const struct smb_filename
*csmb_fname
= NULL
;
1291 time_t timestamp
= 0;
1294 csmb_fname
= fsp
->fsp_name
;
1295 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1297 ×tamp
, NULL
, 0);
1302 if (timestamp
!= 0) {
1306 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
,
1307 aname
, value
, size
, flags
);
1310 static NTSTATUS
ceph_snap_gmt_get_real_filename_at(
1311 struct vfs_handle_struct
*handle
,
1312 struct files_struct
*dirfsp
,
1314 TALLOC_CTX
*mem_ctx
,
1317 time_t timestamp
= 0;
1318 char stripped
[PATH_MAX
+ 1];
1319 char conv
[PATH_MAX
+ 1];
1320 struct smb_filename
*conv_fname
= NULL
;
1324 ret
= ceph_snap_gmt_strip_snapshot(
1331 return map_nt_error_from_unix(-ret
);
1333 if (timestamp
== 0) {
1334 return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1335 handle
, dirfsp
, name
, mem_ctx
, found_name
);
1337 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1338 timestamp
, conv
, sizeof(conv
));
1340 return map_nt_error_from_unix(-ret
);
1343 status
= synthetic_pathref(
1345 dirfsp
->conn
->cwd_fsp
,
1352 if (!NT_STATUS_IS_OK(status
)) {
1356 status
= SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
1357 handle
, conv_fname
->fsp
, name
, mem_ctx
, found_name
);
1358 TALLOC_FREE(conv_fname
);
1362 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct
*handle
,
1363 const struct smb_filename
*csmb_fname
,
1368 time_t timestamp
= 0;
1369 char stripped
[PATH_MAX
+ 1];
1370 char conv
[PATH_MAX
+ 1];
1372 struct smb_filename
*new_fname
;
1375 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1377 ×tamp
, stripped
, sizeof(stripped
));
1382 if (timestamp
== 0) {
1383 return SMB_VFS_NEXT_DISK_FREE(handle
, csmb_fname
,
1384 bsize
, dfree
, dsize
);
1386 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1387 timestamp
, conv
, sizeof(conv
));
1392 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1393 if (new_fname
== NULL
) {
1397 new_fname
->base_name
= conv
;
1399 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, new_fname
,
1400 bsize
, dfree
, dsize
);
1401 saved_errno
= errno
;
1402 TALLOC_FREE(new_fname
);
1403 errno
= saved_errno
;
1407 static int ceph_snap_gmt_get_quota(vfs_handle_struct
*handle
,
1408 const struct smb_filename
*csmb_fname
,
1409 enum SMB_QUOTA_TYPE qtype
,
1413 time_t timestamp
= 0;
1414 char stripped
[PATH_MAX
+ 1];
1415 char conv
[PATH_MAX
+ 1];
1417 struct smb_filename
*new_fname
;
1420 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1422 ×tamp
, stripped
, sizeof(stripped
));
1427 if (timestamp
== 0) {
1428 return SMB_VFS_NEXT_GET_QUOTA(handle
, csmb_fname
, qtype
, id
, dq
);
1430 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1431 timestamp
, conv
, sizeof(conv
));
1436 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1437 if (new_fname
== NULL
) {
1441 new_fname
->base_name
= conv
;
1443 ret
= SMB_VFS_NEXT_GET_QUOTA(handle
, new_fname
, qtype
, id
, dq
);
1444 saved_errno
= errno
;
1445 TALLOC_FREE(new_fname
);
1446 errno
= saved_errno
;
1450 static struct vfs_fn_pointers ceph_snap_fns
= {
1451 .get_shadow_copy_data_fn
= ceph_snap_get_shadow_copy_data
,
1452 .disk_free_fn
= ceph_snap_gmt_disk_free
,
1453 .get_quota_fn
= ceph_snap_gmt_get_quota
,
1454 .renameat_fn
= ceph_snap_gmt_renameat
,
1455 .linkat_fn
= ceph_snap_gmt_linkat
,
1456 .symlinkat_fn
= ceph_snap_gmt_symlinkat
,
1457 .stat_fn
= ceph_snap_gmt_stat
,
1458 .lstat_fn
= ceph_snap_gmt_lstat
,
1459 .openat_fn
= ceph_snap_gmt_openat
,
1460 .unlinkat_fn
= ceph_snap_gmt_unlinkat
,
1461 .fchmod_fn
= ceph_snap_gmt_fchmod
,
1462 .chdir_fn
= ceph_snap_gmt_chdir
,
1463 .fntimes_fn
= ceph_snap_gmt_fntimes
,
1464 .readlinkat_fn
= ceph_snap_gmt_readlinkat
,
1465 .mknodat_fn
= ceph_snap_gmt_mknodat
,
1466 .realpath_fn
= ceph_snap_gmt_realpath
,
1467 .mkdirat_fn
= ceph_snap_gmt_mkdirat
,
1468 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
1469 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
1470 .fsetxattr_fn
= ceph_snap_gmt_fsetxattr
,
1471 .fchflags_fn
= ceph_snap_gmt_fchflags
,
1472 .get_real_filename_at_fn
= ceph_snap_gmt_get_real_filename_at
,
1476 NTSTATUS
vfs_ceph_snapshots_init(TALLOC_CTX
*ctx
)
1478 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1479 "ceph_snapshots", &ceph_snap_fns
);