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"
40 #include "lib/util/smb_strtox.h"
42 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
43 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
44 #define SNAPPER_SIG_CREATE_SNAP_RSP "u"
45 #define SNAPPER_SIG_DEL_SNAPS_RSP ""
46 #define SNAPPER_SIG_STRING_DICT "{ss}"
61 uint32_t num_user_data
;
62 struct snapper_dict
*user_data
;
69 struct snapper_dict
*attrs
;
73 const char *snapper_err_str
;
75 } snapper_err_map
[] = {
76 { "error.no_permissions", NT_STATUS_ACCESS_DENIED
},
79 static NTSTATUS
snapper_err_ntstatus_map(const char *snapper_err_str
)
83 if (snapper_err_str
== NULL
) {
84 return NT_STATUS_UNSUCCESSFUL
;
86 for (i
= 0; i
< ARRAY_SIZE(snapper_err_map
); i
++) {
87 if (!strcmp(snapper_err_map
[i
].snapper_err_str
,
89 return snapper_err_map
[i
].status
;
92 DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str
));
94 return NT_STATUS_UNSUCCESSFUL
;
98 * Strings are UTF-8. Other characters must be encoded hexadecimal as "\x??".
99 * As a consequence "\" must be encoded as "\\".
101 static NTSTATUS
snapper_dbus_str_encode(TALLOC_CTX
*mem_ctx
, const char *in_str
,
110 if (in_str
== NULL
) {
111 return NT_STATUS_INVALID_PARAMETER
;
114 in_len
= strlen(in_str
);
116 /* output can be max 4 times the length of @in_str, +1 for terminator */
117 out_len
= (in_len
* 4) + 1;
119 out_str
= talloc_array(mem_ctx
, char, out_len
);
120 if (out_str
== NULL
) {
121 return NT_STATUS_NO_MEMORY
;
125 for (i
= 0; i
< in_len
; i
++) {
128 if (in_str
[i
] == '\\') {
129 pushed
= snprintf(out_str
+ out_off
, out_len
- out_off
,
131 } else if ((unsigned char)in_str
[i
] > 127) {
132 pushed
= snprintf(out_str
+ out_off
, out_len
- out_off
,
133 "\\x%02x", (unsigned char)in_str
[i
]);
135 /* regular character */
136 *(out_str
+ out_off
) = in_str
[i
];
137 pushed
= sizeof(char);
139 if (pushed
>= out_len
- out_off
) {
140 /* truncated, should never happen */
141 talloc_free(out_str
);
142 return NT_STATUS_INTERNAL_ERROR
;
147 *(out_str
+ out_off
) = '\0';
153 static NTSTATUS
snapper_dbus_str_decode(TALLOC_CTX
*mem_ctx
, const char *in_str
,
162 if (in_str
== NULL
) {
163 return NT_STATUS_INVALID_PARAMETER
;
166 in_len
= strlen(in_str
);
168 /* output cannot be larger than input, +1 for terminator */
169 out_len
= in_len
+ 1;
171 out_str
= talloc_array(mem_ctx
, char, out_len
);
172 if (out_str
== NULL
) {
173 return NT_STATUS_NO_MEMORY
;
177 for (i
= 0; i
< in_len
; i
++) {
180 unsigned int non_ascii_byte
;
182 if (in_str
[i
] != '\\') {
183 out_str
[out_off
] = in_str
[i
];
189 if (in_str
[i
] == '\\') {
190 out_str
[out_off
] = '\\';
193 } else if (in_str
[i
] != 'x') {
194 goto err_invalid_src_encoding
;
197 /* non-ASCII, encoded as two hex chars */
198 for (j
= 0; j
< 2; j
++) {
200 if ((in_str
[i
] == '\0') || !isxdigit(in_str
[i
])) {
201 goto err_invalid_src_encoding
;
203 hex_buf
[j
] = in_str
[i
];
207 sscanf(hex_buf
, "%x", &non_ascii_byte
);
208 out_str
[out_off
] = (unsigned char)non_ascii_byte
;
212 out_str
[out_off
] = '\0';
216 err_invalid_src_encoding
:
217 DEBUG(0, ("invalid encoding %s\n", in_str
));
218 return NT_STATUS_INVALID_PARAMETER
;
221 static DBusConnection
*snapper_dbus_conn_create(void)
224 DBusConnection
*dconn
;
226 dbus_error_init(&err
);
229 * Always create a new DBus connection, to ensure snapperd detects the
230 * correct client [E]UID. With dbus_bus_get() it does not!
232 dconn
= dbus_bus_get_private(DBUS_BUS_SYSTEM
, &err
);
233 if (dbus_error_is_set(&err
)) {
234 DEBUG(0, ("dbus connection error: %s\n", err
.message
));
235 dbus_error_free(&err
);
241 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
242 dbus_connection_set_exit_on_disconnect(dconn
, false);
247 static void snapper_dbus_conn_destroy(DBusConnection
*dconn
)
250 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
254 dbus_connection_close(dconn
);
255 dbus_connection_unref(dconn
);
259 * send the message @send_msg over the dbus and wait for a response, return the
260 * responsee via @recv_msg_out.
261 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
263 static NTSTATUS
snapper_dbus_msg_xchng(DBusConnection
*dconn
,
264 DBusMessage
*send_msg
,
265 DBusMessage
**recv_msg_out
)
267 DBusPendingCall
*pending
;
268 DBusMessage
*recv_msg
;
270 /* send message and get a handle for a reply */
271 if (!dbus_connection_send_with_reply(dconn
, send_msg
, &pending
, -1)) {
272 return NT_STATUS_NO_MEMORY
;
274 if (NULL
== pending
) {
275 DEBUG(0, ("dbus msg send failed\n"));
276 return NT_STATUS_UNSUCCESSFUL
;
279 dbus_connection_flush(dconn
);
281 /* block until we receive a reply */
282 dbus_pending_call_block(pending
);
284 /* get the reply message */
285 recv_msg
= dbus_pending_call_steal_reply(pending
);
286 if (recv_msg
== NULL
) {
287 DEBUG(0, ("Reply Null\n"));
288 return NT_STATUS_UNSUCCESSFUL
;
290 /* free the pending message handle */
291 dbus_pending_call_unref(pending
);
292 *recv_msg_out
= recv_msg
;
297 static NTSTATUS
snapper_type_check(DBusMessageIter
*iter
,
300 int type
= dbus_message_iter_get_arg_type(iter
);
301 if (type
!= expected_type
) {
302 DEBUG(0, ("got type %d, expecting %d\n",
303 type
, expected_type
));
304 return NT_STATUS_INVALID_PARAMETER
;
310 static NTSTATUS
snapper_type_check_get(DBusMessageIter
*iter
,
315 status
= snapper_type_check(iter
, expected_type
);
316 if (!NT_STATUS_IS_OK(status
)) {
320 dbus_message_iter_get_basic(iter
, val
);
325 static NTSTATUS
snapper_dict_unpack(TALLOC_CTX
*mem_ctx
,
326 DBusMessageIter
*iter
,
327 struct snapper_dict
*dict_out
)
331 DBusMessageIter dct_iter
;
335 status
= snapper_type_check(iter
, DBUS_TYPE_DICT_ENTRY
);
336 if (!NT_STATUS_IS_OK(status
)) {
339 dbus_message_iter_recurse(iter
, &dct_iter
);
341 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
343 if (!NT_STATUS_IS_OK(status
)) {
346 status
= snapper_dbus_str_decode(mem_ctx
, key_encoded
, &dict_out
->key
);
347 if (!NT_STATUS_IS_OK(status
)) {
351 dbus_message_iter_next(&dct_iter
);
352 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
354 if (!NT_STATUS_IS_OK(status
)) {
355 talloc_free(dict_out
->key
);
358 status
= snapper_dbus_str_decode(mem_ctx
, val_encoded
, &dict_out
->val
);
359 if (!NT_STATUS_IS_OK(status
)) {
360 talloc_free(dict_out
->key
);
367 static void snapper_dict_array_print(uint32_t num_dicts
,
368 struct snapper_dict
*dicts
)
372 for (i
= 0; i
< num_dicts
; i
++) {
373 DEBUG(10, ("dict (key: %s, val: %s)\n",
374 dicts
[i
].key
, dicts
[i
].val
));
378 static NTSTATUS
snapper_dict_array_unpack(TALLOC_CTX
*mem_ctx
,
379 DBusMessageIter
*iter
,
380 uint32_t *num_dicts_out
,
381 struct snapper_dict
**dicts_out
)
384 DBusMessageIter array_iter
;
386 struct snapper_dict
*dicts
= NULL
;
388 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
389 if (!NT_STATUS_IS_OK(status
)) {
392 dbus_message_iter_recurse(iter
, &array_iter
);
395 while (dbus_message_iter_get_arg_type(&array_iter
)
396 != DBUS_TYPE_INVALID
) {
398 dicts
= talloc_realloc(mem_ctx
, dicts
, struct snapper_dict
,
403 status
= snapper_dict_unpack(mem_ctx
, &array_iter
,
404 &dicts
[num_dicts
- 1]);
405 if (!NT_STATUS_IS_OK(status
)) {
409 dbus_message_iter_next(&array_iter
);
412 *num_dicts_out
= num_dicts
;
418 static NTSTATUS
snapper_list_confs_pack(DBusMessage
**req_msg_out
)
422 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
423 "/org/opensuse/Snapper",
424 "org.opensuse.Snapper",
427 DEBUG(0, ("null msg\n"));
428 return NT_STATUS_NO_MEMORY
;
431 /* no arguments to append */
437 static NTSTATUS
snapper_conf_unpack(TALLOC_CTX
*mem_ctx
,
438 DBusMessageIter
*iter
,
439 struct snapper_conf
*conf_out
)
442 DBusMessageIter st_iter
;
446 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
447 if (!NT_STATUS_IS_OK(status
)) {
450 dbus_message_iter_recurse(iter
, &st_iter
);
452 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
454 if (!NT_STATUS_IS_OK(status
)) {
458 status
= snapper_dbus_str_decode(mem_ctx
, name_encoded
,
460 if (!NT_STATUS_IS_OK(status
)) {
464 dbus_message_iter_next(&st_iter
);
465 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
467 if (!NT_STATUS_IS_OK(status
)) {
468 talloc_free(conf_out
->name
);
472 status
= snapper_dbus_str_decode(mem_ctx
, mnt_encoded
,
474 if (!NT_STATUS_IS_OK(status
)) {
475 talloc_free(conf_out
->name
);
479 dbus_message_iter_next(&st_iter
);
480 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
481 &conf_out
->num_attrs
,
483 if (!NT_STATUS_IS_OK(status
)) {
484 talloc_free(conf_out
->mnt
);
485 talloc_free(conf_out
->name
);
492 static struct snapper_conf
*snapper_conf_array_base_find(int32_t num_confs
,
493 struct snapper_conf
*confs
,
498 for (i
= 0; i
< num_confs
; i
++) {
499 if (strcmp(confs
[i
].mnt
, base
) == 0) {
500 DEBUG(5, ("found snapper conf %s for path %s\n",
501 confs
[i
].name
, base
));
505 DEBUG(5, ("config for base %s not found\n", base
));
510 static void snapper_conf_array_print(int32_t num_confs
,
511 struct snapper_conf
*confs
)
515 for (i
= 0; i
< num_confs
; i
++) {
516 DEBUG(10, ("name: %s, mnt: %s\n",
517 confs
[i
].name
, confs
[i
].mnt
));
518 snapper_dict_array_print(confs
[i
].num_attrs
, confs
[i
].attrs
);
522 static NTSTATUS
snapper_conf_array_unpack(TALLOC_CTX
*mem_ctx
,
523 DBusMessageIter
*iter
,
524 uint32_t *num_confs_out
,
525 struct snapper_conf
**confs_out
)
529 struct snapper_conf
*confs
= NULL
;
530 DBusMessageIter array_iter
;
533 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
534 if (!NT_STATUS_IS_OK(status
)) {
537 dbus_message_iter_recurse(iter
, &array_iter
);
540 while (dbus_message_iter_get_arg_type(&array_iter
)
541 != DBUS_TYPE_INVALID
) {
543 confs
= talloc_realloc(mem_ctx
, confs
, struct snapper_conf
,
548 status
= snapper_conf_unpack(confs
, &array_iter
,
549 &confs
[num_confs
- 1]);
550 if (!NT_STATUS_IS_OK(status
)) {
554 dbus_message_iter_next(&array_iter
);
557 *num_confs_out
= num_confs
;
563 static NTSTATUS
snapper_list_confs_unpack(TALLOC_CTX
*mem_ctx
,
564 DBusConnection
*dconn
,
565 DBusMessage
*rsp_msg
,
566 uint32_t *num_confs_out
,
567 struct snapper_conf
**confs_out
)
570 DBusMessageIter iter
;
573 struct snapper_conf
*confs
;
576 msg_type
= dbus_message_get_type(rsp_msg
);
577 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
578 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
579 DEBUG(0, ("list_confs error response: %s\n", err_str
));
580 return snapper_err_ntstatus_map(err_str
);
583 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
584 DEBUG(0, ("unexpected list_confs ret type: %d\n",
586 return NT_STATUS_INVALID_PARAMETER
;
589 sig
= dbus_message_get_signature(rsp_msg
);
591 || (strcmp(sig
, SNAPPER_SIG_LIST_CONFS_RSP
) != 0)) {
592 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
593 (sig
? sig
: "NULL"), SNAPPER_SIG_LIST_CONFS_RSP
));
594 return NT_STATUS_INVALID_PARAMETER
;
597 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
598 /* FIXME return empty? */
599 DEBUG(0, ("Message has no arguments!\n"));
600 return NT_STATUS_INVALID_PARAMETER
;
603 status
= snapper_conf_array_unpack(mem_ctx
, &iter
, &num_confs
, &confs
);
604 if (!NT_STATUS_IS_OK(status
)) {
605 DEBUG(0, ("failed to unpack conf array\n"));
609 snapper_conf_array_print(num_confs
, confs
);
611 *num_confs_out
= num_confs
;
617 static NTSTATUS
snapper_list_snaps_pack(TALLOC_CTX
*mem_ctx
,
619 DBusMessage
**req_msg_out
)
622 DBusMessageIter args
;
626 msg
= dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
627 "/org/opensuse/Snapper", /* object to call on */
628 "org.opensuse.Snapper", /* interface to call on */
629 "ListSnapshots"); /* method name */
631 DEBUG(0, ("failed to create list snaps message\n"));
632 return NT_STATUS_NO_MEMORY
;
635 status
= snapper_dbus_str_encode(mem_ctx
, snapper_conf
, &conf_encoded
);
636 if (!NT_STATUS_IS_OK(status
)) {
637 dbus_message_unref(msg
);
641 /* append arguments */
642 dbus_message_iter_init_append(msg
, &args
);
643 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
645 talloc_free(conf_encoded
);
646 dbus_message_unref(msg
);
647 return NT_STATUS_NO_MEMORY
;
655 static NTSTATUS
snapper_snap_struct_unpack(TALLOC_CTX
*mem_ctx
,
656 DBusMessageIter
*iter
,
657 struct snapper_snap
*snap_out
)
660 DBusMessageIter st_iter
;
662 char *cleanup_encoded
;
664 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
665 if (!NT_STATUS_IS_OK(status
)) {
668 dbus_message_iter_recurse(iter
, &st_iter
);
670 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
672 if (!NT_STATUS_IS_OK(status
)) {
676 dbus_message_iter_next(&st_iter
);
677 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT16
,
679 if (!NT_STATUS_IS_OK(status
)) {
683 dbus_message_iter_next(&st_iter
);
684 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
686 if (!NT_STATUS_IS_OK(status
)) {
690 dbus_message_iter_next(&st_iter
);
691 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_INT64
,
693 if (!NT_STATUS_IS_OK(status
)) {
697 dbus_message_iter_next(&st_iter
);
698 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
699 &snap_out
->creator_uid
);
700 if (!NT_STATUS_IS_OK(status
)) {
704 dbus_message_iter_next(&st_iter
);
705 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
707 if (!NT_STATUS_IS_OK(status
)) {
711 status
= snapper_dbus_str_decode(mem_ctx
, desc_encoded
,
713 if (!NT_STATUS_IS_OK(status
)) {
717 dbus_message_iter_next(&st_iter
);
718 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
720 if (!NT_STATUS_IS_OK(status
)) {
721 talloc_free(snap_out
->desc
);
725 status
= snapper_dbus_str_decode(mem_ctx
, cleanup_encoded
,
727 if (!NT_STATUS_IS_OK(status
)) {
728 talloc_free(snap_out
->desc
);
732 dbus_message_iter_next(&st_iter
);
733 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
734 &snap_out
->num_user_data
,
735 &snap_out
->user_data
);
736 if (!NT_STATUS_IS_OK(status
)) {
737 talloc_free(snap_out
->cleanup
);
738 talloc_free(snap_out
->desc
);
745 static void snapper_snap_array_print(int32_t num_snaps
,
746 struct snapper_snap
*snaps
)
750 for (i
= 0; i
< num_snaps
; i
++) {
751 DEBUG(10, ("id: %u, "
758 (unsigned int)snaps
[i
].id
,
759 (unsigned int)snaps
[i
].type
,
760 (unsigned int)snaps
[i
].pre_id
,
761 (long int)snaps
[i
].time
,
762 (unsigned int)snaps
[i
].creator_uid
,
765 snapper_dict_array_print(snaps
[i
].num_user_data
,
770 static NTSTATUS
snapper_snap_array_unpack(TALLOC_CTX
*mem_ctx
,
771 DBusMessageIter
*iter
,
772 uint32_t *num_snaps_out
,
773 struct snapper_snap
**snaps_out
)
777 struct snapper_snap
*snaps
= NULL
;
778 DBusMessageIter array_iter
;
781 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
782 if (!NT_STATUS_IS_OK(status
)) {
785 dbus_message_iter_recurse(iter
, &array_iter
);
788 while (dbus_message_iter_get_arg_type(&array_iter
)
789 != DBUS_TYPE_INVALID
) {
791 snaps
= talloc_realloc(mem_ctx
, snaps
, struct snapper_snap
,
796 status
= snapper_snap_struct_unpack(snaps
, &array_iter
,
797 &snaps
[num_snaps
- 1]);
798 if (!NT_STATUS_IS_OK(status
)) {
802 dbus_message_iter_next(&array_iter
);
805 *num_snaps_out
= num_snaps
;
811 static NTSTATUS
snapper_list_snaps_unpack(TALLOC_CTX
*mem_ctx
,
812 DBusMessage
*rsp_msg
,
813 uint32_t *num_snaps_out
,
814 struct snapper_snap
**snaps_out
)
817 DBusMessageIter iter
;
820 struct snapper_snap
*snaps
;
823 msg_type
= dbus_message_get_type(rsp_msg
);
824 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
825 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
826 DEBUG(0, ("list_snaps error response: %s\n", err_str
));
827 return snapper_err_ntstatus_map(err_str
);
830 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
831 DEBUG(0,("unexpected list_snaps ret type: %d\n",
833 return NT_STATUS_INVALID_PARAMETER
;
836 sig
= dbus_message_get_signature(rsp_msg
);
838 || (strcmp(sig
, SNAPPER_SIG_LIST_SNAPS_RSP
) != 0)) {
839 DEBUG(0, ("bad list snaps response sig: %s, "
841 (sig
? sig
: "NULL"),
842 SNAPPER_SIG_LIST_SNAPS_RSP
));
843 return NT_STATUS_INVALID_PARAMETER
;
846 /* read the parameters */
847 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
848 DEBUG(0, ("response has no arguments!\n"));
849 return NT_STATUS_INVALID_PARAMETER
;
852 status
= snapper_snap_array_unpack(mem_ctx
, &iter
, &num_snaps
, &snaps
);
853 if (!NT_STATUS_IS_OK(status
)) {
854 DEBUG(0, ("failed to unpack snap array\n"));
855 return NT_STATUS_INVALID_PARAMETER
;
858 snapper_snap_array_print(num_snaps
, snaps
);
860 *num_snaps_out
= num_snaps
;
866 static NTSTATUS
snapper_create_snap_pack(TALLOC_CTX
*mem_ctx
,
867 const char *snapper_conf
,
869 uint32_t num_user_data
,
870 struct snapper_dict
*user_data
,
871 DBusMessage
**req_msg_out
)
874 DBusMessageIter args
;
875 DBusMessageIter array_iter
;
876 DBusMessageIter struct_iter
;
877 const char *empty
= "";
884 DEBUG(10, ("CreateSingleSnapshot: %s, %s, %s, num user %u\n",
885 snapper_conf
, desc
, empty
, num_user_data
));
887 enc_ctx
= talloc_new(mem_ctx
);
888 if (enc_ctx
== NULL
) {
889 return NT_STATUS_NO_MEMORY
;
892 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
893 "/org/opensuse/Snapper",
894 "org.opensuse.Snapper",
895 "CreateSingleSnapshot");
897 DEBUG(0, ("failed to create req msg\n"));
898 talloc_free(enc_ctx
);
899 return NT_STATUS_NO_MEMORY
;
902 status
= snapper_dbus_str_encode(enc_ctx
, snapper_conf
, &str_encoded
);
903 if (!NT_STATUS_IS_OK(status
)) {
904 dbus_message_unref(msg
);
905 talloc_free(enc_ctx
);
909 /* append arguments */
910 dbus_message_iter_init_append(msg
, &args
);
911 ok
= dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
914 dbus_message_unref(msg
);
915 talloc_free(enc_ctx
);
916 return NT_STATUS_NO_MEMORY
;
919 status
= snapper_dbus_str_encode(enc_ctx
, desc
, &str_encoded
);
920 if (!NT_STATUS_IS_OK(status
)) {
921 dbus_message_unref(msg
);
922 talloc_free(enc_ctx
);
926 ok
= dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
929 dbus_message_unref(msg
);
930 talloc_free(enc_ctx
);
931 return NT_STATUS_NO_MEMORY
;
934 /* cleanup - no need to encode empty string */
935 ok
= dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
938 dbus_message_unref(msg
);
939 talloc_free(enc_ctx
);
940 return NT_STATUS_NO_MEMORY
;
943 ok
= dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
,
944 SNAPPER_SIG_STRING_DICT
,
947 dbus_message_unref(msg
);
948 talloc_free(enc_ctx
);
949 return NT_STATUS_NO_MEMORY
;
952 for (i
= 0; i
< num_user_data
; i
++) {
953 ok
= dbus_message_iter_open_container(&array_iter
,
954 DBUS_TYPE_DICT_ENTRY
,
957 dbus_message_unref(msg
);
958 talloc_free(enc_ctx
);
959 return NT_STATUS_NO_MEMORY
;
962 status
= snapper_dbus_str_encode(enc_ctx
, user_data
[i
].key
,
964 if (!NT_STATUS_IS_OK(status
)) {
965 dbus_message_unref(msg
);
966 talloc_free(enc_ctx
);
970 ok
= dbus_message_iter_append_basic(&struct_iter
,
974 dbus_message_unref(msg
);
975 talloc_free(enc_ctx
);
976 return NT_STATUS_NO_MEMORY
;
979 status
= snapper_dbus_str_encode(enc_ctx
, user_data
[i
].val
,
981 if (!NT_STATUS_IS_OK(status
)) {
982 dbus_message_unref(msg
);
983 talloc_free(enc_ctx
);
987 ok
= dbus_message_iter_append_basic(&struct_iter
,
991 dbus_message_unref(msg
);
992 talloc_free(enc_ctx
);
993 return NT_STATUS_NO_MEMORY
;
996 ok
= dbus_message_iter_close_container(&array_iter
, &struct_iter
);
998 dbus_message_unref(msg
);
999 talloc_free(enc_ctx
);
1000 return NT_STATUS_NO_MEMORY
;
1004 ok
= dbus_message_iter_close_container(&args
, &array_iter
);
1006 dbus_message_unref(msg
);
1007 talloc_free(enc_ctx
);
1008 return NT_STATUS_NO_MEMORY
;
1013 return NT_STATUS_OK
;
1016 static NTSTATUS
snapper_create_snap_unpack(DBusConnection
*conn
,
1017 DBusMessage
*rsp_msg
,
1018 uint32_t *snap_id_out
)
1021 DBusMessageIter iter
;
1026 msg_type
= dbus_message_get_type(rsp_msg
);
1027 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
1028 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
1029 DEBUG(0, ("create snap error response: %s, euid %d egid %d\n",
1030 err_str
, geteuid(), getegid()));
1031 return snapper_err_ntstatus_map(err_str
);
1034 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
1035 DEBUG(0, ("unexpected create snap ret type: %d\n",
1037 return NT_STATUS_INVALID_PARAMETER
;
1040 sig
= dbus_message_get_signature(rsp_msg
);
1042 || (strcmp(sig
, SNAPPER_SIG_CREATE_SNAP_RSP
) != 0)) {
1043 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
1044 (sig
? sig
: "NULL"), SNAPPER_SIG_CREATE_SNAP_RSP
));
1045 return NT_STATUS_INVALID_PARAMETER
;
1048 /* read the parameters */
1049 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
1050 DEBUG(0, ("response has no arguments!\n"));
1051 return NT_STATUS_INVALID_PARAMETER
;
1054 status
= snapper_type_check_get(&iter
, DBUS_TYPE_UINT32
, &snap_id
);
1055 if (!NT_STATUS_IS_OK(status
)) {
1058 *snap_id_out
= snap_id
;
1060 return NT_STATUS_OK
;
1063 static NTSTATUS
snapper_del_snap_pack(TALLOC_CTX
*mem_ctx
,
1064 const char *snapper_conf
,
1066 DBusMessage
**req_msg_out
)
1069 DBusMessageIter args
;
1070 DBusMessageIter array_iter
;
1075 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
1076 "/org/opensuse/Snapper",
1077 "org.opensuse.Snapper",
1080 DEBUG(0, ("failed to create req msg\n"));
1081 return NT_STATUS_NO_MEMORY
;
1084 status
= snapper_dbus_str_encode(mem_ctx
, snapper_conf
, &conf_encoded
);
1085 if (!NT_STATUS_IS_OK(status
)) {
1086 dbus_message_unref(msg
);
1090 /* append arguments */
1091 dbus_message_iter_init_append(msg
, &args
);
1092 ok
= dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
1095 talloc_free(conf_encoded
);
1096 dbus_message_unref(msg
);
1097 return NT_STATUS_NO_MEMORY
;
1100 ok
= dbus_message_iter_open_container(&args
, DBUS_TYPE_ARRAY
,
1101 DBUS_TYPE_UINT32_AS_STRING
,
1104 talloc_free(conf_encoded
);
1105 dbus_message_unref(msg
);
1106 return NT_STATUS_NO_MEMORY
;
1109 ok
= dbus_message_iter_append_basic(&array_iter
,
1113 talloc_free(conf_encoded
);
1114 dbus_message_unref(msg
);
1115 return NT_STATUS_NO_MEMORY
;
1118 dbus_message_iter_close_container(&args
, &array_iter
);
1121 return NT_STATUS_OK
;
1124 static NTSTATUS
snapper_del_snap_unpack(DBusConnection
*conn
,
1125 DBusMessage
*rsp_msg
)
1130 msg_type
= dbus_message_get_type(rsp_msg
);
1131 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
1132 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
1133 DEBUG(0, ("del snap error response: %s\n", err_str
));
1134 return snapper_err_ntstatus_map(err_str
);
1137 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
1138 DEBUG(0, ("unexpected del snap ret type: %d\n",
1140 return NT_STATUS_INVALID_PARAMETER
;
1143 sig
= dbus_message_get_signature(rsp_msg
);
1145 || (strcmp(sig
, SNAPPER_SIG_DEL_SNAPS_RSP
) != 0)) {
1146 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
1147 (sig
? sig
: "NULL"), SNAPPER_SIG_DEL_SNAPS_RSP
));
1148 return NT_STATUS_INVALID_PARAMETER
;
1151 /* no parameters in response */
1153 return NT_STATUS_OK
;
1156 static NTSTATUS
snapper_list_snaps_at_time_pack(TALLOC_CTX
*mem_ctx
,
1157 const char *snapper_conf
,
1160 DBusMessage
**req_msg_out
)
1163 DBusMessageIter args
;
1167 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
1168 "/org/opensuse/Snapper",
1169 "org.opensuse.Snapper",
1170 "ListSnapshotsAtTime");
1172 DEBUG(0, ("failed to create list snaps message\n"));
1173 return NT_STATUS_NO_MEMORY
;
1176 status
= snapper_dbus_str_encode(mem_ctx
, snapper_conf
, &conf_encoded
);
1177 if (!NT_STATUS_IS_OK(status
)) {
1178 dbus_message_unref(msg
);
1182 dbus_message_iter_init_append(msg
, &args
);
1183 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
1185 talloc_free(conf_encoded
);
1186 dbus_message_unref(msg
);
1187 return NT_STATUS_NO_MEMORY
;
1190 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
1192 talloc_free(conf_encoded
);
1193 dbus_message_unref(msg
);
1194 return NT_STATUS_NO_MEMORY
;
1197 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
1199 talloc_free(conf_encoded
);
1200 dbus_message_unref(msg
);
1201 return NT_STATUS_NO_MEMORY
;
1206 return NT_STATUS_OK
;
1208 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
1211 * Determine the snapper snapshot id given a path.
1212 * Ideally this should be determined via a lookup.
1214 static NTSTATUS
snapper_snap_path_to_id(TALLOC_CTX
*mem_ctx
,
1215 const char *snap_path
,
1216 uint32_t *snap_id_out
)
1223 path_dup
= talloc_strdup(mem_ctx
, snap_path
);
1224 if (path_dup
== NULL
) {
1225 return NT_STATUS_NO_MEMORY
;
1228 /* trim trailing '/' */
1229 str_idx
= path_dup
+ strlen(path_dup
) - 1;
1230 while (*str_idx
== '/') {
1235 str_idx
= strrchr(path_dup
, '/');
1236 if ((str_idx
== NULL
)
1237 || (strcmp(str_idx
+ 1, "snapshot") != 0)) {
1238 talloc_free(path_dup
);
1239 return NT_STATUS_INVALID_PARAMETER
;
1242 while (*str_idx
== '/') {
1247 str_idx
= strrchr(path_dup
, '/');
1248 if (str_idx
== NULL
) {
1249 talloc_free(path_dup
);
1250 return NT_STATUS_INVALID_PARAMETER
;
1254 snap_id
= smb_strtoul(str_idx
, NULL
, 10, &error
, SMB_STR_STANDARD
);
1256 talloc_free(path_dup
);
1257 return NT_STATUS_INVALID_PARAMETER
;
1260 talloc_free(path_dup
);
1261 *snap_id_out
= snap_id
;
1262 return NT_STATUS_OK
;
1266 * Determine the snapper snapshot path given an id and base.
1267 * Ideally this should be determined via a lookup.
1269 static NTSTATUS
snapper_snap_id_to_path(TALLOC_CTX
*mem_ctx
,
1270 const char *base_path
,
1272 char **snap_path_out
)
1276 snap_path
= talloc_asprintf(mem_ctx
, "%s/.snapshots/%u/snapshot",
1277 base_path
, snap_id
);
1278 if (snap_path
== NULL
) {
1279 return NT_STATUS_NO_MEMORY
;
1282 *snap_path_out
= snap_path
;
1283 return NT_STATUS_OK
;
1286 static NTSTATUS
snapper_get_conf_call(TALLOC_CTX
*mem_ctx
,
1287 DBusConnection
*dconn
,
1289 char **conf_name_out
,
1290 char **base_path_out
)
1293 DBusMessage
*req_msg
;
1294 DBusMessage
*rsp_msg
;
1295 uint32_t num_confs
= 0;
1296 struct snapper_conf
*confs
= NULL
;
1297 struct snapper_conf
*conf
;
1301 status
= snapper_list_confs_pack(&req_msg
);
1302 if (!NT_STATUS_IS_OK(status
)) {
1306 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1307 if (!NT_STATUS_IS_OK(status
)) {
1311 status
= snapper_list_confs_unpack(mem_ctx
, dconn
, rsp_msg
,
1312 &num_confs
, &confs
);
1313 if (!NT_STATUS_IS_OK(status
)) {
1318 * for now we only support shares where the path directly corresponds
1319 * to a snapper configuration.
1321 conf
= snapper_conf_array_base_find(num_confs
, confs
,
1324 status
= NT_STATUS_NOT_SUPPORTED
;
1325 goto err_array_free
;
1328 conf_name
= talloc_strdup(mem_ctx
, conf
->name
);
1329 if (conf_name
== NULL
) {
1330 status
= NT_STATUS_NO_MEMORY
;
1331 goto err_array_free
;
1333 base_path
= talloc_strdup(mem_ctx
, conf
->mnt
);
1334 if (base_path
== NULL
) {
1335 status
= NT_STATUS_NO_MEMORY
;
1336 goto err_conf_name_free
;
1340 dbus_message_unref(rsp_msg
);
1341 dbus_message_unref(req_msg
);
1343 *conf_name_out
= conf_name
;
1344 *base_path_out
= base_path
;
1346 return NT_STATUS_OK
;
1349 talloc_free(conf_name
);
1353 dbus_message_unref(rsp_msg
);
1355 dbus_message_unref(req_msg
);
1361 * Check whether a path can be shadow copied. Return the base volume, allowing
1362 * the caller to determine if multiple paths lie on the same base volume.
1364 static NTSTATUS
snapper_snap_check_path(struct vfs_handle_struct
*handle
,
1365 TALLOC_CTX
*mem_ctx
,
1366 const char *service_path
,
1370 DBusConnection
*dconn
;
1374 dconn
= snapper_dbus_conn_create();
1375 if (dconn
== NULL
) {
1376 return NT_STATUS_UNSUCCESSFUL
;
1379 status
= snapper_get_conf_call(mem_ctx
, dconn
, service_path
,
1380 &conf_name
, &base_path
);
1381 if (!NT_STATUS_IS_OK(status
)) {
1382 goto err_conn_close
;
1385 talloc_free(conf_name
);
1386 *base_volume
= base_path
;
1387 snapper_dbus_conn_destroy(dconn
);
1389 return NT_STATUS_OK
;
1392 snapper_dbus_conn_destroy(dconn
);
1396 static NTSTATUS
snapper_create_snap_call(TALLOC_CTX
*mem_ctx
,
1397 DBusConnection
*dconn
,
1398 const char *conf_name
,
1399 const char *base_path
,
1400 const char *snap_desc
,
1401 uint32_t num_user_data
,
1402 struct snapper_dict
*user_data
,
1403 char **snap_path_out
)
1406 DBusMessage
*req_msg
;
1407 DBusMessage
*rsp_msg
;
1408 uint32_t snap_id
= 0;
1411 status
= snapper_create_snap_pack(mem_ctx
,
1417 if (!NT_STATUS_IS_OK(status
)) {
1421 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1422 if (!NT_STATUS_IS_OK(status
)) {
1426 status
= snapper_create_snap_unpack(dconn
, rsp_msg
, &snap_id
);
1427 if (!NT_STATUS_IS_OK(status
)) {
1431 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snap_id
,
1433 if (!NT_STATUS_IS_OK(status
)) {
1437 dbus_message_unref(rsp_msg
);
1438 dbus_message_unref(req_msg
);
1440 DEBUG(6, ("created new snapshot %u at %s\n", snap_id
, snap_path
));
1441 *snap_path_out
= snap_path
;
1443 return NT_STATUS_OK
;
1446 dbus_message_unref(rsp_msg
);
1448 dbus_message_unref(req_msg
);
1453 static NTSTATUS
snapper_snap_create(struct vfs_handle_struct
*handle
,
1454 TALLOC_CTX
*mem_ctx
,
1455 const char *base_volume
,
1461 DBusConnection
*dconn
;
1465 char *snap_path
= NULL
;
1466 TALLOC_CTX
*tmp_ctx
;
1468 tmp_ctx
= talloc_new(mem_ctx
);
1469 if (tmp_ctx
== NULL
) {
1470 return NT_STATUS_NO_MEMORY
;
1473 dconn
= snapper_dbus_conn_create();
1474 if (dconn
== NULL
) {
1475 talloc_free(tmp_ctx
);
1476 return NT_STATUS_UNSUCCESSFUL
;
1479 status
= snapper_get_conf_call(tmp_ctx
, dconn
, base_volume
,
1480 &conf_name
, &base_path
);
1481 if (!NT_STATUS_IS_OK(status
)) {
1482 snapper_dbus_conn_destroy(dconn
);
1483 talloc_free(tmp_ctx
);
1487 status
= snapper_create_snap_call(tmp_ctx
, dconn
,
1488 conf_name
, base_path
,
1489 "Snapshot created by Samba",
1492 if (!NT_STATUS_IS_OK(status
)) {
1493 snapper_dbus_conn_destroy(dconn
);
1494 talloc_free(tmp_ctx
);
1498 snapper_dbus_conn_destroy(dconn
);
1499 *_base_path
= talloc_steal(mem_ctx
, base_path
);
1500 *_snap_path
= talloc_steal(mem_ctx
, snap_path
);
1501 talloc_free(tmp_ctx
);
1503 return NT_STATUS_OK
;
1506 static NTSTATUS
snapper_delete_snap_call(TALLOC_CTX
*mem_ctx
,
1507 DBusConnection
*dconn
,
1508 const char *conf_name
,
1512 DBusMessage
*req_msg
= NULL
;
1513 DBusMessage
*rsp_msg
;
1515 status
= snapper_del_snap_pack(mem_ctx
, conf_name
, snap_id
, &req_msg
);
1516 if (!NT_STATUS_IS_OK(status
)) {
1520 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1521 if (!NT_STATUS_IS_OK(status
)) {
1525 status
= snapper_del_snap_unpack(dconn
, rsp_msg
);
1526 if (!NT_STATUS_IS_OK(status
)) {
1530 dbus_message_unref(rsp_msg
);
1531 dbus_message_unref(req_msg
);
1533 DEBUG(6, ("deleted snapshot %u\n", snap_id
));
1535 return NT_STATUS_OK
;
1538 dbus_message_unref(rsp_msg
);
1540 dbus_message_unref(req_msg
);
1545 static NTSTATUS
snapper_snap_delete(struct vfs_handle_struct
*handle
,
1546 TALLOC_CTX
*mem_ctx
,
1550 DBusConnection
*dconn
;
1553 char *snap_base_path
;
1555 TALLOC_CTX
*tmp_ctx
;
1557 tmp_ctx
= talloc_new(mem_ctx
);
1558 if (tmp_ctx
== NULL
) {
1559 return NT_STATUS_NO_MEMORY
;
1562 dconn
= snapper_dbus_conn_create();
1563 if (dconn
== NULL
) {
1564 talloc_free(tmp_ctx
);
1565 return NT_STATUS_UNSUCCESSFUL
;
1568 status
= snapper_get_conf_call(tmp_ctx
, dconn
, base_path
,
1569 &conf_name
, &snap_base_path
);
1570 if (!NT_STATUS_IS_OK(status
)) {
1571 snapper_dbus_conn_destroy(dconn
);
1572 talloc_free(tmp_ctx
);
1576 status
= snapper_snap_path_to_id(tmp_ctx
, snap_path
, &snap_id
);
1577 if (!NT_STATUS_IS_OK(status
)) {
1578 snapper_dbus_conn_destroy(dconn
);
1579 talloc_free(tmp_ctx
);
1583 status
= snapper_delete_snap_call(tmp_ctx
, dconn
, conf_name
, snap_id
);
1584 if (!NT_STATUS_IS_OK(status
)) {
1585 snapper_dbus_conn_destroy(dconn
);
1586 talloc_free(tmp_ctx
);
1590 snapper_dbus_conn_destroy(dconn
);
1591 talloc_free(tmp_ctx
);
1593 return NT_STATUS_OK
;
1596 /* sc_data used as parent talloc context for all labels */
1597 static int snapper_get_shadow_copy_data(struct vfs_handle_struct
*handle
,
1598 struct files_struct
*fsp
,
1599 struct shadow_copy_data
*sc_data
,
1602 DBusConnection
*dconn
;
1603 TALLOC_CTX
*tmp_ctx
;
1607 DBusMessage
*req_msg
= NULL
;
1608 DBusMessage
*rsp_msg
;
1610 struct snapper_snap
*snaps
;
1614 tmp_ctx
= talloc_new(sc_data
);
1615 if (tmp_ctx
== NULL
) {
1616 status
= NT_STATUS_NO_MEMORY
;
1620 dconn
= snapper_dbus_conn_create();
1621 if (dconn
== NULL
) {
1622 status
= NT_STATUS_UNSUCCESSFUL
;
1623 goto err_mem_ctx_free
;
1626 if (fsp
->conn
->connectpath
== NULL
) {
1627 status
= NT_STATUS_INVALID_PARAMETER
;
1631 status
= snapper_get_conf_call(tmp_ctx
, dconn
,
1632 fsp
->conn
->connectpath
,
1635 if (!NT_STATUS_IS_OK(status
)) {
1639 status
= snapper_list_snaps_pack(tmp_ctx
, conf_name
, &req_msg
);
1640 if (!NT_STATUS_IS_OK(status
)) {
1644 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1645 if (!NT_STATUS_IS_OK(status
)) {
1649 status
= snapper_list_snaps_unpack(tmp_ctx
, rsp_msg
,
1650 &num_snaps
, &snaps
);
1651 if (!NT_STATUS_IS_OK(status
)) {
1654 /* we should always get at least one snapshot (current) */
1655 if (num_snaps
== 0) {
1656 DEBUG(1, ("zero snapshots in snap list response\n"));
1657 status
= NT_STATUS_UNSUCCESSFUL
;
1661 /* subtract 1, (current) snapshot is not returned */
1662 sc_data
->num_volumes
= num_snaps
- 1;
1663 sc_data
->labels
= NULL
;
1665 if ((labels
== false) || (sc_data
->num_volumes
== 0)) {
1666 /* tokens need not be added to the labels array */
1670 sc_data
->labels
= talloc_array(sc_data
, SHADOW_COPY_LABEL
,
1671 sc_data
->num_volumes
);
1672 if (sc_data
->labels
== NULL
) {
1673 status
= NT_STATUS_NO_MEMORY
;
1677 /* start at end for descending order, do not include 0 (current) */
1679 for (i
= num_snaps
- 1; i
> 0; i
--) {
1680 char *lbl
= sc_data
->labels
[lbl_off
++];
1681 struct tm gmt_snap_time
;
1685 tm_ret
= gmtime_r((time_t *)&snaps
[i
].time
, &gmt_snap_time
);
1686 if (tm_ret
== NULL
) {
1687 status
= NT_STATUS_UNSUCCESSFUL
;
1688 goto err_labels_free
;
1690 str_sz
= strftime(lbl
, sizeof(SHADOW_COPY_LABEL
),
1691 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
1693 status
= NT_STATUS_UNSUCCESSFUL
;
1694 goto err_labels_free
;
1699 talloc_free(tmp_ctx
);
1700 dbus_message_unref(rsp_msg
);
1701 dbus_message_unref(req_msg
);
1702 snapper_dbus_conn_destroy(dconn
);
1707 TALLOC_FREE(sc_data
->labels
);
1709 dbus_message_unref(rsp_msg
);
1711 dbus_message_unref(req_msg
);
1713 snapper_dbus_conn_destroy(dconn
);
1715 talloc_free(tmp_ctx
);
1717 errno
= map_errno_from_nt_status(status
);
1721 static bool snapper_gmt_strip_snapshot(TALLOC_CTX
*mem_ctx
,
1722 struct vfs_handle_struct
*handle
,
1723 const struct smb_filename
*smb_fname
,
1729 if (smb_fname
->twrp
== 0) {
1733 if (pstripped
!= NULL
) {
1734 stripped
= talloc_strdup(mem_ctx
, smb_fname
->base_name
);
1735 if (stripped
== NULL
) {
1738 *pstripped
= stripped
;
1741 *ptimestamp
= nt_time_to_unix(smb_fname
->twrp
);
1748 static NTSTATUS
snapper_get_snap_at_time_call(TALLOC_CTX
*mem_ctx
,
1749 DBusConnection
*dconn
,
1750 const char *conf_name
,
1751 const char *base_path
,
1753 char **snap_path_out
)
1756 DBusMessage
*req_msg
= NULL
;
1757 DBusMessage
*rsp_msg
;
1759 struct snapper_snap
*snaps
;
1762 status
= snapper_list_snaps_at_time_pack(mem_ctx
,
1767 if (!NT_STATUS_IS_OK(status
)) {
1771 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1772 if (!NT_STATUS_IS_OK(status
)) {
1776 status
= snapper_list_snaps_unpack(mem_ctx
, rsp_msg
,
1777 &num_snaps
, &snaps
);
1778 if (!NT_STATUS_IS_OK(status
)) {
1782 if (num_snaps
== 0) {
1783 DEBUG(4, ("no snapshots found with time: %lu\n",
1784 (unsigned long)snaptime
));
1785 status
= NT_STATUS_INVALID_PARAMETER
;
1786 goto err_snap_array_free
;
1787 } else if (num_snaps
> 0) {
1788 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1789 num_snaps
, (unsigned long)snaptime
));
1792 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snaps
[0].id
,
1794 if (!NT_STATUS_IS_OK(status
)) {
1795 goto err_snap_array_free
;
1798 *snap_path_out
= snap_path
;
1799 err_snap_array_free
:
1802 dbus_message_unref(rsp_msg
);
1804 dbus_message_unref(req_msg
);
1809 static NTSTATUS
snapper_snap_path_expand(struct connection_struct
*conn
,
1810 TALLOC_CTX
*mem_ctx
,
1812 char **snap_dir_out
)
1814 DBusConnection
*dconn
;
1818 char *snap_path
= NULL
;
1820 dconn
= snapper_dbus_conn_create();
1821 if (dconn
== NULL
) {
1822 status
= NT_STATUS_UNSUCCESSFUL
;
1826 if (conn
->connectpath
== NULL
) {
1827 status
= NT_STATUS_INVALID_PARAMETER
;
1831 status
= snapper_get_conf_call(mem_ctx
, dconn
,
1835 if (!NT_STATUS_IS_OK(status
)) {
1839 status
= snapper_get_snap_at_time_call(mem_ctx
, dconn
,
1840 conf_name
, base_path
, snap_time
,
1842 if (!NT_STATUS_IS_OK(status
)) {
1843 goto err_conf_name_free
;
1846 /* confirm snapshot path is nested under base path */
1847 if (strncmp(snap_path
, base_path
, strlen(base_path
)) != 0) {
1848 status
= NT_STATUS_INVALID_PARAMETER
;
1849 goto err_snap_path_free
;
1852 talloc_free(conf_name
);
1853 talloc_free(base_path
);
1854 snapper_dbus_conn_destroy(dconn
);
1855 *snap_dir_out
= snap_path
;
1857 return NT_STATUS_OK
;
1860 talloc_free(snap_path
);
1862 talloc_free(conf_name
);
1863 talloc_free(base_path
);
1865 snapper_dbus_conn_destroy(dconn
);
1870 static char *snapper_gmt_convert(TALLOC_CTX
*mem_ctx
,
1871 struct vfs_handle_struct
*handle
,
1872 const char *name
, time_t timestamp
)
1874 char *snap_path
= NULL
;
1879 status
= snapper_snap_path_expand(handle
->conn
, mem_ctx
, timestamp
,
1881 if (!NT_STATUS_IS_OK(status
)) {
1882 errno
= map_errno_from_nt_status(status
);
1886 path
= talloc_asprintf(mem_ctx
, "%s/%s", snap_path
, name
);
1889 goto err_snap_path_free
;
1892 DEBUG(10, ("converted %s/%s @ time to %s\n",
1893 handle
->conn
->connectpath
, name
, path
));
1897 saved_errno
= errno
;
1898 talloc_free(snap_path
);
1899 errno
= saved_errno
;
1904 static int snapper_gmt_renameat(vfs_handle_struct
*handle
,
1905 files_struct
*srcfsp
,
1906 const struct smb_filename
*smb_fname_src
,
1907 files_struct
*dstfsp
,
1908 const struct smb_filename
*smb_fname_dst
)
1910 time_t timestamp_src
, timestamp_dst
;
1912 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1914 ×tamp_src
, NULL
)) {
1917 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1919 ×tamp_dst
, NULL
)) {
1922 if (timestamp_src
!= 0) {
1926 if (timestamp_dst
!= 0) {
1930 return SMB_VFS_NEXT_RENAMEAT(handle
,
1937 static int snapper_gmt_symlinkat(vfs_handle_struct
*handle
,
1938 const struct smb_filename
*link_contents
,
1939 struct files_struct
*dirfsp
,
1940 const struct smb_filename
*new_smb_fname
)
1942 time_t timestamp_old
= 0;
1943 time_t timestamp_new
= 0;
1945 if (!snapper_gmt_strip_snapshot(talloc_tos(),
1952 if (!snapper_gmt_strip_snapshot(talloc_tos(),
1959 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1963 return SMB_VFS_NEXT_SYMLINKAT(handle
,
1969 static int snapper_gmt_linkat(vfs_handle_struct
*handle
,
1970 files_struct
*srcfsp
,
1971 const struct smb_filename
*old_smb_fname
,
1972 files_struct
*dstfsp
,
1973 const struct smb_filename
*new_smb_fname
,
1976 time_t timestamp_old
= 0;
1977 time_t timestamp_new
= 0;
1979 if (!snapper_gmt_strip_snapshot(talloc_tos(),
1986 if (!snapper_gmt_strip_snapshot(talloc_tos(),
1993 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1997 return SMB_VFS_NEXT_LINKAT(handle
,
2005 static int snapper_gmt_stat(vfs_handle_struct
*handle
,
2006 struct smb_filename
*smb_fname
)
2009 char *stripped
, *tmp
;
2010 int ret
, saved_errno
;
2012 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2014 ×tamp
, &stripped
)) {
2017 if (timestamp
== 0) {
2018 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
2021 tmp
= smb_fname
->base_name
;
2022 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
2023 stripped
, timestamp
);
2024 TALLOC_FREE(stripped
);
2026 if (smb_fname
->base_name
== NULL
) {
2027 smb_fname
->base_name
= tmp
;
2031 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
2032 saved_errno
= errno
;
2034 TALLOC_FREE(smb_fname
->base_name
);
2035 smb_fname
->base_name
= tmp
;
2037 errno
= saved_errno
;
2041 static int snapper_gmt_lstat(vfs_handle_struct
*handle
,
2042 struct smb_filename
*smb_fname
)
2045 char *stripped
, *tmp
;
2046 int ret
, saved_errno
;
2048 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2050 ×tamp
, &stripped
)) {
2053 if (timestamp
== 0) {
2054 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
2057 tmp
= smb_fname
->base_name
;
2058 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
2059 stripped
, timestamp
);
2060 TALLOC_FREE(stripped
);
2062 if (smb_fname
->base_name
== NULL
) {
2063 smb_fname
->base_name
= tmp
;
2067 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
2068 saved_errno
= errno
;
2070 TALLOC_FREE(smb_fname
->base_name
);
2071 smb_fname
->base_name
= tmp
;
2073 errno
= saved_errno
;
2077 static int snapper_gmt_openat(struct vfs_handle_struct
*handle
,
2078 const struct files_struct
*dirfsp
,
2079 const struct smb_filename
*smb_fname_in
,
2080 struct files_struct
*fsp
,
2081 const struct vfs_open_how
*how
)
2083 struct smb_filename
*smb_fname
= NULL
;
2085 char *stripped
= NULL
;
2087 int saved_errno
= 0;
2089 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2091 ×tamp
, &stripped
)) {
2094 if (timestamp
== 0) {
2095 return SMB_VFS_NEXT_OPENAT(handle
,
2102 smb_fname
= cp_smb_filename(talloc_tos(), smb_fname_in
);
2103 if (smb_fname
== NULL
) {
2104 TALLOC_FREE(stripped
);
2108 smb_fname
->base_name
= snapper_gmt_convert(smb_fname
, handle
,
2109 stripped
, timestamp
);
2110 TALLOC_FREE(stripped
);
2112 if (smb_fname
->base_name
== NULL
) {
2113 TALLOC_FREE(smb_fname
);
2118 ret
= SMB_VFS_NEXT_OPENAT(handle
, dirfsp
, smb_fname
, fsp
, how
);
2120 saved_errno
= errno
;
2122 TALLOC_FREE(smb_fname
);
2123 if (saved_errno
!= 0) {
2124 errno
= saved_errno
;
2129 static int snapper_gmt_unlinkat(vfs_handle_struct
*handle
,
2130 struct files_struct
*dirfsp
,
2131 const struct smb_filename
*smb_fname
,
2134 time_t timestamp
= 0;
2136 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2138 ×tamp
, NULL
)) {
2141 if (timestamp
!= 0) {
2145 return SMB_VFS_NEXT_UNLINKAT(handle
,
2151 static int snapper_gmt_fchmod(vfs_handle_struct
*handle
,
2152 struct files_struct
*fsp
,
2155 time_t timestamp
= 0;
2156 const struct smb_filename
*smb_fname
= NULL
;
2158 smb_fname
= fsp
->fsp_name
;
2160 if (!snapper_gmt_strip_snapshot(talloc_tos(),
2168 if (timestamp
!= 0) {
2172 return SMB_VFS_NEXT_FCHMOD(handle
, fsp
, mode
);
2175 static int snapper_gmt_chdir(vfs_handle_struct
*handle
,
2176 const struct smb_filename
*smb_fname
)
2178 time_t timestamp
= 0;
2179 char *stripped
= NULL
;
2181 int saved_errno
= 0;
2183 struct smb_filename
*conv_smb_fname
= NULL
;
2185 if (!snapper_gmt_strip_snapshot(talloc_tos(),
2192 if (timestamp
== 0) {
2193 return SMB_VFS_NEXT_CHDIR(handle
, smb_fname
);
2195 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2196 TALLOC_FREE(stripped
);
2200 conv_smb_fname
= synthetic_smb_fname(talloc_tos(),
2206 if (conv_smb_fname
== NULL
) {
2211 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv_smb_fname
);
2213 saved_errno
= errno
;
2216 TALLOC_FREE(conv_smb_fname
);
2217 if (saved_errno
!= 0) {
2218 errno
= saved_errno
;
2223 static int snapper_gmt_fntimes(vfs_handle_struct
*handle
,
2225 struct smb_file_time
*ft
)
2227 time_t timestamp
= 0;
2229 if (!snapper_gmt_strip_snapshot(talloc_tos(),
2237 if (timestamp
!= 0) {
2242 return SMB_VFS_NEXT_FNTIMES(handle
, fsp
, ft
);
2245 static int snapper_gmt_readlinkat(vfs_handle_struct
*handle
,
2246 const struct files_struct
*dirfsp
,
2247 const struct smb_filename
*smb_fname
,
2251 time_t timestamp
= 0;
2253 int saved_errno
= 0;
2254 struct smb_filename
*full_fname
= NULL
;
2257 * Now this function only looks at smb_fname->twrp
2258 * we don't need to copy out the path. Just use
2259 * smb_fname->base_name directly.
2261 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2263 ×tamp
, NULL
)) {
2266 if (timestamp
== 0) {
2267 return SMB_VFS_NEXT_READLINKAT(handle
,
2273 full_fname
= full_path_from_dirfsp_atname(talloc_tos(),
2276 if (full_fname
== NULL
) {
2280 /* Find the snapshot path from the full pathname. */
2281 full_fname
->base_name
= snapper_gmt_convert(full_fname
,
2283 full_fname
->base_name
,
2285 if (full_fname
->base_name
== NULL
) {
2286 TALLOC_FREE(full_fname
);
2289 ret
= SMB_VFS_NEXT_READLINKAT(handle
,
2290 handle
->conn
->cwd_fsp
,
2295 saved_errno
= errno
;
2297 TALLOC_FREE(full_fname
);
2298 if (saved_errno
!= 0) {
2299 errno
= saved_errno
;
2304 static int snapper_gmt_mknodat(vfs_handle_struct
*handle
,
2305 files_struct
*dirfsp
,
2306 const struct smb_filename
*smb_fname
,
2310 time_t timestamp
= (time_t)0;
2312 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2314 ×tamp
, NULL
)) {
2317 if (timestamp
!= 0) {
2321 return SMB_VFS_NEXT_MKNODAT(handle
,
2328 static struct smb_filename
*snapper_gmt_realpath(vfs_handle_struct
*handle
,
2330 const struct smb_filename
*smb_fname
)
2332 time_t timestamp
= 0;
2333 char *stripped
= NULL
;
2334 struct smb_filename
*result_fname
= NULL
;
2335 struct smb_filename
*conv_smb_fname
= NULL
;
2336 int saved_errno
= 0;
2338 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2340 ×tamp
, &stripped
)) {
2343 if (timestamp
== 0) {
2344 return SMB_VFS_NEXT_REALPATH(handle
, ctx
, smb_fname
);
2347 conv_smb_fname
= cp_smb_filename(talloc_tos(), smb_fname
);
2348 if (conv_smb_fname
== NULL
) {
2351 conv_smb_fname
->base_name
= snapper_gmt_convert(conv_smb_fname
, handle
,
2352 stripped
, timestamp
);
2353 if (conv_smb_fname
->base_name
== NULL
) {
2357 result_fname
= SMB_VFS_NEXT_REALPATH(handle
, ctx
, conv_smb_fname
);
2360 if (result_fname
== NULL
) {
2361 saved_errno
= errno
;
2363 TALLOC_FREE(conv_smb_fname
);
2364 TALLOC_FREE(stripped
);
2365 if (saved_errno
!= 0) {
2366 errno
= saved_errno
;
2368 return result_fname
;
2371 static int snapper_gmt_mkdirat(vfs_handle_struct
*handle
,
2372 struct files_struct
*dirfsp
,
2373 const struct smb_filename
*fname
,
2376 time_t timestamp
= 0;
2378 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2379 ×tamp
, NULL
)) {
2382 if (timestamp
!= 0) {
2386 return SMB_VFS_NEXT_MKDIRAT(handle
,
2392 static int snapper_gmt_fchflags(vfs_handle_struct
*handle
,
2393 struct files_struct
*fsp
,
2396 time_t timestamp
= 0;
2398 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2399 fsp
->fsp_name
, ×tamp
, NULL
)) {
2402 if (timestamp
!= 0) {
2406 return SMB_VFS_NEXT_FCHFLAGS(handle
, fsp
, flags
);
2409 static int snapper_gmt_fsetxattr(struct vfs_handle_struct
*handle
,
2410 struct files_struct
*fsp
,
2411 const char *aname
, const void *value
,
2412 size_t size
, int flags
)
2414 time_t timestamp
= 0;
2415 const struct smb_filename
*smb_fname
= NULL
;
2417 smb_fname
= fsp
->fsp_name
;
2419 if (!snapper_gmt_strip_snapshot(talloc_tos(),
2426 if (timestamp
!= 0) {
2430 return SMB_VFS_NEXT_FSETXATTR(handle
, fsp
,
2431 aname
, value
, size
, flags
);
2434 static NTSTATUS
snapper_gmt_get_real_filename_at(
2435 struct vfs_handle_struct
*handle
,
2436 struct files_struct
*dirfsp
,
2438 TALLOC_CTX
*mem_ctx
,
2444 struct smb_filename
*conv_fname
= NULL
;
2448 ok
= snapper_gmt_strip_snapshot(
2449 talloc_tos(), handle
, dirfsp
->fsp_name
,×tamp
, &stripped
);
2451 return NT_STATUS_NO_MEMORY
;
2453 if (timestamp
== 0) {
2454 return SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
2455 handle
, dirfsp
, name
, mem_ctx
, found_name
);
2457 if (stripped
[0] == '\0') {
2458 *found_name
= talloc_strdup(mem_ctx
, name
);
2459 if (*found_name
== NULL
) {
2460 return NT_STATUS_NO_MEMORY
;
2462 return NT_STATUS_OK
;
2464 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2465 TALLOC_FREE(stripped
);
2467 return map_nt_error_from_unix(errno
);
2470 status
= synthetic_pathref(
2472 dirfsp
->conn
->cwd_fsp
,
2480 status
= SMB_VFS_NEXT_GET_REAL_FILENAME_AT(
2481 handle
, conv_fname
->fsp
, name
, mem_ctx
, found_name
);
2486 static uint64_t snapper_gmt_disk_free(vfs_handle_struct
*handle
,
2487 const struct smb_filename
*smb_fname
,
2492 time_t timestamp
= 0;
2493 char *stripped
= NULL
;
2495 int saved_errno
= 0;
2497 struct smb_filename
*conv_smb_fname
= NULL
;
2499 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2500 smb_fname
, ×tamp
, &stripped
)) {
2501 return (uint64_t)-1;
2503 if (timestamp
== 0) {
2504 return SMB_VFS_NEXT_DISK_FREE(handle
, smb_fname
,
2505 bsize
, dfree
, dsize
);
2508 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2509 TALLOC_FREE(stripped
);
2511 return (uint64_t)-1;
2513 conv_smb_fname
= synthetic_smb_fname(talloc_tos(),
2519 if (conv_smb_fname
== NULL
) {
2522 return (uint64_t)-1;
2525 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv_smb_fname
,
2526 bsize
, dfree
, dsize
);
2528 if (ret
== (uint64_t)-1) {
2529 saved_errno
= errno
;
2531 TALLOC_FREE(conv_smb_fname
);
2532 if (saved_errno
!= 0) {
2533 errno
= saved_errno
;
2538 static int snapper_gmt_get_quota(vfs_handle_struct
*handle
,
2539 const struct smb_filename
*smb_fname
,
2540 enum SMB_QUOTA_TYPE qtype
,
2544 time_t timestamp
= 0;
2545 char *stripped
= NULL
;
2547 int saved_errno
= 0;
2549 struct smb_filename
*conv_smb_fname
= NULL
;
2551 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
2552 smb_fname
, ×tamp
, &stripped
)) {
2555 if (timestamp
== 0) {
2556 return SMB_VFS_NEXT_GET_QUOTA(handle
, smb_fname
, qtype
, id
, dq
);
2559 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2560 TALLOC_FREE(stripped
);
2564 conv_smb_fname
= synthetic_smb_fname(talloc_tos(),
2571 if (conv_smb_fname
== NULL
) {
2576 ret
= SMB_VFS_NEXT_GET_QUOTA(handle
, conv_smb_fname
, qtype
, id
, dq
);
2579 saved_errno
= errno
;
2581 TALLOC_FREE(conv_smb_fname
);
2582 if (saved_errno
!= 0) {
2583 errno
= saved_errno
;
2588 static NTSTATUS
snapper_create_dfs_pathat(struct vfs_handle_struct
*handle
,
2589 struct files_struct
*dirfsp
,
2590 const struct smb_filename
*smb_fname
,
2591 const struct referral
*reflist
,
2592 size_t referral_count
)
2594 time_t timestamp
= 0;
2596 if (!snapper_gmt_strip_snapshot(talloc_tos(),
2601 return NT_STATUS_NO_MEMORY
;
2603 if (timestamp
!= 0) {
2604 return NT_STATUS_MEDIA_WRITE_PROTECTED
;
2606 return SMB_VFS_NEXT_CREATE_DFS_PATHAT(handle
,
2613 static struct vfs_fn_pointers snapper_fns
= {
2614 .snap_check_path_fn
= snapper_snap_check_path
,
2615 .snap_create_fn
= snapper_snap_create
,
2616 .snap_delete_fn
= snapper_snap_delete
,
2617 .get_shadow_copy_data_fn
= snapper_get_shadow_copy_data
,
2618 .create_dfs_pathat_fn
= snapper_create_dfs_pathat
,
2619 .disk_free_fn
= snapper_gmt_disk_free
,
2620 .get_quota_fn
= snapper_gmt_get_quota
,
2621 .renameat_fn
= snapper_gmt_renameat
,
2622 .linkat_fn
= snapper_gmt_linkat
,
2623 .symlinkat_fn
= snapper_gmt_symlinkat
,
2624 .stat_fn
= snapper_gmt_stat
,
2625 .lstat_fn
= snapper_gmt_lstat
,
2626 .openat_fn
= snapper_gmt_openat
,
2627 .unlinkat_fn
= snapper_gmt_unlinkat
,
2628 .fchmod_fn
= snapper_gmt_fchmod
,
2629 .chdir_fn
= snapper_gmt_chdir
,
2630 .fntimes_fn
= snapper_gmt_fntimes
,
2631 .readlinkat_fn
= snapper_gmt_readlinkat
,
2632 .mknodat_fn
= snapper_gmt_mknodat
,
2633 .realpath_fn
= snapper_gmt_realpath
,
2634 .mkdirat_fn
= snapper_gmt_mkdirat
,
2635 .getxattrat_send_fn
= vfs_not_implemented_getxattrat_send
,
2636 .getxattrat_recv_fn
= vfs_not_implemented_getxattrat_recv
,
2637 .fsetxattr_fn
= snapper_gmt_fsetxattr
,
2638 .fchflags_fn
= snapper_gmt_fchflags
,
2639 .get_real_filename_at_fn
= snapper_gmt_get_real_filename_at
,
2643 NTSTATUS
vfs_snapper_init(TALLOC_CTX
*ctx
)
2645 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2646 "snapper", &snapper_fns
);