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 #include <linux/ioctl.h>
29 #include <sys/ioctl.h>
33 #include "include/ntioctl.h"
34 #include "include/smb.h"
35 #include "system/filesys.h"
36 #include "smbd/smbd.h"
37 #include "lib/util/tevent_ntstatus.h"
39 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
40 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
41 #define SNAPPER_SIG_STRING_DICT "{ss}"
56 uint32_t num_user_data
;
57 struct snapper_dict
*user_data
;
64 struct snapper_dict
*attrs
;
68 const char *snapper_err_str
;
70 } snapper_err_map
[] = {
71 { "error.no_permissions", NT_STATUS_ACCESS_DENIED
},
74 static NTSTATUS
snapper_err_ntstatus_map(const char *snapper_err_str
)
78 if (snapper_err_str
== NULL
) {
79 return NT_STATUS_UNSUCCESSFUL
;
81 for (i
= 0; i
< ARRAY_SIZE(snapper_err_map
); i
++) {
82 if (!strcmp(snapper_err_map
[i
].snapper_err_str
,
84 return snapper_err_map
[i
].status
;
87 DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str
));
89 return NT_STATUS_UNSUCCESSFUL
;
92 static DBusConnection
*snapper_dbus_conn_create(void)
95 DBusConnection
*dconn
;
97 dbus_error_init(&err
);
100 * Always create a new DBus connection, to ensure snapperd detects the
101 * correct client [E]UID. With dbus_bus_get() it does not!
103 dconn
= dbus_bus_get_private(DBUS_BUS_SYSTEM
, &err
);
104 if (dbus_error_is_set(&err
)) {
105 DEBUG(0, ("dbus connection error: %s\n", err
.message
));
106 dbus_error_free(&err
);
112 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
113 dbus_connection_set_exit_on_disconnect(dconn
, false);
118 static void snapper_dbus_conn_destroy(DBusConnection
*dconn
)
121 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
125 dbus_connection_close(dconn
);
126 dbus_connection_unref(dconn
);
130 * send the message @send_msg over the dbus and wait for a response, return the
131 * responsee via @recv_msg_out.
132 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
134 static NTSTATUS
snapper_dbus_msg_xchng(DBusConnection
*dconn
,
135 DBusMessage
*send_msg
,
136 DBusMessage
**recv_msg_out
)
138 DBusPendingCall
*pending
;
139 DBusMessage
*recv_msg
;
141 /* send message and get a handle for a reply */
142 if (!dbus_connection_send_with_reply(dconn
, send_msg
, &pending
, -1)) {
143 return NT_STATUS_NO_MEMORY
;
145 if (NULL
== pending
) {
146 DEBUG(0, ("dbus msg send failed\n"));
147 return NT_STATUS_UNSUCCESSFUL
;
150 dbus_connection_flush(dconn
);
152 /* block until we receive a reply */
153 dbus_pending_call_block(pending
);
155 /* get the reply message */
156 recv_msg
= dbus_pending_call_steal_reply(pending
);
157 if (recv_msg
== NULL
) {
158 DEBUG(0, ("Reply Null\n"));
159 return NT_STATUS_UNSUCCESSFUL
;
161 /* free the pending message handle */
162 dbus_pending_call_unref(pending
);
163 *recv_msg_out
= recv_msg
;
168 static NTSTATUS
snapper_type_check(DBusMessageIter
*iter
,
171 int type
= dbus_message_iter_get_arg_type(iter
);
172 if (type
!= expected_type
) {
173 DEBUG(0, ("got type %d, expecting %d\n",
174 type
, expected_type
));
175 return NT_STATUS_INVALID_PARAMETER
;
181 static NTSTATUS
snapper_type_check_get(DBusMessageIter
*iter
,
186 status
= snapper_type_check(iter
, expected_type
);
187 if (!NT_STATUS_IS_OK(status
)) {
191 dbus_message_iter_get_basic(iter
, val
);
196 static NTSTATUS
snapper_dict_unpack(DBusMessageIter
*iter
,
197 struct snapper_dict
*dict_out
)
201 DBusMessageIter dct_iter
;
203 status
= snapper_type_check(iter
, DBUS_TYPE_DICT_ENTRY
);
204 if (!NT_STATUS_IS_OK(status
)) {
207 dbus_message_iter_recurse(iter
, &dct_iter
);
209 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
211 if (!NT_STATUS_IS_OK(status
)) {
215 dbus_message_iter_next(&dct_iter
);
216 status
= snapper_type_check_get(&dct_iter
, DBUS_TYPE_STRING
,
218 if (!NT_STATUS_IS_OK(status
)) {
225 static void snapper_dict_array_print(uint32_t num_dicts
,
226 struct snapper_dict
*dicts
)
230 for (i
= 0; i
< num_dicts
; i
++) {
231 DEBUG(10, ("dict (key: %s, val: %s)\n",
232 dicts
[i
].key
, dicts
[i
].val
));
236 static NTSTATUS
snapper_dict_array_unpack(TALLOC_CTX
*mem_ctx
,
237 DBusMessageIter
*iter
,
238 uint32_t *num_dicts_out
,
239 struct snapper_dict
**dicts_out
)
242 DBusMessageIter array_iter
;
244 struct snapper_dict
*dicts
= NULL
;
246 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
247 if (!NT_STATUS_IS_OK(status
)) {
250 dbus_message_iter_recurse(iter
, &array_iter
);
253 while (dbus_message_iter_get_arg_type(&array_iter
)
254 != DBUS_TYPE_INVALID
) {
256 dicts
= talloc_realloc(mem_ctx
, dicts
, struct snapper_dict
,
261 status
= snapper_dict_unpack(&array_iter
,
262 &dicts
[num_dicts
- 1]);
263 if (!NT_STATUS_IS_OK(status
)) {
267 dbus_message_iter_next(&array_iter
);
270 *num_dicts_out
= num_dicts
;
276 static NTSTATUS
snapper_list_confs_pack(DBusMessage
**req_msg_out
)
280 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
281 "/org/opensuse/Snapper",
282 "org.opensuse.Snapper",
285 DEBUG(0, ("null msg\n"));
286 return NT_STATUS_NO_MEMORY
;
289 /* no arguments to append */
295 static NTSTATUS
snapper_conf_unpack(TALLOC_CTX
*mem_ctx
,
296 DBusMessageIter
*iter
,
297 struct snapper_conf
*conf_out
)
300 DBusMessageIter st_iter
;
302 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
303 if (!NT_STATUS_IS_OK(status
)) {
306 dbus_message_iter_recurse(iter
, &st_iter
);
308 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
310 if (!NT_STATUS_IS_OK(status
)) {
314 dbus_message_iter_next(&st_iter
);
315 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
317 if (!NT_STATUS_IS_OK(status
)) {
321 dbus_message_iter_next(&st_iter
);
322 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
323 &conf_out
->num_attrs
,
329 static void snapper_conf_array_free(int32_t num_confs
,
330 struct snapper_conf
*confs
)
334 for (i
= 0; i
< num_confs
; i
++) {
335 talloc_free(confs
[i
].attrs
);
340 static struct snapper_conf
*snapper_conf_array_base_find(int32_t num_confs
,
341 struct snapper_conf
*confs
,
346 for (i
= 0; i
< num_confs
; i
++) {
347 if (strcmp(confs
[i
].mnt
, base
) == 0) {
348 DEBUG(5, ("found snapper conf %s for path %s\n",
349 confs
[i
].name
, base
));
353 DEBUG(5, ("config for base %s not found\n", base
));
358 static void snapper_conf_array_print(int32_t num_confs
,
359 struct snapper_conf
*confs
)
363 for (i
= 0; i
< num_confs
; i
++) {
364 DEBUG(10, ("name: %s, mnt: %s\n",
365 confs
[i
].name
, confs
[i
].mnt
));
366 snapper_dict_array_print(confs
[i
].num_attrs
, confs
[i
].attrs
);
370 static NTSTATUS
snapper_conf_array_unpack(TALLOC_CTX
*mem_ctx
,
371 DBusMessageIter
*iter
,
372 uint32_t *num_confs_out
,
373 struct snapper_conf
**confs_out
)
377 struct snapper_conf
*confs
= NULL
;
378 DBusMessageIter array_iter
;
381 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
382 if (!NT_STATUS_IS_OK(status
)) {
385 dbus_message_iter_recurse(iter
, &array_iter
);
388 while (dbus_message_iter_get_arg_type(&array_iter
)
389 != DBUS_TYPE_INVALID
) {
391 confs
= talloc_realloc(mem_ctx
, confs
, struct snapper_conf
,
396 status
= snapper_conf_unpack(mem_ctx
, &array_iter
,
397 &confs
[num_confs
- 1]);
398 if (!NT_STATUS_IS_OK(status
)) {
402 dbus_message_iter_next(&array_iter
);
405 *num_confs_out
= num_confs
;
411 static NTSTATUS
snapper_list_confs_unpack(TALLOC_CTX
*mem_ctx
,
412 DBusConnection
*dconn
,
413 DBusMessage
*rsp_msg
,
414 uint32_t *num_confs_out
,
415 struct snapper_conf
**confs_out
)
418 DBusMessageIter iter
;
421 struct snapper_conf
*confs
;
424 msg_type
= dbus_message_get_type(rsp_msg
);
425 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
426 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
427 DEBUG(0, ("list_confs error response: %s\n", err_str
));
428 return snapper_err_ntstatus_map(err_str
);
431 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
432 DEBUG(0, ("unexpected list_confs ret type: %d\n",
434 return NT_STATUS_INVALID_PARAMETER
;
437 sig
= dbus_message_get_signature(rsp_msg
);
439 || (strcmp(sig
, SNAPPER_SIG_LIST_CONFS_RSP
) != 0)) {
440 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
441 (sig
? sig
: "NULL"), SNAPPER_SIG_LIST_CONFS_RSP
));
442 return NT_STATUS_INVALID_PARAMETER
;
445 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
446 /* FIXME return empty? */
447 DEBUG(0, ("Message has no arguments!\n"));
448 return NT_STATUS_INVALID_PARAMETER
;
451 status
= snapper_conf_array_unpack(mem_ctx
, &iter
, &num_confs
, &confs
);
452 if (!NT_STATUS_IS_OK(status
)) {
453 DEBUG(0, ("failed to unpack conf array\n"));
457 snapper_conf_array_print(num_confs
, confs
);
459 *num_confs_out
= num_confs
;
465 static NTSTATUS
snapper_list_snaps_pack(char *snapper_conf
,
466 DBusMessage
**req_msg_out
)
469 DBusMessageIter args
;
471 msg
= dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
472 "/org/opensuse/Snapper", /* object to call on */
473 "org.opensuse.Snapper", /* interface to call on */
474 "ListSnapshots"); /* method name */
476 DEBUG(0, ("failed to create list snaps message\n"));
477 return NT_STATUS_NO_MEMORY
;
480 /* append arguments */
481 dbus_message_iter_init_append(msg
, &args
);
482 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
484 return NT_STATUS_NO_MEMORY
;
492 static NTSTATUS
snapper_snap_struct_unpack(TALLOC_CTX
*mem_ctx
,
493 DBusMessageIter
*iter
,
494 struct snapper_snap
*snap_out
)
497 DBusMessageIter st_iter
;
499 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
500 if (!NT_STATUS_IS_OK(status
)) {
503 dbus_message_iter_recurse(iter
, &st_iter
);
505 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
507 if (!NT_STATUS_IS_OK(status
)) {
511 dbus_message_iter_next(&st_iter
);
512 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT16
,
514 if (!NT_STATUS_IS_OK(status
)) {
518 dbus_message_iter_next(&st_iter
);
519 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
521 if (!NT_STATUS_IS_OK(status
)) {
525 dbus_message_iter_next(&st_iter
);
526 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_INT64
,
528 if (!NT_STATUS_IS_OK(status
)) {
532 dbus_message_iter_next(&st_iter
);
533 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
534 &snap_out
->creator_uid
);
535 if (!NT_STATUS_IS_OK(status
)) {
539 dbus_message_iter_next(&st_iter
);
540 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
542 if (!NT_STATUS_IS_OK(status
)) {
546 dbus_message_iter_next(&st_iter
);
547 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
549 if (!NT_STATUS_IS_OK(status
)) {
553 dbus_message_iter_next(&st_iter
);
554 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
555 &snap_out
->num_user_data
,
556 &snap_out
->user_data
);
561 static void snapper_snap_array_free(int32_t num_snaps
,
562 struct snapper_snap
*snaps
)
566 for (i
= 0; i
< num_snaps
; i
++) {
567 talloc_free(snaps
[i
].user_data
);
572 static void snapper_snap_array_print(int32_t num_snaps
,
573 struct snapper_snap
*snaps
)
577 for (i
= 0; i
< num_snaps
; i
++) {
578 DEBUG(10, ("id: %u, "
585 (unsigned int)snaps
[i
].id
,
586 (unsigned int)snaps
[i
].type
,
587 (unsigned int)snaps
[i
].pre_id
,
588 (long int)snaps
[i
].time
,
589 (unsigned int)snaps
[i
].creator_uid
,
592 snapper_dict_array_print(snaps
[i
].num_user_data
,
597 static NTSTATUS
snapper_snap_array_unpack(TALLOC_CTX
*mem_ctx
,
598 DBusMessageIter
*iter
,
599 uint32_t *num_snaps_out
,
600 struct snapper_snap
**snaps_out
)
604 struct snapper_snap
*snaps
= NULL
;
605 DBusMessageIter array_iter
;
608 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
609 if (!NT_STATUS_IS_OK(status
)) {
612 dbus_message_iter_recurse(iter
, &array_iter
);
615 while (dbus_message_iter_get_arg_type(&array_iter
)
616 != DBUS_TYPE_INVALID
) {
618 snaps
= talloc_realloc(mem_ctx
, snaps
, struct snapper_snap
,
623 status
= snapper_snap_struct_unpack(mem_ctx
, &array_iter
,
624 &snaps
[num_snaps
- 1]);
625 if (!NT_STATUS_IS_OK(status
)) {
629 dbus_message_iter_next(&array_iter
);
632 *num_snaps_out
= num_snaps
;
638 static NTSTATUS
snapper_list_snaps_unpack(TALLOC_CTX
*mem_ctx
,
639 DBusMessage
*rsp_msg
,
640 uint32_t *num_snaps_out
,
641 struct snapper_snap
**snaps_out
)
644 DBusMessageIter iter
;
647 struct snapper_snap
*snaps
;
650 msg_type
= dbus_message_get_type(rsp_msg
);
651 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
652 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
653 DEBUG(0, ("list_snaps error response: %s\n", err_str
));
654 return snapper_err_ntstatus_map(err_str
);
657 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
658 DEBUG(0,("unexpected list_snaps ret type: %d\n",
660 return NT_STATUS_INVALID_PARAMETER
;
663 sig
= dbus_message_get_signature(rsp_msg
);
665 || (strcmp(sig
, SNAPPER_SIG_LIST_SNAPS_RSP
) != 0)) {
666 DEBUG(0, ("bad list snaps response sig: %s, "
668 (sig
? sig
: "NULL"),
669 SNAPPER_SIG_LIST_SNAPS_RSP
));
670 return NT_STATUS_INVALID_PARAMETER
;
673 /* read the parameters */
674 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
675 DEBUG(0, ("response has no arguments!\n"));
676 return NT_STATUS_INVALID_PARAMETER
;
679 status
= snapper_snap_array_unpack(mem_ctx
, &iter
, &num_snaps
, &snaps
);
680 if (!NT_STATUS_IS_OK(status
)) {
681 DEBUG(0, ("failed to unpack snap array\n"));
682 return NT_STATUS_INVALID_PARAMETER
;
685 snapper_snap_array_print(num_snaps
, snaps
);
687 *num_snaps_out
= num_snaps
;
693 static NTSTATUS
snapper_list_snaps_at_time_pack(const char *snapper_conf
,
696 DBusMessage
**req_msg_out
)
699 DBusMessageIter args
;
701 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
702 "/org/opensuse/Snapper",
703 "org.opensuse.Snapper",
704 "ListSnapshotsAtTime");
706 DEBUG(0, ("failed to create list snaps message\n"));
707 return NT_STATUS_NO_MEMORY
;
710 dbus_message_iter_init_append(msg
, &args
);
711 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
713 return NT_STATUS_NO_MEMORY
;
716 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
718 return NT_STATUS_NO_MEMORY
;
721 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
723 return NT_STATUS_NO_MEMORY
;
730 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
733 * Determine the snapper snapshot path given an id and base.
734 * Ideally this should be determined via a lookup.
736 static NTSTATUS
snapper_snap_id_to_path(TALLOC_CTX
*mem_ctx
,
737 const char *base_path
,
739 char **snap_path_out
)
743 snap_path
= talloc_asprintf(mem_ctx
, "%s/.snapshots/%u/snapshot",
745 if (snap_path
== NULL
) {
746 return NT_STATUS_NO_MEMORY
;
749 *snap_path_out
= snap_path
;
753 static NTSTATUS
snapper_get_conf_call(TALLOC_CTX
*mem_ctx
,
754 DBusConnection
*dconn
,
756 char **conf_name_out
,
757 char **base_path_out
)
760 DBusMessage
*req_msg
;
761 DBusMessage
*rsp_msg
;
762 uint32_t num_confs
= 0;
763 struct snapper_conf
*confs
= NULL
;
764 struct snapper_conf
*conf
;
768 status
= snapper_list_confs_pack(&req_msg
);
769 if (!NT_STATUS_IS_OK(status
)) {
773 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
774 if (!NT_STATUS_IS_OK(status
)) {
778 status
= snapper_list_confs_unpack(mem_ctx
, dconn
, rsp_msg
,
780 if (!NT_STATUS_IS_OK(status
)) {
785 * for now we only support shares where the path directly corresponds
786 * to a snapper configuration.
788 conf
= snapper_conf_array_base_find(num_confs
, confs
,
791 status
= NT_STATUS_NOT_SUPPORTED
;
795 conf_name
= talloc_strdup(mem_ctx
, conf
->name
);
796 if (conf_name
== NULL
) {
797 status
= NT_STATUS_NO_MEMORY
;
800 base_path
= talloc_strdup(mem_ctx
, conf
->mnt
);
801 if (base_path
== NULL
) {
802 status
= NT_STATUS_NO_MEMORY
;
803 goto err_conf_name_free
;
806 snapper_conf_array_free(num_confs
, confs
);
807 dbus_message_unref(rsp_msg
);
808 dbus_message_unref(req_msg
);
810 *conf_name_out
= conf_name
;
811 *base_path_out
= base_path
;
816 talloc_free(conf_name
);
818 snapper_conf_array_free(num_confs
, confs
);
820 dbus_message_unref(rsp_msg
);
822 dbus_message_unref(req_msg
);
827 /* sc_data used as parent talloc context for all labels */
828 static int snapper_get_shadow_copy_data(struct vfs_handle_struct
*handle
,
829 struct files_struct
*fsp
,
830 struct shadow_copy_data
*sc_data
,
833 DBusConnection
*dconn
;
838 DBusMessage
*req_msg
;
839 DBusMessage
*rsp_msg
;
841 struct snapper_snap
*snaps
;
845 tmp_ctx
= talloc_new(sc_data
);
846 if (tmp_ctx
== NULL
) {
847 status
= NT_STATUS_NO_MEMORY
;
851 dconn
= snapper_dbus_conn_create();
853 status
= NT_STATUS_UNSUCCESSFUL
;
854 goto err_mem_ctx_free
;
857 if (fsp
->conn
->connectpath
== NULL
) {
858 status
= NT_STATUS_INVALID_PARAMETER
;
862 status
= snapper_get_conf_call(tmp_ctx
, dconn
,
863 fsp
->conn
->connectpath
,
866 if (!NT_STATUS_IS_OK(status
)) {
870 status
= snapper_list_snaps_pack(conf_name
, &req_msg
);
871 if (!NT_STATUS_IS_OK(status
)) {
875 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
876 if (!NT_STATUS_IS_OK(status
)) {
880 status
= snapper_list_snaps_unpack(tmp_ctx
, rsp_msg
,
882 if (!NT_STATUS_IS_OK(status
)) {
885 /* we should always get at least one snapshot (current) */
886 if (num_snaps
== 0) {
887 DEBUG(1, ("zero snapshots in snap list response\n"));
888 status
= NT_STATUS_UNSUCCESSFUL
;
892 /* subtract 1, (current) snapshot is not returned */
893 sc_data
->num_volumes
= num_snaps
- 1;
894 sc_data
->labels
= NULL
;
896 if ((labels
== false) || (sc_data
->num_volumes
== 0)) {
897 /* tokens need not be added to the labels array */
901 sc_data
->labels
= talloc_array(sc_data
, SHADOW_COPY_LABEL
,
902 sc_data
->num_volumes
);
903 if (sc_data
->labels
== NULL
) {
904 status
= NT_STATUS_NO_MEMORY
;
908 /* start at end for decending order, do not include 0 (current) */
910 for (i
= num_snaps
- 1; i
> 0; i
--) {
911 char *lbl
= sc_data
->labels
[lbl_off
++];
912 struct tm gmt_snap_time
;
916 tm_ret
= gmtime_r((time_t *)&snaps
[i
].time
, &gmt_snap_time
);
917 if (tm_ret
== NULL
) {
918 status
= NT_STATUS_UNSUCCESSFUL
;
919 goto err_labels_free
;
921 str_sz
= strftime(lbl
, sizeof(SHADOW_COPY_LABEL
),
922 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
924 status
= NT_STATUS_UNSUCCESSFUL
;
925 goto err_labels_free
;
930 talloc_free(tmp_ctx
);
931 dbus_message_unref(rsp_msg
);
932 dbus_message_unref(req_msg
);
933 snapper_dbus_conn_destroy(dconn
);
938 TALLOC_FREE(sc_data
->labels
);
940 dbus_message_unref(rsp_msg
);
942 dbus_message_unref(req_msg
);
944 snapper_dbus_conn_destroy(dconn
);
946 talloc_free(tmp_ctx
);
948 errno
= map_errno_from_nt_status(status
);
952 static bool snapper_gmt_strip_snapshot(TALLOC_CTX
*mem_ctx
,
953 struct vfs_handle_struct
*handle
,
963 size_t rest_len
, dst_len
;
965 p
= strstr_m(name
, "@GMT-");
969 if ((p
> name
) && (p
[-1] != '/')) {
972 q
= strptime(p
, GMT_FORMAT
, &tm
);
977 timestamp
= timegm(&tm
);
978 if (timestamp
== (time_t)-1) {
981 if ((p
== name
) && (q
[0] == '\0')) {
982 if (pstripped
!= NULL
) {
983 stripped
= talloc_strdup(mem_ctx
, "");
984 if (stripped
== NULL
) {
987 *pstripped
= stripped
;
989 *ptimestamp
= timestamp
;
997 rest_len
= strlen(q
);
998 dst_len
= (p
-name
) + rest_len
;
1000 if (pstripped
!= NULL
) {
1001 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
1002 if (stripped
== NULL
) {
1007 memcpy(stripped
, name
, p
-name
);
1010 memcpy(stripped
+ (p
-name
), q
, rest_len
);
1012 stripped
[dst_len
] = '\0';
1013 *pstripped
= stripped
;
1015 *ptimestamp
= timestamp
;
1022 static NTSTATUS
snapper_get_snap_at_time_call(TALLOC_CTX
*mem_ctx
,
1023 DBusConnection
*dconn
,
1024 const char *conf_name
,
1025 const char *base_path
,
1027 char **snap_path_out
)
1030 DBusMessage
*req_msg
;
1031 DBusMessage
*rsp_msg
;
1033 struct snapper_snap
*snaps
;
1036 status
= snapper_list_snaps_at_time_pack(conf_name
,
1040 if (!NT_STATUS_IS_OK(status
)) {
1044 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1045 if (!NT_STATUS_IS_OK(status
)) {
1049 status
= snapper_list_snaps_unpack(mem_ctx
, rsp_msg
,
1050 &num_snaps
, &snaps
);
1051 if (!NT_STATUS_IS_OK(status
)) {
1055 if (num_snaps
== 0) {
1056 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime
));
1057 status
= NT_STATUS_INVALID_PARAMETER
;
1058 goto err_snap_array_free
;
1059 } else if (num_snaps
> 0) {
1060 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1061 num_snaps
, snaptime
));
1064 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snaps
[0].id
,
1066 if (!NT_STATUS_IS_OK(status
)) {
1067 goto err_snap_array_free
;
1070 *snap_path_out
= snap_path
;
1071 err_snap_array_free
:
1072 snapper_snap_array_free(num_snaps
, snaps
);
1074 dbus_message_unref(rsp_msg
);
1076 dbus_message_unref(req_msg
);
1081 static NTSTATUS
snapper_snap_path_expand(struct connection_struct
*conn
,
1082 TALLOC_CTX
*mem_ctx
,
1084 char **snap_dir_out
)
1086 DBusConnection
*dconn
;
1092 dconn
= snapper_dbus_conn_create();
1093 if (dconn
== NULL
) {
1094 status
= NT_STATUS_UNSUCCESSFUL
;
1098 if (conn
->connectpath
== NULL
) {
1099 status
= NT_STATUS_INVALID_PARAMETER
;
1103 status
= snapper_get_conf_call(mem_ctx
, dconn
,
1107 if (!NT_STATUS_IS_OK(status
)) {
1111 status
= snapper_get_snap_at_time_call(mem_ctx
, dconn
,
1112 conf_name
, base_path
, snap_time
,
1114 if (!NT_STATUS_IS_OK(status
)) {
1115 goto err_conf_name_free
;
1118 /* confirm snapshot path is nested under base path */
1119 if (strncmp(snap_path
, base_path
, strlen(base_path
)) != 0) {
1120 status
= NT_STATUS_INVALID_PARAMETER
;
1121 goto err_snap_path_free
;
1124 talloc_free(conf_name
);
1125 talloc_free(base_path
);
1126 snapper_dbus_conn_destroy(dconn
);
1127 *snap_dir_out
= snap_path
;
1129 return NT_STATUS_OK
;
1132 talloc_free(snap_path
);
1134 talloc_free(conf_name
);
1135 talloc_free(base_path
);
1137 snapper_dbus_conn_destroy(dconn
);
1142 static char *snapper_gmt_convert(TALLOC_CTX
*mem_ctx
,
1143 struct vfs_handle_struct
*handle
,
1144 const char *name
, time_t timestamp
)
1146 char *snap_path
= NULL
;
1151 status
= snapper_snap_path_expand(handle
->conn
, mem_ctx
, timestamp
,
1153 if (!NT_STATUS_IS_OK(status
)) {
1154 errno
= map_errno_from_nt_status(status
);
1158 path
= talloc_asprintf(mem_ctx
, "%s/%s", snap_path
, name
);
1161 goto err_snap_path_free
;
1164 DEBUG(10, ("converted %s/%s @ time to %s\n",
1165 handle
->conn
->connectpath
, name
, path
));
1169 saved_errno
= errno
;
1170 talloc_free(snap_path
);
1171 errno
= saved_errno
;
1176 static DIR *snapper_gmt_opendir(vfs_handle_struct
*handle
,
1187 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1188 ×tamp
, &stripped
)) {
1191 if (timestamp
== 0) {
1192 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
1194 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1195 TALLOC_FREE(stripped
);
1199 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
1200 saved_errno
= errno
;
1202 errno
= saved_errno
;
1206 static int snapper_gmt_rename(vfs_handle_struct
*handle
,
1207 const struct smb_filename
*smb_fname_src
,
1208 const struct smb_filename
*smb_fname_dst
)
1210 time_t timestamp_src
, timestamp_dst
;
1212 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1213 smb_fname_src
->base_name
,
1214 ×tamp_src
, NULL
)) {
1217 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1218 smb_fname_dst
->base_name
,
1219 ×tamp_dst
, NULL
)) {
1222 if (timestamp_src
!= 0) {
1226 if (timestamp_dst
!= 0) {
1230 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
1233 static int snapper_gmt_symlink(vfs_handle_struct
*handle
,
1234 const char *oldname
, const char *newname
)
1236 time_t timestamp_old
, timestamp_new
;
1238 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1239 ×tamp_old
, NULL
)) {
1242 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1243 ×tamp_new
, NULL
)) {
1246 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1250 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
1253 static int snapper_gmt_link(vfs_handle_struct
*handle
,
1254 const char *oldname
, const char *newname
)
1256 time_t timestamp_old
, timestamp_new
;
1258 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1259 ×tamp_old
, NULL
)) {
1262 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1263 ×tamp_new
, NULL
)) {
1266 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1270 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
1273 static int snapper_gmt_stat(vfs_handle_struct
*handle
,
1274 struct smb_filename
*smb_fname
)
1277 char *stripped
, *tmp
;
1278 int ret
, saved_errno
;
1280 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1281 smb_fname
->base_name
,
1282 ×tamp
, &stripped
)) {
1285 if (timestamp
== 0) {
1286 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1289 tmp
= smb_fname
->base_name
;
1290 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1291 stripped
, timestamp
);
1292 TALLOC_FREE(stripped
);
1294 if (smb_fname
->base_name
== NULL
) {
1295 smb_fname
->base_name
= tmp
;
1299 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1300 saved_errno
= errno
;
1302 TALLOC_FREE(smb_fname
->base_name
);
1303 smb_fname
->base_name
= tmp
;
1305 errno
= saved_errno
;
1309 static int snapper_gmt_lstat(vfs_handle_struct
*handle
,
1310 struct smb_filename
*smb_fname
)
1313 char *stripped
, *tmp
;
1314 int ret
, saved_errno
;
1316 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1317 smb_fname
->base_name
,
1318 ×tamp
, &stripped
)) {
1321 if (timestamp
== 0) {
1322 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1325 tmp
= smb_fname
->base_name
;
1326 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1327 stripped
, timestamp
);
1328 TALLOC_FREE(stripped
);
1330 if (smb_fname
->base_name
== NULL
) {
1331 smb_fname
->base_name
= tmp
;
1335 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1336 saved_errno
= errno
;
1338 TALLOC_FREE(smb_fname
->base_name
);
1339 smb_fname
->base_name
= tmp
;
1341 errno
= saved_errno
;
1345 static int snapper_gmt_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
1346 SMB_STRUCT_STAT
*sbuf
)
1351 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1355 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1356 fsp
->fsp_name
->base_name
,
1357 ×tamp
, NULL
)) {
1363 static int snapper_gmt_open(vfs_handle_struct
*handle
,
1364 struct smb_filename
*smb_fname
, files_struct
*fsp
,
1365 int flags
, mode_t mode
)
1368 char *stripped
, *tmp
;
1369 int ret
, saved_errno
;
1371 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1372 smb_fname
->base_name
,
1373 ×tamp
, &stripped
)) {
1376 if (timestamp
== 0) {
1377 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1380 tmp
= smb_fname
->base_name
;
1381 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1382 stripped
, timestamp
);
1383 TALLOC_FREE(stripped
);
1385 if (smb_fname
->base_name
== NULL
) {
1386 smb_fname
->base_name
= tmp
;
1390 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1391 saved_errno
= errno
;
1393 TALLOC_FREE(smb_fname
->base_name
);
1394 smb_fname
->base_name
= tmp
;
1396 errno
= saved_errno
;
1400 static int snapper_gmt_unlink(vfs_handle_struct
*handle
,
1401 const struct smb_filename
*smb_fname
)
1405 int ret
, saved_errno
;
1406 struct smb_filename
*conv
;
1408 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1409 smb_fname
->base_name
,
1410 ×tamp
, &stripped
)) {
1413 if (timestamp
== 0) {
1414 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
1416 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1421 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1422 stripped
, timestamp
);
1423 TALLOC_FREE(stripped
);
1424 if (conv
->base_name
== NULL
) {
1427 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
1428 saved_errno
= errno
;
1430 errno
= saved_errno
;
1434 static int snapper_gmt_chmod(vfs_handle_struct
*handle
, const char *fname
,
1439 int ret
, saved_errno
;
1442 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1443 ×tamp
, &stripped
)) {
1446 if (timestamp
== 0) {
1447 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
1449 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1450 TALLOC_FREE(stripped
);
1454 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
1455 saved_errno
= errno
;
1457 errno
= saved_errno
;
1461 static int snapper_gmt_chown(vfs_handle_struct
*handle
, const char *fname
,
1462 uid_t uid
, gid_t gid
)
1466 int ret
, saved_errno
;
1469 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1470 ×tamp
, &stripped
)) {
1473 if (timestamp
== 0) {
1474 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
1476 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1477 TALLOC_FREE(stripped
);
1481 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1482 saved_errno
= errno
;
1484 errno
= saved_errno
;
1488 static int snapper_gmt_chdir(vfs_handle_struct
*handle
,
1493 int ret
, saved_errno
;
1496 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1497 ×tamp
, &stripped
)) {
1500 if (timestamp
== 0) {
1501 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1503 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1504 TALLOC_FREE(stripped
);
1508 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1509 saved_errno
= errno
;
1511 errno
= saved_errno
;
1515 static int snapper_gmt_ntimes(vfs_handle_struct
*handle
,
1516 const struct smb_filename
*smb_fname
,
1517 struct smb_file_time
*ft
)
1521 int ret
, saved_errno
;
1522 struct smb_filename
*conv
;
1524 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1525 smb_fname
->base_name
,
1526 ×tamp
, &stripped
)) {
1529 if (timestamp
== 0) {
1530 return SMB_VFS_NEXT_NTIMES(handle
, smb_fname
, ft
);
1532 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1537 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1538 stripped
, timestamp
);
1539 TALLOC_FREE(stripped
);
1540 if (conv
->base_name
== NULL
) {
1543 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1544 saved_errno
= errno
;
1546 errno
= saved_errno
;
1550 static int snapper_gmt_readlink(vfs_handle_struct
*handle
,
1551 const char *fname
, char *buf
, size_t bufsiz
)
1555 int ret
, saved_errno
;
1558 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1559 ×tamp
, &stripped
)) {
1562 if (timestamp
== 0) {
1563 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1565 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1566 TALLOC_FREE(stripped
);
1570 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1571 saved_errno
= errno
;
1573 errno
= saved_errno
;
1577 static int snapper_gmt_mknod(vfs_handle_struct
*handle
,
1578 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1582 int ret
, saved_errno
;
1585 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1586 ×tamp
, &stripped
)) {
1589 if (timestamp
== 0) {
1590 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1592 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1593 TALLOC_FREE(stripped
);
1597 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1598 saved_errno
= errno
;
1600 errno
= saved_errno
;
1604 static char *snapper_gmt_realpath(vfs_handle_struct
*handle
,
1608 char *stripped
= NULL
;
1610 char *result
= NULL
;
1613 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1614 ×tamp
, &stripped
)) {
1617 if (timestamp
== 0) {
1618 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1621 tmp
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1626 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1627 if (result
== NULL
) {
1632 saved_errno
= errno
;
1634 TALLOC_FREE(stripped
);
1635 errno
= saved_errno
;
1639 static NTSTATUS
snapper_gmt_fget_nt_acl(vfs_handle_struct
*handle
,
1640 struct files_struct
*fsp
,
1641 uint32 security_info
,
1642 TALLOC_CTX
*mem_ctx
,
1643 struct security_descriptor
**ppdesc
)
1650 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1651 fsp
->fsp_name
->base_name
,
1652 ×tamp
, &stripped
)) {
1653 return map_nt_error_from_unix(errno
);
1655 if (timestamp
== 0) {
1656 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1660 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1661 TALLOC_FREE(stripped
);
1663 return map_nt_error_from_unix(errno
);
1665 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1671 static NTSTATUS
snapper_gmt_get_nt_acl(vfs_handle_struct
*handle
,
1673 uint32 security_info
,
1674 TALLOC_CTX
*mem_ctx
,
1675 struct security_descriptor
**ppdesc
)
1682 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1683 ×tamp
, &stripped
)) {
1684 return map_nt_error_from_unix(errno
);
1686 if (timestamp
== 0) {
1687 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1690 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1691 TALLOC_FREE(stripped
);
1693 return map_nt_error_from_unix(errno
);
1695 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1701 static int snapper_gmt_mkdir(vfs_handle_struct
*handle
,
1702 const char *fname
, mode_t mode
)
1706 int ret
, saved_errno
;
1709 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1710 ×tamp
, &stripped
)) {
1713 if (timestamp
== 0) {
1714 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1716 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1717 TALLOC_FREE(stripped
);
1721 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1722 saved_errno
= errno
;
1724 errno
= saved_errno
;
1728 static int snapper_gmt_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1732 int ret
, saved_errno
;
1735 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1736 ×tamp
, &stripped
)) {
1739 if (timestamp
== 0) {
1740 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1742 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1743 TALLOC_FREE(stripped
);
1747 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1748 saved_errno
= errno
;
1750 errno
= saved_errno
;
1754 static int snapper_gmt_chflags(vfs_handle_struct
*handle
, const char *fname
,
1759 int ret
, saved_errno
;
1762 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1763 ×tamp
, &stripped
)) {
1766 if (timestamp
== 0) {
1767 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1769 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1770 TALLOC_FREE(stripped
);
1774 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1775 saved_errno
= errno
;
1777 errno
= saved_errno
;
1781 static ssize_t
snapper_gmt_getxattr(vfs_handle_struct
*handle
,
1782 const char *fname
, const char *aname
,
1783 void *value
, size_t size
)
1791 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1792 ×tamp
, &stripped
)) {
1795 if (timestamp
== 0) {
1796 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1799 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1800 TALLOC_FREE(stripped
);
1804 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1805 saved_errno
= errno
;
1807 errno
= saved_errno
;
1811 static ssize_t
snapper_gmt_listxattr(struct vfs_handle_struct
*handle
,
1813 char *list
, size_t size
)
1821 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1822 ×tamp
, &stripped
)) {
1825 if (timestamp
== 0) {
1826 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1828 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1829 TALLOC_FREE(stripped
);
1833 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1834 saved_errno
= errno
;
1836 errno
= saved_errno
;
1840 static int snapper_gmt_removexattr(vfs_handle_struct
*handle
,
1841 const char *fname
, const char *aname
)
1845 int ret
, saved_errno
;
1848 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1849 ×tamp
, &stripped
)) {
1852 if (timestamp
== 0) {
1853 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1855 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1856 TALLOC_FREE(stripped
);
1860 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1861 saved_errno
= errno
;
1863 errno
= saved_errno
;
1867 static int snapper_gmt_setxattr(struct vfs_handle_struct
*handle
,
1869 const char *aname
, const void *value
,
1870 size_t size
, int flags
)
1878 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1879 ×tamp
, &stripped
)) {
1882 if (timestamp
== 0) {
1883 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1886 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1887 TALLOC_FREE(stripped
);
1891 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1892 saved_errno
= errno
;
1894 errno
= saved_errno
;
1898 static int snapper_gmt_chmod_acl(vfs_handle_struct
*handle
,
1899 const char *fname
, mode_t mode
)
1907 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1908 ×tamp
, &stripped
)) {
1911 if (timestamp
== 0) {
1912 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1914 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1915 TALLOC_FREE(stripped
);
1919 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1920 saved_errno
= errno
;
1922 errno
= saved_errno
;
1926 static int snapper_gmt_get_real_filename(struct vfs_handle_struct
*handle
,
1929 TALLOC_CTX
*mem_ctx
,
1938 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
1939 ×tamp
, &stripped
)) {
1942 if (timestamp
== 0) {
1943 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1944 mem_ctx
, found_name
);
1946 if (stripped
[0] == '\0') {
1947 *found_name
= talloc_strdup(mem_ctx
, name
);
1948 if (*found_name
== NULL
) {
1954 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1955 TALLOC_FREE(stripped
);
1959 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1960 mem_ctx
, found_name
);
1961 saved_errno
= errno
;
1963 errno
= saved_errno
;
1967 static uint64_t snapper_gmt_disk_free(vfs_handle_struct
*handle
,
1968 const char *path
, bool small_query
,
1969 uint64_t *bsize
, uint64_t *dfree
,
1978 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
1979 ×tamp
, &stripped
)) {
1982 if (timestamp
== 0) {
1983 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1984 bsize
, dfree
, dsize
);
1987 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1988 TALLOC_FREE(stripped
);
1993 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1996 saved_errno
= errno
;
1998 errno
= saved_errno
;
2004 static struct vfs_fn_pointers snapper_fns
= {
2005 .get_shadow_copy_data_fn
= snapper_get_shadow_copy_data
,
2006 .opendir_fn
= snapper_gmt_opendir
,
2007 .disk_free_fn
= snapper_gmt_disk_free
,
2008 .rename_fn
= snapper_gmt_rename
,
2009 .link_fn
= snapper_gmt_link
,
2010 .symlink_fn
= snapper_gmt_symlink
,
2011 .stat_fn
= snapper_gmt_stat
,
2012 .lstat_fn
= snapper_gmt_lstat
,
2013 .fstat_fn
= snapper_gmt_fstat
,
2014 .open_fn
= snapper_gmt_open
,
2015 .unlink_fn
= snapper_gmt_unlink
,
2016 .chmod_fn
= snapper_gmt_chmod
,
2017 .chown_fn
= snapper_gmt_chown
,
2018 .chdir_fn
= snapper_gmt_chdir
,
2019 .ntimes_fn
= snapper_gmt_ntimes
,
2020 .readlink_fn
= snapper_gmt_readlink
,
2021 .mknod_fn
= snapper_gmt_mknod
,
2022 .realpath_fn
= snapper_gmt_realpath
,
2023 .get_nt_acl_fn
= snapper_gmt_get_nt_acl
,
2024 .fget_nt_acl_fn
= snapper_gmt_fget_nt_acl
,
2025 .mkdir_fn
= snapper_gmt_mkdir
,
2026 .rmdir_fn
= snapper_gmt_rmdir
,
2027 .getxattr_fn
= snapper_gmt_getxattr
,
2028 .listxattr_fn
= snapper_gmt_listxattr
,
2029 .removexattr_fn
= snapper_gmt_removexattr
,
2030 .setxattr_fn
= snapper_gmt_setxattr
,
2031 .chmod_acl_fn
= snapper_gmt_chmod_acl
,
2032 .chflags_fn
= snapper_gmt_chflags
,
2033 .get_real_filename_fn
= snapper_gmt_get_real_filename
,
2036 NTSTATUS
vfs_snapper_init(void);
2037 NTSTATUS
vfs_snapper_init(void)
2039 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2040 "snapper", &snapper_fns
);