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
;
95 * Strings are UTF-8. Other characters must be encoded hexadecimal as "\x??".
96 * As a consequence "\" must be encoded as "\\".
98 static NTSTATUS
snapper_dbus_str_encode(TALLOC_CTX
*mem_ctx
, const char *in_str
,
107 if (in_str
== NULL
) {
108 return NT_STATUS_INVALID_PARAMETER
;
111 in_len
= strlen(in_str
);
113 /* output can be max 4 times the length of @in_str, +1 for terminator */
114 out_len
= (in_len
* 4) + 1;
116 out_str
= talloc_array(mem_ctx
, char, out_len
);
117 if (out_str
== NULL
) {
118 return NT_STATUS_NO_MEMORY
;
122 for (i
= 0; i
< in_len
; i
++) {
125 if (in_str
[i
] == '\\') {
126 pushed
= snprintf(out_str
+ out_off
, out_len
- out_off
,
128 } else if ((unsigned char)in_str
[i
] > 127) {
129 pushed
= snprintf(out_str
+ out_off
, out_len
- out_off
,
130 "\\x%02x", (unsigned char)in_str
[i
]);
132 /* regular character */
133 *(out_str
+ out_off
) = in_str
[i
];
134 pushed
= sizeof(char);
136 if (pushed
>= out_len
- out_off
) {
137 /* truncated, should never happen */
138 talloc_free(out_str
);
139 return NT_STATUS_INTERNAL_ERROR
;
144 *(out_str
+ out_off
) = '\0';
150 static NTSTATUS
snapper_dbus_str_decode(TALLOC_CTX
*mem_ctx
, const char *in_str
,
159 if (in_str
== NULL
) {
160 return NT_STATUS_INVALID_PARAMETER
;
163 in_len
= strlen(in_str
);
165 /* output cannot be larger than input, +1 for terminator */
166 out_len
= in_len
+ 1;
168 out_str
= talloc_array(mem_ctx
, char, out_len
);
169 if (out_str
== NULL
) {
170 return NT_STATUS_NO_MEMORY
;
174 for (i
= 0; i
< in_len
; i
++) {
177 unsigned int non_ascii_byte
;
179 if (in_str
[i
] != '\\') {
180 out_str
[out_off
] = in_str
[i
];
186 if (in_str
[i
] == '\\') {
187 out_str
[out_off
] = '\\';
190 } else if (in_str
[i
] != 'x') {
191 goto err_invalid_src_encoding
;
194 /* non-ASCII, encoded as two hex chars */
195 for (j
= 0; j
< 2; j
++) {
197 if ((in_str
[i
] == '\0') || !isxdigit(in_str
[i
])) {
198 goto err_invalid_src_encoding
;
200 hex_buf
[j
] = in_str
[i
];
204 sscanf(hex_buf
, "%x", &non_ascii_byte
);
205 out_str
[out_off
] = (unsigned char)non_ascii_byte
;
209 out_str
[out_off
] = '\0';
213 err_invalid_src_encoding
:
214 DEBUG(0, ("invalid encoding %s\n", in_str
));
215 return NT_STATUS_INVALID_PARAMETER
;
218 static DBusConnection
*snapper_dbus_conn_create(void)
221 DBusConnection
*dconn
;
223 dbus_error_init(&err
);
226 * Always create a new DBus connection, to ensure snapperd detects the
227 * correct client [E]UID. With dbus_bus_get() it does not!
229 dconn
= dbus_bus_get_private(DBUS_BUS_SYSTEM
, &err
);
230 if (dbus_error_is_set(&err
)) {
231 DEBUG(0, ("dbus connection error: %s\n", err
.message
));
232 dbus_error_free(&err
);
238 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
239 dbus_connection_set_exit_on_disconnect(dconn
, false);
244 static void snapper_dbus_conn_destroy(DBusConnection
*dconn
)
247 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
251 dbus_connection_close(dconn
);
252 dbus_connection_unref(dconn
);
256 * send the message @send_msg over the dbus and wait for a response, return the
257 * responsee via @recv_msg_out.
258 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
260 static NTSTATUS
snapper_dbus_msg_xchng(DBusConnection
*dconn
,
261 DBusMessage
*send_msg
,
262 DBusMessage
**recv_msg_out
)
264 DBusPendingCall
*pending
;
265 DBusMessage
*recv_msg
;
267 /* send message and get a handle for a reply */
268 if (!dbus_connection_send_with_reply(dconn
, send_msg
, &pending
, -1)) {
269 return NT_STATUS_NO_MEMORY
;
271 if (NULL
== pending
) {
272 DEBUG(0, ("dbus msg send failed\n"));
273 return NT_STATUS_UNSUCCESSFUL
;
276 dbus_connection_flush(dconn
);
278 /* block until we receive a reply */
279 dbus_pending_call_block(pending
);
281 /* get the reply message */
282 recv_msg
= dbus_pending_call_steal_reply(pending
);
283 if (recv_msg
== NULL
) {
284 DEBUG(0, ("Reply Null\n"));
285 return NT_STATUS_UNSUCCESSFUL
;
287 /* free the pending message handle */
288 dbus_pending_call_unref(pending
);
289 *recv_msg_out
= recv_msg
;
294 static NTSTATUS
snapper_type_check(DBusMessageIter
*iter
,
297 int type
= dbus_message_iter_get_arg_type(iter
);
298 if (type
!= expected_type
) {
299 DEBUG(0, ("got type %d, expecting %d\n",
300 type
, expected_type
));
301 return NT_STATUS_INVALID_PARAMETER
;
307 static NTSTATUS
snapper_type_check_get(DBusMessageIter
*iter
,
312 status
= snapper_type_check(iter
, expected_type
);
313 if (!NT_STATUS_IS_OK(status
)) {
317 dbus_message_iter_get_basic(iter
, val
);
322 static NTSTATUS
snapper_dict_unpack(TALLOC_CTX
*mem_ctx
,
323 DBusMessageIter
*iter
,
324 struct snapper_dict
*dict_out
)
328 DBusMessageIter dct_iter
;
332 status
= snapper_type_check(iter
, DBUS_TYPE_DICT_ENTRY
);
333 if (!NT_STATUS_IS_OK(status
)) {
336 dbus_message_iter_recurse(iter
, &dct_iter
);
338 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
340 if (!NT_STATUS_IS_OK(status
)) {
343 status
= snapper_dbus_str_decode(mem_ctx
, key_encoded
, &dict_out
->key
);
344 if (!NT_STATUS_IS_OK(status
)) {
348 dbus_message_iter_next(&dct_iter
);
349 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
351 if (!NT_STATUS_IS_OK(status
)) {
352 talloc_free(dict_out
->key
);
355 status
= snapper_dbus_str_decode(mem_ctx
, val_encoded
, &dict_out
->val
);
356 if (!NT_STATUS_IS_OK(status
)) {
357 talloc_free(dict_out
->key
);
364 static void snapper_dict_array_print(uint32_t num_dicts
,
365 struct snapper_dict
*dicts
)
369 for (i
= 0; i
< num_dicts
; i
++) {
370 DEBUG(10, ("dict (key: %s, val: %s)\n",
371 dicts
[i
].key
, dicts
[i
].val
));
375 static NTSTATUS
snapper_dict_array_unpack(TALLOC_CTX
*mem_ctx
,
376 DBusMessageIter
*iter
,
377 uint32_t *num_dicts_out
,
378 struct snapper_dict
**dicts_out
)
381 DBusMessageIter array_iter
;
383 struct snapper_dict
*dicts
= NULL
;
385 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
386 if (!NT_STATUS_IS_OK(status
)) {
389 dbus_message_iter_recurse(iter
, &array_iter
);
392 while (dbus_message_iter_get_arg_type(&array_iter
)
393 != DBUS_TYPE_INVALID
) {
395 dicts
= talloc_realloc(mem_ctx
, dicts
, struct snapper_dict
,
400 status
= snapper_dict_unpack(mem_ctx
, &array_iter
,
401 &dicts
[num_dicts
- 1]);
402 if (!NT_STATUS_IS_OK(status
)) {
406 dbus_message_iter_next(&array_iter
);
409 *num_dicts_out
= num_dicts
;
415 static NTSTATUS
snapper_list_confs_pack(DBusMessage
**req_msg_out
)
419 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
420 "/org/opensuse/Snapper",
421 "org.opensuse.Snapper",
424 DEBUG(0, ("null msg\n"));
425 return NT_STATUS_NO_MEMORY
;
428 /* no arguments to append */
434 static NTSTATUS
snapper_conf_unpack(TALLOC_CTX
*mem_ctx
,
435 DBusMessageIter
*iter
,
436 struct snapper_conf
*conf_out
)
439 DBusMessageIter st_iter
;
443 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
444 if (!NT_STATUS_IS_OK(status
)) {
447 dbus_message_iter_recurse(iter
, &st_iter
);
449 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
451 if (!NT_STATUS_IS_OK(status
)) {
455 status
= snapper_dbus_str_decode(mem_ctx
, name_encoded
,
457 if (!NT_STATUS_IS_OK(status
)) {
461 dbus_message_iter_next(&st_iter
);
462 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
464 if (!NT_STATUS_IS_OK(status
)) {
465 talloc_free(conf_out
->name
);
469 status
= snapper_dbus_str_decode(mem_ctx
, mnt_encoded
,
471 if (!NT_STATUS_IS_OK(status
)) {
472 talloc_free(conf_out
->name
);
476 dbus_message_iter_next(&st_iter
);
477 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
478 &conf_out
->num_attrs
,
480 if (!NT_STATUS_IS_OK(status
)) {
481 talloc_free(conf_out
->mnt
);
482 talloc_free(conf_out
->name
);
489 static struct snapper_conf
*snapper_conf_array_base_find(int32_t num_confs
,
490 struct snapper_conf
*confs
,
495 for (i
= 0; i
< num_confs
; i
++) {
496 if (strcmp(confs
[i
].mnt
, base
) == 0) {
497 DEBUG(5, ("found snapper conf %s for path %s\n",
498 confs
[i
].name
, base
));
502 DEBUG(5, ("config for base %s not found\n", base
));
507 static void snapper_conf_array_print(int32_t num_confs
,
508 struct snapper_conf
*confs
)
512 for (i
= 0; i
< num_confs
; i
++) {
513 DEBUG(10, ("name: %s, mnt: %s\n",
514 confs
[i
].name
, confs
[i
].mnt
));
515 snapper_dict_array_print(confs
[i
].num_attrs
, confs
[i
].attrs
);
519 static NTSTATUS
snapper_conf_array_unpack(TALLOC_CTX
*mem_ctx
,
520 DBusMessageIter
*iter
,
521 uint32_t *num_confs_out
,
522 struct snapper_conf
**confs_out
)
526 struct snapper_conf
*confs
= NULL
;
527 DBusMessageIter array_iter
;
530 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
531 if (!NT_STATUS_IS_OK(status
)) {
534 dbus_message_iter_recurse(iter
, &array_iter
);
537 while (dbus_message_iter_get_arg_type(&array_iter
)
538 != DBUS_TYPE_INVALID
) {
540 confs
= talloc_realloc(mem_ctx
, confs
, struct snapper_conf
,
545 status
= snapper_conf_unpack(confs
, &array_iter
,
546 &confs
[num_confs
- 1]);
547 if (!NT_STATUS_IS_OK(status
)) {
551 dbus_message_iter_next(&array_iter
);
554 *num_confs_out
= num_confs
;
560 static NTSTATUS
snapper_list_confs_unpack(TALLOC_CTX
*mem_ctx
,
561 DBusConnection
*dconn
,
562 DBusMessage
*rsp_msg
,
563 uint32_t *num_confs_out
,
564 struct snapper_conf
**confs_out
)
567 DBusMessageIter iter
;
570 struct snapper_conf
*confs
;
573 msg_type
= dbus_message_get_type(rsp_msg
);
574 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
575 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
576 DEBUG(0, ("list_confs error response: %s\n", err_str
));
577 return snapper_err_ntstatus_map(err_str
);
580 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
581 DEBUG(0, ("unexpected list_confs ret type: %d\n",
583 return NT_STATUS_INVALID_PARAMETER
;
586 sig
= dbus_message_get_signature(rsp_msg
);
588 || (strcmp(sig
, SNAPPER_SIG_LIST_CONFS_RSP
) != 0)) {
589 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
590 (sig
? sig
: "NULL"), SNAPPER_SIG_LIST_CONFS_RSP
));
591 return NT_STATUS_INVALID_PARAMETER
;
594 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
595 /* FIXME return empty? */
596 DEBUG(0, ("Message has no arguments!\n"));
597 return NT_STATUS_INVALID_PARAMETER
;
600 status
= snapper_conf_array_unpack(mem_ctx
, &iter
, &num_confs
, &confs
);
601 if (!NT_STATUS_IS_OK(status
)) {
602 DEBUG(0, ("failed to unpack conf array\n"));
606 snapper_conf_array_print(num_confs
, confs
);
608 *num_confs_out
= num_confs
;
614 static NTSTATUS
snapper_list_snaps_pack(TALLOC_CTX
*mem_ctx
,
616 DBusMessage
**req_msg_out
)
619 DBusMessageIter args
;
623 msg
= dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
624 "/org/opensuse/Snapper", /* object to call on */
625 "org.opensuse.Snapper", /* interface to call on */
626 "ListSnapshots"); /* method name */
628 DEBUG(0, ("failed to create list snaps message\n"));
629 return NT_STATUS_NO_MEMORY
;
632 status
= snapper_dbus_str_encode(mem_ctx
, snapper_conf
, &conf_encoded
);
633 if (!NT_STATUS_IS_OK(status
)) {
634 dbus_message_unref(msg
);
638 /* append arguments */
639 dbus_message_iter_init_append(msg
, &args
);
640 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
642 talloc_free(conf_encoded
);
643 dbus_message_unref(msg
);
644 return NT_STATUS_NO_MEMORY
;
652 static NTSTATUS
snapper_snap_struct_unpack(TALLOC_CTX
*mem_ctx
,
653 DBusMessageIter
*iter
,
654 struct snapper_snap
*snap_out
)
657 DBusMessageIter st_iter
;
659 char *cleanup_encoded
;
661 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
662 if (!NT_STATUS_IS_OK(status
)) {
665 dbus_message_iter_recurse(iter
, &st_iter
);
667 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
669 if (!NT_STATUS_IS_OK(status
)) {
673 dbus_message_iter_next(&st_iter
);
674 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT16
,
676 if (!NT_STATUS_IS_OK(status
)) {
680 dbus_message_iter_next(&st_iter
);
681 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
683 if (!NT_STATUS_IS_OK(status
)) {
687 dbus_message_iter_next(&st_iter
);
688 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_INT64
,
690 if (!NT_STATUS_IS_OK(status
)) {
694 dbus_message_iter_next(&st_iter
);
695 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
696 &snap_out
->creator_uid
);
697 if (!NT_STATUS_IS_OK(status
)) {
701 dbus_message_iter_next(&st_iter
);
702 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
704 if (!NT_STATUS_IS_OK(status
)) {
708 status
= snapper_dbus_str_decode(mem_ctx
, desc_encoded
,
710 if (!NT_STATUS_IS_OK(status
)) {
714 dbus_message_iter_next(&st_iter
);
715 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
717 if (!NT_STATUS_IS_OK(status
)) {
718 talloc_free(snap_out
->desc
);
722 status
= snapper_dbus_str_decode(mem_ctx
, cleanup_encoded
,
724 if (!NT_STATUS_IS_OK(status
)) {
725 talloc_free(snap_out
->desc
);
729 dbus_message_iter_next(&st_iter
);
730 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
731 &snap_out
->num_user_data
,
732 &snap_out
->user_data
);
733 if (!NT_STATUS_IS_OK(status
)) {
734 talloc_free(snap_out
->cleanup
);
735 talloc_free(snap_out
->desc
);
742 static void snapper_snap_array_print(int32_t num_snaps
,
743 struct snapper_snap
*snaps
)
747 for (i
= 0; i
< num_snaps
; i
++) {
748 DEBUG(10, ("id: %u, "
755 (unsigned int)snaps
[i
].id
,
756 (unsigned int)snaps
[i
].type
,
757 (unsigned int)snaps
[i
].pre_id
,
758 (long int)snaps
[i
].time
,
759 (unsigned int)snaps
[i
].creator_uid
,
762 snapper_dict_array_print(snaps
[i
].num_user_data
,
767 static NTSTATUS
snapper_snap_array_unpack(TALLOC_CTX
*mem_ctx
,
768 DBusMessageIter
*iter
,
769 uint32_t *num_snaps_out
,
770 struct snapper_snap
**snaps_out
)
774 struct snapper_snap
*snaps
= NULL
;
775 DBusMessageIter array_iter
;
778 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
779 if (!NT_STATUS_IS_OK(status
)) {
782 dbus_message_iter_recurse(iter
, &array_iter
);
785 while (dbus_message_iter_get_arg_type(&array_iter
)
786 != DBUS_TYPE_INVALID
) {
788 snaps
= talloc_realloc(mem_ctx
, snaps
, struct snapper_snap
,
793 status
= snapper_snap_struct_unpack(snaps
, &array_iter
,
794 &snaps
[num_snaps
- 1]);
795 if (!NT_STATUS_IS_OK(status
)) {
799 dbus_message_iter_next(&array_iter
);
802 *num_snaps_out
= num_snaps
;
808 static NTSTATUS
snapper_list_snaps_unpack(TALLOC_CTX
*mem_ctx
,
809 DBusMessage
*rsp_msg
,
810 uint32_t *num_snaps_out
,
811 struct snapper_snap
**snaps_out
)
814 DBusMessageIter iter
;
817 struct snapper_snap
*snaps
;
820 msg_type
= dbus_message_get_type(rsp_msg
);
821 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
822 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
823 DEBUG(0, ("list_snaps error response: %s\n", err_str
));
824 return snapper_err_ntstatus_map(err_str
);
827 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
828 DEBUG(0,("unexpected list_snaps ret type: %d\n",
830 return NT_STATUS_INVALID_PARAMETER
;
833 sig
= dbus_message_get_signature(rsp_msg
);
835 || (strcmp(sig
, SNAPPER_SIG_LIST_SNAPS_RSP
) != 0)) {
836 DEBUG(0, ("bad list snaps response sig: %s, "
838 (sig
? sig
: "NULL"),
839 SNAPPER_SIG_LIST_SNAPS_RSP
));
840 return NT_STATUS_INVALID_PARAMETER
;
843 /* read the parameters */
844 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
845 DEBUG(0, ("response has no arguments!\n"));
846 return NT_STATUS_INVALID_PARAMETER
;
849 status
= snapper_snap_array_unpack(mem_ctx
, &iter
, &num_snaps
, &snaps
);
850 if (!NT_STATUS_IS_OK(status
)) {
851 DEBUG(0, ("failed to unpack snap array\n"));
852 return NT_STATUS_INVALID_PARAMETER
;
855 snapper_snap_array_print(num_snaps
, snaps
);
857 *num_snaps_out
= num_snaps
;
863 static NTSTATUS
snapper_list_snaps_at_time_pack(TALLOC_CTX
*mem_ctx
,
864 const char *snapper_conf
,
867 DBusMessage
**req_msg_out
)
870 DBusMessageIter args
;
874 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
875 "/org/opensuse/Snapper",
876 "org.opensuse.Snapper",
877 "ListSnapshotsAtTime");
879 DEBUG(0, ("failed to create list snaps message\n"));
880 return NT_STATUS_NO_MEMORY
;
883 status
= snapper_dbus_str_encode(mem_ctx
, snapper_conf
, &conf_encoded
);
884 if (!NT_STATUS_IS_OK(status
)) {
885 dbus_message_unref(msg
);
889 dbus_message_iter_init_append(msg
, &args
);
890 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
892 talloc_free(conf_encoded
);
893 dbus_message_unref(msg
);
894 return NT_STATUS_NO_MEMORY
;
897 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
899 talloc_free(conf_encoded
);
900 dbus_message_unref(msg
);
901 return NT_STATUS_NO_MEMORY
;
904 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
906 talloc_free(conf_encoded
);
907 dbus_message_unref(msg
);
908 return NT_STATUS_NO_MEMORY
;
915 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
918 * Determine the snapper snapshot path given an id and base.
919 * Ideally this should be determined via a lookup.
921 static NTSTATUS
snapper_snap_id_to_path(TALLOC_CTX
*mem_ctx
,
922 const char *base_path
,
924 char **snap_path_out
)
928 snap_path
= talloc_asprintf(mem_ctx
, "%s/.snapshots/%u/snapshot",
930 if (snap_path
== NULL
) {
931 return NT_STATUS_NO_MEMORY
;
934 *snap_path_out
= snap_path
;
938 static NTSTATUS
snapper_get_conf_call(TALLOC_CTX
*mem_ctx
,
939 DBusConnection
*dconn
,
941 char **conf_name_out
,
942 char **base_path_out
)
945 DBusMessage
*req_msg
;
946 DBusMessage
*rsp_msg
;
947 uint32_t num_confs
= 0;
948 struct snapper_conf
*confs
= NULL
;
949 struct snapper_conf
*conf
;
953 status
= snapper_list_confs_pack(&req_msg
);
954 if (!NT_STATUS_IS_OK(status
)) {
958 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
959 if (!NT_STATUS_IS_OK(status
)) {
963 status
= snapper_list_confs_unpack(mem_ctx
, dconn
, rsp_msg
,
965 if (!NT_STATUS_IS_OK(status
)) {
970 * for now we only support shares where the path directly corresponds
971 * to a snapper configuration.
973 conf
= snapper_conf_array_base_find(num_confs
, confs
,
976 status
= NT_STATUS_NOT_SUPPORTED
;
980 conf_name
= talloc_strdup(mem_ctx
, conf
->name
);
981 if (conf_name
== NULL
) {
982 status
= NT_STATUS_NO_MEMORY
;
985 base_path
= talloc_strdup(mem_ctx
, conf
->mnt
);
986 if (base_path
== NULL
) {
987 status
= NT_STATUS_NO_MEMORY
;
988 goto err_conf_name_free
;
992 dbus_message_unref(rsp_msg
);
993 dbus_message_unref(req_msg
);
995 *conf_name_out
= conf_name
;
996 *base_path_out
= base_path
;
1001 talloc_free(conf_name
);
1005 dbus_message_unref(rsp_msg
);
1007 dbus_message_unref(req_msg
);
1012 /* sc_data used as parent talloc context for all labels */
1013 static int snapper_get_shadow_copy_data(struct vfs_handle_struct
*handle
,
1014 struct files_struct
*fsp
,
1015 struct shadow_copy_data
*sc_data
,
1018 DBusConnection
*dconn
;
1019 TALLOC_CTX
*tmp_ctx
;
1023 DBusMessage
*req_msg
;
1024 DBusMessage
*rsp_msg
;
1026 struct snapper_snap
*snaps
;
1030 tmp_ctx
= talloc_new(sc_data
);
1031 if (tmp_ctx
== NULL
) {
1032 status
= NT_STATUS_NO_MEMORY
;
1036 dconn
= snapper_dbus_conn_create();
1037 if (dconn
== NULL
) {
1038 status
= NT_STATUS_UNSUCCESSFUL
;
1039 goto err_mem_ctx_free
;
1042 if (fsp
->conn
->connectpath
== NULL
) {
1043 status
= NT_STATUS_INVALID_PARAMETER
;
1047 status
= snapper_get_conf_call(tmp_ctx
, dconn
,
1048 fsp
->conn
->connectpath
,
1051 if (!NT_STATUS_IS_OK(status
)) {
1055 status
= snapper_list_snaps_pack(tmp_ctx
, conf_name
, &req_msg
);
1056 if (!NT_STATUS_IS_OK(status
)) {
1060 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1061 if (!NT_STATUS_IS_OK(status
)) {
1065 status
= snapper_list_snaps_unpack(tmp_ctx
, rsp_msg
,
1066 &num_snaps
, &snaps
);
1067 if (!NT_STATUS_IS_OK(status
)) {
1070 /* we should always get at least one snapshot (current) */
1071 if (num_snaps
== 0) {
1072 DEBUG(1, ("zero snapshots in snap list response\n"));
1073 status
= NT_STATUS_UNSUCCESSFUL
;
1077 /* subtract 1, (current) snapshot is not returned */
1078 sc_data
->num_volumes
= num_snaps
- 1;
1079 sc_data
->labels
= NULL
;
1081 if ((labels
== false) || (sc_data
->num_volumes
== 0)) {
1082 /* tokens need not be added to the labels array */
1086 sc_data
->labels
= talloc_array(sc_data
, SHADOW_COPY_LABEL
,
1087 sc_data
->num_volumes
);
1088 if (sc_data
->labels
== NULL
) {
1089 status
= NT_STATUS_NO_MEMORY
;
1093 /* start at end for decending order, do not include 0 (current) */
1095 for (i
= num_snaps
- 1; i
> 0; i
--) {
1096 char *lbl
= sc_data
->labels
[lbl_off
++];
1097 struct tm gmt_snap_time
;
1101 tm_ret
= gmtime_r((time_t *)&snaps
[i
].time
, &gmt_snap_time
);
1102 if (tm_ret
== NULL
) {
1103 status
= NT_STATUS_UNSUCCESSFUL
;
1104 goto err_labels_free
;
1106 str_sz
= strftime(lbl
, sizeof(SHADOW_COPY_LABEL
),
1107 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
1109 status
= NT_STATUS_UNSUCCESSFUL
;
1110 goto err_labels_free
;
1115 talloc_free(tmp_ctx
);
1116 dbus_message_unref(rsp_msg
);
1117 dbus_message_unref(req_msg
);
1118 snapper_dbus_conn_destroy(dconn
);
1123 TALLOC_FREE(sc_data
->labels
);
1125 dbus_message_unref(rsp_msg
);
1127 dbus_message_unref(req_msg
);
1129 snapper_dbus_conn_destroy(dconn
);
1131 talloc_free(tmp_ctx
);
1133 errno
= map_errno_from_nt_status(status
);
1137 static bool snapper_gmt_strip_snapshot(TALLOC_CTX
*mem_ctx
,
1138 struct vfs_handle_struct
*handle
,
1148 size_t rest_len
, dst_len
;
1150 p
= strstr_m(name
, "@GMT-");
1154 if ((p
> name
) && (p
[-1] != '/')) {
1157 q
= strptime(p
, GMT_FORMAT
, &tm
);
1162 timestamp
= timegm(&tm
);
1163 if (timestamp
== (time_t)-1) {
1166 if ((p
== name
) && (q
[0] == '\0')) {
1167 if (pstripped
!= NULL
) {
1168 stripped
= talloc_strdup(mem_ctx
, "");
1169 if (stripped
== NULL
) {
1172 *pstripped
= stripped
;
1174 *ptimestamp
= timestamp
;
1182 rest_len
= strlen(q
);
1183 dst_len
= (p
-name
) + rest_len
;
1185 if (pstripped
!= NULL
) {
1186 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
1187 if (stripped
== NULL
) {
1192 memcpy(stripped
, name
, p
-name
);
1195 memcpy(stripped
+ (p
-name
), q
, rest_len
);
1197 stripped
[dst_len
] = '\0';
1198 *pstripped
= stripped
;
1200 *ptimestamp
= timestamp
;
1207 static NTSTATUS
snapper_get_snap_at_time_call(TALLOC_CTX
*mem_ctx
,
1208 DBusConnection
*dconn
,
1209 const char *conf_name
,
1210 const char *base_path
,
1212 char **snap_path_out
)
1215 DBusMessage
*req_msg
;
1216 DBusMessage
*rsp_msg
;
1218 struct snapper_snap
*snaps
;
1221 status
= snapper_list_snaps_at_time_pack(mem_ctx
,
1226 if (!NT_STATUS_IS_OK(status
)) {
1230 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1231 if (!NT_STATUS_IS_OK(status
)) {
1235 status
= snapper_list_snaps_unpack(mem_ctx
, rsp_msg
,
1236 &num_snaps
, &snaps
);
1237 if (!NT_STATUS_IS_OK(status
)) {
1241 if (num_snaps
== 0) {
1242 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime
));
1243 status
= NT_STATUS_INVALID_PARAMETER
;
1244 goto err_snap_array_free
;
1245 } else if (num_snaps
> 0) {
1246 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1247 num_snaps
, snaptime
));
1250 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snaps
[0].id
,
1252 if (!NT_STATUS_IS_OK(status
)) {
1253 goto err_snap_array_free
;
1256 *snap_path_out
= snap_path
;
1257 err_snap_array_free
:
1260 dbus_message_unref(rsp_msg
);
1262 dbus_message_unref(req_msg
);
1267 static NTSTATUS
snapper_snap_path_expand(struct connection_struct
*conn
,
1268 TALLOC_CTX
*mem_ctx
,
1270 char **snap_dir_out
)
1272 DBusConnection
*dconn
;
1278 dconn
= snapper_dbus_conn_create();
1279 if (dconn
== NULL
) {
1280 status
= NT_STATUS_UNSUCCESSFUL
;
1284 if (conn
->connectpath
== NULL
) {
1285 status
= NT_STATUS_INVALID_PARAMETER
;
1289 status
= snapper_get_conf_call(mem_ctx
, dconn
,
1293 if (!NT_STATUS_IS_OK(status
)) {
1297 status
= snapper_get_snap_at_time_call(mem_ctx
, dconn
,
1298 conf_name
, base_path
, snap_time
,
1300 if (!NT_STATUS_IS_OK(status
)) {
1301 goto err_conf_name_free
;
1304 /* confirm snapshot path is nested under base path */
1305 if (strncmp(snap_path
, base_path
, strlen(base_path
)) != 0) {
1306 status
= NT_STATUS_INVALID_PARAMETER
;
1307 goto err_snap_path_free
;
1310 talloc_free(conf_name
);
1311 talloc_free(base_path
);
1312 snapper_dbus_conn_destroy(dconn
);
1313 *snap_dir_out
= snap_path
;
1315 return NT_STATUS_OK
;
1318 talloc_free(snap_path
);
1320 talloc_free(conf_name
);
1321 talloc_free(base_path
);
1323 snapper_dbus_conn_destroy(dconn
);
1328 static char *snapper_gmt_convert(TALLOC_CTX
*mem_ctx
,
1329 struct vfs_handle_struct
*handle
,
1330 const char *name
, time_t timestamp
)
1332 char *snap_path
= NULL
;
1337 status
= snapper_snap_path_expand(handle
->conn
, mem_ctx
, timestamp
,
1339 if (!NT_STATUS_IS_OK(status
)) {
1340 errno
= map_errno_from_nt_status(status
);
1344 path
= talloc_asprintf(mem_ctx
, "%s/%s", snap_path
, name
);
1347 goto err_snap_path_free
;
1350 DEBUG(10, ("converted %s/%s @ time to %s\n",
1351 handle
->conn
->connectpath
, name
, path
));
1355 saved_errno
= errno
;
1356 talloc_free(snap_path
);
1357 errno
= saved_errno
;
1362 static DIR *snapper_gmt_opendir(vfs_handle_struct
*handle
,
1373 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1374 ×tamp
, &stripped
)) {
1377 if (timestamp
== 0) {
1378 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
1380 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1381 TALLOC_FREE(stripped
);
1385 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
1386 saved_errno
= errno
;
1388 errno
= saved_errno
;
1392 static int snapper_gmt_rename(vfs_handle_struct
*handle
,
1393 const struct smb_filename
*smb_fname_src
,
1394 const struct smb_filename
*smb_fname_dst
)
1396 time_t timestamp_src
, timestamp_dst
;
1398 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1399 smb_fname_src
->base_name
,
1400 ×tamp_src
, NULL
)) {
1403 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1404 smb_fname_dst
->base_name
,
1405 ×tamp_dst
, NULL
)) {
1408 if (timestamp_src
!= 0) {
1412 if (timestamp_dst
!= 0) {
1416 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
1419 static int snapper_gmt_symlink(vfs_handle_struct
*handle
,
1420 const char *oldname
, const char *newname
)
1422 time_t timestamp_old
, timestamp_new
;
1424 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1425 ×tamp_old
, NULL
)) {
1428 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1429 ×tamp_new
, NULL
)) {
1432 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1436 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
1439 static int snapper_gmt_link(vfs_handle_struct
*handle
,
1440 const char *oldname
, const char *newname
)
1442 time_t timestamp_old
, timestamp_new
;
1444 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1445 ×tamp_old
, NULL
)) {
1448 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1449 ×tamp_new
, NULL
)) {
1452 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1456 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
1459 static int snapper_gmt_stat(vfs_handle_struct
*handle
,
1460 struct smb_filename
*smb_fname
)
1463 char *stripped
, *tmp
;
1464 int ret
, saved_errno
;
1466 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1467 smb_fname
->base_name
,
1468 ×tamp
, &stripped
)) {
1471 if (timestamp
== 0) {
1472 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1475 tmp
= smb_fname
->base_name
;
1476 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1477 stripped
, timestamp
);
1478 TALLOC_FREE(stripped
);
1480 if (smb_fname
->base_name
== NULL
) {
1481 smb_fname
->base_name
= tmp
;
1485 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1486 saved_errno
= errno
;
1488 TALLOC_FREE(smb_fname
->base_name
);
1489 smb_fname
->base_name
= tmp
;
1491 errno
= saved_errno
;
1495 static int snapper_gmt_lstat(vfs_handle_struct
*handle
,
1496 struct smb_filename
*smb_fname
)
1499 char *stripped
, *tmp
;
1500 int ret
, saved_errno
;
1502 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1503 smb_fname
->base_name
,
1504 ×tamp
, &stripped
)) {
1507 if (timestamp
== 0) {
1508 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1511 tmp
= smb_fname
->base_name
;
1512 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1513 stripped
, timestamp
);
1514 TALLOC_FREE(stripped
);
1516 if (smb_fname
->base_name
== NULL
) {
1517 smb_fname
->base_name
= tmp
;
1521 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1522 saved_errno
= errno
;
1524 TALLOC_FREE(smb_fname
->base_name
);
1525 smb_fname
->base_name
= tmp
;
1527 errno
= saved_errno
;
1531 static int snapper_gmt_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
1532 SMB_STRUCT_STAT
*sbuf
)
1537 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1541 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1542 fsp
->fsp_name
->base_name
,
1543 ×tamp
, NULL
)) {
1549 static int snapper_gmt_open(vfs_handle_struct
*handle
,
1550 struct smb_filename
*smb_fname
, files_struct
*fsp
,
1551 int flags
, mode_t mode
)
1554 char *stripped
, *tmp
;
1555 int ret
, saved_errno
;
1557 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1558 smb_fname
->base_name
,
1559 ×tamp
, &stripped
)) {
1562 if (timestamp
== 0) {
1563 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1566 tmp
= smb_fname
->base_name
;
1567 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1568 stripped
, timestamp
);
1569 TALLOC_FREE(stripped
);
1571 if (smb_fname
->base_name
== NULL
) {
1572 smb_fname
->base_name
= tmp
;
1576 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1577 saved_errno
= errno
;
1579 TALLOC_FREE(smb_fname
->base_name
);
1580 smb_fname
->base_name
= tmp
;
1582 errno
= saved_errno
;
1586 static int snapper_gmt_unlink(vfs_handle_struct
*handle
,
1587 const struct smb_filename
*smb_fname
)
1591 int ret
, saved_errno
;
1592 struct smb_filename
*conv
;
1594 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1595 smb_fname
->base_name
,
1596 ×tamp
, &stripped
)) {
1599 if (timestamp
== 0) {
1600 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
1602 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1607 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1608 stripped
, timestamp
);
1609 TALLOC_FREE(stripped
);
1610 if (conv
->base_name
== NULL
) {
1613 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
1614 saved_errno
= errno
;
1616 errno
= saved_errno
;
1620 static int snapper_gmt_chmod(vfs_handle_struct
*handle
, const char *fname
,
1625 int ret
, saved_errno
;
1628 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1629 ×tamp
, &stripped
)) {
1632 if (timestamp
== 0) {
1633 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
1635 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1636 TALLOC_FREE(stripped
);
1640 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
1641 saved_errno
= errno
;
1643 errno
= saved_errno
;
1647 static int snapper_gmt_chown(vfs_handle_struct
*handle
, const char *fname
,
1648 uid_t uid
, gid_t gid
)
1652 int ret
, saved_errno
;
1655 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1656 ×tamp
, &stripped
)) {
1659 if (timestamp
== 0) {
1660 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
1662 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1663 TALLOC_FREE(stripped
);
1667 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1668 saved_errno
= errno
;
1670 errno
= saved_errno
;
1674 static int snapper_gmt_chdir(vfs_handle_struct
*handle
,
1679 int ret
, saved_errno
;
1682 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1683 ×tamp
, &stripped
)) {
1686 if (timestamp
== 0) {
1687 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1689 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1690 TALLOC_FREE(stripped
);
1694 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1695 saved_errno
= errno
;
1697 errno
= saved_errno
;
1701 static int snapper_gmt_ntimes(vfs_handle_struct
*handle
,
1702 const struct smb_filename
*smb_fname
,
1703 struct smb_file_time
*ft
)
1707 int ret
, saved_errno
;
1708 struct smb_filename
*conv
;
1710 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1711 smb_fname
->base_name
,
1712 ×tamp
, &stripped
)) {
1715 if (timestamp
== 0) {
1716 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1718 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1723 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1724 stripped
, timestamp
);
1725 TALLOC_FREE(stripped
);
1726 if (conv
->base_name
== NULL
) {
1729 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1730 saved_errno
= errno
;
1732 errno
= saved_errno
;
1736 static int snapper_gmt_readlink(vfs_handle_struct
*handle
,
1737 const char *fname
, char *buf
, size_t bufsiz
)
1741 int ret
, saved_errno
;
1744 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1745 ×tamp
, &stripped
)) {
1748 if (timestamp
== 0) {
1749 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1751 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1752 TALLOC_FREE(stripped
);
1756 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1757 saved_errno
= errno
;
1759 errno
= saved_errno
;
1763 static int snapper_gmt_mknod(vfs_handle_struct
*handle
,
1764 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1768 int ret
, saved_errno
;
1771 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1772 ×tamp
, &stripped
)) {
1775 if (timestamp
== 0) {
1776 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1778 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1779 TALLOC_FREE(stripped
);
1783 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1784 saved_errno
= errno
;
1786 errno
= saved_errno
;
1790 static char *snapper_gmt_realpath(vfs_handle_struct
*handle
,
1794 char *stripped
= NULL
;
1796 char *result
= NULL
;
1799 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1800 ×tamp
, &stripped
)) {
1803 if (timestamp
== 0) {
1804 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1807 tmp
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1812 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1813 if (result
== NULL
) {
1818 saved_errno
= errno
;
1820 TALLOC_FREE(stripped
);
1821 errno
= saved_errno
;
1825 static NTSTATUS
snapper_gmt_fget_nt_acl(vfs_handle_struct
*handle
,
1826 struct files_struct
*fsp
,
1827 uint32 security_info
,
1828 TALLOC_CTX
*mem_ctx
,
1829 struct security_descriptor
**ppdesc
)
1836 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1837 fsp
->fsp_name
->base_name
,
1838 ×tamp
, &stripped
)) {
1839 return map_nt_error_from_unix(errno
);
1841 if (timestamp
== 0) {
1842 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1846 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1847 TALLOC_FREE(stripped
);
1849 return map_nt_error_from_unix(errno
);
1851 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1857 static NTSTATUS
snapper_gmt_get_nt_acl(vfs_handle_struct
*handle
,
1859 uint32 security_info
,
1860 TALLOC_CTX
*mem_ctx
,
1861 struct security_descriptor
**ppdesc
)
1868 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1869 ×tamp
, &stripped
)) {
1870 return map_nt_error_from_unix(errno
);
1872 if (timestamp
== 0) {
1873 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1876 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1877 TALLOC_FREE(stripped
);
1879 return map_nt_error_from_unix(errno
);
1881 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1887 static int snapper_gmt_mkdir(vfs_handle_struct
*handle
,
1888 const char *fname
, mode_t mode
)
1892 int ret
, saved_errno
;
1895 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1896 ×tamp
, &stripped
)) {
1899 if (timestamp
== 0) {
1900 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1902 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1903 TALLOC_FREE(stripped
);
1907 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1908 saved_errno
= errno
;
1910 errno
= saved_errno
;
1914 static int snapper_gmt_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1918 int ret
, saved_errno
;
1921 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1922 ×tamp
, &stripped
)) {
1925 if (timestamp
== 0) {
1926 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1928 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1929 TALLOC_FREE(stripped
);
1933 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1934 saved_errno
= errno
;
1936 errno
= saved_errno
;
1940 static int snapper_gmt_chflags(vfs_handle_struct
*handle
, const char *fname
,
1945 int ret
, saved_errno
;
1948 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1949 ×tamp
, &stripped
)) {
1952 if (timestamp
== 0) {
1953 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1955 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1956 TALLOC_FREE(stripped
);
1960 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1961 saved_errno
= errno
;
1963 errno
= saved_errno
;
1967 static ssize_t
snapper_gmt_getxattr(vfs_handle_struct
*handle
,
1968 const char *fname
, const char *aname
,
1969 void *value
, size_t size
)
1977 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1978 ×tamp
, &stripped
)) {
1981 if (timestamp
== 0) {
1982 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1985 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1986 TALLOC_FREE(stripped
);
1990 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1991 saved_errno
= errno
;
1993 errno
= saved_errno
;
1997 static ssize_t
snapper_gmt_listxattr(struct vfs_handle_struct
*handle
,
1999 char *list
, size_t size
)
2007 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2008 ×tamp
, &stripped
)) {
2011 if (timestamp
== 0) {
2012 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
2014 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2015 TALLOC_FREE(stripped
);
2019 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
2020 saved_errno
= errno
;
2022 errno
= saved_errno
;
2026 static int snapper_gmt_removexattr(vfs_handle_struct
*handle
,
2027 const char *fname
, const char *aname
)
2031 int ret
, saved_errno
;
2034 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2035 ×tamp
, &stripped
)) {
2038 if (timestamp
== 0) {
2039 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
2041 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2042 TALLOC_FREE(stripped
);
2046 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
2047 saved_errno
= errno
;
2049 errno
= saved_errno
;
2053 static int snapper_gmt_setxattr(struct vfs_handle_struct
*handle
,
2055 const char *aname
, const void *value
,
2056 size_t size
, int flags
)
2064 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2065 ×tamp
, &stripped
)) {
2068 if (timestamp
== 0) {
2069 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
2072 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2073 TALLOC_FREE(stripped
);
2077 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
2078 saved_errno
= errno
;
2080 errno
= saved_errno
;
2084 static int snapper_gmt_chmod_acl(vfs_handle_struct
*handle
,
2085 const char *fname
, mode_t mode
)
2093 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2094 ×tamp
, &stripped
)) {
2097 if (timestamp
== 0) {
2098 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
2100 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2101 TALLOC_FREE(stripped
);
2105 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
2106 saved_errno
= errno
;
2108 errno
= saved_errno
;
2112 static int snapper_gmt_get_real_filename(struct vfs_handle_struct
*handle
,
2115 TALLOC_CTX
*mem_ctx
,
2124 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
2125 ×tamp
, &stripped
)) {
2128 if (timestamp
== 0) {
2129 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
2130 mem_ctx
, found_name
);
2132 if (stripped
[0] == '\0') {
2133 *found_name
= talloc_strdup(mem_ctx
, name
);
2134 if (*found_name
== NULL
) {
2140 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2141 TALLOC_FREE(stripped
);
2145 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
2146 mem_ctx
, found_name
);
2147 saved_errno
= errno
;
2149 errno
= saved_errno
;
2153 static uint64_t snapper_gmt_disk_free(vfs_handle_struct
*handle
,
2154 const char *path
, uint64_t *bsize
,
2155 uint64_t *dfree
, uint64_t *dsize
)
2163 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
2164 ×tamp
, &stripped
)) {
2167 if (timestamp
== 0) {
2168 return SMB_VFS_NEXT_DISK_FREE(handle
, path
,
2169 bsize
, dfree
, dsize
);
2172 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2173 TALLOC_FREE(stripped
);
2178 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, bsize
, dfree
, dsize
);
2180 saved_errno
= errno
;
2182 errno
= saved_errno
;
2188 static struct vfs_fn_pointers snapper_fns
= {
2189 .get_shadow_copy_data_fn
= snapper_get_shadow_copy_data
,
2190 .opendir_fn
= snapper_gmt_opendir
,
2191 .disk_free_fn
= snapper_gmt_disk_free
,
2192 .rename_fn
= snapper_gmt_rename
,
2193 .link_fn
= snapper_gmt_link
,
2194 .symlink_fn
= snapper_gmt_symlink
,
2195 .stat_fn
= snapper_gmt_stat
,
2196 .lstat_fn
= snapper_gmt_lstat
,
2197 .fstat_fn
= snapper_gmt_fstat
,
2198 .open_fn
= snapper_gmt_open
,
2199 .unlink_fn
= snapper_gmt_unlink
,
2200 .chmod_fn
= snapper_gmt_chmod
,
2201 .chown_fn
= snapper_gmt_chown
,
2202 .chdir_fn
= snapper_gmt_chdir
,
2203 .ntimes_fn
= snapper_gmt_ntimes
,
2204 .readlink_fn
= snapper_gmt_readlink
,
2205 .mknod_fn
= snapper_gmt_mknod
,
2206 .realpath_fn
= snapper_gmt_realpath
,
2207 .get_nt_acl_fn
= snapper_gmt_get_nt_acl
,
2208 .fget_nt_acl_fn
= snapper_gmt_fget_nt_acl
,
2209 .mkdir_fn
= snapper_gmt_mkdir
,
2210 .rmdir_fn
= snapper_gmt_rmdir
,
2211 .getxattr_fn
= snapper_gmt_getxattr
,
2212 .listxattr_fn
= snapper_gmt_listxattr
,
2213 .removexattr_fn
= snapper_gmt_removexattr
,
2214 .setxattr_fn
= snapper_gmt_setxattr
,
2215 .chmod_acl_fn
= snapper_gmt_chmod_acl
,
2216 .chflags_fn
= snapper_gmt_chflags
,
2217 .get_real_filename_fn
= snapper_gmt_get_real_filename
,
2220 NTSTATUS
vfs_snapper_init(void);
2221 NTSTATUS
vfs_snapper_init(void)
2223 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2224 "snapper", &snapper_fns
);