vfs_ceph_snapshots: fix root relative path handling
[Samba.git] / source3 / modules / vfs_ceph_snapshots.c
blob64f195f4add4ccdbbb5a41e9b0abc4bf56931c3a
1 /*
2 * Module for accessing CephFS snapshots as Previous Versions. This module is
3 * separate to vfs_ceph, so that it can also be used atop a CephFS kernel backed
4 * share with vfs_default.
6 * Copyright (C) David Disseldorp 2019
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 3 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, see <http://www.gnu.org/licenses/>.
22 #include <dirent.h>
23 #include <libgen.h>
24 #include "includes.h"
25 #include "include/ntioctl.h"
26 #include "include/smb.h"
27 #include "system/filesys.h"
28 #include "smbd/smbd.h"
29 #include "lib/util/tevent_ntstatus.h"
31 #undef DBGC_CLASS
32 #define DBGC_CLASS DBGC_VFS
35 * CephFS has a magic snapshots subdirectory in all parts of the directory tree.
36 * This module automatically makes all snapshots in this subdir visible to SMB
37 * clients (if permitted by corresponding access control).
39 #define CEPH_SNAP_SUBDIR_DEFAULT ".snap"
41 * The ceph.snap.btime (virtual) extended attribute carries the snapshot
42 * creation time in $secs.$nsecs format. It was added as part of
43 * https://tracker.ceph.com/issues/38838. Running Samba atop old Ceph versions
44 * which don't provide this xattr will not be able to enumerate or access
45 * snapshots using this module. As an alternative, vfs_shadow_copy2 could be
46 * used instead, alongside special shadow:format snapshot directory names.
48 #define CEPH_SNAP_BTIME_XATTR "ceph.snap.btime"
50 static int ceph_snap_get_btime(struct vfs_handle_struct *handle,
51 struct smb_filename *smb_fname,
52 time_t *_snap_secs)
54 int ret;
55 char snap_btime[33];
56 char *s = NULL;
57 char *endptr = NULL;
58 struct timespec snap_timespec;
59 int err;
61 ret = SMB_VFS_NEXT_GETXATTR(handle, smb_fname, CEPH_SNAP_BTIME_XATTR,
62 snap_btime, sizeof(snap_btime));
63 if (ret < 0) {
64 DBG_ERR("failed to get %s xattr: %s\n",
65 CEPH_SNAP_BTIME_XATTR, strerror(errno));
66 return -errno;
69 if (ret == 0 || ret >= sizeof(snap_btime) - 1) {
70 return -EINVAL;
73 /* ensure zero termination */
74 snap_btime[ret] = '\0';
76 /* format is sec.nsec */
77 s = strchr(snap_btime, '.');
78 if (s == NULL) {
79 DBG_ERR("invalid %s xattr value: %s\n",
80 CEPH_SNAP_BTIME_XATTR, snap_btime);
81 return -EINVAL;
84 /* First component is seconds, extract it */
85 *s = '\0';
86 snap_timespec.tv_sec = smb_strtoull(snap_btime,
87 &endptr,
88 10,
89 &err,
90 SMB_STR_FULL_STR_CONV);
91 if (err != 0) {
92 return -err;
95 /* second component is nsecs */
96 s++;
97 snap_timespec.tv_nsec = smb_strtoul(s,
98 &endptr,
99 10,
100 &err,
101 SMB_STR_FULL_STR_CONV);
102 if (err != 0) {
103 return -err;
107 * >> 30 is a rough divide by ~10**9. No need to be exact, as @GMT
108 * tokens only offer 1-second resolution (while twrp is nsec).
110 *_snap_secs = snap_timespec.tv_sec + (snap_timespec.tv_nsec >> 30);
112 return 0;
116 * XXX Ceph snapshots can be created with sub-second granularity, which means
117 * that multiple snapshots may be mapped to the same @GMT- label.
119 * @this_label is a pre-zeroed buffer to be filled with a @GMT label
120 * @return 0 if label successfully filled or -errno on error.
122 static int ceph_snap_fill_label(struct vfs_handle_struct *handle,
123 TALLOC_CTX *tmp_ctx,
124 const char *parent_snapsdir,
125 const char *subdir,
126 SHADOW_COPY_LABEL this_label)
128 struct smb_filename *smb_fname;
129 time_t snap_secs;
130 struct tm gmt_snap_time;
131 struct tm *tm_ret;
132 size_t str_sz;
133 char snap_path[PATH_MAX + 1];
134 int ret;
137 * CephFS snapshot creation times are available via a special
138 * xattr - snapshot b/m/ctimes all match the snap source.
140 ret = snprintf(snap_path, sizeof(snap_path), "%s/%s",
141 parent_snapsdir, subdir);
142 if (ret >= sizeof(snap_path)) {
143 return -EINVAL;
146 smb_fname = synthetic_smb_fname(tmp_ctx, snap_path,
147 NULL, NULL, 0);
148 if (smb_fname == NULL) {
149 return -ENOMEM;
152 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
153 if (ret < 0) {
154 return ret;
157 tm_ret = gmtime_r(&snap_secs, &gmt_snap_time);
158 if (tm_ret == NULL) {
159 return -EINVAL;
161 str_sz = strftime(this_label, sizeof(SHADOW_COPY_LABEL),
162 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
163 if (str_sz == 0) {
164 DBG_ERR("failed to convert tm to @GMT token\n");
165 return -EINVAL;
168 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
169 snap_path, this_label);
171 return 0;
174 static int ceph_snap_enum_snapdir(struct vfs_handle_struct *handle,
175 struct smb_filename *snaps_dname,
176 bool labels,
177 struct shadow_copy_data *sc_data)
179 NTSTATUS status;
180 int ret;
181 DIR *d = NULL;
182 struct dirent *e = NULL;
183 uint32_t slots;
185 status = smbd_check_access_rights(handle->conn,
186 snaps_dname,
187 false,
188 SEC_DIR_LIST);
189 if (!NT_STATUS_IS_OK(status)) {
190 DEBUG(0,("user does not have list permission "
191 "on snapdir %s\n",
192 snaps_dname->base_name));
193 ret = -map_errno_from_nt_status(status);
194 goto err_out;
197 DBG_DEBUG("enumerating shadow copy dir at %s\n",
198 snaps_dname->base_name);
201 * CephFS stat(dir).size *normally* returns the number of child entries
202 * for a given dir, but it unfortunately that's not the case for the one
203 * place we need it (dir=.snap), so we need to dynamically determine it
204 * via readdir.
206 d = SMB_VFS_NEXT_OPENDIR(handle, snaps_dname, NULL, 0);
207 if (d == NULL) {
208 ret = -errno;
209 goto err_out;
212 slots = 0;
213 sc_data->num_volumes = 0;
214 sc_data->labels = NULL;
216 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
217 e != NULL;
218 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
219 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
220 continue;
222 sc_data->num_volumes++;
223 if (!labels) {
224 continue;
226 if (sc_data->num_volumes > slots) {
227 uint32_t new_slot_count = slots + 10;
228 SMB_ASSERT(new_slot_count > slots);
229 sc_data->labels = talloc_realloc(sc_data,
230 sc_data->labels,
231 SHADOW_COPY_LABEL,
232 new_slot_count);
233 if (sc_data->labels == NULL) {
234 ret = -ENOMEM;
235 goto err_closedir;
237 memset(sc_data->labels[slots], 0,
238 sizeof(SHADOW_COPY_LABEL) * 10);
240 DBG_DEBUG("%d->%d slots for enum_snaps response\n",
241 slots, new_slot_count);
242 slots = new_slot_count;
244 DBG_DEBUG("filling shadow copy label for %s/%s\n",
245 snaps_dname->base_name, e->d_name);
246 ret = ceph_snap_fill_label(handle, snaps_dname,
247 snaps_dname->base_name, e->d_name,
248 sc_data->labels[sc_data->num_volumes - 1]);
249 if (ret < 0) {
250 goto err_closedir;
254 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
255 if (ret != 0) {
256 ret = -errno;
257 goto err_out;
260 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
261 snaps_dname->base_name, sc_data->num_volumes);
263 return 0;
265 err_closedir:
266 SMB_VFS_NEXT_CLOSEDIR(handle, d);
267 err_out:
268 TALLOC_FREE(sc_data->labels);
269 return ret;
273 * Prior reading: The Meaning of Path Names
274 * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
276 * translate paths so that we can use the parent dir for .snap access:
277 * myfile -> parent= trimmed=myfile
278 * /a -> parent=/ trimmed=a
279 * dir/sub/file -> parent=dir/sub trimmed=file
280 * /dir/sub -> parent=/dir/ trimmed=sub
282 static int ceph_snap_get_parent_path(const char *connectpath,
283 const char *path,
284 char *_parent_buf,
285 size_t buflen,
286 const char **_trimmed)
288 const char *p;
289 size_t len;
290 int ret;
292 if (!strcmp(path, "/")) {
293 DBG_ERR("can't go past root for %s .snap dir\n", path);
294 return -EINVAL;
297 p = strrchr_m(path, '/'); /* Find final '/', if any */
298 if (p == NULL) {
299 DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
300 ret = strlcpy(_parent_buf, "", buflen);
301 if (ret >= buflen) {
302 return -EINVAL;
304 if (_trimmed != NULL) {
305 *_trimmed = path;
307 return 0;
310 SMB_ASSERT(p >= path);
311 len = p - path;
313 ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
314 if (ret >= buflen) {
315 return -EINVAL;
318 /* for absolute paths, check that we're not going outside the share */
319 if ((len > 0) && (_parent_buf[0] == '/')) {
320 bool connectpath_match = false;
321 size_t clen = strlen(connectpath);
322 DBG_DEBUG("checking absolute path %s lies within share at %s\n",
323 _parent_buf, connectpath);
324 /* need to check for separator, to avoid /x/abcd vs /x/ab */
325 connectpath_match = (strncmp(connectpath,
326 _parent_buf,
327 clen) == 0);
328 if (!connectpath_match
329 || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
330 DBG_ERR("%s parent path is outside of share at %s\n",
331 _parent_buf, connectpath);
332 return -EINVAL;
336 if (_trimmed != NULL) {
338 * point to path component which was trimmed from _parent_buf
339 * excluding path separator.
341 *_trimmed = p + 1;
344 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
345 path, _parent_buf, p + 1);
347 return 0;
350 static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
351 struct files_struct *fsp,
352 struct shadow_copy_data *sc_data,
353 bool labels)
355 int ret;
356 TALLOC_CTX *tmp_ctx;
357 const char *parent_dir = NULL;
358 char tmp[PATH_MAX + 1];
359 char snaps_path[PATH_MAX + 1];
360 struct smb_filename *snaps_dname = NULL;
361 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
362 "ceph", "snapdir",
363 CEPH_SNAP_SUBDIR_DEFAULT);
365 DBG_DEBUG("getting shadow copy data for %s\n",
366 fsp->fsp_name->base_name);
368 tmp_ctx = talloc_new(fsp);
369 if (tmp_ctx == NULL) {
370 ret = -ENOMEM;
371 goto err_out;
374 if (sc_data == NULL) {
375 ret = -EINVAL;
376 goto err_out;
379 if (fsp->is_directory) {
380 parent_dir = fsp->fsp_name->base_name;
381 } else {
382 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
383 fsp->fsp_name->base_name,
384 tmp,
385 sizeof(tmp),
386 NULL); /* trimmed */
387 if (ret < 0) {
388 goto err_out;
390 parent_dir = tmp;
393 if (strlen(parent_dir) == 0) {
394 ret = strlcpy(snaps_path, snapdir, sizeof(snaps_path));
395 } else {
396 ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
397 parent_dir, snapdir);
399 if (ret >= sizeof(snaps_path)) {
400 ret = -EINVAL;
401 goto err_out;
404 snaps_dname = synthetic_smb_fname(tmp_ctx,
405 snaps_path,
406 NULL,
407 NULL,
408 fsp->fsp_name->flags);
409 if (snaps_dname == NULL) {
410 ret = -ENOMEM;
411 goto err_out;
414 ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
415 if (ret < 0) {
416 goto err_out;
419 talloc_free(tmp_ctx);
420 return 0;
422 err_out:
423 talloc_free(tmp_ctx);
424 errno = -ret;
425 return -1;
428 static bool ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
429 const char *name,
430 time_t *_timestamp,
431 char *_stripped_buf,
432 size_t buflen)
434 struct tm tm;
435 time_t timestamp;
436 const char *p;
437 char *q;
438 size_t rest_len, dst_len;
439 ptrdiff_t len_before_gmt;
441 p = strstr_m(name, "@GMT-");
442 if (p == NULL) {
443 goto no_snapshot;
445 if ((p > name) && (p[-1] != '/')) {
446 goto no_snapshot;
448 len_before_gmt = p - name;
449 q = strptime(p, GMT_FORMAT, &tm);
450 if (q == NULL) {
451 goto no_snapshot;
453 tm.tm_isdst = -1;
454 timestamp = timegm(&tm);
455 if (timestamp == (time_t)-1) {
456 goto no_snapshot;
458 if (q[0] == '\0') {
460 * The name consists of only the GMT token or the GMT
461 * token is at the end of the path.
463 if (_stripped_buf != NULL) {
464 if (len_before_gmt >= buflen) {
465 return -EINVAL;
467 if (len_before_gmt > 0) {
469 * There is a slash before the @GMT-. Remove it
470 * and copy the result.
472 len_before_gmt -= 1;
473 strlcpy(_stripped_buf, name, len_before_gmt);
474 } else {
475 _stripped_buf[0] = '\0'; /* token only */
477 DBG_DEBUG("GMT token in %s stripped to %s\n",
478 name, _stripped_buf);
480 *_timestamp = timestamp;
481 return 0;
483 if (q[0] != '/') {
485 * It is not a complete path component, i.e. the path
486 * component continues after the gmt-token.
488 goto no_snapshot;
490 q += 1;
492 rest_len = strlen(q);
493 dst_len = len_before_gmt + rest_len;
494 SMB_ASSERT(dst_len >= rest_len);
496 if (_stripped_buf != NULL) {
497 if (dst_len >= buflen) {
498 return -EINVAL;
500 if (p > name) {
501 memcpy(_stripped_buf, name, len_before_gmt);
503 if (rest_len > 0) {
504 memcpy(_stripped_buf + len_before_gmt, q, rest_len);
506 _stripped_buf[dst_len] = '\0';
507 DBG_DEBUG("GMT token in %s stripped to %s\n",
508 name, _stripped_buf);
510 *_timestamp = timestamp;
511 return 0;
512 no_snapshot:
513 *_timestamp = 0;
514 return 0;
517 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
518 const char *name,
519 time_t timestamp,
520 char *_converted_buf,
521 size_t buflen)
523 int ret;
524 NTSTATUS status;
525 DIR *d = NULL;
526 struct dirent *e = NULL;
527 struct smb_filename *snaps_dname = NULL;
528 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
529 "ceph", "snapdir",
530 CEPH_SNAP_SUBDIR_DEFAULT);
531 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
533 if (tmp_ctx == NULL) {
534 ret = -ENOMEM;
535 goto err_out;
539 * Temporally use the caller's return buffer for this.
541 if (strlen(name) == 0) {
542 ret = strlcpy(_converted_buf, snapdir, buflen);
543 } else {
544 ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
546 if (ret >= buflen) {
547 ret = -EINVAL;
548 goto err_out;
551 snaps_dname = synthetic_smb_fname(tmp_ctx,
552 _converted_buf,
553 NULL,
554 NULL,
555 0); /* XXX check? */
556 if (snaps_dname == NULL) {
557 ret = -ENOMEM;
558 goto err_out;
561 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
562 ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
563 if (ret < 0) {
564 ret = -errno;
565 goto err_out;
568 status = smbd_check_access_rights(handle->conn,
569 snaps_dname,
570 false,
571 SEC_DIR_LIST);
572 if (!NT_STATUS_IS_OK(status)) {
573 DEBUG(0,("user does not have list permission "
574 "on snapdir %s\n",
575 snaps_dname->base_name));
576 ret = -map_errno_from_nt_status(status);
577 goto err_out;
580 DBG_DEBUG("enumerating shadow copy dir at %s\n",
581 snaps_dname->base_name);
583 d = SMB_VFS_NEXT_OPENDIR(handle, snaps_dname, NULL, 0);
584 if (d == NULL) {
585 ret = -errno;
586 goto err_out;
589 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
590 e != NULL;
591 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
592 struct smb_filename *smb_fname;
593 time_t snap_secs;
595 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
596 continue;
599 ret = snprintf(_converted_buf, buflen, "%s/%s",
600 snaps_dname->base_name, e->d_name);
601 if (ret >= buflen) {
602 ret = -EINVAL;
603 goto err_closedir;
606 smb_fname = synthetic_smb_fname(tmp_ctx, _converted_buf,
607 NULL, NULL, 0);
608 if (smb_fname == NULL) {
609 ret = -ENOMEM;
610 goto err_closedir;
613 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
614 if (ret < 0) {
615 goto err_closedir;
619 * check gmt_snap_time matches @timestamp
621 if (timestamp == snap_secs) {
622 break;
624 DBG_DEBUG("[connectpath %s] %s@%lld no match for snap %s@%lld\n",
625 handle->conn->connectpath, name, (long long)timestamp,
626 e->d_name, (long long)snap_secs);
629 if (e == NULL) {
630 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
631 handle->conn->connectpath, name, (long long)timestamp);
632 ret = -ENOENT;
633 goto err_closedir;
636 /* found, _converted_buf already contains path of interest */
637 DBG_DEBUG("[connectpath %s] converted %s @ time %lld to %s\n",
638 handle->conn->connectpath, name, (long long)timestamp,
639 _converted_buf);
641 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
642 if (ret != 0) {
643 ret = -errno;
644 goto err_out;
646 talloc_free(tmp_ctx);
647 return 0;
649 err_closedir:
650 SMB_VFS_NEXT_CLOSEDIR(handle, d);
651 err_out:
652 talloc_free(tmp_ctx);
653 return ret;
656 static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
657 const char *name,
658 time_t timestamp,
659 char *_converted_buf,
660 size_t buflen)
662 int ret;
663 char parent[PATH_MAX + 1];
664 const char *trimmed = NULL;
666 * CephFS Snapshots for a given dir are nested under the ./.snap subdir
667 * *or* under ../.snap/dir (and subsequent parent dirs).
668 * Child dirs inherit snapshots created in parent dirs if the child
669 * exists at the time of snapshot creation.
671 * At this point we don't know whether @name refers to a file or dir, so
672 * first assume it's a dir (with a corresponding .snaps subdir)
674 ret = ceph_snap_gmt_convert_dir(handle,
675 name,
676 timestamp,
677 _converted_buf,
678 buflen);
679 if (ret >= 0) {
680 /* all done: .snap subdir exists - @name is a dir */
681 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
682 return ret;
685 /* @name/.snap access failed, attempt snapshot access via parent */
686 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
687 name);
689 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
690 name,
691 parent,
692 sizeof(parent),
693 &trimmed);
694 if (ret < 0) {
695 return ret;
698 ret = ceph_snap_gmt_convert_dir(handle,
699 parent,
700 timestamp,
701 _converted_buf,
702 buflen);
703 if (ret < 0) {
704 return ret;
708 * found snapshot via parent. Append the child path component
709 * that was trimmed... +1 for path separator + 1 for null termination.
711 if (strlen(_converted_buf) + 1 + strlen(trimmed) + 1 > buflen) {
712 return -EINVAL;
714 strlcat(_converted_buf, "/", buflen);
715 strlcat(_converted_buf, trimmed, buflen);
717 return 0;
720 static DIR *ceph_snap_gmt_opendir(vfs_handle_struct *handle,
721 const struct smb_filename *csmb_fname,
722 const char *mask,
723 uint32_t attr)
725 time_t timestamp = 0;
726 char stripped[PATH_MAX + 1];
727 int ret;
728 DIR *dir;
729 int saved_errno;
730 struct smb_filename *conv_smb_fname = NULL;
731 char conv[PATH_MAX + 1];
733 ret = ceph_snap_gmt_strip_snapshot(handle,
734 csmb_fname->base_name,
735 &timestamp,
736 stripped, sizeof(stripped));
737 if (ret < 0) {
738 errno = -ret;
739 return NULL;
741 if (timestamp == 0) {
742 return SMB_VFS_NEXT_OPENDIR(handle, csmb_fname, mask, attr);
744 ret = ceph_snap_gmt_convert_dir(handle, stripped,
745 timestamp, conv, sizeof(conv));
746 if (ret < 0) {
747 errno = -ret;
748 return NULL;
750 conv_smb_fname = synthetic_smb_fname(talloc_tos(),
751 conv,
752 NULL,
753 NULL,
754 csmb_fname->flags);
755 if (conv_smb_fname == NULL) {
756 errno = ENOMEM;
757 return NULL;
760 dir = SMB_VFS_NEXT_OPENDIR(handle, conv_smb_fname, mask, attr);
761 saved_errno = errno;
762 TALLOC_FREE(conv_smb_fname);
763 errno = saved_errno;
764 return dir;
767 static int ceph_snap_gmt_renameat(vfs_handle_struct *handle,
768 files_struct *srcfsp,
769 const struct smb_filename *smb_fname_src,
770 files_struct *dstfsp,
771 const struct smb_filename *smb_fname_dst)
773 int ret;
774 time_t timestamp_src, timestamp_dst;
776 ret = ceph_snap_gmt_strip_snapshot(handle,
777 smb_fname_src->base_name,
778 &timestamp_src, NULL, 0);
779 if (ret < 0) {
780 errno = -ret;
781 return -1;
783 ret = ceph_snap_gmt_strip_snapshot(handle,
784 smb_fname_dst->base_name,
785 &timestamp_dst, NULL, 0);
786 if (ret < 0) {
787 errno = -ret;
788 return -1;
790 if (timestamp_src != 0) {
791 errno = EXDEV;
792 return -1;
794 if (timestamp_dst != 0) {
795 errno = EROFS;
796 return -1;
798 return SMB_VFS_NEXT_RENAMEAT(handle,
799 srcfsp,
800 smb_fname_src,
801 dstfsp,
802 smb_fname_dst);
805 /* block links from writeable shares to snapshots for now, like other modules */
806 static int ceph_snap_gmt_symlinkat(vfs_handle_struct *handle,
807 const char *link_contents,
808 struct files_struct *dirfsp,
809 const struct smb_filename *new_smb_fname)
811 int ret;
812 time_t timestamp_old = 0;
813 time_t timestamp_new = 0;
815 ret = ceph_snap_gmt_strip_snapshot(handle,
816 link_contents,
817 &timestamp_old,
818 NULL, 0);
819 if (ret < 0) {
820 errno = -ret;
821 return -1;
823 ret = ceph_snap_gmt_strip_snapshot(handle,
824 new_smb_fname->base_name,
825 &timestamp_new,
826 NULL, 0);
827 if (ret < 0) {
828 errno = -ret;
829 return -1;
831 if ((timestamp_old != 0) || (timestamp_new != 0)) {
832 errno = EROFS;
833 return -1;
835 return SMB_VFS_NEXT_SYMLINKAT(handle,
836 link_contents,
837 dirfsp,
838 new_smb_fname);
841 static int ceph_snap_gmt_linkat(vfs_handle_struct *handle,
842 files_struct *srcfsp,
843 const struct smb_filename *old_smb_fname,
844 files_struct *dstfsp,
845 const struct smb_filename *new_smb_fname,
846 int flags)
848 int ret;
849 time_t timestamp_old = 0;
850 time_t timestamp_new = 0;
852 ret = ceph_snap_gmt_strip_snapshot(handle,
853 old_smb_fname->base_name,
854 &timestamp_old,
855 NULL, 0);
856 if (ret < 0) {
857 errno = -ret;
858 return -1;
860 ret = ceph_snap_gmt_strip_snapshot(handle,
861 new_smb_fname->base_name,
862 &timestamp_new,
863 NULL, 0);
864 if (ret < 0) {
865 errno = -ret;
866 return -1;
868 if ((timestamp_old != 0) || (timestamp_new != 0)) {
869 errno = EROFS;
870 return -1;
872 return SMB_VFS_NEXT_LINKAT(handle,
873 srcfsp,
874 old_smb_fname,
875 dstfsp,
876 new_smb_fname,
877 flags);
880 static int ceph_snap_gmt_stat(vfs_handle_struct *handle,
881 struct smb_filename *smb_fname)
883 time_t timestamp = 0;
884 char stripped[PATH_MAX + 1];
885 char conv[PATH_MAX + 1];
886 char *tmp;
887 int ret;
889 ret = ceph_snap_gmt_strip_snapshot(handle,
890 smb_fname->base_name,
891 &timestamp, stripped, sizeof(stripped));
892 if (ret < 0) {
893 errno = -ret;
894 return -1;
896 if (timestamp == 0) {
897 return SMB_VFS_NEXT_STAT(handle, smb_fname);
900 ret = ceph_snap_gmt_convert(handle, stripped,
901 timestamp, conv, sizeof(conv));
902 if (ret < 0) {
903 errno = -ret;
904 return -1;
906 tmp = smb_fname->base_name;
907 smb_fname->base_name = conv;
909 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
910 smb_fname->base_name = tmp;
911 return ret;
914 static int ceph_snap_gmt_lstat(vfs_handle_struct *handle,
915 struct smb_filename *smb_fname)
917 time_t timestamp = 0;
918 char stripped[PATH_MAX + 1];
919 char conv[PATH_MAX + 1];
920 char *tmp;
921 int ret;
923 ret = ceph_snap_gmt_strip_snapshot(handle,
924 smb_fname->base_name,
925 &timestamp, stripped, sizeof(stripped));
926 if (ret < 0) {
927 errno = -ret;
928 return -1;
930 if (timestamp == 0) {
931 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
934 ret = ceph_snap_gmt_convert(handle, stripped,
935 timestamp, conv, sizeof(conv));
936 if (ret < 0) {
937 errno = -ret;
938 return -1;
940 tmp = smb_fname->base_name;
941 smb_fname->base_name = conv;
943 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
944 smb_fname->base_name = tmp;
945 return ret;
948 static int ceph_snap_gmt_open(vfs_handle_struct *handle,
949 struct smb_filename *smb_fname, files_struct *fsp,
950 int flags, mode_t mode)
952 time_t timestamp = 0;
953 char stripped[PATH_MAX + 1];
954 char conv[PATH_MAX + 1];
955 char *tmp;
956 int ret;
958 ret = ceph_snap_gmt_strip_snapshot(handle,
959 smb_fname->base_name,
960 &timestamp, stripped, sizeof(stripped));
961 if (ret < 0) {
962 errno = -ret;
963 return -1;
965 if (timestamp == 0) {
966 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
969 ret = ceph_snap_gmt_convert(handle, stripped,
970 timestamp, conv, sizeof(conv));
971 if (ret < 0) {
972 errno = -ret;
973 return -1;
975 tmp = smb_fname->base_name;
976 smb_fname->base_name = conv;
978 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
979 smb_fname->base_name = tmp;
980 return ret;
983 static int ceph_snap_gmt_unlinkat(vfs_handle_struct *handle,
984 struct files_struct *dirfsp,
985 const struct smb_filename *csmb_fname,
986 int flags)
988 time_t timestamp = 0;
989 int ret;
991 ret = ceph_snap_gmt_strip_snapshot(handle,
992 csmb_fname->base_name,
993 &timestamp, NULL, 0);
994 if (ret < 0) {
995 errno = -ret;
996 return -1;
998 if (timestamp != 0) {
999 errno = EROFS;
1000 return -1;
1002 return SMB_VFS_NEXT_UNLINKAT(handle,
1003 dirfsp,
1004 csmb_fname,
1005 flags);
1008 static int ceph_snap_gmt_chmod(vfs_handle_struct *handle,
1009 const struct smb_filename *csmb_fname,
1010 mode_t mode)
1012 time_t timestamp = 0;
1013 int ret;
1015 ret = ceph_snap_gmt_strip_snapshot(handle,
1016 csmb_fname->base_name,
1017 &timestamp, NULL, 0);
1018 if (ret < 0) {
1019 errno = -ret;
1020 return -1;
1022 if (timestamp != 0) {
1023 errno = EROFS;
1024 return -1;
1026 return SMB_VFS_NEXT_CHMOD(handle, csmb_fname, mode);
1029 static int ceph_snap_gmt_chdir(vfs_handle_struct *handle,
1030 const struct smb_filename *csmb_fname)
1032 time_t timestamp = 0;
1033 char stripped[PATH_MAX + 1];
1034 char conv[PATH_MAX + 1];
1035 int ret;
1036 struct smb_filename *new_fname;
1037 int saved_errno;
1039 ret = ceph_snap_gmt_strip_snapshot(handle,
1040 csmb_fname->base_name,
1041 &timestamp, stripped, sizeof(stripped));
1042 if (ret < 0) {
1043 errno = -ret;
1044 return -1;
1046 if (timestamp == 0) {
1047 return SMB_VFS_NEXT_CHDIR(handle, csmb_fname);
1050 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1051 timestamp, conv, sizeof(conv));
1052 if (ret < 0) {
1053 errno = -ret;
1054 return -1;
1056 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1057 if (new_fname == NULL) {
1058 errno = ENOMEM;
1059 return -1;
1061 new_fname->base_name = conv;
1063 ret = SMB_VFS_NEXT_CHDIR(handle, new_fname);
1064 saved_errno = errno;
1065 TALLOC_FREE(new_fname);
1066 errno = saved_errno;
1067 return ret;
1070 static int ceph_snap_gmt_ntimes(vfs_handle_struct *handle,
1071 const struct smb_filename *csmb_fname,
1072 struct smb_file_time *ft)
1074 time_t timestamp = 0;
1075 int ret;
1077 ret = ceph_snap_gmt_strip_snapshot(handle,
1078 csmb_fname->base_name,
1079 &timestamp, NULL, 0);
1080 if (ret < 0) {
1081 errno = -ret;
1082 return -1;
1084 if (timestamp != 0) {
1085 errno = EROFS;
1086 return -1;
1088 return SMB_VFS_NEXT_NTIMES(handle, csmb_fname, ft);
1091 static int ceph_snap_gmt_readlinkat(vfs_handle_struct *handle,
1092 files_struct *dirfsp,
1093 const struct smb_filename *csmb_fname,
1094 char *buf,
1095 size_t bufsiz)
1097 time_t timestamp = 0;
1098 char stripped[PATH_MAX + 1];
1099 char conv[PATH_MAX + 1];
1100 int ret;
1101 struct smb_filename *new_fname;
1102 int saved_errno;
1104 ret = ceph_snap_gmt_strip_snapshot(handle,
1105 csmb_fname->base_name,
1106 &timestamp, stripped, sizeof(stripped));
1107 if (ret < 0) {
1108 errno = -ret;
1109 return -1;
1111 if (timestamp == 0) {
1112 return SMB_VFS_NEXT_READLINKAT(handle,
1113 dirfsp,
1114 csmb_fname,
1115 buf,
1116 bufsiz);
1118 ret = ceph_snap_gmt_convert(handle, stripped,
1119 timestamp, conv, sizeof(conv));
1120 if (ret < 0) {
1121 errno = -ret;
1122 return -1;
1124 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1125 if (new_fname == NULL) {
1126 errno = ENOMEM;
1127 return -1;
1129 new_fname->base_name = conv;
1131 ret = SMB_VFS_NEXT_READLINKAT(handle,
1132 dirfsp,
1133 new_fname,
1134 buf,
1135 bufsiz);
1136 saved_errno = errno;
1137 TALLOC_FREE(new_fname);
1138 errno = saved_errno;
1139 return ret;
1142 static int ceph_snap_gmt_mknodat(vfs_handle_struct *handle,
1143 files_struct *dirfsp,
1144 const struct smb_filename *csmb_fname,
1145 mode_t mode,
1146 SMB_DEV_T dev)
1148 time_t timestamp = 0;
1149 int ret;
1151 ret = ceph_snap_gmt_strip_snapshot(handle,
1152 csmb_fname->base_name,
1153 &timestamp, NULL, 0);
1154 if (ret < 0) {
1155 errno = -ret;
1156 return -1;
1158 if (timestamp != 0) {
1159 errno = EROFS;
1160 return -1;
1162 return SMB_VFS_NEXT_MKNODAT(handle,
1163 dirfsp,
1164 csmb_fname,
1165 mode,
1166 dev);
1169 static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1170 TALLOC_CTX *ctx,
1171 const struct smb_filename *csmb_fname)
1173 time_t timestamp = 0;
1174 char stripped[PATH_MAX + 1];
1175 char conv[PATH_MAX + 1];
1176 struct smb_filename *result_fname;
1177 int ret;
1178 struct smb_filename *new_fname;
1179 int saved_errno;
1181 ret = ceph_snap_gmt_strip_snapshot(handle,
1182 csmb_fname->base_name,
1183 &timestamp, stripped, sizeof(stripped));
1184 if (ret < 0) {
1185 errno = -ret;
1186 return NULL;
1188 if (timestamp == 0) {
1189 return SMB_VFS_NEXT_REALPATH(handle, ctx, csmb_fname);
1191 ret = ceph_snap_gmt_convert(handle, stripped,
1192 timestamp, conv, sizeof(conv));
1193 if (ret < 0) {
1194 errno = -ret;
1195 return NULL;
1197 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1198 if (new_fname == NULL) {
1199 errno = ENOMEM;
1200 return NULL;
1202 new_fname->base_name = conv;
1204 result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, new_fname);
1205 saved_errno = errno;
1206 TALLOC_FREE(new_fname);
1207 errno = saved_errno;
1208 return result_fname;
1212 * XXX this should have gone through open() conversion, so why do we need
1213 * a handler here? posix_fget_nt_acl() falls back to posix_get_nt_acl() for
1214 * dirs (or fd == -1).
1216 static NTSTATUS ceph_snap_gmt_fget_nt_acl(vfs_handle_struct *handle,
1217 struct files_struct *fsp,
1218 uint32_t security_info,
1219 TALLOC_CTX *mem_ctx,
1220 struct security_descriptor **ppdesc)
1222 time_t timestamp = 0;
1223 char stripped[PATH_MAX + 1];
1224 char conv[PATH_MAX + 1];
1225 struct smb_filename *smb_fname;
1226 int ret;
1227 NTSTATUS status;
1229 ret = ceph_snap_gmt_strip_snapshot(handle,
1230 fsp->fsp_name->base_name,
1231 &timestamp, stripped, sizeof(stripped));
1232 if (ret < 0) {
1233 return map_nt_error_from_unix(-ret);
1235 if (timestamp == 0) {
1236 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1237 mem_ctx,
1238 ppdesc);
1240 ret = ceph_snap_gmt_convert(handle, stripped,
1241 timestamp, conv, sizeof(conv));
1242 if (ret < 0) {
1243 return map_nt_error_from_unix(-ret);
1246 smb_fname = synthetic_smb_fname(mem_ctx,
1247 conv,
1248 NULL,
1249 NULL,
1250 fsp->fsp_name->flags);
1251 if (smb_fname == NULL) {
1252 return NT_STATUS_NO_MEMORY;
1255 status = SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname, security_info,
1256 mem_ctx, ppdesc);
1257 TALLOC_FREE(smb_fname);
1258 return status;
1261 static NTSTATUS ceph_snap_gmt_get_nt_acl(vfs_handle_struct *handle,
1262 const struct smb_filename *csmb_fname,
1263 uint32_t security_info,
1264 TALLOC_CTX *mem_ctx,
1265 struct security_descriptor **ppdesc)
1267 time_t timestamp = 0;
1268 char stripped[PATH_MAX + 1];
1269 char conv[PATH_MAX + 1];
1270 int ret;
1271 NTSTATUS status;
1272 struct smb_filename *new_fname;
1273 int saved_errno;
1275 ret = ceph_snap_gmt_strip_snapshot(handle,
1276 csmb_fname->base_name,
1277 &timestamp, stripped, sizeof(stripped));
1278 if (ret < 0) {
1279 return map_nt_error_from_unix(-ret);
1281 if (timestamp == 0) {
1282 return SMB_VFS_NEXT_GET_NT_ACL(handle, csmb_fname, security_info,
1283 mem_ctx, ppdesc);
1285 ret = ceph_snap_gmt_convert(handle, stripped,
1286 timestamp, conv, sizeof(conv));
1287 if (ret < 0) {
1288 return map_nt_error_from_unix(-ret);
1290 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1291 if (new_fname == NULL) {
1292 return NT_STATUS_NO_MEMORY;
1294 new_fname->base_name = conv;
1296 status = SMB_VFS_NEXT_GET_NT_ACL(handle, new_fname, security_info,
1297 mem_ctx, ppdesc);
1298 saved_errno = errno;
1299 TALLOC_FREE(new_fname);
1300 errno = saved_errno;
1301 return status;
1304 static int ceph_snap_gmt_mkdirat(vfs_handle_struct *handle,
1305 struct files_struct *dirfsp,
1306 const struct smb_filename *csmb_fname,
1307 mode_t mode)
1309 time_t timestamp = 0;
1310 int ret;
1312 ret = ceph_snap_gmt_strip_snapshot(handle,
1313 csmb_fname->base_name,
1314 &timestamp, NULL, 0);
1315 if (ret < 0) {
1316 errno = -ret;
1317 return -1;
1319 if (timestamp != 0) {
1320 errno = EROFS;
1321 return -1;
1323 return SMB_VFS_NEXT_MKDIRAT(handle,
1324 dirfsp,
1325 csmb_fname,
1326 mode);
1329 static int ceph_snap_gmt_chflags(vfs_handle_struct *handle,
1330 const struct smb_filename *csmb_fname,
1331 unsigned int flags)
1333 time_t timestamp = 0;
1334 int ret;
1336 ret = ceph_snap_gmt_strip_snapshot(handle,
1337 csmb_fname->base_name,
1338 &timestamp, NULL, 0);
1339 if (ret < 0) {
1340 errno = -ret;
1341 return -1;
1343 if (timestamp != 0) {
1344 errno = EROFS;
1345 return -1;
1347 return SMB_VFS_NEXT_CHFLAGS(handle, csmb_fname, flags);
1350 static ssize_t ceph_snap_gmt_getxattr(vfs_handle_struct *handle,
1351 const struct smb_filename *csmb_fname,
1352 const char *aname,
1353 void *value,
1354 size_t size)
1356 time_t timestamp = 0;
1357 char stripped[PATH_MAX + 1];
1358 char conv[PATH_MAX + 1];
1359 int ret;
1360 struct smb_filename *new_fname;
1361 int saved_errno;
1363 ret = ceph_snap_gmt_strip_snapshot(handle,
1364 csmb_fname->base_name,
1365 &timestamp, stripped, sizeof(stripped));
1366 if (ret < 0) {
1367 errno = -ret;
1368 return -1;
1370 if (timestamp == 0) {
1371 return SMB_VFS_NEXT_GETXATTR(handle, csmb_fname, aname, value,
1372 size);
1374 ret = ceph_snap_gmt_convert(handle, stripped,
1375 timestamp, conv, sizeof(conv));
1376 if (ret < 0) {
1377 errno = -ret;
1378 return -1;
1380 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1381 if (new_fname == NULL) {
1382 errno = ENOMEM;
1383 return -1;
1385 new_fname->base_name = conv;
1387 ret = SMB_VFS_NEXT_GETXATTR(handle, new_fname, aname, value, size);
1388 saved_errno = errno;
1389 TALLOC_FREE(new_fname);
1390 errno = saved_errno;
1391 return ret;
1394 static ssize_t ceph_snap_gmt_listxattr(struct vfs_handle_struct *handle,
1395 const struct smb_filename *csmb_fname,
1396 char *list, size_t size)
1398 time_t timestamp = 0;
1399 char stripped[PATH_MAX + 1];
1400 char conv[PATH_MAX + 1];
1401 int ret;
1402 struct smb_filename *new_fname;
1403 int saved_errno;
1405 ret = ceph_snap_gmt_strip_snapshot(handle,
1406 csmb_fname->base_name,
1407 &timestamp, stripped, sizeof(stripped));
1408 if (ret < 0) {
1409 errno = -ret;
1410 return -1;
1412 if (timestamp == 0) {
1413 return SMB_VFS_NEXT_LISTXATTR(handle, csmb_fname, list, size);
1415 ret = ceph_snap_gmt_convert(handle, stripped,
1416 timestamp, conv, sizeof(conv));
1417 if (ret < 0) {
1418 errno = -ret;
1419 return -1;
1421 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1422 if (new_fname == NULL) {
1423 errno = ENOMEM;
1424 return -1;
1426 new_fname->base_name = conv;
1428 ret = SMB_VFS_NEXT_LISTXATTR(handle, new_fname, list, size);
1429 saved_errno = errno;
1430 TALLOC_FREE(new_fname);
1431 errno = saved_errno;
1432 return ret;
1435 static int ceph_snap_gmt_removexattr(vfs_handle_struct *handle,
1436 const struct smb_filename *csmb_fname,
1437 const char *aname)
1439 time_t timestamp = 0;
1440 int ret;
1442 ret = ceph_snap_gmt_strip_snapshot(handle,
1443 csmb_fname->base_name,
1444 &timestamp, NULL, 0);
1445 if (ret < 0) {
1446 errno = -ret;
1447 return -1;
1449 if (timestamp != 0) {
1450 errno = EROFS;
1451 return -1;
1453 return SMB_VFS_NEXT_REMOVEXATTR(handle, csmb_fname, aname);
1456 static int ceph_snap_gmt_setxattr(struct vfs_handle_struct *handle,
1457 const struct smb_filename *csmb_fname,
1458 const char *aname, const void *value,
1459 size_t size, int flags)
1461 time_t timestamp = 0;
1462 int ret;
1464 ret = ceph_snap_gmt_strip_snapshot(handle,
1465 csmb_fname->base_name,
1466 &timestamp, NULL, 0);
1467 if (ret < 0) {
1468 errno = -ret;
1469 return -1;
1471 if (timestamp != 0) {
1472 errno = EROFS;
1473 return -1;
1475 return SMB_VFS_NEXT_SETXATTR(handle, csmb_fname,
1476 aname, value, size, flags);
1479 static int ceph_snap_gmt_get_real_filename(struct vfs_handle_struct *handle,
1480 const char *path,
1481 const char *name,
1482 TALLOC_CTX *mem_ctx,
1483 char **found_name)
1485 time_t timestamp = 0;
1486 char stripped[PATH_MAX + 1];
1487 char conv[PATH_MAX + 1];
1488 int ret;
1490 ret = ceph_snap_gmt_strip_snapshot(handle, path,
1491 &timestamp, stripped, sizeof(stripped));
1492 if (ret < 0) {
1493 errno = -ret;
1494 return -1;
1496 if (timestamp == 0) {
1497 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1498 mem_ctx, found_name);
1500 ret = ceph_snap_gmt_convert_dir(handle, stripped,
1501 timestamp, conv, sizeof(conv));
1502 if (ret < 0) {
1503 errno = -ret;
1504 return -1;
1506 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1507 mem_ctx, found_name);
1508 return ret;
1511 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1512 const struct smb_filename *csmb_fname,
1513 uint64_t *bsize,
1514 uint64_t *dfree,
1515 uint64_t *dsize)
1517 time_t timestamp = 0;
1518 char stripped[PATH_MAX + 1];
1519 char conv[PATH_MAX + 1];
1520 int ret;
1521 struct smb_filename *new_fname;
1522 int saved_errno;
1524 ret = ceph_snap_gmt_strip_snapshot(handle,
1525 csmb_fname->base_name,
1526 &timestamp, stripped, sizeof(stripped));
1527 if (ret < 0) {
1528 errno = -ret;
1529 return -1;
1531 if (timestamp == 0) {
1532 return SMB_VFS_NEXT_DISK_FREE(handle, csmb_fname,
1533 bsize, dfree, dsize);
1535 ret = ceph_snap_gmt_convert(handle, stripped,
1536 timestamp, conv, sizeof(conv));
1537 if (ret < 0) {
1538 errno = -ret;
1539 return -1;
1541 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1542 if (new_fname == NULL) {
1543 errno = ENOMEM;
1544 return -1;
1546 new_fname->base_name = conv;
1548 ret = SMB_VFS_NEXT_DISK_FREE(handle, new_fname,
1549 bsize, dfree, dsize);
1550 saved_errno = errno;
1551 TALLOC_FREE(new_fname);
1552 errno = saved_errno;
1553 return ret;
1556 static int ceph_snap_gmt_get_quota(vfs_handle_struct *handle,
1557 const struct smb_filename *csmb_fname,
1558 enum SMB_QUOTA_TYPE qtype,
1559 unid_t id,
1560 SMB_DISK_QUOTA *dq)
1562 time_t timestamp = 0;
1563 char stripped[PATH_MAX + 1];
1564 char conv[PATH_MAX + 1];
1565 int ret;
1566 struct smb_filename *new_fname;
1567 int saved_errno;
1569 ret = ceph_snap_gmt_strip_snapshot(handle,
1570 csmb_fname->base_name,
1571 &timestamp, stripped, sizeof(stripped));
1572 if (ret < 0) {
1573 errno = -ret;
1574 return -1;
1576 if (timestamp == 0) {
1577 return SMB_VFS_NEXT_GET_QUOTA(handle, csmb_fname, qtype, id, dq);
1579 ret = ceph_snap_gmt_convert(handle, stripped,
1580 timestamp, conv, sizeof(conv));
1581 if (ret < 0) {
1582 errno = -ret;
1583 return -1;
1585 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1586 if (new_fname == NULL) {
1587 errno = ENOMEM;
1588 return -1;
1590 new_fname->base_name = conv;
1592 ret = SMB_VFS_NEXT_GET_QUOTA(handle, new_fname, qtype, id, dq);
1593 saved_errno = errno;
1594 TALLOC_FREE(new_fname);
1595 errno = saved_errno;
1596 return ret;
1599 static struct vfs_fn_pointers ceph_snap_fns = {
1600 .get_shadow_copy_data_fn = ceph_snap_get_shadow_copy_data,
1601 .opendir_fn = ceph_snap_gmt_opendir,
1602 .disk_free_fn = ceph_snap_gmt_disk_free,
1603 .get_quota_fn = ceph_snap_gmt_get_quota,
1604 .renameat_fn = ceph_snap_gmt_renameat,
1605 .linkat_fn = ceph_snap_gmt_linkat,
1606 .symlinkat_fn = ceph_snap_gmt_symlinkat,
1607 .stat_fn = ceph_snap_gmt_stat,
1608 .lstat_fn = ceph_snap_gmt_lstat,
1609 .open_fn = ceph_snap_gmt_open,
1610 .unlinkat_fn = ceph_snap_gmt_unlinkat,
1611 .chmod_fn = ceph_snap_gmt_chmod,
1612 .chdir_fn = ceph_snap_gmt_chdir,
1613 .ntimes_fn = ceph_snap_gmt_ntimes,
1614 .readlinkat_fn = ceph_snap_gmt_readlinkat,
1615 .mknodat_fn = ceph_snap_gmt_mknodat,
1616 .realpath_fn = ceph_snap_gmt_realpath,
1617 .get_nt_acl_fn = ceph_snap_gmt_get_nt_acl,
1618 .fget_nt_acl_fn = ceph_snap_gmt_fget_nt_acl,
1619 .get_nt_acl_fn = ceph_snap_gmt_get_nt_acl,
1620 .mkdirat_fn = ceph_snap_gmt_mkdirat,
1621 .getxattr_fn = ceph_snap_gmt_getxattr,
1622 .getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
1623 .getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
1624 .listxattr_fn = ceph_snap_gmt_listxattr,
1625 .removexattr_fn = ceph_snap_gmt_removexattr,
1626 .setxattr_fn = ceph_snap_gmt_setxattr,
1627 .chflags_fn = ceph_snap_gmt_chflags,
1628 .get_real_filename_fn = ceph_snap_gmt_get_real_filename,
1631 static_decl_vfs;
1632 NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1634 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1635 "ceph_snapshots", &ceph_snap_fns);