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(DBusMessageIter
*iter
,
323 struct snapper_dict
*dict_out
)
327 DBusMessageIter dct_iter
;
329 status
= snapper_type_check(iter
, DBUS_TYPE_DICT_ENTRY
);
330 if (!NT_STATUS_IS_OK(status
)) {
333 dbus_message_iter_recurse(iter
, &dct_iter
);
335 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
337 if (!NT_STATUS_IS_OK(status
)) {
341 dbus_message_iter_next(&dct_iter
);
342 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
344 if (!NT_STATUS_IS_OK(status
)) {
351 static void snapper_dict_array_print(uint32_t num_dicts
,
352 struct snapper_dict
*dicts
)
356 for (i
= 0; i
< num_dicts
; i
++) {
357 DEBUG(10, ("dict (key: %s, val: %s)\n",
358 dicts
[i
].key
, dicts
[i
].val
));
362 static NTSTATUS
snapper_dict_array_unpack(TALLOC_CTX
*mem_ctx
,
363 DBusMessageIter
*iter
,
364 uint32_t *num_dicts_out
,
365 struct snapper_dict
**dicts_out
)
368 DBusMessageIter array_iter
;
370 struct snapper_dict
*dicts
= NULL
;
372 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
373 if (!NT_STATUS_IS_OK(status
)) {
376 dbus_message_iter_recurse(iter
, &array_iter
);
379 while (dbus_message_iter_get_arg_type(&array_iter
)
380 != DBUS_TYPE_INVALID
) {
382 dicts
= talloc_realloc(mem_ctx
, dicts
, struct snapper_dict
,
387 status
= snapper_dict_unpack(&array_iter
,
388 &dicts
[num_dicts
- 1]);
389 if (!NT_STATUS_IS_OK(status
)) {
393 dbus_message_iter_next(&array_iter
);
396 *num_dicts_out
= num_dicts
;
402 static NTSTATUS
snapper_list_confs_pack(DBusMessage
**req_msg_out
)
406 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
407 "/org/opensuse/Snapper",
408 "org.opensuse.Snapper",
411 DEBUG(0, ("null msg\n"));
412 return NT_STATUS_NO_MEMORY
;
415 /* no arguments to append */
421 static NTSTATUS
snapper_conf_unpack(TALLOC_CTX
*mem_ctx
,
422 DBusMessageIter
*iter
,
423 struct snapper_conf
*conf_out
)
426 DBusMessageIter st_iter
;
428 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
429 if (!NT_STATUS_IS_OK(status
)) {
432 dbus_message_iter_recurse(iter
, &st_iter
);
434 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
436 if (!NT_STATUS_IS_OK(status
)) {
440 dbus_message_iter_next(&st_iter
);
441 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
443 if (!NT_STATUS_IS_OK(status
)) {
447 dbus_message_iter_next(&st_iter
);
448 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
449 &conf_out
->num_attrs
,
455 static struct snapper_conf
*snapper_conf_array_base_find(int32_t num_confs
,
456 struct snapper_conf
*confs
,
461 for (i
= 0; i
< num_confs
; i
++) {
462 if (strcmp(confs
[i
].mnt
, base
) == 0) {
463 DEBUG(5, ("found snapper conf %s for path %s\n",
464 confs
[i
].name
, base
));
468 DEBUG(5, ("config for base %s not found\n", base
));
473 static void snapper_conf_array_print(int32_t num_confs
,
474 struct snapper_conf
*confs
)
478 for (i
= 0; i
< num_confs
; i
++) {
479 DEBUG(10, ("name: %s, mnt: %s\n",
480 confs
[i
].name
, confs
[i
].mnt
));
481 snapper_dict_array_print(confs
[i
].num_attrs
, confs
[i
].attrs
);
485 static NTSTATUS
snapper_conf_array_unpack(TALLOC_CTX
*mem_ctx
,
486 DBusMessageIter
*iter
,
487 uint32_t *num_confs_out
,
488 struct snapper_conf
**confs_out
)
492 struct snapper_conf
*confs
= NULL
;
493 DBusMessageIter array_iter
;
496 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
497 if (!NT_STATUS_IS_OK(status
)) {
500 dbus_message_iter_recurse(iter
, &array_iter
);
503 while (dbus_message_iter_get_arg_type(&array_iter
)
504 != DBUS_TYPE_INVALID
) {
506 confs
= talloc_realloc(mem_ctx
, confs
, struct snapper_conf
,
511 status
= snapper_conf_unpack(confs
, &array_iter
,
512 &confs
[num_confs
- 1]);
513 if (!NT_STATUS_IS_OK(status
)) {
517 dbus_message_iter_next(&array_iter
);
520 *num_confs_out
= num_confs
;
526 static NTSTATUS
snapper_list_confs_unpack(TALLOC_CTX
*mem_ctx
,
527 DBusConnection
*dconn
,
528 DBusMessage
*rsp_msg
,
529 uint32_t *num_confs_out
,
530 struct snapper_conf
**confs_out
)
533 DBusMessageIter iter
;
536 struct snapper_conf
*confs
;
539 msg_type
= dbus_message_get_type(rsp_msg
);
540 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
541 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
542 DEBUG(0, ("list_confs error response: %s\n", err_str
));
543 return snapper_err_ntstatus_map(err_str
);
546 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
547 DEBUG(0, ("unexpected list_confs ret type: %d\n",
549 return NT_STATUS_INVALID_PARAMETER
;
552 sig
= dbus_message_get_signature(rsp_msg
);
554 || (strcmp(sig
, SNAPPER_SIG_LIST_CONFS_RSP
) != 0)) {
555 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
556 (sig
? sig
: "NULL"), SNAPPER_SIG_LIST_CONFS_RSP
));
557 return NT_STATUS_INVALID_PARAMETER
;
560 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
561 /* FIXME return empty? */
562 DEBUG(0, ("Message has no arguments!\n"));
563 return NT_STATUS_INVALID_PARAMETER
;
566 status
= snapper_conf_array_unpack(mem_ctx
, &iter
, &num_confs
, &confs
);
567 if (!NT_STATUS_IS_OK(status
)) {
568 DEBUG(0, ("failed to unpack conf array\n"));
572 snapper_conf_array_print(num_confs
, confs
);
574 *num_confs_out
= num_confs
;
580 static NTSTATUS
snapper_list_snaps_pack(char *snapper_conf
,
581 DBusMessage
**req_msg_out
)
584 DBusMessageIter args
;
586 msg
= dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
587 "/org/opensuse/Snapper", /* object to call on */
588 "org.opensuse.Snapper", /* interface to call on */
589 "ListSnapshots"); /* method name */
591 DEBUG(0, ("failed to create list snaps message\n"));
592 return NT_STATUS_NO_MEMORY
;
595 /* append arguments */
596 dbus_message_iter_init_append(msg
, &args
);
597 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
599 dbus_message_unref(msg
);
600 return NT_STATUS_NO_MEMORY
;
608 static NTSTATUS
snapper_snap_struct_unpack(TALLOC_CTX
*mem_ctx
,
609 DBusMessageIter
*iter
,
610 struct snapper_snap
*snap_out
)
613 DBusMessageIter st_iter
;
615 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
616 if (!NT_STATUS_IS_OK(status
)) {
619 dbus_message_iter_recurse(iter
, &st_iter
);
621 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
623 if (!NT_STATUS_IS_OK(status
)) {
627 dbus_message_iter_next(&st_iter
);
628 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT16
,
630 if (!NT_STATUS_IS_OK(status
)) {
634 dbus_message_iter_next(&st_iter
);
635 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
637 if (!NT_STATUS_IS_OK(status
)) {
641 dbus_message_iter_next(&st_iter
);
642 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_INT64
,
644 if (!NT_STATUS_IS_OK(status
)) {
648 dbus_message_iter_next(&st_iter
);
649 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
650 &snap_out
->creator_uid
);
651 if (!NT_STATUS_IS_OK(status
)) {
655 dbus_message_iter_next(&st_iter
);
656 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
658 if (!NT_STATUS_IS_OK(status
)) {
662 dbus_message_iter_next(&st_iter
);
663 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
665 if (!NT_STATUS_IS_OK(status
)) {
669 dbus_message_iter_next(&st_iter
);
670 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
671 &snap_out
->num_user_data
,
672 &snap_out
->user_data
);
677 static void snapper_snap_array_print(int32_t num_snaps
,
678 struct snapper_snap
*snaps
)
682 for (i
= 0; i
< num_snaps
; i
++) {
683 DEBUG(10, ("id: %u, "
690 (unsigned int)snaps
[i
].id
,
691 (unsigned int)snaps
[i
].type
,
692 (unsigned int)snaps
[i
].pre_id
,
693 (long int)snaps
[i
].time
,
694 (unsigned int)snaps
[i
].creator_uid
,
697 snapper_dict_array_print(snaps
[i
].num_user_data
,
702 static NTSTATUS
snapper_snap_array_unpack(TALLOC_CTX
*mem_ctx
,
703 DBusMessageIter
*iter
,
704 uint32_t *num_snaps_out
,
705 struct snapper_snap
**snaps_out
)
709 struct snapper_snap
*snaps
= NULL
;
710 DBusMessageIter array_iter
;
713 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
714 if (!NT_STATUS_IS_OK(status
)) {
717 dbus_message_iter_recurse(iter
, &array_iter
);
720 while (dbus_message_iter_get_arg_type(&array_iter
)
721 != DBUS_TYPE_INVALID
) {
723 snaps
= talloc_realloc(mem_ctx
, snaps
, struct snapper_snap
,
728 status
= snapper_snap_struct_unpack(snaps
, &array_iter
,
729 &snaps
[num_snaps
- 1]);
730 if (!NT_STATUS_IS_OK(status
)) {
734 dbus_message_iter_next(&array_iter
);
737 *num_snaps_out
= num_snaps
;
743 static NTSTATUS
snapper_list_snaps_unpack(TALLOC_CTX
*mem_ctx
,
744 DBusMessage
*rsp_msg
,
745 uint32_t *num_snaps_out
,
746 struct snapper_snap
**snaps_out
)
749 DBusMessageIter iter
;
752 struct snapper_snap
*snaps
;
755 msg_type
= dbus_message_get_type(rsp_msg
);
756 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
757 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
758 DEBUG(0, ("list_snaps error response: %s\n", err_str
));
759 return snapper_err_ntstatus_map(err_str
);
762 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
763 DEBUG(0,("unexpected list_snaps ret type: %d\n",
765 return NT_STATUS_INVALID_PARAMETER
;
768 sig
= dbus_message_get_signature(rsp_msg
);
770 || (strcmp(sig
, SNAPPER_SIG_LIST_SNAPS_RSP
) != 0)) {
771 DEBUG(0, ("bad list snaps response sig: %s, "
773 (sig
? sig
: "NULL"),
774 SNAPPER_SIG_LIST_SNAPS_RSP
));
775 return NT_STATUS_INVALID_PARAMETER
;
778 /* read the parameters */
779 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
780 DEBUG(0, ("response has no arguments!\n"));
781 return NT_STATUS_INVALID_PARAMETER
;
784 status
= snapper_snap_array_unpack(mem_ctx
, &iter
, &num_snaps
, &snaps
);
785 if (!NT_STATUS_IS_OK(status
)) {
786 DEBUG(0, ("failed to unpack snap array\n"));
787 return NT_STATUS_INVALID_PARAMETER
;
790 snapper_snap_array_print(num_snaps
, snaps
);
792 *num_snaps_out
= num_snaps
;
798 static NTSTATUS
snapper_list_snaps_at_time_pack(const char *snapper_conf
,
801 DBusMessage
**req_msg_out
)
804 DBusMessageIter args
;
806 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
807 "/org/opensuse/Snapper",
808 "org.opensuse.Snapper",
809 "ListSnapshotsAtTime");
811 DEBUG(0, ("failed to create list snaps message\n"));
812 return NT_STATUS_NO_MEMORY
;
815 dbus_message_iter_init_append(msg
, &args
);
816 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
818 dbus_message_unref(msg
);
819 return NT_STATUS_NO_MEMORY
;
822 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
824 dbus_message_unref(msg
);
825 return NT_STATUS_NO_MEMORY
;
828 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
830 dbus_message_unref(msg
);
831 return NT_STATUS_NO_MEMORY
;
838 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
841 * Determine the snapper snapshot path given an id and base.
842 * Ideally this should be determined via a lookup.
844 static NTSTATUS
snapper_snap_id_to_path(TALLOC_CTX
*mem_ctx
,
845 const char *base_path
,
847 char **snap_path_out
)
851 snap_path
= talloc_asprintf(mem_ctx
, "%s/.snapshots/%u/snapshot",
853 if (snap_path
== NULL
) {
854 return NT_STATUS_NO_MEMORY
;
857 *snap_path_out
= snap_path
;
861 static NTSTATUS
snapper_get_conf_call(TALLOC_CTX
*mem_ctx
,
862 DBusConnection
*dconn
,
864 char **conf_name_out
,
865 char **base_path_out
)
868 DBusMessage
*req_msg
;
869 DBusMessage
*rsp_msg
;
870 uint32_t num_confs
= 0;
871 struct snapper_conf
*confs
= NULL
;
872 struct snapper_conf
*conf
;
876 status
= snapper_list_confs_pack(&req_msg
);
877 if (!NT_STATUS_IS_OK(status
)) {
881 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
882 if (!NT_STATUS_IS_OK(status
)) {
886 status
= snapper_list_confs_unpack(mem_ctx
, dconn
, rsp_msg
,
888 if (!NT_STATUS_IS_OK(status
)) {
893 * for now we only support shares where the path directly corresponds
894 * to a snapper configuration.
896 conf
= snapper_conf_array_base_find(num_confs
, confs
,
899 status
= NT_STATUS_NOT_SUPPORTED
;
903 conf_name
= talloc_strdup(mem_ctx
, conf
->name
);
904 if (conf_name
== NULL
) {
905 status
= NT_STATUS_NO_MEMORY
;
908 base_path
= talloc_strdup(mem_ctx
, conf
->mnt
);
909 if (base_path
== NULL
) {
910 status
= NT_STATUS_NO_MEMORY
;
911 goto err_conf_name_free
;
915 dbus_message_unref(rsp_msg
);
916 dbus_message_unref(req_msg
);
918 *conf_name_out
= conf_name
;
919 *base_path_out
= base_path
;
924 talloc_free(conf_name
);
928 dbus_message_unref(rsp_msg
);
930 dbus_message_unref(req_msg
);
935 /* sc_data used as parent talloc context for all labels */
936 static int snapper_get_shadow_copy_data(struct vfs_handle_struct
*handle
,
937 struct files_struct
*fsp
,
938 struct shadow_copy_data
*sc_data
,
941 DBusConnection
*dconn
;
946 DBusMessage
*req_msg
;
947 DBusMessage
*rsp_msg
;
949 struct snapper_snap
*snaps
;
953 tmp_ctx
= talloc_new(sc_data
);
954 if (tmp_ctx
== NULL
) {
955 status
= NT_STATUS_NO_MEMORY
;
959 dconn
= snapper_dbus_conn_create();
961 status
= NT_STATUS_UNSUCCESSFUL
;
962 goto err_mem_ctx_free
;
965 if (fsp
->conn
->connectpath
== NULL
) {
966 status
= NT_STATUS_INVALID_PARAMETER
;
970 status
= snapper_get_conf_call(tmp_ctx
, dconn
,
971 fsp
->conn
->connectpath
,
974 if (!NT_STATUS_IS_OK(status
)) {
978 status
= snapper_list_snaps_pack(conf_name
, &req_msg
);
979 if (!NT_STATUS_IS_OK(status
)) {
983 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
984 if (!NT_STATUS_IS_OK(status
)) {
988 status
= snapper_list_snaps_unpack(tmp_ctx
, rsp_msg
,
990 if (!NT_STATUS_IS_OK(status
)) {
993 /* we should always get at least one snapshot (current) */
994 if (num_snaps
== 0) {
995 DEBUG(1, ("zero snapshots in snap list response\n"));
996 status
= NT_STATUS_UNSUCCESSFUL
;
1000 /* subtract 1, (current) snapshot is not returned */
1001 sc_data
->num_volumes
= num_snaps
- 1;
1002 sc_data
->labels
= NULL
;
1004 if ((labels
== false) || (sc_data
->num_volumes
== 0)) {
1005 /* tokens need not be added to the labels array */
1009 sc_data
->labels
= talloc_array(sc_data
, SHADOW_COPY_LABEL
,
1010 sc_data
->num_volumes
);
1011 if (sc_data
->labels
== NULL
) {
1012 status
= NT_STATUS_NO_MEMORY
;
1016 /* start at end for decending order, do not include 0 (current) */
1018 for (i
= num_snaps
- 1; i
> 0; i
--) {
1019 char *lbl
= sc_data
->labels
[lbl_off
++];
1020 struct tm gmt_snap_time
;
1024 tm_ret
= gmtime_r((time_t *)&snaps
[i
].time
, &gmt_snap_time
);
1025 if (tm_ret
== NULL
) {
1026 status
= NT_STATUS_UNSUCCESSFUL
;
1027 goto err_labels_free
;
1029 str_sz
= strftime(lbl
, sizeof(SHADOW_COPY_LABEL
),
1030 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
1032 status
= NT_STATUS_UNSUCCESSFUL
;
1033 goto err_labels_free
;
1038 talloc_free(tmp_ctx
);
1039 dbus_message_unref(rsp_msg
);
1040 dbus_message_unref(req_msg
);
1041 snapper_dbus_conn_destroy(dconn
);
1046 TALLOC_FREE(sc_data
->labels
);
1048 dbus_message_unref(rsp_msg
);
1050 dbus_message_unref(req_msg
);
1052 snapper_dbus_conn_destroy(dconn
);
1054 talloc_free(tmp_ctx
);
1056 errno
= map_errno_from_nt_status(status
);
1060 static bool snapper_gmt_strip_snapshot(TALLOC_CTX
*mem_ctx
,
1061 struct vfs_handle_struct
*handle
,
1071 size_t rest_len
, dst_len
;
1073 p
= strstr_m(name
, "@GMT-");
1077 if ((p
> name
) && (p
[-1] != '/')) {
1080 q
= strptime(p
, GMT_FORMAT
, &tm
);
1085 timestamp
= timegm(&tm
);
1086 if (timestamp
== (time_t)-1) {
1089 if ((p
== name
) && (q
[0] == '\0')) {
1090 if (pstripped
!= NULL
) {
1091 stripped
= talloc_strdup(mem_ctx
, "");
1092 if (stripped
== NULL
) {
1095 *pstripped
= stripped
;
1097 *ptimestamp
= timestamp
;
1105 rest_len
= strlen(q
);
1106 dst_len
= (p
-name
) + rest_len
;
1108 if (pstripped
!= NULL
) {
1109 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
1110 if (stripped
== NULL
) {
1115 memcpy(stripped
, name
, p
-name
);
1118 memcpy(stripped
+ (p
-name
), q
, rest_len
);
1120 stripped
[dst_len
] = '\0';
1121 *pstripped
= stripped
;
1123 *ptimestamp
= timestamp
;
1130 static NTSTATUS
snapper_get_snap_at_time_call(TALLOC_CTX
*mem_ctx
,
1131 DBusConnection
*dconn
,
1132 const char *conf_name
,
1133 const char *base_path
,
1135 char **snap_path_out
)
1138 DBusMessage
*req_msg
;
1139 DBusMessage
*rsp_msg
;
1141 struct snapper_snap
*snaps
;
1144 status
= snapper_list_snaps_at_time_pack(conf_name
,
1148 if (!NT_STATUS_IS_OK(status
)) {
1152 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1153 if (!NT_STATUS_IS_OK(status
)) {
1157 status
= snapper_list_snaps_unpack(mem_ctx
, rsp_msg
,
1158 &num_snaps
, &snaps
);
1159 if (!NT_STATUS_IS_OK(status
)) {
1163 if (num_snaps
== 0) {
1164 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime
));
1165 status
= NT_STATUS_INVALID_PARAMETER
;
1166 goto err_snap_array_free
;
1167 } else if (num_snaps
> 0) {
1168 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1169 num_snaps
, snaptime
));
1172 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snaps
[0].id
,
1174 if (!NT_STATUS_IS_OK(status
)) {
1175 goto err_snap_array_free
;
1178 *snap_path_out
= snap_path
;
1179 err_snap_array_free
:
1182 dbus_message_unref(rsp_msg
);
1184 dbus_message_unref(req_msg
);
1189 static NTSTATUS
snapper_snap_path_expand(struct connection_struct
*conn
,
1190 TALLOC_CTX
*mem_ctx
,
1192 char **snap_dir_out
)
1194 DBusConnection
*dconn
;
1200 dconn
= snapper_dbus_conn_create();
1201 if (dconn
== NULL
) {
1202 status
= NT_STATUS_UNSUCCESSFUL
;
1206 if (conn
->connectpath
== NULL
) {
1207 status
= NT_STATUS_INVALID_PARAMETER
;
1211 status
= snapper_get_conf_call(mem_ctx
, dconn
,
1215 if (!NT_STATUS_IS_OK(status
)) {
1219 status
= snapper_get_snap_at_time_call(mem_ctx
, dconn
,
1220 conf_name
, base_path
, snap_time
,
1222 if (!NT_STATUS_IS_OK(status
)) {
1223 goto err_conf_name_free
;
1226 /* confirm snapshot path is nested under base path */
1227 if (strncmp(snap_path
, base_path
, strlen(base_path
)) != 0) {
1228 status
= NT_STATUS_INVALID_PARAMETER
;
1229 goto err_snap_path_free
;
1232 talloc_free(conf_name
);
1233 talloc_free(base_path
);
1234 snapper_dbus_conn_destroy(dconn
);
1235 *snap_dir_out
= snap_path
;
1237 return NT_STATUS_OK
;
1240 talloc_free(snap_path
);
1242 talloc_free(conf_name
);
1243 talloc_free(base_path
);
1245 snapper_dbus_conn_destroy(dconn
);
1250 static char *snapper_gmt_convert(TALLOC_CTX
*mem_ctx
,
1251 struct vfs_handle_struct
*handle
,
1252 const char *name
, time_t timestamp
)
1254 char *snap_path
= NULL
;
1259 status
= snapper_snap_path_expand(handle
->conn
, mem_ctx
, timestamp
,
1261 if (!NT_STATUS_IS_OK(status
)) {
1262 errno
= map_errno_from_nt_status(status
);
1266 path
= talloc_asprintf(mem_ctx
, "%s/%s", snap_path
, name
);
1269 goto err_snap_path_free
;
1272 DEBUG(10, ("converted %s/%s @ time to %s\n",
1273 handle
->conn
->connectpath
, name
, path
));
1277 saved_errno
= errno
;
1278 talloc_free(snap_path
);
1279 errno
= saved_errno
;
1284 static DIR *snapper_gmt_opendir(vfs_handle_struct
*handle
,
1295 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1296 ×tamp
, &stripped
)) {
1299 if (timestamp
== 0) {
1300 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
1302 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1303 TALLOC_FREE(stripped
);
1307 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
1308 saved_errno
= errno
;
1310 errno
= saved_errno
;
1314 static int snapper_gmt_rename(vfs_handle_struct
*handle
,
1315 const struct smb_filename
*smb_fname_src
,
1316 const struct smb_filename
*smb_fname_dst
)
1318 time_t timestamp_src
, timestamp_dst
;
1320 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1321 smb_fname_src
->base_name
,
1322 ×tamp_src
, NULL
)) {
1325 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1326 smb_fname_dst
->base_name
,
1327 ×tamp_dst
, NULL
)) {
1330 if (timestamp_src
!= 0) {
1334 if (timestamp_dst
!= 0) {
1338 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
1341 static int snapper_gmt_symlink(vfs_handle_struct
*handle
,
1342 const char *oldname
, const char *newname
)
1344 time_t timestamp_old
, timestamp_new
;
1346 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1347 ×tamp_old
, NULL
)) {
1350 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1351 ×tamp_new
, NULL
)) {
1354 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1358 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
1361 static int snapper_gmt_link(vfs_handle_struct
*handle
,
1362 const char *oldname
, const char *newname
)
1364 time_t timestamp_old
, timestamp_new
;
1366 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1367 ×tamp_old
, NULL
)) {
1370 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1371 ×tamp_new
, NULL
)) {
1374 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1378 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
1381 static int snapper_gmt_stat(vfs_handle_struct
*handle
,
1382 struct smb_filename
*smb_fname
)
1385 char *stripped
, *tmp
;
1386 int ret
, saved_errno
;
1388 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1389 smb_fname
->base_name
,
1390 ×tamp
, &stripped
)) {
1393 if (timestamp
== 0) {
1394 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1397 tmp
= smb_fname
->base_name
;
1398 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1399 stripped
, timestamp
);
1400 TALLOC_FREE(stripped
);
1402 if (smb_fname
->base_name
== NULL
) {
1403 smb_fname
->base_name
= tmp
;
1407 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1408 saved_errno
= errno
;
1410 TALLOC_FREE(smb_fname
->base_name
);
1411 smb_fname
->base_name
= tmp
;
1413 errno
= saved_errno
;
1417 static int snapper_gmt_lstat(vfs_handle_struct
*handle
,
1418 struct smb_filename
*smb_fname
)
1421 char *stripped
, *tmp
;
1422 int ret
, saved_errno
;
1424 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1425 smb_fname
->base_name
,
1426 ×tamp
, &stripped
)) {
1429 if (timestamp
== 0) {
1430 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1433 tmp
= smb_fname
->base_name
;
1434 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1435 stripped
, timestamp
);
1436 TALLOC_FREE(stripped
);
1438 if (smb_fname
->base_name
== NULL
) {
1439 smb_fname
->base_name
= tmp
;
1443 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1444 saved_errno
= errno
;
1446 TALLOC_FREE(smb_fname
->base_name
);
1447 smb_fname
->base_name
= tmp
;
1449 errno
= saved_errno
;
1453 static int snapper_gmt_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
1454 SMB_STRUCT_STAT
*sbuf
)
1459 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1463 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1464 fsp
->fsp_name
->base_name
,
1465 ×tamp
, NULL
)) {
1471 static int snapper_gmt_open(vfs_handle_struct
*handle
,
1472 struct smb_filename
*smb_fname
, files_struct
*fsp
,
1473 int flags
, mode_t mode
)
1476 char *stripped
, *tmp
;
1477 int ret
, saved_errno
;
1479 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1480 smb_fname
->base_name
,
1481 ×tamp
, &stripped
)) {
1484 if (timestamp
== 0) {
1485 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1488 tmp
= smb_fname
->base_name
;
1489 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1490 stripped
, timestamp
);
1491 TALLOC_FREE(stripped
);
1493 if (smb_fname
->base_name
== NULL
) {
1494 smb_fname
->base_name
= tmp
;
1498 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1499 saved_errno
= errno
;
1501 TALLOC_FREE(smb_fname
->base_name
);
1502 smb_fname
->base_name
= tmp
;
1504 errno
= saved_errno
;
1508 static int snapper_gmt_unlink(vfs_handle_struct
*handle
,
1509 const struct smb_filename
*smb_fname
)
1513 int ret
, saved_errno
;
1514 struct smb_filename
*conv
;
1516 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1517 smb_fname
->base_name
,
1518 ×tamp
, &stripped
)) {
1521 if (timestamp
== 0) {
1522 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
1524 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1529 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1530 stripped
, timestamp
);
1531 TALLOC_FREE(stripped
);
1532 if (conv
->base_name
== NULL
) {
1535 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
1536 saved_errno
= errno
;
1538 errno
= saved_errno
;
1542 static int snapper_gmt_chmod(vfs_handle_struct
*handle
, const char *fname
,
1547 int ret
, saved_errno
;
1550 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1551 ×tamp
, &stripped
)) {
1554 if (timestamp
== 0) {
1555 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
1557 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1558 TALLOC_FREE(stripped
);
1562 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
1563 saved_errno
= errno
;
1565 errno
= saved_errno
;
1569 static int snapper_gmt_chown(vfs_handle_struct
*handle
, const char *fname
,
1570 uid_t uid
, gid_t gid
)
1574 int ret
, saved_errno
;
1577 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1578 ×tamp
, &stripped
)) {
1581 if (timestamp
== 0) {
1582 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
1584 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1585 TALLOC_FREE(stripped
);
1589 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1590 saved_errno
= errno
;
1592 errno
= saved_errno
;
1596 static int snapper_gmt_chdir(vfs_handle_struct
*handle
,
1601 int ret
, saved_errno
;
1604 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1605 ×tamp
, &stripped
)) {
1608 if (timestamp
== 0) {
1609 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1611 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1612 TALLOC_FREE(stripped
);
1616 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1617 saved_errno
= errno
;
1619 errno
= saved_errno
;
1623 static int snapper_gmt_ntimes(vfs_handle_struct
*handle
,
1624 const struct smb_filename
*smb_fname
,
1625 struct smb_file_time
*ft
)
1629 int ret
, saved_errno
;
1630 struct smb_filename
*conv
;
1632 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1633 smb_fname
->base_name
,
1634 ×tamp
, &stripped
)) {
1637 if (timestamp
== 0) {
1638 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1640 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1645 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1646 stripped
, timestamp
);
1647 TALLOC_FREE(stripped
);
1648 if (conv
->base_name
== NULL
) {
1651 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1652 saved_errno
= errno
;
1654 errno
= saved_errno
;
1658 static int snapper_gmt_readlink(vfs_handle_struct
*handle
,
1659 const char *fname
, char *buf
, size_t bufsiz
)
1663 int ret
, saved_errno
;
1666 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1667 ×tamp
, &stripped
)) {
1670 if (timestamp
== 0) {
1671 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1673 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1674 TALLOC_FREE(stripped
);
1678 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1679 saved_errno
= errno
;
1681 errno
= saved_errno
;
1685 static int snapper_gmt_mknod(vfs_handle_struct
*handle
,
1686 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1690 int ret
, saved_errno
;
1693 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1694 ×tamp
, &stripped
)) {
1697 if (timestamp
== 0) {
1698 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1700 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1701 TALLOC_FREE(stripped
);
1705 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1706 saved_errno
= errno
;
1708 errno
= saved_errno
;
1712 static char *snapper_gmt_realpath(vfs_handle_struct
*handle
,
1716 char *stripped
= NULL
;
1718 char *result
= NULL
;
1721 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1722 ×tamp
, &stripped
)) {
1725 if (timestamp
== 0) {
1726 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1729 tmp
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1734 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1735 if (result
== NULL
) {
1740 saved_errno
= errno
;
1742 TALLOC_FREE(stripped
);
1743 errno
= saved_errno
;
1747 static NTSTATUS
snapper_gmt_fget_nt_acl(vfs_handle_struct
*handle
,
1748 struct files_struct
*fsp
,
1749 uint32 security_info
,
1750 TALLOC_CTX
*mem_ctx
,
1751 struct security_descriptor
**ppdesc
)
1758 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1759 fsp
->fsp_name
->base_name
,
1760 ×tamp
, &stripped
)) {
1761 return map_nt_error_from_unix(errno
);
1763 if (timestamp
== 0) {
1764 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1768 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1769 TALLOC_FREE(stripped
);
1771 return map_nt_error_from_unix(errno
);
1773 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1779 static NTSTATUS
snapper_gmt_get_nt_acl(vfs_handle_struct
*handle
,
1781 uint32 security_info
,
1782 TALLOC_CTX
*mem_ctx
,
1783 struct security_descriptor
**ppdesc
)
1790 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1791 ×tamp
, &stripped
)) {
1792 return map_nt_error_from_unix(errno
);
1794 if (timestamp
== 0) {
1795 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1798 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1799 TALLOC_FREE(stripped
);
1801 return map_nt_error_from_unix(errno
);
1803 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1809 static int snapper_gmt_mkdir(vfs_handle_struct
*handle
,
1810 const char *fname
, mode_t mode
)
1814 int ret
, saved_errno
;
1817 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1818 ×tamp
, &stripped
)) {
1821 if (timestamp
== 0) {
1822 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1824 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1825 TALLOC_FREE(stripped
);
1829 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1830 saved_errno
= errno
;
1832 errno
= saved_errno
;
1836 static int snapper_gmt_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1840 int ret
, saved_errno
;
1843 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1844 ×tamp
, &stripped
)) {
1847 if (timestamp
== 0) {
1848 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1850 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1851 TALLOC_FREE(stripped
);
1855 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1856 saved_errno
= errno
;
1858 errno
= saved_errno
;
1862 static int snapper_gmt_chflags(vfs_handle_struct
*handle
, const char *fname
,
1867 int ret
, saved_errno
;
1870 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1871 ×tamp
, &stripped
)) {
1874 if (timestamp
== 0) {
1875 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1877 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1878 TALLOC_FREE(stripped
);
1882 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1883 saved_errno
= errno
;
1885 errno
= saved_errno
;
1889 static ssize_t
snapper_gmt_getxattr(vfs_handle_struct
*handle
,
1890 const char *fname
, const char *aname
,
1891 void *value
, size_t size
)
1899 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1900 ×tamp
, &stripped
)) {
1903 if (timestamp
== 0) {
1904 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1907 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1908 TALLOC_FREE(stripped
);
1912 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1913 saved_errno
= errno
;
1915 errno
= saved_errno
;
1919 static ssize_t
snapper_gmt_listxattr(struct vfs_handle_struct
*handle
,
1921 char *list
, size_t size
)
1929 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1930 ×tamp
, &stripped
)) {
1933 if (timestamp
== 0) {
1934 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1936 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1937 TALLOC_FREE(stripped
);
1941 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1942 saved_errno
= errno
;
1944 errno
= saved_errno
;
1948 static int snapper_gmt_removexattr(vfs_handle_struct
*handle
,
1949 const char *fname
, const char *aname
)
1953 int ret
, saved_errno
;
1956 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1957 ×tamp
, &stripped
)) {
1960 if (timestamp
== 0) {
1961 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1963 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1964 TALLOC_FREE(stripped
);
1968 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1969 saved_errno
= errno
;
1971 errno
= saved_errno
;
1975 static int snapper_gmt_setxattr(struct vfs_handle_struct
*handle
,
1977 const char *aname
, const void *value
,
1978 size_t size
, int flags
)
1986 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1987 ×tamp
, &stripped
)) {
1990 if (timestamp
== 0) {
1991 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1994 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1995 TALLOC_FREE(stripped
);
1999 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
2000 saved_errno
= errno
;
2002 errno
= saved_errno
;
2006 static int snapper_gmt_chmod_acl(vfs_handle_struct
*handle
,
2007 const char *fname
, mode_t mode
)
2015 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
2016 ×tamp
, &stripped
)) {
2019 if (timestamp
== 0) {
2020 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
2022 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2023 TALLOC_FREE(stripped
);
2027 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
2028 saved_errno
= errno
;
2030 errno
= saved_errno
;
2034 static int snapper_gmt_get_real_filename(struct vfs_handle_struct
*handle
,
2037 TALLOC_CTX
*mem_ctx
,
2046 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
2047 ×tamp
, &stripped
)) {
2050 if (timestamp
== 0) {
2051 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
2052 mem_ctx
, found_name
);
2054 if (stripped
[0] == '\0') {
2055 *found_name
= talloc_strdup(mem_ctx
, name
);
2056 if (*found_name
== NULL
) {
2062 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2063 TALLOC_FREE(stripped
);
2067 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
2068 mem_ctx
, found_name
);
2069 saved_errno
= errno
;
2071 errno
= saved_errno
;
2075 static uint64_t snapper_gmt_disk_free(vfs_handle_struct
*handle
,
2076 const char *path
, bool small_query
,
2077 uint64_t *bsize
, uint64_t *dfree
,
2086 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
2087 ×tamp
, &stripped
)) {
2090 if (timestamp
== 0) {
2091 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
2092 bsize
, dfree
, dsize
);
2095 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
2096 TALLOC_FREE(stripped
);
2101 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
2104 saved_errno
= errno
;
2106 errno
= saved_errno
;
2112 static struct vfs_fn_pointers snapper_fns
= {
2113 .get_shadow_copy_data_fn
= snapper_get_shadow_copy_data
,
2114 .opendir_fn
= snapper_gmt_opendir
,
2115 .disk_free_fn
= snapper_gmt_disk_free
,
2116 .rename_fn
= snapper_gmt_rename
,
2117 .link_fn
= snapper_gmt_link
,
2118 .symlink_fn
= snapper_gmt_symlink
,
2119 .stat_fn
= snapper_gmt_stat
,
2120 .lstat_fn
= snapper_gmt_lstat
,
2121 .fstat_fn
= snapper_gmt_fstat
,
2122 .open_fn
= snapper_gmt_open
,
2123 .unlink_fn
= snapper_gmt_unlink
,
2124 .chmod_fn
= snapper_gmt_chmod
,
2125 .chown_fn
= snapper_gmt_chown
,
2126 .chdir_fn
= snapper_gmt_chdir
,
2127 .ntimes_fn
= snapper_gmt_ntimes
,
2128 .readlink_fn
= snapper_gmt_readlink
,
2129 .mknod_fn
= snapper_gmt_mknod
,
2130 .realpath_fn
= snapper_gmt_realpath
,
2131 .get_nt_acl_fn
= snapper_gmt_get_nt_acl
,
2132 .fget_nt_acl_fn
= snapper_gmt_fget_nt_acl
,
2133 .mkdir_fn
= snapper_gmt_mkdir
,
2134 .rmdir_fn
= snapper_gmt_rmdir
,
2135 .getxattr_fn
= snapper_gmt_getxattr
,
2136 .listxattr_fn
= snapper_gmt_listxattr
,
2137 .removexattr_fn
= snapper_gmt_removexattr
,
2138 .setxattr_fn
= snapper_gmt_setxattr
,
2139 .chmod_acl_fn
= snapper_gmt_chmod_acl
,
2140 .chflags_fn
= snapper_gmt_chflags
,
2141 .get_real_filename_fn
= snapper_gmt_get_real_filename
,
2144 NTSTATUS
vfs_snapper_init(void);
2145 NTSTATUS
vfs_snapper_init(void)
2147 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2148 "snapper", &snapper_fns
);