winexe: Use C99 initializer for poptOption in winexe.c
[Samba.git] / source3 / modules / vfs_ceph_snapshots.c
blob4183069a5c2f557fa9164c95f89d11653ddb2154
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 = strtoull_err(snap_btime, &endptr, 10, &err);
87 if (err != 0) {
88 return -err;
90 if ((endptr == snap_btime) || (*endptr != '\0')) {
91 DBG_ERR("couldn't process snap.tv_sec in %s\n", snap_btime);
92 return -EINVAL;
95 /* second component is nsecs */
96 s++;
97 snap_timespec.tv_nsec = strtoul_err(s, &endptr, 10, &err);
98 if (err != 0) {
99 return -err;
101 if ((endptr == s) || (*endptr != '\0')) {
102 DBG_ERR("couldn't process snap.tv_nsec in %s\n", s);
103 return -EINVAL;
107 * >> 30 is a rough divide by ~10**9. No need to be exact, as @GMT
108 * tokens only offer 1-second resolution (while twrp is nsec).
110 *_snap_secs = snap_timespec.tv_sec + (snap_timespec.tv_nsec >> 30);
112 return 0;
116 * XXX Ceph snapshots can be created with sub-second granularity, which means
117 * that multiple snapshots may be mapped to the same @GMT- label.
119 * @this_label is a pre-zeroed buffer to be filled with a @GMT label
120 * @return 0 if label successfully filled or -errno on error.
122 static int ceph_snap_fill_label(struct vfs_handle_struct *handle,
123 TALLOC_CTX *tmp_ctx,
124 const char *parent_snapsdir,
125 const char *subdir,
126 SHADOW_COPY_LABEL this_label)
128 struct smb_filename *smb_fname;
129 time_t snap_secs;
130 struct tm gmt_snap_time;
131 struct tm *tm_ret;
132 size_t str_sz;
133 char snap_path[PATH_MAX + 1];
134 int ret;
137 * CephFS snapshot creation times are available via a special
138 * xattr - snapshot b/m/ctimes all match the snap source.
140 ret = snprintf(snap_path, sizeof(snap_path), "%s/%s",
141 parent_snapsdir, subdir);
142 if (ret >= sizeof(snap_path)) {
143 return -EINVAL;
146 smb_fname = synthetic_smb_fname(tmp_ctx, snap_path,
147 NULL, NULL, 0);
148 if (smb_fname == NULL) {
149 return -ENOMEM;
152 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
153 if (ret < 0) {
154 return ret;
157 tm_ret = gmtime_r(&snap_secs, &gmt_snap_time);
158 if (tm_ret == NULL) {
159 return -EINVAL;
161 str_sz = strftime(this_label, sizeof(SHADOW_COPY_LABEL),
162 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
163 if (str_sz == 0) {
164 DBG_ERR("failed to convert tm to @GMT token\n");
165 return -EINVAL;
168 DBG_DEBUG("mapped snapshot at %s to enum snaps label %s\n",
169 snap_path, this_label);
171 return 0;
174 static int ceph_snap_enum_snapdir(struct vfs_handle_struct *handle,
175 struct smb_filename *snaps_dname,
176 bool labels,
177 struct shadow_copy_data *sc_data)
179 NTSTATUS status;
180 int ret;
181 DIR *d = NULL;
182 struct dirent *e = NULL;
183 uint32_t slots;
185 status = smbd_check_access_rights(handle->conn,
186 snaps_dname,
187 false,
188 SEC_DIR_LIST);
189 if (!NT_STATUS_IS_OK(status)) {
190 DEBUG(0,("user does not have list permission "
191 "on snapdir %s\n",
192 snaps_dname->base_name));
193 ret = -map_errno_from_nt_status(status);
194 goto err_out;
197 DBG_DEBUG("enumerating shadow copy dir at %s\n",
198 snaps_dname->base_name);
201 * CephFS stat(dir).size *normally* returns the number of child entries
202 * for a given dir, but it unfortunately that's not the case for the one
203 * place we need it (dir=.snap), so we need to dynamically determine it
204 * via readdir.
206 d = SMB_VFS_NEXT_OPENDIR(handle, snaps_dname, NULL, 0);
207 if (d == NULL) {
208 ret = -errno;
209 goto err_out;
212 slots = 0;
213 sc_data->num_volumes = 0;
214 sc_data->labels = NULL;
216 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
217 e != NULL;
218 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
219 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
220 continue;
222 sc_data->num_volumes++;
223 if (!labels) {
224 continue;
226 if (sc_data->num_volumes > slots) {
227 uint32_t new_slot_count = slots + 10;
228 SMB_ASSERT(new_slot_count > slots);
229 sc_data->labels = talloc_realloc(sc_data,
230 sc_data->labels,
231 SHADOW_COPY_LABEL,
232 new_slot_count);
233 if (sc_data->labels == NULL) {
234 ret = -ENOMEM;
235 goto err_closedir;
237 memset(sc_data->labels[slots], 0,
238 sizeof(SHADOW_COPY_LABEL) * 10);
240 DBG_DEBUG("%d->%d slots for enum_snaps response\n",
241 slots, new_slot_count);
242 slots = new_slot_count;
244 DBG_DEBUG("filling shadow copy label for %s/%s\n",
245 snaps_dname->base_name, e->d_name);
246 ret = ceph_snap_fill_label(handle, snaps_dname,
247 snaps_dname->base_name, e->d_name,
248 sc_data->labels[sc_data->num_volumes - 1]);
249 if (ret < 0) {
250 goto err_closedir;
254 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
255 if (ret != 0) {
256 ret = -errno;
257 goto err_out;
260 DBG_DEBUG("%s shadow copy enumeration found %d labels \n",
261 snaps_dname->base_name, sc_data->num_volumes);
263 return 0;
265 err_closedir:
266 SMB_VFS_NEXT_CLOSEDIR(handle, d);
267 err_out:
268 TALLOC_FREE(sc_data->labels);
269 return ret;
273 * Prior reading: The Meaning of Path Names
274 * https://wiki.samba.org/index.php/Writing_a_Samba_VFS_Module
276 * translate paths so that we can use the parent dir for .snap access:
277 * myfile -> parent= trimmed=myfile
278 * /a -> parent=/ trimmed=a
279 * dir/sub/file -> parent=dir/sub trimmed=file
280 * /dir/sub -> parent=/dir/ trimmed=sub
282 static int ceph_snap_get_parent_path(const char *connectpath,
283 const char *path,
284 char *_parent_buf,
285 size_t buflen,
286 const char **_trimmed)
288 const char *p;
289 size_t len;
290 int ret;
292 if (!strcmp(path, "/")) {
293 DBG_ERR("can't go past root for %s .snap dir\n", path);
294 return -EINVAL;
297 p = strrchr_m(path, '/'); /* Find final '/', if any */
298 if (p == NULL) {
299 DBG_DEBUG("parent .snap dir for %s is cwd\n", path);
300 ret = strlcpy(_parent_buf, "", buflen);
301 if (ret >= buflen) {
302 return -EINVAL;
304 if (_trimmed != NULL) {
305 *_trimmed = path;
307 return 0;
310 SMB_ASSERT(p >= path);
311 len = p - path;
313 ret = snprintf(_parent_buf, buflen, "%.*s", (int)len, path);
314 if (ret >= buflen) {
315 return -EINVAL;
318 /* for absolute paths, check that we're not going outside the share */
319 if ((len > 0) && (_parent_buf[0] == '/')) {
320 bool connectpath_match = false;
321 size_t clen = strlen(connectpath);
322 DBG_DEBUG("checking absolute path %s lies within share at %s\n",
323 _parent_buf, connectpath);
324 /* need to check for separator, to avoid /x/abcd vs /x/ab */
325 connectpath_match = (strncmp(connectpath,
326 _parent_buf,
327 clen) == 0);
328 if (!connectpath_match
329 || ((_parent_buf[clen] != '/') && (_parent_buf[clen] != '\0'))) {
330 DBG_ERR("%s parent path is outside of share at %s\n",
331 _parent_buf, connectpath);
332 return -EINVAL;
336 if (_trimmed != NULL) {
338 * point to path component which was trimmed from _parent_buf
339 * excluding path separator.
341 *_trimmed = p + 1;
344 DBG_DEBUG("generated parent .snap path for %s as %s (trimmed \"%s\")\n",
345 path, _parent_buf, p + 1);
347 return 0;
350 static int ceph_snap_get_shadow_copy_data(struct vfs_handle_struct *handle,
351 struct files_struct *fsp,
352 struct shadow_copy_data *sc_data,
353 bool labels)
355 int ret;
356 TALLOC_CTX *tmp_ctx;
357 const char *parent_dir = NULL;
358 char tmp[PATH_MAX + 1];
359 char snaps_path[PATH_MAX + 1];
360 struct smb_filename *snaps_dname = NULL;
361 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
362 "ceph", "snapdir",
363 CEPH_SNAP_SUBDIR_DEFAULT);
365 DBG_DEBUG("getting shadow copy data for %s\n",
366 fsp->fsp_name->base_name);
368 tmp_ctx = talloc_new(fsp);
369 if (tmp_ctx == NULL) {
370 ret = -ENOMEM;
371 goto err_out;
374 if (sc_data == NULL) {
375 ret = -EINVAL;
376 goto err_out;
379 if (fsp->is_directory) {
380 parent_dir = fsp->fsp_name->base_name;
381 } else {
382 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
383 fsp->fsp_name->base_name,
384 tmp,
385 sizeof(tmp),
386 NULL); /* trimmed */
387 if (ret < 0) {
388 goto err_out;
390 parent_dir = tmp;
393 ret = snprintf(snaps_path, sizeof(snaps_path), "%s/%s",
394 parent_dir, snapdir);
395 if (ret >= sizeof(snaps_path)) {
396 ret = -EINVAL;
397 goto err_out;
400 snaps_dname = synthetic_smb_fname(tmp_ctx,
401 snaps_path,
402 NULL,
403 NULL,
404 fsp->fsp_name->flags);
405 if (snaps_dname == NULL) {
406 ret = -ENOMEM;
407 goto err_out;
410 ret = ceph_snap_enum_snapdir(handle, snaps_dname, labels, sc_data);
411 if (ret < 0) {
412 goto err_out;
415 talloc_free(tmp_ctx);
416 return 0;
418 err_out:
419 talloc_free(tmp_ctx);
420 errno = -ret;
421 return -1;
424 static bool ceph_snap_gmt_strip_snapshot(struct vfs_handle_struct *handle,
425 const char *name,
426 time_t *_timestamp,
427 char *_stripped_buf,
428 size_t buflen)
430 struct tm tm;
431 time_t timestamp;
432 const char *p;
433 char *q;
434 size_t rest_len, dst_len;
435 ptrdiff_t len_before_gmt;
437 p = strstr_m(name, "@GMT-");
438 if (p == NULL) {
439 goto no_snapshot;
441 if ((p > name) && (p[-1] != '/')) {
442 goto no_snapshot;
444 len_before_gmt = p - name;
445 q = strptime(p, GMT_FORMAT, &tm);
446 if (q == NULL) {
447 goto no_snapshot;
449 tm.tm_isdst = -1;
450 timestamp = timegm(&tm);
451 if (timestamp == (time_t)-1) {
452 goto no_snapshot;
454 if (q[0] == '\0') {
456 * The name consists of only the GMT token or the GMT
457 * token is at the end of the path.
459 if (_stripped_buf != NULL) {
460 if (len_before_gmt >= buflen) {
461 return -EINVAL;
463 if (len_before_gmt > 0) {
465 * There is a slash before the @GMT-. Remove it
466 * and copy the result.
468 len_before_gmt -= 1;
469 strlcpy(_stripped_buf, name, len_before_gmt);
470 } else {
471 _stripped_buf[0] = '\0'; /* token only */
473 DBG_DEBUG("GMT token in %s stripped to %s\n",
474 name, _stripped_buf);
476 *_timestamp = timestamp;
477 return 0;
479 if (q[0] != '/') {
481 * It is not a complete path component, i.e. the path
482 * component continues after the gmt-token.
484 goto no_snapshot;
486 q += 1;
488 rest_len = strlen(q);
489 dst_len = len_before_gmt + rest_len;
490 SMB_ASSERT(dst_len >= rest_len);
492 if (_stripped_buf != NULL) {
493 if (dst_len >= buflen) {
494 return -EINVAL;
496 if (p > name) {
497 memcpy(_stripped_buf, name, len_before_gmt);
499 if (rest_len > 0) {
500 memcpy(_stripped_buf + len_before_gmt, q, rest_len);
502 _stripped_buf[dst_len] = '\0';
504 *_timestamp = timestamp;
505 DBG_DEBUG("GMT token in %s stripped to %s\n", name, _stripped_buf);
506 return 0;
507 no_snapshot:
508 *_timestamp = 0;
509 return 0;
512 static int ceph_snap_gmt_convert_dir(struct vfs_handle_struct *handle,
513 const char *name,
514 time_t timestamp,
515 char *_converted_buf,
516 size_t buflen)
518 int ret;
519 NTSTATUS status;
520 DIR *d = NULL;
521 struct dirent *e = NULL;
522 struct smb_filename *snaps_dname = NULL;
523 const char *snapdir = lp_parm_const_string(SNUM(handle->conn),
524 "ceph", "snapdir",
525 CEPH_SNAP_SUBDIR_DEFAULT);
526 TALLOC_CTX *tmp_ctx = talloc_new(NULL);
528 if (tmp_ctx == NULL) {
529 ret = -ENOMEM;
530 goto err_out;
534 * Temporally use the caller's return buffer for this.
536 ret = snprintf(_converted_buf, buflen, "%s/%s", name, snapdir);
537 if (ret >= buflen) {
538 ret = -EINVAL;
539 goto err_out;
542 snaps_dname = synthetic_smb_fname(tmp_ctx,
543 _converted_buf,
544 NULL,
545 NULL,
546 0); /* XXX check? */
547 if (snaps_dname == NULL) {
548 ret = -ENOMEM;
549 goto err_out;
552 /* stat first to trigger error fallback in ceph_snap_gmt_convert() */
553 ret = SMB_VFS_NEXT_STAT(handle, snaps_dname);
554 if (ret < 0) {
555 ret = -errno;
556 goto err_out;
559 status = smbd_check_access_rights(handle->conn,
560 snaps_dname,
561 false,
562 SEC_DIR_LIST);
563 if (!NT_STATUS_IS_OK(status)) {
564 DEBUG(0,("user does not have list permission "
565 "on snapdir %s\n",
566 snaps_dname->base_name));
567 ret = -map_errno_from_nt_status(status);
568 goto err_out;
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);
575 if (d == NULL) {
576 ret = -errno;
577 goto err_out;
580 for (e = SMB_VFS_NEXT_READDIR(handle, d, NULL);
581 e != NULL;
582 e = SMB_VFS_NEXT_READDIR(handle, d, NULL)) {
583 struct smb_filename *smb_fname;
584 time_t snap_secs;
586 if (ISDOT(e->d_name) || ISDOTDOT(e->d_name)) {
587 continue;
590 ret = snprintf(_converted_buf, buflen, "%s/%s",
591 snaps_dname->base_name, e->d_name);
592 if (ret >= buflen) {
593 ret = -EINVAL;
594 goto err_closedir;
597 smb_fname = synthetic_smb_fname(tmp_ctx, _converted_buf,
598 NULL, NULL, 0);
599 if (smb_fname == NULL) {
600 ret = -ENOMEM;
601 goto err_closedir;
604 ret = ceph_snap_get_btime(handle, smb_fname, &snap_secs);
605 if (ret < 0) {
606 goto err_closedir;
610 * check gmt_snap_time matches @timestamp
612 if (timestamp == snap_secs) {
613 break;
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);
620 if (e == NULL) {
621 DBG_INFO("[connectpath %s] failed to find %s @ time %lld\n",
622 handle->conn->connectpath, name, (long long)timestamp);
623 ret = -ENOENT;
624 goto err_closedir;
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,
630 _converted_buf);
632 ret = SMB_VFS_NEXT_CLOSEDIR(handle, d);
633 if (ret != 0) {
634 ret = -errno;
635 goto err_out;
637 talloc_free(tmp_ctx);
638 return 0;
640 err_closedir:
641 SMB_VFS_NEXT_CLOSEDIR(handle, d);
642 err_out:
643 talloc_free(tmp_ctx);
644 return ret;
647 static int ceph_snap_gmt_convert(struct vfs_handle_struct *handle,
648 const char *name,
649 time_t timestamp,
650 char *_converted_buf,
651 size_t buflen)
653 int ret;
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,
666 name,
667 timestamp,
668 _converted_buf,
669 buflen);
670 if (ret >= 0) {
671 /* all done: .snap subdir exists - @name is a dir */
672 DBG_DEBUG("%s is a dir, accessing snaps via .snap\n", name);
673 return ret;
676 /* @name/.snap access failed, attempt snapshot access via parent */
677 DBG_DEBUG("%s/.snap access failed, attempting parent access\n",
678 name);
680 ret = ceph_snap_get_parent_path(handle->conn->connectpath,
681 name,
682 parent,
683 sizeof(parent),
684 &trimmed);
685 if (ret < 0) {
686 return ret;
689 ret = ceph_snap_gmt_convert_dir(handle,
690 parent,
691 timestamp,
692 _converted_buf,
693 buflen);
694 if (ret < 0) {
695 return ret;
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) {
703 return -EINVAL;
705 strlcat(_converted_buf, "/", buflen);
706 strlcat(_converted_buf, trimmed, buflen);
708 return 0;
711 static DIR *ceph_snap_gmt_opendir(vfs_handle_struct *handle,
712 const struct smb_filename *csmb_fname,
713 const char *mask,
714 uint32_t attr)
716 time_t timestamp = 0;
717 char stripped[PATH_MAX + 1];
718 int ret;
719 DIR *dir;
720 int saved_errno;
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,
726 &timestamp,
727 stripped, sizeof(stripped));
728 if (ret < 0) {
729 errno = -ret;
730 return NULL;
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));
737 if (ret < 0) {
738 errno = -ret;
739 return NULL;
741 conv_smb_fname = synthetic_smb_fname(talloc_tos(),
742 conv,
743 NULL,
744 NULL,
745 csmb_fname->flags);
746 if (conv_smb_fname == NULL) {
747 errno = ENOMEM;
748 return NULL;
751 dir = SMB_VFS_NEXT_OPENDIR(handle, conv_smb_fname, mask, attr);
752 saved_errno = errno;
753 TALLOC_FREE(conv_smb_fname);
754 errno = saved_errno;
755 return dir;
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)
762 int ret;
763 time_t timestamp_src, timestamp_dst;
765 ret = ceph_snap_gmt_strip_snapshot(handle,
766 smb_fname_src->base_name,
767 &timestamp_src, NULL, 0);
768 if (ret < 0) {
769 errno = -ret;
770 return -1;
772 ret = ceph_snap_gmt_strip_snapshot(handle,
773 smb_fname_dst->base_name,
774 &timestamp_dst, NULL, 0);
775 if (ret < 0) {
776 errno = -ret;
777 return -1;
779 if (timestamp_src != 0) {
780 errno = EXDEV;
781 return -1;
783 if (timestamp_dst != 0) {
784 errno = EROFS;
785 return -1;
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)
795 int ret;
796 time_t timestamp_old = 0;
797 time_t timestamp_new = 0;
799 ret = ceph_snap_gmt_strip_snapshot(handle,
800 link_contents,
801 &timestamp_old,
802 NULL, 0);
803 if (ret < 0) {
804 errno = -ret;
805 return -1;
807 ret = ceph_snap_gmt_strip_snapshot(handle,
808 new_smb_fname->base_name,
809 &timestamp_new,
810 NULL, 0);
811 if (ret < 0) {
812 errno = -ret;
813 return -1;
815 if ((timestamp_old != 0) || (timestamp_new != 0)) {
816 errno = EROFS;
817 return -1;
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)
826 int ret;
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,
832 &timestamp_old,
833 NULL, 0);
834 if (ret < 0) {
835 errno = -ret;
836 return -1;
838 ret = ceph_snap_gmt_strip_snapshot(handle,
839 new_smb_fname->base_name,
840 &timestamp_new,
841 NULL, 0);
842 if (ret < 0) {
843 errno = -ret;
844 return -1;
846 if ((timestamp_old != 0) || (timestamp_new != 0)) {
847 errno = EROFS;
848 return -1;
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];
859 char *tmp;
860 int ret;
862 ret = ceph_snap_gmt_strip_snapshot(handle,
863 smb_fname->base_name,
864 &timestamp, stripped, sizeof(stripped));
865 if (ret < 0) {
866 errno = -ret;
867 return -1;
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));
875 if (ret < 0) {
876 errno = -ret;
877 return -1;
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;
884 return ret;
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];
893 char *tmp;
894 int ret;
896 ret = ceph_snap_gmt_strip_snapshot(handle,
897 smb_fname->base_name,
898 &timestamp, stripped, sizeof(stripped));
899 if (ret < 0) {
900 errno = -ret;
901 return -1;
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));
909 if (ret < 0) {
910 errno = -ret;
911 return -1;
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;
918 return ret;
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];
928 char *tmp;
929 int ret;
931 ret = ceph_snap_gmt_strip_snapshot(handle,
932 smb_fname->base_name,
933 &timestamp, stripped, sizeof(stripped));
934 if (ret < 0) {
935 errno = -ret;
936 return -1;
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));
944 if (ret < 0) {
945 errno = -ret;
946 return -1;
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;
953 return ret;
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];
962 int ret;
963 struct smb_filename *new_fname;
964 int saved_errno;
966 ret = ceph_snap_gmt_strip_snapshot(handle,
967 csmb_fname->base_name,
968 &timestamp, stripped, sizeof(stripped));
969 if (ret < 0) {
970 errno = -ret;
971 return -1;
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));
979 if (ret < 0) {
980 errno = -ret;
981 return -1;
983 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
984 if (new_fname == NULL) {
985 errno = ENOMEM;
986 return -1;
988 new_fname->base_name = conv;
990 ret = SMB_VFS_NEXT_UNLINK(handle, new_fname);
991 saved_errno = errno;
992 TALLOC_FREE(new_fname);
993 errno = saved_errno;
994 return ret;
997 static int ceph_snap_gmt_chmod(vfs_handle_struct *handle,
998 const struct smb_filename *csmb_fname,
999 mode_t mode)
1001 time_t timestamp = 0;
1002 char stripped[PATH_MAX + 1];
1003 char conv[PATH_MAX + 1];
1004 int ret;
1005 struct smb_filename *new_fname;
1006 int saved_errno;
1008 ret = ceph_snap_gmt_strip_snapshot(handle,
1009 csmb_fname->base_name,
1010 &timestamp, stripped, sizeof(stripped));
1011 if (ret < 0) {
1012 errno = -ret;
1013 return -1;
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));
1021 if (ret < 0) {
1022 errno = -ret;
1023 return -1;
1025 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1026 if (new_fname == NULL) {
1027 errno = ENOMEM;
1028 return -1;
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;
1036 return ret;
1039 static int ceph_snap_gmt_chown(vfs_handle_struct *handle,
1040 const struct smb_filename *csmb_fname,
1041 uid_t uid,
1042 gid_t gid)
1044 time_t timestamp = 0;
1045 char stripped[PATH_MAX + 1];
1046 char conv[PATH_MAX + 1];
1047 int ret;
1048 struct smb_filename *new_fname;
1049 int saved_errno;
1051 ret = ceph_snap_gmt_strip_snapshot(handle,
1052 csmb_fname->base_name,
1053 &timestamp, stripped, sizeof(stripped));
1054 if (ret < 0) {
1055 errno = -ret;
1056 return -1;
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));
1064 if (ret < 0) {
1065 errno = -ret;
1066 return -1;
1068 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1069 if (new_fname == NULL) {
1070 errno = ENOMEM;
1071 return -1;
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;
1079 return ret;
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];
1088 int ret;
1089 struct smb_filename *new_fname;
1090 int saved_errno;
1092 ret = ceph_snap_gmt_strip_snapshot(handle,
1093 csmb_fname->base_name,
1094 &timestamp, stripped, sizeof(stripped));
1095 if (ret < 0) {
1096 errno = -ret;
1097 return -1;
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));
1105 if (ret < 0) {
1106 errno = -ret;
1107 return -1;
1109 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1110 if (new_fname == NULL) {
1111 errno = ENOMEM;
1112 return -1;
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;
1120 return ret;
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];
1130 int ret;
1131 struct smb_filename *new_fname;
1132 int saved_errno;
1134 ret = ceph_snap_gmt_strip_snapshot(handle,
1135 csmb_fname->base_name,
1136 &timestamp, stripped, sizeof(stripped));
1137 if (ret < 0) {
1138 errno = -ret;
1139 return -1;
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));
1147 if (ret < 0) {
1148 errno = -ret;
1149 return -1;
1151 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1152 if (new_fname == NULL) {
1153 errno = ENOMEM;
1154 return -1;
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;
1162 return ret;
1165 static int ceph_snap_gmt_readlink(vfs_handle_struct *handle,
1166 const struct smb_filename *csmb_fname,
1167 char *buf,
1168 size_t bufsiz)
1170 time_t timestamp = 0;
1171 char stripped[PATH_MAX + 1];
1172 char conv[PATH_MAX + 1];
1173 int ret;
1174 struct smb_filename *new_fname;
1175 int saved_errno;
1177 ret = ceph_snap_gmt_strip_snapshot(handle,
1178 csmb_fname->base_name,
1179 &timestamp, stripped, sizeof(stripped));
1180 if (ret < 0) {
1181 errno = -ret;
1182 return -1;
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));
1189 if (ret < 0) {
1190 errno = -ret;
1191 return -1;
1193 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1194 if (new_fname == NULL) {
1195 errno = ENOMEM;
1196 return -1;
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;
1204 return ret;
1207 static int ceph_snap_gmt_mknod(vfs_handle_struct *handle,
1208 const struct smb_filename *csmb_fname,
1209 mode_t mode,
1210 SMB_DEV_T dev)
1212 time_t timestamp = 0;
1213 char stripped[PATH_MAX + 1];
1214 char conv[PATH_MAX + 1];
1215 int ret;
1216 struct smb_filename *new_fname;
1217 int saved_errno;
1219 ret = ceph_snap_gmt_strip_snapshot(handle,
1220 csmb_fname->base_name,
1221 &timestamp, stripped, sizeof(stripped));
1222 if (ret < 0) {
1223 errno = -ret;
1224 return -1;
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));
1231 if (ret < 0) {
1232 errno = -ret;
1233 return -1;
1235 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1236 if (new_fname == NULL) {
1237 errno = ENOMEM;
1238 return -1;
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;
1246 return ret;
1249 static struct smb_filename *ceph_snap_gmt_realpath(vfs_handle_struct *handle,
1250 TALLOC_CTX *ctx,
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;
1257 int ret;
1258 struct smb_filename *new_fname;
1259 int saved_errno;
1261 ret = ceph_snap_gmt_strip_snapshot(handle,
1262 csmb_fname->base_name,
1263 &timestamp, stripped, sizeof(stripped));
1264 if (ret < 0) {
1265 errno = -ret;
1266 return NULL;
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));
1273 if (ret < 0) {
1274 errno = -ret;
1275 return NULL;
1277 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1278 if (new_fname == NULL) {
1279 errno = ENOMEM;
1280 return 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;
1306 int ret;
1307 NTSTATUS status;
1309 ret = ceph_snap_gmt_strip_snapshot(handle,
1310 fsp->fsp_name->base_name,
1311 &timestamp, stripped, sizeof(stripped));
1312 if (ret < 0) {
1313 return map_nt_error_from_unix(-ret);
1315 if (timestamp == 0) {
1316 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1317 mem_ctx,
1318 ppdesc);
1320 ret = ceph_snap_gmt_convert(handle, stripped,
1321 timestamp, conv, sizeof(conv));
1322 if (ret < 0) {
1323 return map_nt_error_from_unix(-ret);
1326 smb_fname = synthetic_smb_fname(mem_ctx,
1327 conv,
1328 NULL,
1329 NULL,
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,
1336 mem_ctx, ppdesc);
1337 TALLOC_FREE(smb_fname);
1338 return status;
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];
1350 int ret;
1351 NTSTATUS status;
1352 struct smb_filename *new_fname;
1353 int saved_errno;
1355 ret = ceph_snap_gmt_strip_snapshot(handle,
1356 csmb_fname->base_name,
1357 &timestamp, stripped, sizeof(stripped));
1358 if (ret < 0) {
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,
1363 mem_ctx, ppdesc);
1365 ret = ceph_snap_gmt_convert(handle, stripped,
1366 timestamp, conv, sizeof(conv));
1367 if (ret < 0) {
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,
1377 mem_ctx, ppdesc);
1378 saved_errno = errno;
1379 TALLOC_FREE(new_fname);
1380 errno = saved_errno;
1381 return status;
1384 static int ceph_snap_gmt_mkdir(vfs_handle_struct *handle,
1385 const struct smb_filename *csmb_fname,
1386 mode_t mode)
1388 time_t timestamp = 0;
1389 char stripped[PATH_MAX + 1];
1390 char conv[PATH_MAX + 1];
1391 int ret;
1392 struct smb_filename *new_fname;
1393 int saved_errno;
1395 ret = ceph_snap_gmt_strip_snapshot(handle,
1396 csmb_fname->base_name,
1397 &timestamp, stripped, sizeof(stripped));
1398 if (ret < 0) {
1399 errno = -ret;
1400 return -1;
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));
1407 if (ret < 0) {
1408 errno = -ret;
1409 return -1;
1411 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1412 if (new_fname == NULL) {
1413 errno = ENOMEM;
1414 return -1;
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;
1422 return ret;
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];
1431 int ret;
1432 struct smb_filename *new_fname;
1433 int saved_errno;
1435 ret = ceph_snap_gmt_strip_snapshot(handle,
1436 csmb_fname->base_name,
1437 &timestamp, stripped, sizeof(stripped));
1438 if (ret < 0) {
1439 errno = -ret;
1440 return -1;
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));
1447 if (ret < 0) {
1448 errno = -ret;
1449 return -1;
1451 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1452 if (new_fname == NULL) {
1453 errno = ENOMEM;
1454 return -1;
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;
1462 return ret;
1465 static int ceph_snap_gmt_chflags(vfs_handle_struct *handle,
1466 const struct smb_filename *csmb_fname,
1467 unsigned int flags)
1469 time_t timestamp = 0;
1470 char stripped[PATH_MAX + 1];
1471 char conv[PATH_MAX + 1];
1472 int ret;
1473 struct smb_filename *new_fname;
1474 int saved_errno;
1476 ret = ceph_snap_gmt_strip_snapshot(handle,
1477 csmb_fname->base_name,
1478 &timestamp, stripped, sizeof(stripped));
1479 if (ret < 0) {
1480 errno = -ret;
1481 return -1;
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));
1488 if (ret < 0) {
1489 errno = -ret;
1490 return -1;
1492 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1493 if (new_fname == NULL) {
1494 errno = ENOMEM;
1495 return -1;
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;
1503 return ret;
1506 static ssize_t ceph_snap_gmt_getxattr(vfs_handle_struct *handle,
1507 const struct smb_filename *csmb_fname,
1508 const char *aname,
1509 void *value,
1510 size_t size)
1512 time_t timestamp = 0;
1513 char stripped[PATH_MAX + 1];
1514 char conv[PATH_MAX + 1];
1515 int ret;
1516 struct smb_filename *new_fname;
1517 int saved_errno;
1519 ret = ceph_snap_gmt_strip_snapshot(handle,
1520 csmb_fname->base_name,
1521 &timestamp, stripped, sizeof(stripped));
1522 if (ret < 0) {
1523 errno = -ret;
1524 return -1;
1526 if (timestamp == 0) {
1527 return SMB_VFS_NEXT_GETXATTR(handle, csmb_fname, aname, value,
1528 size);
1530 ret = ceph_snap_gmt_convert(handle, stripped,
1531 timestamp, conv, sizeof(conv));
1532 if (ret < 0) {
1533 errno = -ret;
1534 return -1;
1536 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1537 if (new_fname == NULL) {
1538 errno = ENOMEM;
1539 return -1;
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;
1547 return ret;
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];
1557 int ret;
1558 struct smb_filename *new_fname;
1559 int saved_errno;
1561 ret = ceph_snap_gmt_strip_snapshot(handle,
1562 csmb_fname->base_name,
1563 &timestamp, stripped, sizeof(stripped));
1564 if (ret < 0) {
1565 errno = -ret;
1566 return -1;
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));
1573 if (ret < 0) {
1574 errno = -ret;
1575 return -1;
1577 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1578 if (new_fname == NULL) {
1579 errno = ENOMEM;
1580 return -1;
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;
1588 return ret;
1591 static int ceph_snap_gmt_removexattr(vfs_handle_struct *handle,
1592 const struct smb_filename *csmb_fname,
1593 const char *aname)
1595 time_t timestamp = 0;
1596 char stripped[PATH_MAX + 1];
1597 char conv[PATH_MAX + 1];
1598 int ret;
1599 struct smb_filename *new_fname;
1600 int saved_errno;
1602 ret = ceph_snap_gmt_strip_snapshot(handle,
1603 csmb_fname->base_name,
1604 &timestamp, stripped, sizeof(stripped));
1605 if (ret < 0) {
1606 errno = -ret;
1607 return -1;
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));
1614 if (ret < 0) {
1615 errno = -ret;
1616 return -1;
1618 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1619 if (new_fname == NULL) {
1620 errno = ENOMEM;
1621 return -1;
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;
1629 return ret;
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];
1640 int ret;
1641 struct smb_filename *new_fname;
1642 int saved_errno;
1644 ret = ceph_snap_gmt_strip_snapshot(handle,
1645 csmb_fname->base_name,
1646 &timestamp, stripped, sizeof(stripped));
1647 if (ret < 0) {
1648 errno = -ret;
1649 return -1;
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));
1657 if (ret < 0) {
1658 errno = -ret;
1659 return -1;
1661 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1662 if (new_fname == NULL) {
1663 errno = ENOMEM;
1664 return -1;
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;
1673 return ret;
1676 static int ceph_snap_gmt_get_real_filename(struct vfs_handle_struct *handle,
1677 const char *path,
1678 const char *name,
1679 TALLOC_CTX *mem_ctx,
1680 char **found_name)
1682 time_t timestamp = 0;
1683 char stripped[PATH_MAX + 1];
1684 char conv[PATH_MAX + 1];
1685 int ret;
1687 ret = ceph_snap_gmt_strip_snapshot(handle, path,
1688 &timestamp, stripped, sizeof(stripped));
1689 if (ret < 0) {
1690 errno = -ret;
1691 return -1;
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));
1699 if (ret < 0) {
1700 errno = -ret;
1701 return -1;
1703 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1704 mem_ctx, found_name);
1705 return ret;
1708 static uint64_t ceph_snap_gmt_disk_free(vfs_handle_struct *handle,
1709 const struct smb_filename *csmb_fname,
1710 uint64_t *bsize,
1711 uint64_t *dfree,
1712 uint64_t *dsize)
1714 time_t timestamp = 0;
1715 char stripped[PATH_MAX + 1];
1716 char conv[PATH_MAX + 1];
1717 int ret;
1718 struct smb_filename *new_fname;
1719 int saved_errno;
1721 ret = ceph_snap_gmt_strip_snapshot(handle,
1722 csmb_fname->base_name,
1723 &timestamp, stripped, sizeof(stripped));
1724 if (ret < 0) {
1725 errno = -ret;
1726 return -1;
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));
1734 if (ret < 0) {
1735 errno = -ret;
1736 return -1;
1738 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1739 if (new_fname == NULL) {
1740 errno = ENOMEM;
1741 return -1;
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;
1750 return ret;
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,
1756 unid_t id,
1757 SMB_DISK_QUOTA *dq)
1759 time_t timestamp = 0;
1760 char stripped[PATH_MAX + 1];
1761 char conv[PATH_MAX + 1];
1762 int ret;
1763 struct smb_filename *new_fname;
1764 int saved_errno;
1766 ret = ceph_snap_gmt_strip_snapshot(handle,
1767 csmb_fname->base_name,
1768 &timestamp, stripped, sizeof(stripped));
1769 if (ret < 0) {
1770 errno = -ret;
1771 return -1;
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));
1778 if (ret < 0) {
1779 errno = -ret;
1780 return -1;
1782 new_fname = cp_smb_filename(talloc_tos(), csmb_fname);
1783 if (new_fname == NULL) {
1784 errno = ENOMEM;
1785 return -1;
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;
1793 return ret;
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,
1830 static_decl_vfs;
1831 NTSTATUS vfs_ceph_snapshots_init(TALLOC_CTX *ctx)
1833 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1834 "ceph_snapshots", &ceph_snap_fns);