2 * Module for snapshot management using snapper
4 * Copyright (C) David Disseldorp 2012
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include <dbus/dbus.h>
21 #include <linux/ioctl.h>
22 #include <sys/ioctl.h>
26 #include "include/ntioctl.h"
27 #include "system/filesys.h"
28 #include "smbd/smbd.h"
29 #include "lib/util/tevent_ntstatus.h"
31 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uqutussa{ss})"
32 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
33 #define SNAPPER_SIG_CREATE_SNAP_RSP "u"
34 #define SNAPPER_SIG_DEL_SNAPS_RSP ""
35 #define SNAPPER_SIG_STRING_DICT "{ss}"
50 uint32_t num_user_data;
51 struct snapper_dict *user_data;
58 struct snapper_dict *attrs;
61 static DBusConnection *snapper_dbus_conn(void)
64 DBusConnection *dconn;
66 dbus_error_init(&err);
68 dconn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
69 if (dbus_error_is_set(&err)) {
70 DEBUG(0, ("dbus connection error: %s\n", err.message));
71 dbus_error_free(&err);
81 * send the message @send_msg over the dbus and wait for a response, return the
82 * responsee via @recv_msg_out.
83 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
85 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
86 DBusMessage *send_msg,
87 DBusMessage **recv_msg_out)
89 DBusPendingCall *pending;
90 DBusMessage *recv_msg;
92 /* send message and get a handle for a reply */
93 if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
94 return NT_STATUS_NO_MEMORY;
96 if (NULL == pending) {
97 DEBUG(0, ("dbus msg send failed\n"));
98 return NT_STATUS_UNSUCCESSFUL;
101 dbus_connection_flush(dconn);
103 /* block until we receive a reply */
104 dbus_pending_call_block(pending);
106 /* get the reply message */
107 recv_msg = dbus_pending_call_steal_reply(pending);
108 if (recv_msg == NULL) {
109 DEBUG(0, ("Reply Null\n"));
110 return NT_STATUS_UNSUCCESSFUL;
112 /* free the pending message handle */
113 dbus_pending_call_unref(pending);
114 *recv_msg_out = recv_msg;
119 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
122 int type = dbus_message_iter_get_arg_type(iter);
123 if (type != expected_type) {
124 DEBUG(0, ("got type %d, expecting %d\n",
125 type, expected_type));
126 return NT_STATUS_INVALID_PARAMETER;
132 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
137 status = snapper_type_check(iter, expected_type);
138 if (!NT_STATUS_IS_OK(status)) {
142 dbus_message_iter_get_basic(iter, val);
147 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
148 struct snapper_dict *dict_out)
152 DBusMessageIter dct_iter;
154 status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
155 if (!NT_STATUS_IS_OK(status)) {
158 dbus_message_iter_recurse(iter, &dct_iter);
160 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
162 if (!NT_STATUS_IS_OK(status)) {
166 dbus_message_iter_next(&dct_iter);
167 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
169 if (!NT_STATUS_IS_OK(status)) {
176 static void snapper_dict_array_print(uint32_t num_dicts,
177 struct snapper_dict *dicts)
181 for (i = 0; i < num_dicts; i++) {
182 DEBUG(10, ("dict (key: %s, val: %s)\n",
183 dicts[i].key, dicts[i].val));
187 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
188 DBusMessageIter *iter,
189 uint32_t *num_dicts_out,
190 struct snapper_dict **dicts_out)
193 DBusMessageIter array_iter;
195 struct snapper_dict *dicts = NULL;
197 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
198 if (!NT_STATUS_IS_OK(status)) {
201 dbus_message_iter_recurse(iter, &array_iter);
204 while (dbus_message_iter_get_arg_type(&array_iter)
205 != DBUS_TYPE_INVALID) {
207 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
212 status = snapper_dict_unpack(&array_iter,
213 &dicts[num_dicts - 1]);
214 if (!NT_STATUS_IS_OK(status)) {
218 dbus_message_iter_next(&array_iter);
221 *num_dicts_out = num_dicts;
227 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
231 msg = dbus_message_new_method_call("org.opensuse.Snapper",
232 "/org/opensuse/Snapper",
233 "org.opensuse.Snapper",
236 DEBUG(0, ("null msg\n"));
237 return NT_STATUS_NO_MEMORY;
240 /* no arguments to append */
246 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
247 DBusMessageIter *iter,
248 struct snapper_conf *conf_out)
251 DBusMessageIter st_iter;
253 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
254 if (!NT_STATUS_IS_OK(status)) {
257 dbus_message_iter_recurse(iter, &st_iter);
259 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
261 if (!NT_STATUS_IS_OK(status)) {
265 dbus_message_iter_next(&st_iter);
266 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
268 if (!NT_STATUS_IS_OK(status)) {
272 dbus_message_iter_next(&st_iter);
273 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
274 &conf_out->num_attrs,
280 static void snapper_conf_array_free(int32_t num_confs,
281 struct snapper_conf *confs)
285 for (i = 0; i < num_confs; i++) {
286 talloc_free(confs[i].attrs);
291 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
292 struct snapper_conf *confs,
297 for (i = 0; i < num_confs; i++) {
298 if (strcmp(confs[i].mnt, base) == 0) {
299 DEBUG(5, ("found snapper conf %s for path %s\n",
300 confs[i].name, base));
304 DEBUG(5, ("config for base %s not found\n", base));
309 static void snapper_conf_array_print(int32_t num_confs,
310 struct snapper_conf *confs)
314 for (i = 0; i < num_confs; i++) {
315 DEBUG(10, ("name: %s, mnt: %s\n",
316 confs[i].name, confs[i].mnt));
317 snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
321 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
322 DBusMessageIter *iter,
323 uint32_t *num_confs_out,
324 struct snapper_conf **confs_out)
328 struct snapper_conf *confs = NULL;
329 DBusMessageIter array_iter;
332 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
333 if (!NT_STATUS_IS_OK(status)) {
336 dbus_message_iter_recurse(iter, &array_iter);
339 while (dbus_message_iter_get_arg_type(&array_iter)
340 != DBUS_TYPE_INVALID) {
342 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
347 status = snapper_conf_unpack(mem_ctx, &array_iter,
348 &confs[num_confs - 1]);
349 if (!NT_STATUS_IS_OK(status)) {
353 dbus_message_iter_next(&array_iter);
356 *num_confs_out = num_confs;
362 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
363 DBusConnection *dconn,
364 DBusMessage *rsp_msg,
365 uint32_t *num_confs_out,
366 struct snapper_conf **confs_out)
369 DBusMessageIter iter;
372 struct snapper_conf *confs;
375 msg_type = dbus_message_get_type(rsp_msg);
376 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
377 DEBUG(0, ("list_confs error response: %s\n",
378 dbus_message_get_error_name(rsp_msg)));
379 return NT_STATUS_INVALID_PARAMETER;
382 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
383 DEBUG(0, ("unexpected list_confs ret type: %d\n",
385 return NT_STATUS_INVALID_PARAMETER;
388 sig = dbus_message_get_signature(rsp_msg);
390 || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
391 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
392 (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
393 return NT_STATUS_INVALID_PARAMETER;
396 if (!dbus_message_iter_init(rsp_msg, &iter)) {
397 /* FIXME return empty? */
398 DEBUG(0, ("Message has no arguments!\n"));
399 return NT_STATUS_INVALID_PARAMETER;
402 status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
403 if (!NT_STATUS_IS_OK(status)) {
404 DEBUG(0, ("failed to unpack conf array\n"));
408 snapper_conf_array_print(num_confs, confs);
410 *num_confs_out = num_confs;
416 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
417 DBusMessage **req_msg_out)
420 DBusMessageIter args;
422 msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
423 "/org/opensuse/Snapper", /* object to call on */
424 "org.opensuse.Snapper", /* interface to call on */
425 "ListSnapshots"); /* method name */
427 DEBUG(0, ("failed to create list snaps message\n"));
428 return NT_STATUS_NO_MEMORY;
431 /* append arguments */
432 dbus_message_iter_init_append(msg, &args);
433 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
435 return NT_STATUS_NO_MEMORY;
443 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
444 DBusMessageIter *iter,
445 struct snapper_snap *snap_out)
448 DBusMessageIter st_iter;
450 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
451 if (!NT_STATUS_IS_OK(status)) {
454 dbus_message_iter_recurse(iter, &st_iter);
456 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
458 if (!NT_STATUS_IS_OK(status)) {
462 dbus_message_iter_next(&st_iter);
463 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
465 if (!NT_STATUS_IS_OK(status)) {
469 dbus_message_iter_next(&st_iter);
470 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
472 if (!NT_STATUS_IS_OK(status)) {
476 dbus_message_iter_next(&st_iter);
477 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT64,
479 if (!NT_STATUS_IS_OK(status)) {
483 dbus_message_iter_next(&st_iter);
484 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
485 &snap_out->creator_uid);
486 if (!NT_STATUS_IS_OK(status)) {
490 dbus_message_iter_next(&st_iter);
491 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
493 if (!NT_STATUS_IS_OK(status)) {
497 dbus_message_iter_next(&st_iter);
498 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
500 if (!NT_STATUS_IS_OK(status)) {
504 dbus_message_iter_next(&st_iter);
505 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
506 &snap_out->num_user_data,
507 &snap_out->user_data);
512 static void snapper_snap_array_free(int32_t num_snaps,
513 struct snapper_snap *snaps)
517 for (i = 0; i < num_snaps; i++) {
518 talloc_free(snaps[i].user_data);
523 static void snapper_snap_array_print(int32_t num_snaps,
524 struct snapper_snap *snaps)
528 for (i = 0; i < num_snaps; i++) {
529 DEBUG(10, ("id: %u, "
540 snaps[i].creator_uid,
543 snapper_dict_array_print(snaps[i].num_user_data,
548 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
549 DBusMessageIter *iter,
550 uint32_t *num_snaps_out,
551 struct snapper_snap **snaps_out)
555 struct snapper_snap *snaps = NULL;
556 DBusMessageIter array_iter;
559 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
560 if (!NT_STATUS_IS_OK(status)) {
563 dbus_message_iter_recurse(iter, &array_iter);
566 while (dbus_message_iter_get_arg_type(&array_iter)
567 != DBUS_TYPE_INVALID) {
569 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
574 status = snapper_snap_struct_unpack(mem_ctx, &array_iter,
575 &snaps[num_snaps - 1]);
576 if (!NT_STATUS_IS_OK(status)) {
580 dbus_message_iter_next(&array_iter);
583 *num_snaps_out = num_snaps;
589 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
590 DBusMessage *rsp_msg,
591 uint32_t *num_snaps_out,
592 struct snapper_snap **snaps_out)
595 DBusMessageIter iter;
598 struct snapper_snap *snaps;
601 msg_type = dbus_message_get_type(rsp_msg);
602 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
603 DEBUG(0, ("list_snaps error response: %s\n",
604 dbus_message_get_error_name(rsp_msg)));
605 return NT_STATUS_INVALID_PARAMETER;
608 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
609 DEBUG(0,("unexpected list_snaps ret type: %d\n",
611 return NT_STATUS_INVALID_PARAMETER;
614 sig = dbus_message_get_signature(rsp_msg);
616 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
617 DEBUG(0, ("bad list snaps response sig: %s, "
619 (sig ? sig : "NULL"),
620 SNAPPER_SIG_LIST_SNAPS_RSP));
621 return NT_STATUS_INVALID_PARAMETER;
624 /* read the parameters */
625 if (!dbus_message_iter_init(rsp_msg, &iter)) {
626 DEBUG(0, ("response has no arguments!\n"));
627 return NT_STATUS_INVALID_PARAMETER;
630 status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
631 if (!NT_STATUS_IS_OK(status)) {
632 DEBUG(0, ("failed to unpack snap array\n"));
633 return NT_STATUS_INVALID_PARAMETER;
636 snapper_snap_array_print(num_snaps, snaps);
638 *num_snaps_out = num_snaps;
644 static NTSTATUS snapper_create_snap_pack(const char *snapper_conf,
646 uint32_t num_user_data,
647 struct snapper_dict *user_data,
648 DBusMessage **req_msg_out)
651 DBusMessageIter args;
652 DBusMessageIter array_iter;
653 DBusMessageIter struct_iter;
654 const char *empty = "";
658 DEBUG(10, ("CreateSingleSnapshot: %s, %s, %s, num user %u\n",
659 snapper_conf, desc, empty, num_user_data));
661 msg = dbus_message_new_method_call("org.opensuse.Snapper",
662 "/org/opensuse/Snapper",
663 "org.opensuse.Snapper",
664 "CreateSingleSnapshot");
666 DEBUG(0, ("failed to create req msg\n"));
667 return NT_STATUS_NO_MEMORY;
670 /* append arguments */
671 dbus_message_iter_init_append(msg, &args);
672 ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
675 return NT_STATUS_NO_MEMORY;
678 ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
681 return NT_STATUS_NO_MEMORY;
685 ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
688 return NT_STATUS_NO_MEMORY;
691 ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
692 SNAPPER_SIG_STRING_DICT,
695 return NT_STATUS_NO_MEMORY;
698 for (i = 0; i < num_user_data; i++) {
699 ok = dbus_message_iter_open_container(&array_iter,
700 DBUS_TYPE_DICT_ENTRY,
703 return NT_STATUS_NO_MEMORY;
706 ok = dbus_message_iter_append_basic(&struct_iter,
710 return NT_STATUS_NO_MEMORY;
712 ok = dbus_message_iter_append_basic(&struct_iter,
716 return NT_STATUS_NO_MEMORY;
719 ok = dbus_message_iter_close_container(&array_iter, &struct_iter);
721 return NT_STATUS_NO_MEMORY;
725 ok = dbus_message_iter_close_container(&args, &array_iter);
727 return NT_STATUS_NO_MEMORY;
735 static NTSTATUS snapper_create_snap_unpack(DBusConnection *conn,
736 DBusMessage *rsp_msg,
737 uint32_t *snap_id_out)
740 DBusMessageIter iter;
745 msg_type = dbus_message_get_type(rsp_msg);
746 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
747 DEBUG(0, ("create snap error response: %s\n",
748 dbus_message_get_error_name(rsp_msg)));
749 DEBUG(0, ("euid %d egid %d\n", geteuid(), getegid()));
750 return NT_STATUS_INVALID_PARAMETER;
753 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
754 DEBUG(0, ("unexpected create snap ret type: %d\n",
756 return NT_STATUS_INVALID_PARAMETER;
759 sig = dbus_message_get_signature(rsp_msg);
761 || (strcmp(sig, SNAPPER_SIG_CREATE_SNAP_RSP) != 0)) {
762 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
763 (sig ? sig : "NULL"), SNAPPER_SIG_CREATE_SNAP_RSP));
764 return NT_STATUS_INVALID_PARAMETER;
767 /* read the parameters */
768 if (!dbus_message_iter_init(rsp_msg, &iter)) {
769 DEBUG(0, ("response has no arguments!\n"));
770 return NT_STATUS_INVALID_PARAMETER;
773 status = snapper_type_check_get(&iter, DBUS_TYPE_UINT32, &snap_id);
774 if (!NT_STATUS_IS_OK(status)) {
777 *snap_id_out = snap_id;
782 static NTSTATUS snapper_del_snap_pack(const char *snapper_conf,
784 DBusMessage **req_msg_out)
787 DBusMessageIter args;
788 DBusMessageIter array_iter;
791 msg = dbus_message_new_method_call("org.opensuse.Snapper",
792 "/org/opensuse/Snapper",
793 "org.opensuse.Snapper",
796 DEBUG(0, ("failed to create req msg\n"));
797 return NT_STATUS_NO_MEMORY;
800 /* append arguments */
801 dbus_message_iter_init_append(msg, &args);
802 ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
805 return NT_STATUS_NO_MEMORY;
808 ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
809 DBUS_TYPE_UINT32_AS_STRING,
812 return NT_STATUS_NO_MEMORY;
815 ok = dbus_message_iter_append_basic(&array_iter,
819 return NT_STATUS_NO_MEMORY;
822 dbus_message_iter_close_container(&args, &array_iter);
828 static NTSTATUS snapper_del_snap_unpack(DBusConnection *conn,
829 DBusMessage *rsp_msg)
834 msg_type = dbus_message_get_type(rsp_msg);
835 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
836 DEBUG(0, ("del snap error response: %s\n",
837 dbus_message_get_error_name(rsp_msg)));
838 return NT_STATUS_INVALID_PARAMETER;
841 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
842 DEBUG(0, ("unexpected del snap ret type: %d\n",
844 return NT_STATUS_INVALID_PARAMETER;
847 sig = dbus_message_get_signature(rsp_msg);
849 || (strcmp(sig, SNAPPER_SIG_DEL_SNAPS_RSP) != 0)) {
850 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
851 (sig ? sig : "NULL"), SNAPPER_SIG_DEL_SNAPS_RSP));
852 return NT_STATUS_INVALID_PARAMETER;
855 /* no parameters in response */
860 static NTSTATUS snapper_list_snaps_at_time_pack(const char *snapper_conf,
863 DBusMessage **req_msg_out)
866 DBusMessageIter args;
868 msg = dbus_message_new_method_call("org.opensuse.Snapper",
869 "/org/opensuse/Snapper",
870 "org.opensuse.Snapper",
871 "ListSnapshotsAtTime");
873 DEBUG(0, ("failed to create list snaps message\n"));
874 return NT_STATUS_NO_MEMORY;
877 dbus_message_iter_init_append(msg, &args);
878 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
880 return NT_STATUS_NO_MEMORY;
883 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64,
885 return NT_STATUS_NO_MEMORY;
888 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64,
890 return NT_STATUS_NO_MEMORY;
897 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
900 * Determine the snapper snapshot id given a path.
901 * Ideally this should be determined via a lookup.
903 static NTSTATUS snapper_snap_path_to_id(TALLOC_CTX *mem_ctx,
904 const char *snap_path,
905 uint32_t *snap_id_out)
912 path_dup = talloc_strdup(mem_ctx, snap_path);
913 if (path_dup == NULL) {
914 return NT_STATUS_NO_MEMORY;
917 /* trim trailing '/' */
918 str_idx = path_dup + strlen(path_dup) - 1;
919 while (*str_idx == '/') {
924 str_idx = strrchr(path_dup, '/');
925 if ((str_idx == NULL)
926 || (strcmp(str_idx + 1, "snapshot") != 0)) {
927 talloc_free(path_dup);
928 return NT_STATUS_INVALID_PARAMETER;
931 while (*str_idx == '/') {
936 str_idx = strrchr(path_dup, '/');
937 if (str_idx == NULL) {
938 talloc_free(path_dup);
939 return NT_STATUS_INVALID_PARAMETER;
943 snap_id = strtoul(str_idx, &str_end, 10);
944 if (str_idx == str_end) {
945 talloc_free(path_dup);
946 return NT_STATUS_INVALID_PARAMETER;
949 talloc_free(path_dup);
950 *snap_id_out = snap_id;
955 * Determine the snapper snapshot path given an id and base.
956 * Ideally this should be determined via a lookup.
958 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
959 const char *base_path,
961 char **snap_path_out)
965 snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
967 if (snap_path == NULL) {
968 return NT_STATUS_NO_MEMORY;
971 *snap_path_out = snap_path;
975 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
976 DBusConnection *dconn,
978 char **conf_name_out,
979 char **base_path_out)
982 DBusMessage *req_msg;
983 DBusMessage *rsp_msg;
984 uint32_t num_confs = 0;
985 struct snapper_conf *confs = NULL;
986 struct snapper_conf *conf;
990 status = snapper_list_confs_pack(&req_msg);
991 if (!NT_STATUS_IS_OK(status)) {
995 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
996 if (!NT_STATUS_IS_OK(status)) {
1000 status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
1001 &num_confs, &confs);
1002 if (!NT_STATUS_IS_OK(status)) {
1007 * for now we only support shares where the path directly corresponds
1008 * to a snapper configuration.
1010 conf = snapper_conf_array_base_find(num_confs, confs,
1013 status = NT_STATUS_NOT_SUPPORTED;
1014 goto err_array_free;
1017 conf_name = talloc_strdup(mem_ctx, conf->name);
1018 if (conf_name == NULL) {
1019 status = NT_STATUS_NO_MEMORY;
1020 goto err_array_free;
1022 base_path = talloc_strdup(mem_ctx, conf->mnt);
1023 if (base_path == NULL) {
1024 status = NT_STATUS_NO_MEMORY;
1025 goto err_conf_name_free;
1028 snapper_conf_array_free(num_confs, confs);
1029 dbus_message_unref(rsp_msg);
1030 dbus_message_unref(req_msg);
1032 *conf_name_out = conf_name;
1033 *base_path_out = base_path;
1035 return NT_STATUS_OK;
1038 talloc_free(conf_name);
1040 snapper_conf_array_free(num_confs, confs);
1042 dbus_message_unref(rsp_msg);
1044 dbus_message_unref(req_msg);
1050 * Check whether a path can be shadow copied. Return the base volume, allowing
1051 * the caller to determine if multiple paths lie on the same base volume.
1053 static NTSTATUS snapper_snap_check_path(struct vfs_handle_struct *handle,
1054 TALLOC_CTX *mem_ctx,
1055 const char *service_path,
1059 DBusConnection *dconn;
1063 dconn = snapper_dbus_conn();
1064 if (dconn == NULL) {
1065 return NT_STATUS_UNSUCCESSFUL;
1068 status = snapper_get_conf_call(mem_ctx, dconn, service_path,
1069 &conf_name, &base_path);
1070 if (!NT_STATUS_IS_OK(status)) {
1071 goto err_conn_close;
1074 talloc_free(conf_name);
1075 *base_volume = base_path;
1076 dbus_connection_unref(dconn);
1078 return NT_STATUS_OK;
1081 dbus_connection_unref(dconn);
1085 static NTSTATUS snapper_create_snap_call(TALLOC_CTX *mem_ctx,
1086 DBusConnection *dconn,
1087 const char *conf_name,
1088 const char *base_path,
1089 const char *snap_desc,
1090 uint32_t num_user_data,
1091 struct snapper_dict *user_data,
1092 char **snap_path_out)
1095 DBusMessage *req_msg;
1096 DBusMessage *rsp_msg;
1100 status = snapper_create_snap_pack(conf_name,
1105 if (!NT_STATUS_IS_OK(status)) {
1109 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1110 if (!NT_STATUS_IS_OK(status)) {
1114 status = snapper_create_snap_unpack(dconn, rsp_msg, &snap_id);
1115 if (!NT_STATUS_IS_OK(status)) {
1119 status = snapper_snap_id_to_path(mem_ctx, base_path, snap_id,
1121 if (!NT_STATUS_IS_OK(status)) {
1125 dbus_message_unref(rsp_msg);
1126 dbus_message_unref(req_msg);
1128 DEBUG(6, ("created new snapshot %u at %s\n", snap_id, snap_path));
1129 *snap_path_out = snap_path;
1131 return NT_STATUS_OK;
1134 dbus_message_unref(rsp_msg);
1136 dbus_message_unref(req_msg);
1141 struct snapper_snap_create_state {
1148 static struct tevent_req *snapper_snap_create_send(struct vfs_handle_struct *handle,
1149 TALLOC_CTX *mem_ctx,
1150 struct tevent_context *ev,
1151 const char *base_volume,
1155 struct tevent_req *req;
1156 DBusConnection *dconn;
1157 struct snapper_snap_create_state *create_state;
1160 req = tevent_req_create(mem_ctx, &create_state,
1161 struct snapper_snap_create_state);
1166 dconn = snapper_dbus_conn();
1167 if (dconn == NULL) {
1168 tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
1169 return tevent_req_post(req, ev);
1172 status = snapper_get_conf_call(create_state, dconn, base_volume,
1173 &create_state->conf_name,
1174 &create_state->base_path);
1175 if (tevent_req_nterror(req, status)) {
1176 dbus_connection_unref(dconn);
1177 return tevent_req_post(req, ev);
1180 status = snapper_create_snap_call(create_state, dconn,
1181 create_state->conf_name,
1182 create_state->base_path,
1183 "Snapshot created by Samba",
1185 &create_state->snap_path);
1186 if (tevent_req_nterror(req, status)) {
1187 dbus_connection_unref(dconn);
1188 return tevent_req_post(req, ev);
1191 dbus_connection_unref(dconn);
1193 tevent_req_done(req);
1194 return tevent_req_post(req, ev);
1197 static NTSTATUS snapper_snap_create_recv(struct vfs_handle_struct *handle,
1198 struct tevent_req *req,
1199 TALLOC_CTX *mem_ctx,
1200 char **base_path, char **snap_path)
1202 struct snapper_snap_create_state *create_state = tevent_req_data(req,
1203 struct snapper_snap_create_state);
1206 if (tevent_req_is_nterror(req, &status)) {
1207 tevent_req_received(req);
1211 *base_path = talloc_steal(mem_ctx, create_state->base_path);
1212 if (*base_path == NULL) {
1213 tevent_req_received(req);
1214 return NT_STATUS_NO_MEMORY;
1216 *snap_path = talloc_steal(mem_ctx, create_state->snap_path);
1217 if (*snap_path == NULL) {
1218 tevent_req_received(req);
1219 return NT_STATUS_NO_MEMORY;
1222 tevent_req_received(req);
1223 return NT_STATUS_OK;
1226 static NTSTATUS snapper_delete_snap_call(TALLOC_CTX *mem_ctx,
1227 DBusConnection *dconn,
1228 const char *conf_name,
1232 DBusMessage *req_msg;
1233 DBusMessage *rsp_msg;
1235 status = snapper_del_snap_pack(conf_name, snap_id, &req_msg);
1236 if (!NT_STATUS_IS_OK(status)) {
1240 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1241 if (!NT_STATUS_IS_OK(status)) {
1245 status = snapper_del_snap_unpack(dconn, rsp_msg);
1246 if (!NT_STATUS_IS_OK(status)) {
1250 dbus_message_unref(rsp_msg);
1251 dbus_message_unref(req_msg);
1253 DEBUG(6, ("deleted snapshot %u\n", snap_id));
1255 return NT_STATUS_OK;
1258 dbus_message_unref(rsp_msg);
1260 dbus_message_unref(req_msg);
1265 struct snapper_snap_delete_state {
1272 static struct tevent_req *snapper_snap_delete_send(struct vfs_handle_struct *handle,
1273 TALLOC_CTX *mem_ctx,
1274 struct tevent_context *ev,
1278 struct tevent_req *req;
1279 DBusConnection *dconn;
1280 struct snapper_snap_delete_state *delete_state;
1283 req = tevent_req_create(mem_ctx, &delete_state,
1284 struct snapper_snap_delete_state);
1289 dconn = snapper_dbus_conn();
1290 if (dconn == NULL) {
1291 tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
1292 return tevent_req_post(req, ev);
1295 status = snapper_get_conf_call(delete_state, dconn, base_path,
1296 &delete_state->conf_name,
1297 &delete_state->base_path);
1298 if (tevent_req_nterror(req, status)) {
1299 dbus_connection_unref(dconn);
1300 return tevent_req_post(req, ev);
1303 status = snapper_snap_path_to_id(delete_state, snap_path,
1304 &delete_state->snap_id);
1305 if (tevent_req_nterror(req, status)) {
1306 dbus_connection_unref(dconn);
1307 return tevent_req_post(req, ev);
1310 status = snapper_delete_snap_call(delete_state, dconn,
1311 delete_state->conf_name,
1312 delete_state->snap_id);
1313 if (tevent_req_nterror(req, status)) {
1314 dbus_connection_unref(dconn);
1315 return tevent_req_post(req, ev);
1318 dbus_connection_unref(dconn);
1320 tevent_req_done(req);
1321 return tevent_req_post(req, ev);
1324 static NTSTATUS snapper_snap_delete_recv(struct vfs_handle_struct *handle,
1325 struct tevent_req *req)
1329 if (tevent_req_is_nterror(req, &status)) {
1330 tevent_req_received(req);
1333 tevent_req_received(req);
1334 return NT_STATUS_OK;
1337 /* sc_data used as parent talloc context for all labels */
1338 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
1339 struct files_struct *fsp,
1340 struct shadow_copy_data *sc_data,
1343 DBusConnection *dconn;
1344 TALLOC_CTX *tmp_ctx;
1348 DBusMessage *req_msg;
1349 DBusMessage *rsp_msg;
1351 struct snapper_snap *snaps;
1355 tmp_ctx = talloc_new(sc_data);
1356 if (tmp_ctx == NULL) {
1357 status = NT_STATUS_NO_MEMORY;
1361 dconn = snapper_dbus_conn();
1362 if (dconn == NULL) {
1363 status = NT_STATUS_UNSUCCESSFUL;
1364 goto err_mem_ctx_free;
1367 if (fsp->conn->connectpath == NULL) {
1368 status = NT_STATUS_INVALID_PARAMETER;
1372 status = snapper_get_conf_call(tmp_ctx, dconn,
1373 fsp->conn->connectpath,
1376 if (!NT_STATUS_IS_OK(status)) {
1380 status = snapper_list_snaps_pack(conf_name, &req_msg);
1381 if (!NT_STATUS_IS_OK(status)) {
1385 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1386 if (!NT_STATUS_IS_OK(status)) {
1390 status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
1391 &num_snaps, &snaps);
1392 if (!NT_STATUS_IS_OK(status)) {
1395 /* we should always get at least one snapshot (current) */
1396 if (num_snaps == 0) {
1397 DEBUG(6, ("zero snapshots in snap list response\n"));
1401 /* subtract 1, (current) snapshot is not returned */
1402 sc_data->num_volumes = num_snaps - 1;
1403 sc_data->labels = NULL;
1405 if ((labels == false) || (sc_data->num_volumes == 0)) {
1406 /* tokens need not be added to the labels array */
1410 sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
1411 sc_data->num_volumes);
1412 if (sc_data->labels == NULL) {
1413 status = NT_STATUS_NO_MEMORY;
1417 /* start at end for decending order, do not include 0 (current) */
1419 for (i = num_snaps - 1; i > 0; i--) {
1420 char *lbl = sc_data->labels[lbl_off++];
1421 struct tm gmt_snap_time;
1425 tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
1426 if (tm_ret == NULL) {
1427 status = NT_STATUS_UNSUCCESSFUL;
1428 goto err_labels_free;
1430 str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
1431 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
1433 status = NT_STATUS_UNSUCCESSFUL;
1434 goto err_labels_free;
1439 talloc_free(tmp_ctx);
1440 dbus_message_unref(rsp_msg);
1441 dbus_message_unref(req_msg);
1442 dbus_connection_unref(dconn);
1447 TALLOC_FREE(sc_data->labels);
1449 dbus_message_unref(rsp_msg);
1451 dbus_message_unref(req_msg);
1453 dbus_connection_unref(dconn);
1455 talloc_free(tmp_ctx);
1457 /* all errors are collapsed to a -1 return code */
1461 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1462 DBusConnection *dconn,
1463 const char *conf_name,
1464 const char *base_path,
1466 char **snap_path_out)
1469 DBusMessage *req_msg;
1470 DBusMessage *rsp_msg;
1472 struct snapper_snap *snaps;
1475 status = snapper_list_snaps_at_time_pack(conf_name,
1479 if (!NT_STATUS_IS_OK(status)) {
1483 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1484 if (!NT_STATUS_IS_OK(status)) {
1488 status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1489 &num_snaps, &snaps);
1490 if (!NT_STATUS_IS_OK(status)) {
1494 if (num_snaps == 0) {
1495 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime));
1496 status = NT_STATUS_INVALID_PARAMETER;
1497 goto err_snap_array_free;
1498 } else if (num_snaps > 0) {
1499 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1500 num_snaps, snaptime));
1503 status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1505 if (!NT_STATUS_IS_OK(status)) {
1506 goto err_snap_array_free;
1509 snapper_snap_array_free(num_snaps, snaps);
1510 dbus_message_unref(rsp_msg);
1511 dbus_message_unref(req_msg);
1513 *snap_path_out = snap_path;
1515 return NT_STATUS_OK;
1517 err_snap_array_free:
1518 snapper_snap_array_free(num_snaps, snaps);
1520 dbus_message_unref(rsp_msg);
1522 dbus_message_unref(req_msg);
1527 static NTSTATUS snapper_snap_dir_expand(struct vfs_handle_struct *handle,
1528 TALLOC_CTX *mem_ctx,
1530 char **snap_dir_out)
1532 DBusConnection *dconn;
1537 char *snap_path_nobase;
1539 dconn = snapper_dbus_conn();
1540 if (dconn == NULL) {
1541 status = NT_STATUS_UNSUCCESSFUL;
1545 if (handle->conn->connectpath == NULL) {
1546 status = NT_STATUS_INVALID_PARAMETER;
1550 status = snapper_get_conf_call(mem_ctx, dconn,
1551 handle->conn->connectpath,
1554 if (!NT_STATUS_IS_OK(status)) {
1558 status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1559 conf_name, base_path, snap_time,
1561 if (!NT_STATUS_IS_OK(status)) {
1562 goto err_conf_name_free;
1565 /* strip the base path from the snap path for return */
1566 if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1567 status = NT_STATUS_INVALID_PARAMETER;
1568 goto err_snap_path_free;
1571 snap_path_nobase = talloc_strdup(mem_ctx, snap_path + strlen(base_path));
1572 if (snap_path_nobase == NULL) {
1573 status = NT_STATUS_NO_MEMORY;
1574 goto err_snap_path_free;
1577 talloc_free(snap_path);
1578 talloc_free(conf_name);
1579 talloc_free(base_path);
1580 dbus_connection_unref(dconn);
1581 *snap_dir_out = snap_path_nobase;
1583 return NT_STATUS_OK;
1586 talloc_free(snap_path);
1588 talloc_free(conf_name);
1589 talloc_free(base_path);
1591 dbus_connection_unref(dconn);
1596 static struct vfs_fn_pointers snapper_fns = {
1597 .snap_check_path_fn = snapper_snap_check_path,
1598 .snap_create_send_fn = snapper_snap_create_send,
1599 .snap_create_recv_fn = snapper_snap_create_recv,
1600 .snap_delete_send_fn = snapper_snap_delete_send,
1601 .snap_delete_recv_fn = snapper_snap_delete_recv,
1602 .get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
1603 .snap_dir_expand_fn = snapper_snap_dir_expand,
1606 NTSTATUS vfs_snapper_init(void);
1607 NTSTATUS vfs_snapper_init(void)
1609 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1610 "snapper", &snapper_fns);