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
= strtoull_err(snap_btime
, &endptr
, 10, &err
);
90 if ((endptr
== snap_btime
) || (*endptr
!= '\0')) {
91 DBG_ERR("couldn't process snap.tv_sec in %s\n", snap_btime
);
95 /* second component is nsecs */
97 snap_timespec
.tv_nsec
= strtoul_err(s
, &endptr
, 10, &err
);
101 if ((endptr
== s
) || (*endptr
!= '\0')) {
102 DBG_ERR("couldn't process snap.tv_nsec in %s\n", s
);
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
, snap_path
,
148 if (smb_fname
== NULL
) {
152 ret
= ceph_snap_get_btime(handle
, smb_fname
, &snap_secs
);
157 tm_ret
= gmtime_r(&snap_secs
, &gmt_snap_time
);
158 if (tm_ret
== NULL
) {
161 str_sz
= strftime(this_label
, sizeof(SHADOW_COPY_LABEL
),
162 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
164 DBG_ERR("failed to convert tm to @GMT token\n");
168 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
169 snap_path
, this_label
);
174 static int ceph_snap_enum_snapdir(struct vfs_handle_struct
*handle
,
175 struct smb_filename
*snaps_dname
,
177 struct shadow_copy_data
*sc_data
)
182 struct dirent
*e
= NULL
;
185 status
= smbd_check_access_rights(handle
->conn
,
189 if (!NT_STATUS_IS_OK(status
)) {
190 DEBUG(0,("user does not have list permission "
192 snaps_dname
->base_name
));
193 ret
= -map_errno_from_nt_status(status
);
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
206 d
= SMB_VFS_NEXT_OPENDIR(handle
, snaps_dname
, NULL
, 0);
213 sc_data
->num_volumes
= 0;
214 sc_data
->labels
= NULL
;
216 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
218 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
)) {
219 if (ISDOT(e
->d_name
) || ISDOTDOT(e
->d_name
)) {
222 sc_data
->num_volumes
++;
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
,
233 if (sc_data
->labels
== NULL
) {
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]);
254 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
260 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
261 snaps_dname
->base_name
, sc_data
->num_volumes
);
266 SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
268 TALLOC_FREE(sc_data
->labels
);
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
,
286 const char **_trimmed
)
292 if (!strcmp(path
, "/")) {
293 DBG_ERR("can't go past root for %s .snap dir\n", path
);
297 p
= strrchr_m(path
, '/'); /* Find final '/', if any */
299 DBG_DEBUG("parent .snap dir for %s is cwd\n", path
);
300 ret
= strlcpy(_parent_buf
, "", buflen
);
304 if (_trimmed
!= NULL
) {
310 SMB_ASSERT(p
>= path
);
313 ret
= snprintf(_parent_buf
, buflen
, "%.*s", (int)len
, path
);
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
,
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
);
336 if (_trimmed
!= NULL
) {
338 * point to path component which was trimmed from _parent_buf
339 * excluding path separator.
344 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
345 path
, _parent_buf
, p
+ 1);
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
,
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
),
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
) {
374 if (sc_data
== NULL
) {
379 if (fsp
->is_directory
) {
380 parent_dir
= fsp
->fsp_name
->base_name
;
382 ret
= ceph_snap_get_parent_path(handle
->conn
->connectpath
,
383 fsp
->fsp_name
->base_name
,
393 ret
= snprintf(snaps_path
, sizeof(snaps_path
), "%s/%s",
394 parent_dir
, snapdir
);
395 if (ret
>= sizeof(snaps_path
)) {
400 snaps_dname
= synthetic_smb_fname(tmp_ctx
,
404 fsp
->fsp_name
->flags
);
405 if (snaps_dname
== NULL
) {
410 ret
= ceph_snap_enum_snapdir(handle
, snaps_dname
, labels
, sc_data
);
415 talloc_free(tmp_ctx
);
419 talloc_free(tmp_ctx
);
424 static bool ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct
*handle
,
434 size_t rest_len
, dst_len
;
435 ptrdiff_t len_before_gmt
;
437 p
= strstr_m(name
, "@GMT-");
441 if ((p
> name
) && (p
[-1] != '/')) {
444 len_before_gmt
= p
- name
;
445 q
= strptime(p
, GMT_FORMAT
, &tm
);
450 timestamp
= timegm(&tm
);
451 if (timestamp
== (time_t)-1) {
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
) {
463 if (len_before_gmt
> 0) {
465 * There is a slash before the @GMT-. Remove it
466 * and copy the result.
469 strlcpy(_stripped_buf
, name
, len_before_gmt
);
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
;
481 * It is not a complete path component, i.e. the path
482 * component continues after the gmt-token.
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
) {
497 memcpy(_stripped_buf
, name
, len_before_gmt
);
500 memcpy(_stripped_buf
+ len_before_gmt
, q
, rest_len
);
502 _stripped_buf
[dst_len
] = '\0';
504 *_timestamp
= timestamp
;
505 DBG_DEBUG("GMT token in %s stripped to %s\n", name
, _stripped_buf
);
512 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct
*handle
,
515 char *_converted_buf
,
521 struct dirent
*e
= NULL
;
522 struct smb_filename
*snaps_dname
= NULL
;
523 const char *snapdir
= lp_parm_const_string(SNUM(handle
->conn
),
525 CEPH_SNAP_SUBDIR_DEFAULT
);
526 TALLOC_CTX
*tmp_ctx
= talloc_new(NULL
);
528 if (tmp_ctx
== NULL
) {
534 * Temporally use the caller's return buffer for this.
536 ret
= snprintf(_converted_buf
, buflen
, "%s/%s", name
, snapdir
);
542 snaps_dname
= synthetic_smb_fname(tmp_ctx
,
547 if (snaps_dname
== NULL
) {
552 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
553 ret
= SMB_VFS_NEXT_STAT(handle
, snaps_dname
);
559 status
= smbd_check_access_rights(handle
->conn
,
563 if (!NT_STATUS_IS_OK(status
)) {
564 DEBUG(0,("user does not have list permission "
566 snaps_dname
->base_name
));
567 ret
= -map_errno_from_nt_status(status
);
571 DBG_DEBUG("enumerating shadow copy dir at %s\n",
572 snaps_dname
->base_name
);
574 d
= SMB_VFS_NEXT_OPENDIR(handle
, snaps_dname
, NULL
, 0);
580 for (e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
);
582 e
= SMB_VFS_NEXT_READDIR(handle
, d
, NULL
)) {
583 struct smb_filename
*smb_fname
;
586 if (ISDOT(e
->d_name
) || ISDOTDOT(e
->d_name
)) {
590 ret
= snprintf(_converted_buf
, buflen
, "%s/%s",
591 snaps_dname
->base_name
, e
->d_name
);
597 smb_fname
= synthetic_smb_fname(tmp_ctx
, _converted_buf
,
599 if (smb_fname
== NULL
) {
604 ret
= ceph_snap_get_btime(handle
, smb_fname
, &snap_secs
);
610 * check gmt_snap_time matches @timestamp
612 if (timestamp
== snap_secs
) {
615 DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
616 handle
->conn
->connectpath
, name
, (long long)timestamp
,
617 e
->d_name
, (long long)snap_secs
);
621 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
622 handle
->conn
->connectpath
, name
, (long long)timestamp
);
627 /* found, _converted_buf already contains path of interest */
628 DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
629 handle
->conn
->connectpath
, name
, (long long)timestamp
,
632 ret
= SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
637 talloc_free(tmp_ctx
);
641 SMB_VFS_NEXT_CLOSEDIR(handle
, d
);
643 talloc_free(tmp_ctx
);
647 static int ceph_snap_gmt_convert(struct vfs_handle_struct
*handle
,
650 char *_converted_buf
,
654 char parent
[PATH_MAX
+ 1];
655 const char *trimmed
= NULL
;
657 * CephFS Snapshots for a given dir are nested under the ./.snap subdir
658 * *or* under ../.snap/dir (and subsequent parent dirs).
659 * Child dirs inherit snapshots created in parent dirs if the child
660 * exists at the time of snapshot creation.
662 * At this point we don't know whether @name refers to a file or dir, so
663 * first assume it's a dir (with a corresponding .snaps subdir)
665 ret
= ceph_snap_gmt_convert_dir(handle
,
671 /* all done: .snap subdir exists - @name is a dir */
672 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name
);
676 /* @name/.snap access failed, attempt snapshot access via parent */
677 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
680 ret
= ceph_snap_get_parent_path(handle
->conn
->connectpath
,
689 ret
= ceph_snap_gmt_convert_dir(handle
,
699 * found snapshot via parent. Append the child path component
700 * that was trimmed... +1 for path separator + 1 for null termination.
702 if (strlen(_converted_buf
) + 1 + strlen(trimmed
) + 1 > buflen
) {
705 strlcat(_converted_buf
, "/", buflen
);
706 strlcat(_converted_buf
, trimmed
, buflen
);
711 static DIR *ceph_snap_gmt_opendir(vfs_handle_struct
*handle
,
712 const struct smb_filename
*csmb_fname
,
716 time_t timestamp
= 0;
717 char stripped
[PATH_MAX
+ 1];
721 struct smb_filename
*conv_smb_fname
= NULL
;
722 char conv
[PATH_MAX
+ 1];
724 ret
= ceph_snap_gmt_strip_snapshot(handle
,
725 csmb_fname
->base_name
,
727 stripped
, sizeof(stripped
));
732 if (timestamp
== 0) {
733 return SMB_VFS_NEXT_OPENDIR(handle
, csmb_fname
, mask
, attr
);
735 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
736 timestamp
, conv
, sizeof(conv
));
741 conv_smb_fname
= synthetic_smb_fname(talloc_tos(),
746 if (conv_smb_fname
== NULL
) {
751 dir
= SMB_VFS_NEXT_OPENDIR(handle
, conv_smb_fname
, mask
, attr
);
753 TALLOC_FREE(conv_smb_fname
);
758 static int ceph_snap_gmt_rename(vfs_handle_struct
*handle
,
759 const struct smb_filename
*smb_fname_src
,
760 const struct smb_filename
*smb_fname_dst
)
763 time_t timestamp_src
, timestamp_dst
;
765 ret
= ceph_snap_gmt_strip_snapshot(handle
,
766 smb_fname_src
->base_name
,
767 ×tamp_src
, NULL
, 0);
772 ret
= ceph_snap_gmt_strip_snapshot(handle
,
773 smb_fname_dst
->base_name
,
774 ×tamp_dst
, NULL
, 0);
779 if (timestamp_src
!= 0) {
783 if (timestamp_dst
!= 0) {
787 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
790 /* block links from writeable shares to snapshots for now, like other modules */
791 static int ceph_snap_gmt_symlink(vfs_handle_struct
*handle
,
792 const char *link_contents
,
793 const struct smb_filename
*new_smb_fname
)
796 time_t timestamp_old
= 0;
797 time_t timestamp_new
= 0;
799 ret
= ceph_snap_gmt_strip_snapshot(handle
,
807 ret
= ceph_snap_gmt_strip_snapshot(handle
,
808 new_smb_fname
->base_name
,
815 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
819 return SMB_VFS_NEXT_SYMLINK(handle
, link_contents
, new_smb_fname
);
822 static int ceph_snap_gmt_link(vfs_handle_struct
*handle
,
823 const struct smb_filename
*old_smb_fname
,
824 const struct smb_filename
*new_smb_fname
)
827 time_t timestamp_old
= 0;
828 time_t timestamp_new
= 0;
830 ret
= ceph_snap_gmt_strip_snapshot(handle
,
831 old_smb_fname
->base_name
,
838 ret
= ceph_snap_gmt_strip_snapshot(handle
,
839 new_smb_fname
->base_name
,
846 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
850 return SMB_VFS_NEXT_LINK(handle
, old_smb_fname
, new_smb_fname
);
853 static int ceph_snap_gmt_stat(vfs_handle_struct
*handle
,
854 struct smb_filename
*smb_fname
)
856 time_t timestamp
= 0;
857 char stripped
[PATH_MAX
+ 1];
858 char conv
[PATH_MAX
+ 1];
862 ret
= ceph_snap_gmt_strip_snapshot(handle
,
863 smb_fname
->base_name
,
864 ×tamp
, stripped
, sizeof(stripped
));
869 if (timestamp
== 0) {
870 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
873 ret
= ceph_snap_gmt_convert(handle
, stripped
,
874 timestamp
, conv
, sizeof(conv
));
879 tmp
= smb_fname
->base_name
;
880 smb_fname
->base_name
= conv
;
882 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
883 smb_fname
->base_name
= tmp
;
887 static int ceph_snap_gmt_lstat(vfs_handle_struct
*handle
,
888 struct smb_filename
*smb_fname
)
890 time_t timestamp
= 0;
891 char stripped
[PATH_MAX
+ 1];
892 char conv
[PATH_MAX
+ 1];
896 ret
= ceph_snap_gmt_strip_snapshot(handle
,
897 smb_fname
->base_name
,
898 ×tamp
, stripped
, sizeof(stripped
));
903 if (timestamp
== 0) {
904 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
907 ret
= ceph_snap_gmt_convert(handle
, stripped
,
908 timestamp
, conv
, sizeof(conv
));
913 tmp
= smb_fname
->base_name
;
914 smb_fname
->base_name
= conv
;
916 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
917 smb_fname
->base_name
= tmp
;
921 static int ceph_snap_gmt_open(vfs_handle_struct
*handle
,
922 struct smb_filename
*smb_fname
, files_struct
*fsp
,
923 int flags
, mode_t mode
)
925 time_t timestamp
= 0;
926 char stripped
[PATH_MAX
+ 1];
927 char conv
[PATH_MAX
+ 1];
931 ret
= ceph_snap_gmt_strip_snapshot(handle
,
932 smb_fname
->base_name
,
933 ×tamp
, stripped
, sizeof(stripped
));
938 if (timestamp
== 0) {
939 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
942 ret
= ceph_snap_gmt_convert(handle
, stripped
,
943 timestamp
, conv
, sizeof(conv
));
948 tmp
= smb_fname
->base_name
;
949 smb_fname
->base_name
= conv
;
951 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
952 smb_fname
->base_name
= tmp
;
956 static int ceph_snap_gmt_unlink(vfs_handle_struct
*handle
,
957 const struct smb_filename
*csmb_fname
)
959 time_t timestamp
= 0;
960 char stripped
[PATH_MAX
+ 1];
961 char conv
[PATH_MAX
+ 1];
963 struct smb_filename
*new_fname
;
966 ret
= ceph_snap_gmt_strip_snapshot(handle
,
967 csmb_fname
->base_name
,
968 ×tamp
, stripped
, sizeof(stripped
));
973 if (timestamp
== 0) {
974 return SMB_VFS_NEXT_UNLINK(handle
, csmb_fname
);
977 ret
= ceph_snap_gmt_convert(handle
, stripped
,
978 timestamp
, conv
, sizeof(conv
));
983 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
984 if (new_fname
== NULL
) {
988 new_fname
->base_name
= conv
;
990 ret
= SMB_VFS_NEXT_UNLINK(handle
, new_fname
);
992 TALLOC_FREE(new_fname
);
997 static int ceph_snap_gmt_chmod(vfs_handle_struct
*handle
,
998 const struct smb_filename
*csmb_fname
,
1001 time_t timestamp
= 0;
1002 char stripped
[PATH_MAX
+ 1];
1003 char conv
[PATH_MAX
+ 1];
1005 struct smb_filename
*new_fname
;
1008 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1009 csmb_fname
->base_name
,
1010 ×tamp
, stripped
, sizeof(stripped
));
1015 if (timestamp
== 0) {
1016 return SMB_VFS_NEXT_CHMOD(handle
, csmb_fname
, mode
);
1019 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1020 timestamp
, conv
, sizeof(conv
));
1025 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1026 if (new_fname
== NULL
) {
1030 new_fname
->base_name
= conv
;
1032 ret
= SMB_VFS_NEXT_CHMOD(handle
, new_fname
, mode
);
1033 saved_errno
= errno
;
1034 TALLOC_FREE(new_fname
);
1035 errno
= saved_errno
;
1039 static int ceph_snap_gmt_chown(vfs_handle_struct
*handle
,
1040 const struct smb_filename
*csmb_fname
,
1044 time_t timestamp
= 0;
1045 char stripped
[PATH_MAX
+ 1];
1046 char conv
[PATH_MAX
+ 1];
1048 struct smb_filename
*new_fname
;
1051 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1052 csmb_fname
->base_name
,
1053 ×tamp
, stripped
, sizeof(stripped
));
1058 if (timestamp
== 0) {
1059 return SMB_VFS_NEXT_CHOWN(handle
, csmb_fname
, uid
, gid
);
1062 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1063 timestamp
, conv
, sizeof(conv
));
1068 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1069 if (new_fname
== NULL
) {
1073 new_fname
->base_name
= conv
;
1075 ret
= SMB_VFS_NEXT_CHOWN(handle
, new_fname
, uid
, gid
);
1076 saved_errno
= errno
;
1077 TALLOC_FREE(new_fname
);
1078 errno
= saved_errno
;
1082 static int ceph_snap_gmt_chdir(vfs_handle_struct
*handle
,
1083 const struct smb_filename
*csmb_fname
)
1085 time_t timestamp
= 0;
1086 char stripped
[PATH_MAX
+ 1];
1087 char conv
[PATH_MAX
+ 1];
1089 struct smb_filename
*new_fname
;
1092 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1093 csmb_fname
->base_name
,
1094 ×tamp
, stripped
, sizeof(stripped
));
1099 if (timestamp
== 0) {
1100 return SMB_VFS_NEXT_CHDIR(handle
, csmb_fname
);
1103 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1104 timestamp
, conv
, sizeof(conv
));
1109 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1110 if (new_fname
== NULL
) {
1114 new_fname
->base_name
= conv
;
1116 ret
= SMB_VFS_NEXT_CHDIR(handle
, new_fname
);
1117 saved_errno
= errno
;
1118 TALLOC_FREE(new_fname
);
1119 errno
= saved_errno
;
1123 static int ceph_snap_gmt_ntimes(vfs_handle_struct
*handle
,
1124 const struct smb_filename
*csmb_fname
,
1125 struct smb_file_time
*ft
)
1127 time_t timestamp
= 0;
1128 char stripped
[PATH_MAX
+ 1];
1129 char conv
[PATH_MAX
+ 1];
1131 struct smb_filename
*new_fname
;
1134 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1135 csmb_fname
->base_name
,
1136 ×tamp
, stripped
, sizeof(stripped
));
1141 if (timestamp
== 0) {
1142 return SMB_VFS_NEXT_NTIMES(handle
, csmb_fname
, ft
);
1145 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1146 timestamp
, conv
, sizeof(conv
));
1151 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1152 if (new_fname
== NULL
) {
1156 new_fname
->base_name
= conv
;
1158 ret
= SMB_VFS_NEXT_NTIMES(handle
, new_fname
, ft
);
1159 saved_errno
= errno
;
1160 TALLOC_FREE(new_fname
);
1161 errno
= saved_errno
;
1165 static int ceph_snap_gmt_readlink(vfs_handle_struct
*handle
,
1166 const struct smb_filename
*csmb_fname
,
1170 time_t timestamp
= 0;
1171 char stripped
[PATH_MAX
+ 1];
1172 char conv
[PATH_MAX
+ 1];
1174 struct smb_filename
*new_fname
;
1177 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1178 csmb_fname
->base_name
,
1179 ×tamp
, stripped
, sizeof(stripped
));
1184 if (timestamp
== 0) {
1185 return SMB_VFS_NEXT_READLINK(handle
, csmb_fname
, buf
, bufsiz
);
1187 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1188 timestamp
, conv
, sizeof(conv
));
1193 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1194 if (new_fname
== NULL
) {
1198 new_fname
->base_name
= conv
;
1200 ret
= SMB_VFS_NEXT_READLINK(handle
, new_fname
, buf
, bufsiz
);
1201 saved_errno
= errno
;
1202 TALLOC_FREE(new_fname
);
1203 errno
= saved_errno
;
1207 static int ceph_snap_gmt_mknod(vfs_handle_struct
*handle
,
1208 const struct smb_filename
*csmb_fname
,
1212 time_t timestamp
= 0;
1213 char stripped
[PATH_MAX
+ 1];
1214 char conv
[PATH_MAX
+ 1];
1216 struct smb_filename
*new_fname
;
1219 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1220 csmb_fname
->base_name
,
1221 ×tamp
, stripped
, sizeof(stripped
));
1226 if (timestamp
== 0) {
1227 return SMB_VFS_NEXT_MKNOD(handle
, csmb_fname
, mode
, dev
);
1229 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1230 timestamp
, conv
, sizeof(conv
));
1235 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1236 if (new_fname
== NULL
) {
1240 new_fname
->base_name
= conv
;
1242 ret
= SMB_VFS_NEXT_MKNOD(handle
, new_fname
, mode
, dev
);
1243 saved_errno
= errno
;
1244 TALLOC_FREE(new_fname
);
1245 errno
= saved_errno
;
1249 static struct smb_filename
*ceph_snap_gmt_realpath(vfs_handle_struct
*handle
,
1251 const struct smb_filename
*csmb_fname
)
1253 time_t timestamp
= 0;
1254 char stripped
[PATH_MAX
+ 1];
1255 char conv
[PATH_MAX
+ 1];
1256 struct smb_filename
*result_fname
;
1258 struct smb_filename
*new_fname
;
1261 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1262 csmb_fname
->base_name
,
1263 ×tamp
, stripped
, sizeof(stripped
));
1268 if (timestamp
== 0) {
1269 return SMB_VFS_NEXT_REALPATH(handle
, ctx
, csmb_fname
);
1271 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1272 timestamp
, conv
, sizeof(conv
));
1277 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1278 if (new_fname
== NULL
) {
1282 new_fname
->base_name
= conv
;
1284 result_fname
= SMB_VFS_NEXT_REALPATH(handle
, ctx
, new_fname
);
1285 saved_errno
= errno
;
1286 TALLOC_FREE(new_fname
);
1287 errno
= saved_errno
;
1288 return result_fname
;
1292 * XXX this should have gone through open() conversion, so why do we need
1293 * a handler here? posix_fget_nt_acl() falls back to posix_get_nt_acl() for
1294 * dirs (or fd == -1).
1296 static NTSTATUS
ceph_snap_gmt_fget_nt_acl(vfs_handle_struct
*handle
,
1297 struct files_struct
*fsp
,
1298 uint32_t security_info
,
1299 TALLOC_CTX
*mem_ctx
,
1300 struct security_descriptor
**ppdesc
)
1302 time_t timestamp
= 0;
1303 char stripped
[PATH_MAX
+ 1];
1304 char conv
[PATH_MAX
+ 1];
1305 struct smb_filename
*smb_fname
;
1309 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1310 fsp
->fsp_name
->base_name
,
1311 ×tamp
, stripped
, sizeof(stripped
));
1313 return map_nt_error_from_unix(-ret
);
1315 if (timestamp
== 0) {
1316 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1320 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1321 timestamp
, conv
, sizeof(conv
));
1323 return map_nt_error_from_unix(-ret
);
1326 smb_fname
= synthetic_smb_fname(mem_ctx
,
1330 fsp
->fsp_name
->flags
);
1331 if (smb_fname
== NULL
) {
1332 return NT_STATUS_NO_MEMORY
;
1335 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, smb_fname
, security_info
,
1337 TALLOC_FREE(smb_fname
);
1341 static NTSTATUS
ceph_snap_gmt_get_nt_acl(vfs_handle_struct
*handle
,
1342 const struct smb_filename
*csmb_fname
,
1343 uint32_t security_info
,
1344 TALLOC_CTX
*mem_ctx
,
1345 struct security_descriptor
**ppdesc
)
1347 time_t timestamp
= 0;
1348 char stripped
[PATH_MAX
+ 1];
1349 char conv
[PATH_MAX
+ 1];
1352 struct smb_filename
*new_fname
;
1355 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1356 csmb_fname
->base_name
,
1357 ×tamp
, stripped
, sizeof(stripped
));
1359 return map_nt_error_from_unix(-ret
);
1361 if (timestamp
== 0) {
1362 return SMB_VFS_NEXT_GET_NT_ACL(handle
, csmb_fname
, security_info
,
1365 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1366 timestamp
, conv
, sizeof(conv
));
1368 return map_nt_error_from_unix(-ret
);
1370 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1371 if (new_fname
== NULL
) {
1372 return NT_STATUS_NO_MEMORY
;
1374 new_fname
->base_name
= conv
;
1376 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, new_fname
, security_info
,
1378 saved_errno
= errno
;
1379 TALLOC_FREE(new_fname
);
1380 errno
= saved_errno
;
1384 static int ceph_snap_gmt_mkdir(vfs_handle_struct
*handle
,
1385 const struct smb_filename
*csmb_fname
,
1388 time_t timestamp
= 0;
1389 char stripped
[PATH_MAX
+ 1];
1390 char conv
[PATH_MAX
+ 1];
1392 struct smb_filename
*new_fname
;
1395 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1396 csmb_fname
->base_name
,
1397 ×tamp
, stripped
, sizeof(stripped
));
1402 if (timestamp
== 0) {
1403 return SMB_VFS_NEXT_MKDIR(handle
, csmb_fname
, mode
);
1405 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1406 timestamp
, conv
, sizeof(conv
));
1411 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1412 if (new_fname
== NULL
) {
1416 new_fname
->base_name
= conv
;
1418 ret
= SMB_VFS_NEXT_MKDIR(handle
, new_fname
, mode
);
1419 saved_errno
= errno
;
1420 TALLOC_FREE(new_fname
);
1421 errno
= saved_errno
;
1425 static int ceph_snap_gmt_rmdir(vfs_handle_struct
*handle
,
1426 const struct smb_filename
*csmb_fname
)
1428 time_t timestamp
= 0;
1429 char stripped
[PATH_MAX
+ 1];
1430 char conv
[PATH_MAX
+ 1];
1432 struct smb_filename
*new_fname
;
1435 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1436 csmb_fname
->base_name
,
1437 ×tamp
, stripped
, sizeof(stripped
));
1442 if (timestamp
== 0) {
1443 return SMB_VFS_NEXT_RMDIR(handle
, csmb_fname
);
1445 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1446 timestamp
, conv
, sizeof(conv
));
1451 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1452 if (new_fname
== NULL
) {
1456 new_fname
->base_name
= conv
;
1458 ret
= SMB_VFS_NEXT_RMDIR(handle
, new_fname
);
1459 saved_errno
= errno
;
1460 TALLOC_FREE(new_fname
);
1461 errno
= saved_errno
;
1465 static int ceph_snap_gmt_chflags(vfs_handle_struct
*handle
,
1466 const struct smb_filename
*csmb_fname
,
1469 time_t timestamp
= 0;
1470 char stripped
[PATH_MAX
+ 1];
1471 char conv
[PATH_MAX
+ 1];
1473 struct smb_filename
*new_fname
;
1476 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1477 csmb_fname
->base_name
,
1478 ×tamp
, stripped
, sizeof(stripped
));
1483 if (timestamp
== 0) {
1484 return SMB_VFS_NEXT_CHFLAGS(handle
, csmb_fname
, flags
);
1486 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1487 timestamp
, conv
, sizeof(conv
));
1492 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1493 if (new_fname
== NULL
) {
1497 new_fname
->base_name
= conv
;
1499 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, new_fname
, flags
);
1500 saved_errno
= errno
;
1501 TALLOC_FREE(new_fname
);
1502 errno
= saved_errno
;
1506 static ssize_t
ceph_snap_gmt_getxattr(vfs_handle_struct
*handle
,
1507 const struct smb_filename
*csmb_fname
,
1512 time_t timestamp
= 0;
1513 char stripped
[PATH_MAX
+ 1];
1514 char conv
[PATH_MAX
+ 1];
1516 struct smb_filename
*new_fname
;
1519 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1520 csmb_fname
->base_name
,
1521 ×tamp
, stripped
, sizeof(stripped
));
1526 if (timestamp
== 0) {
1527 return SMB_VFS_NEXT_GETXATTR(handle
, csmb_fname
, aname
, value
,
1530 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1531 timestamp
, conv
, sizeof(conv
));
1536 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1537 if (new_fname
== NULL
) {
1541 new_fname
->base_name
= conv
;
1543 ret
= SMB_VFS_NEXT_GETXATTR(handle
, new_fname
, aname
, value
, size
);
1544 saved_errno
= errno
;
1545 TALLOC_FREE(new_fname
);
1546 errno
= saved_errno
;
1550 static ssize_t
ceph_snap_gmt_listxattr(struct vfs_handle_struct
*handle
,
1551 const struct smb_filename
*csmb_fname
,
1552 char *list
, size_t size
)
1554 time_t timestamp
= 0;
1555 char stripped
[PATH_MAX
+ 1];
1556 char conv
[PATH_MAX
+ 1];
1558 struct smb_filename
*new_fname
;
1561 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1562 csmb_fname
->base_name
,
1563 ×tamp
, stripped
, sizeof(stripped
));
1568 if (timestamp
== 0) {
1569 return SMB_VFS_NEXT_LISTXATTR(handle
, csmb_fname
, list
, size
);
1571 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1572 timestamp
, conv
, sizeof(conv
));
1577 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1578 if (new_fname
== NULL
) {
1582 new_fname
->base_name
= conv
;
1584 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, new_fname
, list
, size
);
1585 saved_errno
= errno
;
1586 TALLOC_FREE(new_fname
);
1587 errno
= saved_errno
;
1591 static int ceph_snap_gmt_removexattr(vfs_handle_struct
*handle
,
1592 const struct smb_filename
*csmb_fname
,
1595 time_t timestamp
= 0;
1596 char stripped
[PATH_MAX
+ 1];
1597 char conv
[PATH_MAX
+ 1];
1599 struct smb_filename
*new_fname
;
1602 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1603 csmb_fname
->base_name
,
1604 ×tamp
, stripped
, sizeof(stripped
));
1609 if (timestamp
== 0) {
1610 return SMB_VFS_NEXT_REMOVEXATTR(handle
, csmb_fname
, aname
);
1612 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1613 timestamp
, conv
, sizeof(conv
));
1618 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1619 if (new_fname
== NULL
) {
1623 new_fname
->base_name
= conv
;
1625 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, new_fname
, aname
);
1626 saved_errno
= errno
;
1627 TALLOC_FREE(new_fname
);
1628 errno
= saved_errno
;
1632 static int ceph_snap_gmt_setxattr(struct vfs_handle_struct
*handle
,
1633 const struct smb_filename
*csmb_fname
,
1634 const char *aname
, const void *value
,
1635 size_t size
, int flags
)
1637 time_t timestamp
= 0;
1638 char stripped
[PATH_MAX
+ 1];
1639 char conv
[PATH_MAX
+ 1];
1641 struct smb_filename
*new_fname
;
1644 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1645 csmb_fname
->base_name
,
1646 ×tamp
, stripped
, sizeof(stripped
));
1651 if (timestamp
== 0) {
1652 return SMB_VFS_NEXT_SETXATTR(handle
, csmb_fname
,
1653 aname
, value
, size
, flags
);
1655 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1656 timestamp
, conv
, sizeof(conv
));
1661 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1662 if (new_fname
== NULL
) {
1666 new_fname
->base_name
= conv
;
1668 ret
= SMB_VFS_NEXT_SETXATTR(handle
, new_fname
,
1669 aname
, value
, size
, flags
);
1670 saved_errno
= errno
;
1671 TALLOC_FREE(new_fname
);
1672 errno
= saved_errno
;
1676 static int ceph_snap_gmt_get_real_filename(struct vfs_handle_struct
*handle
,
1679 TALLOC_CTX
*mem_ctx
,
1682 time_t timestamp
= 0;
1683 char stripped
[PATH_MAX
+ 1];
1684 char conv
[PATH_MAX
+ 1];
1687 ret
= ceph_snap_gmt_strip_snapshot(handle
, path
,
1688 ×tamp
, stripped
, sizeof(stripped
));
1693 if (timestamp
== 0) {
1694 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1695 mem_ctx
, found_name
);
1697 ret
= ceph_snap_gmt_convert_dir(handle
, stripped
,
1698 timestamp
, conv
, sizeof(conv
));
1703 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1704 mem_ctx
, found_name
);
1708 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct
*handle
,
1709 const struct smb_filename
*csmb_fname
,
1714 time_t timestamp
= 0;
1715 char stripped
[PATH_MAX
+ 1];
1716 char conv
[PATH_MAX
+ 1];
1718 struct smb_filename
*new_fname
;
1721 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1722 csmb_fname
->base_name
,
1723 ×tamp
, stripped
, sizeof(stripped
));
1728 if (timestamp
== 0) {
1729 return SMB_VFS_NEXT_DISK_FREE(handle
, csmb_fname
,
1730 bsize
, dfree
, dsize
);
1732 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1733 timestamp
, conv
, sizeof(conv
));
1738 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1739 if (new_fname
== NULL
) {
1743 new_fname
->base_name
= conv
;
1745 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, new_fname
,
1746 bsize
, dfree
, dsize
);
1747 saved_errno
= errno
;
1748 TALLOC_FREE(new_fname
);
1749 errno
= saved_errno
;
1753 static int ceph_snap_gmt_get_quota(vfs_handle_struct
*handle
,
1754 const struct smb_filename
*csmb_fname
,
1755 enum SMB_QUOTA_TYPE qtype
,
1759 time_t timestamp
= 0;
1760 char stripped
[PATH_MAX
+ 1];
1761 char conv
[PATH_MAX
+ 1];
1763 struct smb_filename
*new_fname
;
1766 ret
= ceph_snap_gmt_strip_snapshot(handle
,
1767 csmb_fname
->base_name
,
1768 ×tamp
, stripped
, sizeof(stripped
));
1773 if (timestamp
== 0) {
1774 return SMB_VFS_NEXT_GET_QUOTA(handle
, csmb_fname
, qtype
, id
, dq
);
1776 ret
= ceph_snap_gmt_convert(handle
, stripped
,
1777 timestamp
, conv
, sizeof(conv
));
1782 new_fname
= cp_smb_filename(talloc_tos(), csmb_fname
);
1783 if (new_fname
== NULL
) {
1787 new_fname
->base_name
= conv
;
1789 ret
= SMB_VFS_NEXT_GET_QUOTA(handle
, new_fname
, qtype
, id
, dq
);
1790 saved_errno
= errno
;
1791 TALLOC_FREE(new_fname
);
1792 errno
= saved_errno
;
1796 static struct vfs_fn_pointers ceph_snap_fns
= {
1797 .get_shadow_copy_data_fn
= ceph_snap_get_shadow_copy_data
,
1798 .opendir_fn
= ceph_snap_gmt_opendir
,
1799 .disk_free_fn
= ceph_snap_gmt_disk_free
,
1800 .get_quota_fn
= ceph_snap_gmt_get_quota
,
1801 .rename_fn
= ceph_snap_gmt_rename
,
1802 .link_fn
= ceph_snap_gmt_link
,
1803 .symlink_fn
= ceph_snap_gmt_symlink
,
1804 .stat_fn
= ceph_snap_gmt_stat
,
1805 .lstat_fn
= ceph_snap_gmt_lstat
,
1806 .open_fn
= ceph_snap_gmt_open
,
1807 .unlink_fn
= ceph_snap_gmt_unlink
,
1808 .chmod_fn
= ceph_snap_gmt_chmod
,
1809 .chown_fn
= ceph_snap_gmt_chown
,
1810 .chdir_fn
= ceph_snap_gmt_chdir
,
1811 .ntimes_fn
= ceph_snap_gmt_ntimes
,
1812 .readlink_fn
= ceph_snap_gmt_readlink
,
1813 .mknod_fn
= ceph_snap_gmt_mknod
,
1814 .realpath_fn
= ceph_snap_gmt_realpath
,
1815 .get_nt_acl_fn
= ceph_snap_gmt_get_nt_acl
,
1816 .fget_nt_acl_fn
= ceph_snap_gmt_fget_nt_acl
,
1817 .get_nt_acl_fn
= ceph_snap_gmt_get_nt_acl
,
1818 .mkdir_fn
= ceph_snap_gmt_mkdir
,
1819 .rmdir_fn
= ceph_snap_gmt_rmdir
,
1820 .getxattr_fn
= ceph_snap_gmt_getxattr
,
1821 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
1822 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
1823 .listxattr_fn
= ceph_snap_gmt_listxattr
,
1824 .removexattr_fn
= ceph_snap_gmt_removexattr
,
1825 .setxattr_fn
= ceph_snap_gmt_setxattr
,
1826 .chflags_fn
= ceph_snap_gmt_chflags
,
1827 .get_real_filename_fn
= ceph_snap_gmt_get_real_filename
,
1831 NTSTATUS
vfs_ceph_snapshots_init(TALLOC_CTX
*ctx
)
1833 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
1834 "ceph_snapshots", &ceph_snap_fns
);