2 * Module for snapshot IO using snapper
4 * Copyright (C) David Disseldorp 2012-2014
6 * Portions taken from vfs_shadow_copy2.c:
7 * Copyright (C) Andrew Tridgell 2007
8 * Copyright (C) Ed Plese 2009
9 * Copyright (C) Volker Lendecke 2011
10 * Copyright (C) Christian Ambach 2011
11 * Copyright (C) Michael Adam 2013
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 3 of the License, or
16 * (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, see <http://www.gnu.org/licenses/>.
27 #include <dbus/dbus.h>
28 #ifdef HAVE_LINUX_IOCTL_H
29 #include <linux/ioctl.h>
31 #include <sys/ioctl.h>
35 #include "include/ntioctl.h"
36 #include "include/smb.h"
37 #include "system/filesys.h"
38 #include "smbd/smbd.h"
39 #include "lib/util/tevent_ntstatus.h"
41 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
42 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
43 #define SNAPPER_SIG_STRING_DICT "{ss}"
58 uint32_t num_user_data;
59 struct snapper_dict *user_data;
66 struct snapper_dict *attrs;
70 const char *snapper_err_str;
72 } snapper_err_map[] = {
73 { "error.no_permissions", NT_STATUS_ACCESS_DENIED },
76 static NTSTATUS snapper_err_ntstatus_map(const char *snapper_err_str)
80 if (snapper_err_str == NULL) {
81 return NT_STATUS_UNSUCCESSFUL;
83 for (i = 0; i < ARRAY_SIZE(snapper_err_map); i++) {
84 if (!strcmp(snapper_err_map[i].snapper_err_str,
86 return snapper_err_map[i].status;
89 DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str));
91 return NT_STATUS_UNSUCCESSFUL;
94 static DBusConnection *snapper_dbus_conn_create(void)
97 DBusConnection *dconn;
99 dbus_error_init(&err);
102 * Always create a new DBus connection, to ensure snapperd detects the
103 * correct client [E]UID. With dbus_bus_get() it does not!
105 dconn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
106 if (dbus_error_is_set(&err)) {
107 DEBUG(0, ("dbus connection error: %s\n", err.message));
108 dbus_error_free(&err);
114 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
115 dbus_connection_set_exit_on_disconnect(dconn, false);
120 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
123 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
127 dbus_connection_close(dconn);
128 dbus_connection_unref(dconn);
132 * send the message @send_msg over the dbus and wait for a response, return the
133 * responsee via @recv_msg_out.
134 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
136 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
137 DBusMessage *send_msg,
138 DBusMessage **recv_msg_out)
140 DBusPendingCall *pending;
141 DBusMessage *recv_msg;
143 /* send message and get a handle for a reply */
144 if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
145 return NT_STATUS_NO_MEMORY;
147 if (NULL == pending) {
148 DEBUG(0, ("dbus msg send failed\n"));
149 return NT_STATUS_UNSUCCESSFUL;
152 dbus_connection_flush(dconn);
154 /* block until we receive a reply */
155 dbus_pending_call_block(pending);
157 /* get the reply message */
158 recv_msg = dbus_pending_call_steal_reply(pending);
159 if (recv_msg == NULL) {
160 DEBUG(0, ("Reply Null\n"));
161 return NT_STATUS_UNSUCCESSFUL;
163 /* free the pending message handle */
164 dbus_pending_call_unref(pending);
165 *recv_msg_out = recv_msg;
170 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
173 int type = dbus_message_iter_get_arg_type(iter);
174 if (type != expected_type) {
175 DEBUG(0, ("got type %d, expecting %d\n",
176 type, expected_type));
177 return NT_STATUS_INVALID_PARAMETER;
183 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
188 status = snapper_type_check(iter, expected_type);
189 if (!NT_STATUS_IS_OK(status)) {
193 dbus_message_iter_get_basic(iter, val);
198 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
199 struct snapper_dict *dict_out)
203 DBusMessageIter dct_iter;
205 status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
206 if (!NT_STATUS_IS_OK(status)) {
209 dbus_message_iter_recurse(iter, &dct_iter);
211 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
213 if (!NT_STATUS_IS_OK(status)) {
217 dbus_message_iter_next(&dct_iter);
218 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
220 if (!NT_STATUS_IS_OK(status)) {
227 static void snapper_dict_array_print(uint32_t num_dicts,
228 struct snapper_dict *dicts)
232 for (i = 0; i < num_dicts; i++) {
233 DEBUG(10, ("dict (key: %s, val: %s)\n",
234 dicts[i].key, dicts[i].val));
238 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
239 DBusMessageIter *iter,
240 uint32_t *num_dicts_out,
241 struct snapper_dict **dicts_out)
244 DBusMessageIter array_iter;
246 struct snapper_dict *dicts = NULL;
248 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
249 if (!NT_STATUS_IS_OK(status)) {
252 dbus_message_iter_recurse(iter, &array_iter);
255 while (dbus_message_iter_get_arg_type(&array_iter)
256 != DBUS_TYPE_INVALID) {
258 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
263 status = snapper_dict_unpack(&array_iter,
264 &dicts[num_dicts - 1]);
265 if (!NT_STATUS_IS_OK(status)) {
269 dbus_message_iter_next(&array_iter);
272 *num_dicts_out = num_dicts;
278 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
282 msg = dbus_message_new_method_call("org.opensuse.Snapper",
283 "/org/opensuse/Snapper",
284 "org.opensuse.Snapper",
287 DEBUG(0, ("null msg\n"));
288 return NT_STATUS_NO_MEMORY;
291 /* no arguments to append */
297 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
298 DBusMessageIter *iter,
299 struct snapper_conf *conf_out)
302 DBusMessageIter st_iter;
304 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
305 if (!NT_STATUS_IS_OK(status)) {
308 dbus_message_iter_recurse(iter, &st_iter);
310 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
312 if (!NT_STATUS_IS_OK(status)) {
316 dbus_message_iter_next(&st_iter);
317 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
319 if (!NT_STATUS_IS_OK(status)) {
323 dbus_message_iter_next(&st_iter);
324 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
325 &conf_out->num_attrs,
331 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
332 struct snapper_conf *confs,
337 for (i = 0; i < num_confs; i++) {
338 if (strcmp(confs[i].mnt, base) == 0) {
339 DEBUG(5, ("found snapper conf %s for path %s\n",
340 confs[i].name, base));
344 DEBUG(5, ("config for base %s not found\n", base));
349 static void snapper_conf_array_print(int32_t num_confs,
350 struct snapper_conf *confs)
354 for (i = 0; i < num_confs; i++) {
355 DEBUG(10, ("name: %s, mnt: %s\n",
356 confs[i].name, confs[i].mnt));
357 snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
361 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
362 DBusMessageIter *iter,
363 uint32_t *num_confs_out,
364 struct snapper_conf **confs_out)
368 struct snapper_conf *confs = NULL;
369 DBusMessageIter array_iter;
372 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
373 if (!NT_STATUS_IS_OK(status)) {
376 dbus_message_iter_recurse(iter, &array_iter);
379 while (dbus_message_iter_get_arg_type(&array_iter)
380 != DBUS_TYPE_INVALID) {
382 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
387 status = snapper_conf_unpack(confs, &array_iter,
388 &confs[num_confs - 1]);
389 if (!NT_STATUS_IS_OK(status)) {
393 dbus_message_iter_next(&array_iter);
396 *num_confs_out = num_confs;
402 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
403 DBusConnection *dconn,
404 DBusMessage *rsp_msg,
405 uint32_t *num_confs_out,
406 struct snapper_conf **confs_out)
409 DBusMessageIter iter;
412 struct snapper_conf *confs;
415 msg_type = dbus_message_get_type(rsp_msg);
416 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
417 const char *err_str = dbus_message_get_error_name(rsp_msg);
418 DEBUG(0, ("list_confs error response: %s\n", err_str));
419 return snapper_err_ntstatus_map(err_str);
422 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
423 DEBUG(0, ("unexpected list_confs ret type: %d\n",
425 return NT_STATUS_INVALID_PARAMETER;
428 sig = dbus_message_get_signature(rsp_msg);
430 || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
431 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
432 (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
433 return NT_STATUS_INVALID_PARAMETER;
436 if (!dbus_message_iter_init(rsp_msg, &iter)) {
437 /* FIXME return empty? */
438 DEBUG(0, ("Message has no arguments!\n"));
439 return NT_STATUS_INVALID_PARAMETER;
442 status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
443 if (!NT_STATUS_IS_OK(status)) {
444 DEBUG(0, ("failed to unpack conf array\n"));
448 snapper_conf_array_print(num_confs, confs);
450 *num_confs_out = num_confs;
456 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
457 DBusMessage **req_msg_out)
460 DBusMessageIter args;
462 msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
463 "/org/opensuse/Snapper", /* object to call on */
464 "org.opensuse.Snapper", /* interface to call on */
465 "ListSnapshots"); /* method name */
467 DEBUG(0, ("failed to create list snaps message\n"));
468 return NT_STATUS_NO_MEMORY;
471 /* append arguments */
472 dbus_message_iter_init_append(msg, &args);
473 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
475 return NT_STATUS_NO_MEMORY;
483 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
484 DBusMessageIter *iter,
485 struct snapper_snap *snap_out)
488 DBusMessageIter st_iter;
490 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
491 if (!NT_STATUS_IS_OK(status)) {
494 dbus_message_iter_recurse(iter, &st_iter);
496 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
498 if (!NT_STATUS_IS_OK(status)) {
502 dbus_message_iter_next(&st_iter);
503 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
505 if (!NT_STATUS_IS_OK(status)) {
509 dbus_message_iter_next(&st_iter);
510 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
512 if (!NT_STATUS_IS_OK(status)) {
516 dbus_message_iter_next(&st_iter);
517 status = snapper_type_check_get(&st_iter, DBUS_TYPE_INT64,
519 if (!NT_STATUS_IS_OK(status)) {
523 dbus_message_iter_next(&st_iter);
524 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
525 &snap_out->creator_uid);
526 if (!NT_STATUS_IS_OK(status)) {
530 dbus_message_iter_next(&st_iter);
531 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
533 if (!NT_STATUS_IS_OK(status)) {
537 dbus_message_iter_next(&st_iter);
538 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
540 if (!NT_STATUS_IS_OK(status)) {
544 dbus_message_iter_next(&st_iter);
545 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
546 &snap_out->num_user_data,
547 &snap_out->user_data);
552 static void snapper_snap_array_print(int32_t num_snaps,
553 struct snapper_snap *snaps)
557 for (i = 0; i < num_snaps; i++) {
558 DEBUG(10, ("id: %u, "
565 (unsigned int)snaps[i].id,
566 (unsigned int)snaps[i].type,
567 (unsigned int)snaps[i].pre_id,
568 (long int)snaps[i].time,
569 (unsigned int)snaps[i].creator_uid,
572 snapper_dict_array_print(snaps[i].num_user_data,
577 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
578 DBusMessageIter *iter,
579 uint32_t *num_snaps_out,
580 struct snapper_snap **snaps_out)
584 struct snapper_snap *snaps = NULL;
585 DBusMessageIter array_iter;
588 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
589 if (!NT_STATUS_IS_OK(status)) {
592 dbus_message_iter_recurse(iter, &array_iter);
595 while (dbus_message_iter_get_arg_type(&array_iter)
596 != DBUS_TYPE_INVALID) {
598 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
603 status = snapper_snap_struct_unpack(snaps, &array_iter,
604 &snaps[num_snaps - 1]);
605 if (!NT_STATUS_IS_OK(status)) {
609 dbus_message_iter_next(&array_iter);
612 *num_snaps_out = num_snaps;
618 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
619 DBusMessage *rsp_msg,
620 uint32_t *num_snaps_out,
621 struct snapper_snap **snaps_out)
624 DBusMessageIter iter;
627 struct snapper_snap *snaps;
630 msg_type = dbus_message_get_type(rsp_msg);
631 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
632 const char *err_str = dbus_message_get_error_name(rsp_msg);
633 DEBUG(0, ("list_snaps error response: %s\n", err_str));
634 return snapper_err_ntstatus_map(err_str);
637 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
638 DEBUG(0,("unexpected list_snaps ret type: %d\n",
640 return NT_STATUS_INVALID_PARAMETER;
643 sig = dbus_message_get_signature(rsp_msg);
645 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
646 DEBUG(0, ("bad list snaps response sig: %s, "
648 (sig ? sig : "NULL"),
649 SNAPPER_SIG_LIST_SNAPS_RSP));
650 return NT_STATUS_INVALID_PARAMETER;
653 /* read the parameters */
654 if (!dbus_message_iter_init(rsp_msg, &iter)) {
655 DEBUG(0, ("response has no arguments!\n"));
656 return NT_STATUS_INVALID_PARAMETER;
659 status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
660 if (!NT_STATUS_IS_OK(status)) {
661 DEBUG(0, ("failed to unpack snap array\n"));
662 return NT_STATUS_INVALID_PARAMETER;
665 snapper_snap_array_print(num_snaps, snaps);
667 *num_snaps_out = num_snaps;
673 static NTSTATUS snapper_list_snaps_at_time_pack(const char *snapper_conf,
676 DBusMessage **req_msg_out)
679 DBusMessageIter args;
681 msg = dbus_message_new_method_call("org.opensuse.Snapper",
682 "/org/opensuse/Snapper",
683 "org.opensuse.Snapper",
684 "ListSnapshotsAtTime");
686 DEBUG(0, ("failed to create list snaps message\n"));
687 return NT_STATUS_NO_MEMORY;
690 dbus_message_iter_init_append(msg, &args);
691 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
693 return NT_STATUS_NO_MEMORY;
696 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
698 return NT_STATUS_NO_MEMORY;
701 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
703 return NT_STATUS_NO_MEMORY;
710 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
713 * Determine the snapper snapshot path given an id and base.
714 * Ideally this should be determined via a lookup.
716 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
717 const char *base_path,
719 char **snap_path_out)
723 snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
725 if (snap_path == NULL) {
726 return NT_STATUS_NO_MEMORY;
729 *snap_path_out = snap_path;
733 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
734 DBusConnection *dconn,
736 char **conf_name_out,
737 char **base_path_out)
740 DBusMessage *req_msg;
741 DBusMessage *rsp_msg;
742 uint32_t num_confs = 0;
743 struct snapper_conf *confs = NULL;
744 struct snapper_conf *conf;
748 status = snapper_list_confs_pack(&req_msg);
749 if (!NT_STATUS_IS_OK(status)) {
753 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
754 if (!NT_STATUS_IS_OK(status)) {
758 status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
760 if (!NT_STATUS_IS_OK(status)) {
765 * for now we only support shares where the path directly corresponds
766 * to a snapper configuration.
768 conf = snapper_conf_array_base_find(num_confs, confs,
771 status = NT_STATUS_NOT_SUPPORTED;
775 conf_name = talloc_strdup(mem_ctx, conf->name);
776 if (conf_name == NULL) {
777 status = NT_STATUS_NO_MEMORY;
780 base_path = talloc_strdup(mem_ctx, conf->mnt);
781 if (base_path == NULL) {
782 status = NT_STATUS_NO_MEMORY;
783 goto err_conf_name_free;
787 dbus_message_unref(rsp_msg);
788 dbus_message_unref(req_msg);
790 *conf_name_out = conf_name;
791 *base_path_out = base_path;
796 talloc_free(conf_name);
800 dbus_message_unref(rsp_msg);
802 dbus_message_unref(req_msg);
807 /* sc_data used as parent talloc context for all labels */
808 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
809 struct files_struct *fsp,
810 struct shadow_copy_data *sc_data,
813 DBusConnection *dconn;
818 DBusMessage *req_msg;
819 DBusMessage *rsp_msg;
821 struct snapper_snap *snaps;
825 tmp_ctx = talloc_new(sc_data);
826 if (tmp_ctx == NULL) {
827 status = NT_STATUS_NO_MEMORY;
831 dconn = snapper_dbus_conn_create();
833 status = NT_STATUS_UNSUCCESSFUL;
834 goto err_mem_ctx_free;
837 if (fsp->conn->connectpath == NULL) {
838 status = NT_STATUS_INVALID_PARAMETER;
842 status = snapper_get_conf_call(tmp_ctx, dconn,
843 fsp->conn->connectpath,
846 if (!NT_STATUS_IS_OK(status)) {
850 status = snapper_list_snaps_pack(conf_name, &req_msg);
851 if (!NT_STATUS_IS_OK(status)) {
855 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
856 if (!NT_STATUS_IS_OK(status)) {
860 status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
862 if (!NT_STATUS_IS_OK(status)) {
865 /* we should always get at least one snapshot (current) */
866 if (num_snaps == 0) {
867 DEBUG(1, ("zero snapshots in snap list response\n"));
868 status = NT_STATUS_UNSUCCESSFUL;
872 /* subtract 1, (current) snapshot is not returned */
873 sc_data->num_volumes = num_snaps - 1;
874 sc_data->labels = NULL;
876 if ((labels == false) || (sc_data->num_volumes == 0)) {
877 /* tokens need not be added to the labels array */
881 sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
882 sc_data->num_volumes);
883 if (sc_data->labels == NULL) {
884 status = NT_STATUS_NO_MEMORY;
888 /* start at end for decending order, do not include 0 (current) */
890 for (i = num_snaps - 1; i > 0; i--) {
891 char *lbl = sc_data->labels[lbl_off++];
892 struct tm gmt_snap_time;
896 tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
897 if (tm_ret == NULL) {
898 status = NT_STATUS_UNSUCCESSFUL;
899 goto err_labels_free;
901 str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
902 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
904 status = NT_STATUS_UNSUCCESSFUL;
905 goto err_labels_free;
910 talloc_free(tmp_ctx);
911 dbus_message_unref(rsp_msg);
912 dbus_message_unref(req_msg);
913 snapper_dbus_conn_destroy(dconn);
918 TALLOC_FREE(sc_data->labels);
920 dbus_message_unref(rsp_msg);
922 dbus_message_unref(req_msg);
924 snapper_dbus_conn_destroy(dconn);
926 talloc_free(tmp_ctx);
928 errno = map_errno_from_nt_status(status);
932 static bool snapper_gmt_strip_snapshot(TALLOC_CTX *mem_ctx,
933 struct vfs_handle_struct *handle,
943 size_t rest_len, dst_len;
945 p = strstr_m(name, "@GMT-");
949 if ((p > name) && (p[-1] != '/')) {
952 q = strptime(p, GMT_FORMAT, &tm);
957 timestamp = timegm(&tm);
958 if (timestamp == (time_t)-1) {
961 if ((p == name) && (q[0] == '\0')) {
962 if (pstripped != NULL) {
963 stripped = talloc_strdup(mem_ctx, "");
964 if (stripped == NULL) {
967 *pstripped = stripped;
969 *ptimestamp = timestamp;
977 rest_len = strlen(q);
978 dst_len = (p-name) + rest_len;
980 if (pstripped != NULL) {
981 stripped = talloc_array(mem_ctx, char, dst_len+1);
982 if (stripped == NULL) {
987 memcpy(stripped, name, p-name);
990 memcpy(stripped + (p-name), q, rest_len);
992 stripped[dst_len] = '\0';
993 *pstripped = stripped;
995 *ptimestamp = timestamp;
1002 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1003 DBusConnection *dconn,
1004 const char *conf_name,
1005 const char *base_path,
1007 char **snap_path_out)
1010 DBusMessage *req_msg;
1011 DBusMessage *rsp_msg;
1013 struct snapper_snap *snaps;
1016 status = snapper_list_snaps_at_time_pack(conf_name,
1020 if (!NT_STATUS_IS_OK(status)) {
1024 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1025 if (!NT_STATUS_IS_OK(status)) {
1029 status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1030 &num_snaps, &snaps);
1031 if (!NT_STATUS_IS_OK(status)) {
1035 if (num_snaps == 0) {
1036 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime));
1037 status = NT_STATUS_INVALID_PARAMETER;
1038 goto err_snap_array_free;
1039 } else if (num_snaps > 0) {
1040 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1041 num_snaps, snaptime));
1044 status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1046 if (!NT_STATUS_IS_OK(status)) {
1047 goto err_snap_array_free;
1050 *snap_path_out = snap_path;
1051 err_snap_array_free:
1054 dbus_message_unref(rsp_msg);
1056 dbus_message_unref(req_msg);
1061 static NTSTATUS snapper_snap_path_expand(struct connection_struct *conn,
1062 TALLOC_CTX *mem_ctx,
1064 char **snap_dir_out)
1066 DBusConnection *dconn;
1072 dconn = snapper_dbus_conn_create();
1073 if (dconn == NULL) {
1074 status = NT_STATUS_UNSUCCESSFUL;
1078 if (conn->connectpath == NULL) {
1079 status = NT_STATUS_INVALID_PARAMETER;
1083 status = snapper_get_conf_call(mem_ctx, dconn,
1087 if (!NT_STATUS_IS_OK(status)) {
1091 status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1092 conf_name, base_path, snap_time,
1094 if (!NT_STATUS_IS_OK(status)) {
1095 goto err_conf_name_free;
1098 /* confirm snapshot path is nested under base path */
1099 if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1100 status = NT_STATUS_INVALID_PARAMETER;
1101 goto err_snap_path_free;
1104 talloc_free(conf_name);
1105 talloc_free(base_path);
1106 snapper_dbus_conn_destroy(dconn);
1107 *snap_dir_out = snap_path;
1109 return NT_STATUS_OK;
1112 talloc_free(snap_path);
1114 talloc_free(conf_name);
1115 talloc_free(base_path);
1117 snapper_dbus_conn_destroy(dconn);
1122 static char *snapper_gmt_convert(TALLOC_CTX *mem_ctx,
1123 struct vfs_handle_struct *handle,
1124 const char *name, time_t timestamp)
1126 char *snap_path = NULL;
1131 status = snapper_snap_path_expand(handle->conn, mem_ctx, timestamp,
1133 if (!NT_STATUS_IS_OK(status)) {
1134 errno = map_errno_from_nt_status(status);
1138 path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1141 goto err_snap_path_free;
1144 DEBUG(10, ("converted %s/%s @ time to %s\n",
1145 handle->conn->connectpath, name, path));
1149 saved_errno = errno;
1150 talloc_free(snap_path);
1151 errno = saved_errno;
1156 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1167 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1168 ×tamp, &stripped)) {
1171 if (timestamp == 0) {
1172 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
1174 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1175 TALLOC_FREE(stripped);
1179 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
1180 saved_errno = errno;
1182 errno = saved_errno;
1186 static int snapper_gmt_rename(vfs_handle_struct *handle,
1187 const struct smb_filename *smb_fname_src,
1188 const struct smb_filename *smb_fname_dst)
1190 time_t timestamp_src, timestamp_dst;
1192 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1193 smb_fname_src->base_name,
1194 ×tamp_src, NULL)) {
1197 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1198 smb_fname_dst->base_name,
1199 ×tamp_dst, NULL)) {
1202 if (timestamp_src != 0) {
1206 if (timestamp_dst != 0) {
1210 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1213 static int snapper_gmt_symlink(vfs_handle_struct *handle,
1214 const char *oldname, const char *newname)
1216 time_t timestamp_old, timestamp_new;
1218 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1219 ×tamp_old, NULL)) {
1222 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1223 ×tamp_new, NULL)) {
1226 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1230 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
1233 static int snapper_gmt_link(vfs_handle_struct *handle,
1234 const char *oldname, const char *newname)
1236 time_t timestamp_old, timestamp_new;
1238 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1239 ×tamp_old, NULL)) {
1242 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1243 ×tamp_new, NULL)) {
1246 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1250 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
1253 static int snapper_gmt_stat(vfs_handle_struct *handle,
1254 struct smb_filename *smb_fname)
1257 char *stripped, *tmp;
1258 int ret, saved_errno;
1260 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1261 smb_fname->base_name,
1262 ×tamp, &stripped)) {
1265 if (timestamp == 0) {
1266 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1269 tmp = smb_fname->base_name;
1270 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1271 stripped, timestamp);
1272 TALLOC_FREE(stripped);
1274 if (smb_fname->base_name == NULL) {
1275 smb_fname->base_name = tmp;
1279 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1280 saved_errno = errno;
1282 TALLOC_FREE(smb_fname->base_name);
1283 smb_fname->base_name = tmp;
1285 errno = saved_errno;
1289 static int snapper_gmt_lstat(vfs_handle_struct *handle,
1290 struct smb_filename *smb_fname)
1293 char *stripped, *tmp;
1294 int ret, saved_errno;
1296 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1297 smb_fname->base_name,
1298 ×tamp, &stripped)) {
1301 if (timestamp == 0) {
1302 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1305 tmp = smb_fname->base_name;
1306 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1307 stripped, timestamp);
1308 TALLOC_FREE(stripped);
1310 if (smb_fname->base_name == NULL) {
1311 smb_fname->base_name = tmp;
1315 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1316 saved_errno = errno;
1318 TALLOC_FREE(smb_fname->base_name);
1319 smb_fname->base_name = tmp;
1321 errno = saved_errno;
1325 static int snapper_gmt_fstat(vfs_handle_struct *handle, files_struct *fsp,
1326 SMB_STRUCT_STAT *sbuf)
1331 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1335 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1336 fsp->fsp_name->base_name,
1337 ×tamp, NULL)) {
1343 static int snapper_gmt_open(vfs_handle_struct *handle,
1344 struct smb_filename *smb_fname, files_struct *fsp,
1345 int flags, mode_t mode)
1348 char *stripped, *tmp;
1349 int ret, saved_errno;
1351 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1352 smb_fname->base_name,
1353 ×tamp, &stripped)) {
1356 if (timestamp == 0) {
1357 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1360 tmp = smb_fname->base_name;
1361 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1362 stripped, timestamp);
1363 TALLOC_FREE(stripped);
1365 if (smb_fname->base_name == NULL) {
1366 smb_fname->base_name = tmp;
1370 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1371 saved_errno = errno;
1373 TALLOC_FREE(smb_fname->base_name);
1374 smb_fname->base_name = tmp;
1376 errno = saved_errno;
1380 static int snapper_gmt_unlink(vfs_handle_struct *handle,
1381 const struct smb_filename *smb_fname)
1385 int ret, saved_errno;
1386 struct smb_filename *conv;
1388 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1389 smb_fname->base_name,
1390 ×tamp, &stripped)) {
1393 if (timestamp == 0) {
1394 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1396 conv = cp_smb_filename(talloc_tos(), smb_fname);
1401 conv->base_name = snapper_gmt_convert(conv, handle,
1402 stripped, timestamp);
1403 TALLOC_FREE(stripped);
1404 if (conv->base_name == NULL) {
1407 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1408 saved_errno = errno;
1410 errno = saved_errno;
1414 static int snapper_gmt_chmod(vfs_handle_struct *handle, const char *fname,
1419 int ret, saved_errno;
1422 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1423 ×tamp, &stripped)) {
1426 if (timestamp == 0) {
1427 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
1429 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1430 TALLOC_FREE(stripped);
1434 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1435 saved_errno = errno;
1437 errno = saved_errno;
1441 static int snapper_gmt_chown(vfs_handle_struct *handle, const char *fname,
1442 uid_t uid, gid_t gid)
1446 int ret, saved_errno;
1449 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1450 ×tamp, &stripped)) {
1453 if (timestamp == 0) {
1454 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
1456 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1457 TALLOC_FREE(stripped);
1461 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1462 saved_errno = errno;
1464 errno = saved_errno;
1468 static int snapper_gmt_chdir(vfs_handle_struct *handle,
1473 int ret, saved_errno;
1476 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1477 ×tamp, &stripped)) {
1480 if (timestamp == 0) {
1481 return SMB_VFS_NEXT_CHDIR(handle, fname);
1483 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1484 TALLOC_FREE(stripped);
1488 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1489 saved_errno = errno;
1491 errno = saved_errno;
1495 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
1496 const struct smb_filename *smb_fname,
1497 struct smb_file_time *ft)
1501 int ret, saved_errno;
1502 struct smb_filename *conv;
1504 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1505 smb_fname->base_name,
1506 ×tamp, &stripped)) {
1509 if (timestamp == 0) {
1510 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1512 conv = cp_smb_filename(talloc_tos(), smb_fname);
1517 conv->base_name = snapper_gmt_convert(conv, handle,
1518 stripped, timestamp);
1519 TALLOC_FREE(stripped);
1520 if (conv->base_name == NULL) {
1523 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1524 saved_errno = errno;
1526 errno = saved_errno;
1530 static int snapper_gmt_readlink(vfs_handle_struct *handle,
1531 const char *fname, char *buf, size_t bufsiz)
1535 int ret, saved_errno;
1538 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1539 ×tamp, &stripped)) {
1542 if (timestamp == 0) {
1543 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1545 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1546 TALLOC_FREE(stripped);
1550 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1551 saved_errno = errno;
1553 errno = saved_errno;
1557 static int snapper_gmt_mknod(vfs_handle_struct *handle,
1558 const char *fname, mode_t mode, SMB_DEV_T dev)
1562 int ret, saved_errno;
1565 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1566 ×tamp, &stripped)) {
1569 if (timestamp == 0) {
1570 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1572 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1573 TALLOC_FREE(stripped);
1577 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1578 saved_errno = errno;
1580 errno = saved_errno;
1584 static char *snapper_gmt_realpath(vfs_handle_struct *handle,
1588 char *stripped = NULL;
1590 char *result = NULL;
1593 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1594 ×tamp, &stripped)) {
1597 if (timestamp == 0) {
1598 return SMB_VFS_NEXT_REALPATH(handle, fname);
1601 tmp = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1606 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1607 if (result == NULL) {
1612 saved_errno = errno;
1614 TALLOC_FREE(stripped);
1615 errno = saved_errno;
1619 static NTSTATUS snapper_gmt_fget_nt_acl(vfs_handle_struct *handle,
1620 struct files_struct *fsp,
1621 uint32 security_info,
1622 TALLOC_CTX *mem_ctx,
1623 struct security_descriptor **ppdesc)
1630 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1631 fsp->fsp_name->base_name,
1632 ×tamp, &stripped)) {
1633 return map_nt_error_from_unix(errno);
1635 if (timestamp == 0) {
1636 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1640 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1641 TALLOC_FREE(stripped);
1643 return map_nt_error_from_unix(errno);
1645 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1651 static NTSTATUS snapper_gmt_get_nt_acl(vfs_handle_struct *handle,
1653 uint32 security_info,
1654 TALLOC_CTX *mem_ctx,
1655 struct security_descriptor **ppdesc)
1662 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1663 ×tamp, &stripped)) {
1664 return map_nt_error_from_unix(errno);
1666 if (timestamp == 0) {
1667 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1670 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1671 TALLOC_FREE(stripped);
1673 return map_nt_error_from_unix(errno);
1675 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1681 static int snapper_gmt_mkdir(vfs_handle_struct *handle,
1682 const char *fname, mode_t mode)
1686 int ret, saved_errno;
1689 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1690 ×tamp, &stripped)) {
1693 if (timestamp == 0) {
1694 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1696 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1697 TALLOC_FREE(stripped);
1701 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1702 saved_errno = errno;
1704 errno = saved_errno;
1708 static int snapper_gmt_rmdir(vfs_handle_struct *handle, const char *fname)
1712 int ret, saved_errno;
1715 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1716 ×tamp, &stripped)) {
1719 if (timestamp == 0) {
1720 return SMB_VFS_NEXT_RMDIR(handle, fname);
1722 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1723 TALLOC_FREE(stripped);
1727 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1728 saved_errno = errno;
1730 errno = saved_errno;
1734 static int snapper_gmt_chflags(vfs_handle_struct *handle, const char *fname,
1739 int ret, saved_errno;
1742 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1743 ×tamp, &stripped)) {
1746 if (timestamp == 0) {
1747 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1749 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1750 TALLOC_FREE(stripped);
1754 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1755 saved_errno = errno;
1757 errno = saved_errno;
1761 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
1762 const char *fname, const char *aname,
1763 void *value, size_t size)
1771 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1772 ×tamp, &stripped)) {
1775 if (timestamp == 0) {
1776 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1779 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1780 TALLOC_FREE(stripped);
1784 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1785 saved_errno = errno;
1787 errno = saved_errno;
1791 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
1793 char *list, size_t size)
1801 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1802 ×tamp, &stripped)) {
1805 if (timestamp == 0) {
1806 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1808 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1809 TALLOC_FREE(stripped);
1813 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1814 saved_errno = errno;
1816 errno = saved_errno;
1820 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
1821 const char *fname, const char *aname)
1825 int ret, saved_errno;
1828 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1829 ×tamp, &stripped)) {
1832 if (timestamp == 0) {
1833 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1835 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1836 TALLOC_FREE(stripped);
1840 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1841 saved_errno = errno;
1843 errno = saved_errno;
1847 static int snapper_gmt_setxattr(struct vfs_handle_struct *handle,
1849 const char *aname, const void *value,
1850 size_t size, int flags)
1858 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1859 ×tamp, &stripped)) {
1862 if (timestamp == 0) {
1863 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1866 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1867 TALLOC_FREE(stripped);
1871 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1872 saved_errno = errno;
1874 errno = saved_errno;
1878 static int snapper_gmt_chmod_acl(vfs_handle_struct *handle,
1879 const char *fname, mode_t mode)
1887 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1888 ×tamp, &stripped)) {
1891 if (timestamp == 0) {
1892 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1894 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1895 TALLOC_FREE(stripped);
1899 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1900 saved_errno = errno;
1902 errno = saved_errno;
1906 static int snapper_gmt_get_real_filename(struct vfs_handle_struct *handle,
1909 TALLOC_CTX *mem_ctx,
1918 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1919 ×tamp, &stripped)) {
1922 if (timestamp == 0) {
1923 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1924 mem_ctx, found_name);
1926 if (stripped[0] == '\0') {
1927 *found_name = talloc_strdup(mem_ctx, name);
1928 if (*found_name == NULL) {
1934 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1935 TALLOC_FREE(stripped);
1939 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1940 mem_ctx, found_name);
1941 saved_errno = errno;
1943 errno = saved_errno;
1947 static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
1948 const char *path, bool small_query,
1949 uint64_t *bsize, uint64_t *dfree,
1958 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1959 ×tamp, &stripped)) {
1962 if (timestamp == 0) {
1963 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1964 bsize, dfree, dsize);
1967 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1968 TALLOC_FREE(stripped);
1973 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1976 saved_errno = errno;
1978 errno = saved_errno;
1984 static struct vfs_fn_pointers snapper_fns = {
1985 .get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
1986 .opendir_fn = snapper_gmt_opendir,
1987 .disk_free_fn = snapper_gmt_disk_free,
1988 .rename_fn = snapper_gmt_rename,
1989 .link_fn = snapper_gmt_link,
1990 .symlink_fn = snapper_gmt_symlink,
1991 .stat_fn = snapper_gmt_stat,
1992 .lstat_fn = snapper_gmt_lstat,
1993 .fstat_fn = snapper_gmt_fstat,
1994 .open_fn = snapper_gmt_open,
1995 .unlink_fn = snapper_gmt_unlink,
1996 .chmod_fn = snapper_gmt_chmod,
1997 .chown_fn = snapper_gmt_chown,
1998 .chdir_fn = snapper_gmt_chdir,
1999 .ntimes_fn = snapper_gmt_ntimes,
2000 .readlink_fn = snapper_gmt_readlink,
2001 .mknod_fn = snapper_gmt_mknod,
2002 .realpath_fn = snapper_gmt_realpath,
2003 .get_nt_acl_fn = snapper_gmt_get_nt_acl,
2004 .fget_nt_acl_fn = snapper_gmt_fget_nt_acl,
2005 .mkdir_fn = snapper_gmt_mkdir,
2006 .rmdir_fn = snapper_gmt_rmdir,
2007 .getxattr_fn = snapper_gmt_getxattr,
2008 .listxattr_fn = snapper_gmt_listxattr,
2009 .removexattr_fn = snapper_gmt_removexattr,
2010 .setxattr_fn = snapper_gmt_setxattr,
2011 .chmod_acl_fn = snapper_gmt_chmod_acl,
2012 .chflags_fn = snapper_gmt_chflags,
2013 .get_real_filename_fn = snapper_gmt_get_real_filename,
2016 NTSTATUS vfs_snapper_init(void);
2017 NTSTATUS vfs_snapper_init(void)
2019 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2020 "snapper", &snapper_fns);