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 struct snapper_conf
*snapper_conf_array_base_find(int32_t num_confs
,
330 struct snapper_conf
*confs
,
335 for (i
= 0; i
< num_confs
; i
++) {
336 if (strcmp(confs
[i
].mnt
, base
) == 0) {
337 DEBUG(5, ("found snapper conf %s for path %s\n",
338 confs
[i
].name
, base
));
342 DEBUG(5, ("config for base %s not found\n", base
));
347 static void snapper_conf_array_print(int32_t num_confs
,
348 struct snapper_conf
*confs
)
352 for (i
= 0; i
< num_confs
; i
++) {
353 DEBUG(10, ("name: %s, mnt: %s\n",
354 confs
[i
].name
, confs
[i
].mnt
));
355 snapper_dict_array_print(confs
[i
].num_attrs
, confs
[i
].attrs
);
359 static NTSTATUS
snapper_conf_array_unpack(TALLOC_CTX
*mem_ctx
,
360 DBusMessageIter
*iter
,
361 uint32_t *num_confs_out
,
362 struct snapper_conf
**confs_out
)
366 struct snapper_conf
*confs
= NULL
;
367 DBusMessageIter array_iter
;
370 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
371 if (!NT_STATUS_IS_OK(status
)) {
374 dbus_message_iter_recurse(iter
, &array_iter
);
377 while (dbus_message_iter_get_arg_type(&array_iter
)
378 != DBUS_TYPE_INVALID
) {
380 confs
= talloc_realloc(mem_ctx
, confs
, struct snapper_conf
,
385 status
= snapper_conf_unpack(confs
, &array_iter
,
386 &confs
[num_confs
- 1]);
387 if (!NT_STATUS_IS_OK(status
)) {
391 dbus_message_iter_next(&array_iter
);
394 *num_confs_out
= num_confs
;
400 static NTSTATUS
snapper_list_confs_unpack(TALLOC_CTX
*mem_ctx
,
401 DBusConnection
*dconn
,
402 DBusMessage
*rsp_msg
,
403 uint32_t *num_confs_out
,
404 struct snapper_conf
**confs_out
)
407 DBusMessageIter iter
;
410 struct snapper_conf
*confs
;
413 msg_type
= dbus_message_get_type(rsp_msg
);
414 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
415 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
416 DEBUG(0, ("list_confs error response: %s\n", err_str
));
417 return snapper_err_ntstatus_map(err_str
);
420 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
421 DEBUG(0, ("unexpected list_confs ret type: %d\n",
423 return NT_STATUS_INVALID_PARAMETER
;
426 sig
= dbus_message_get_signature(rsp_msg
);
428 || (strcmp(sig
, SNAPPER_SIG_LIST_CONFS_RSP
) != 0)) {
429 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
430 (sig
? sig
: "NULL"), SNAPPER_SIG_LIST_CONFS_RSP
));
431 return NT_STATUS_INVALID_PARAMETER
;
434 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
435 /* FIXME return empty? */
436 DEBUG(0, ("Message has no arguments!\n"));
437 return NT_STATUS_INVALID_PARAMETER
;
440 status
= snapper_conf_array_unpack(mem_ctx
, &iter
, &num_confs
, &confs
);
441 if (!NT_STATUS_IS_OK(status
)) {
442 DEBUG(0, ("failed to unpack conf array\n"));
446 snapper_conf_array_print(num_confs
, confs
);
448 *num_confs_out
= num_confs
;
454 static NTSTATUS
snapper_list_snaps_pack(char *snapper_conf
,
455 DBusMessage
**req_msg_out
)
458 DBusMessageIter args
;
460 msg
= dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
461 "/org/opensuse/Snapper", /* object to call on */
462 "org.opensuse.Snapper", /* interface to call on */
463 "ListSnapshots"); /* method name */
465 DEBUG(0, ("failed to create list snaps message\n"));
466 return NT_STATUS_NO_MEMORY
;
469 /* append arguments */
470 dbus_message_iter_init_append(msg
, &args
);
471 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
473 return NT_STATUS_NO_MEMORY
;
481 static NTSTATUS
snapper_snap_struct_unpack(TALLOC_CTX
*mem_ctx
,
482 DBusMessageIter
*iter
,
483 struct snapper_snap
*snap_out
)
486 DBusMessageIter st_iter
;
488 status
= snapper_type_check(iter
, DBUS_TYPE_STRUCT
);
489 if (!NT_STATUS_IS_OK(status
)) {
492 dbus_message_iter_recurse(iter
, &st_iter
);
494 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
496 if (!NT_STATUS_IS_OK(status
)) {
500 dbus_message_iter_next(&st_iter
);
501 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT16
,
503 if (!NT_STATUS_IS_OK(status
)) {
507 dbus_message_iter_next(&st_iter
);
508 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
510 if (!NT_STATUS_IS_OK(status
)) {
514 dbus_message_iter_next(&st_iter
);
515 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_INT64
,
517 if (!NT_STATUS_IS_OK(status
)) {
521 dbus_message_iter_next(&st_iter
);
522 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_UINT32
,
523 &snap_out
->creator_uid
);
524 if (!NT_STATUS_IS_OK(status
)) {
528 dbus_message_iter_next(&st_iter
);
529 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
531 if (!NT_STATUS_IS_OK(status
)) {
535 dbus_message_iter_next(&st_iter
);
536 status
= snapper_type_check_get(&st_iter
, DBUS_TYPE_STRING
,
538 if (!NT_STATUS_IS_OK(status
)) {
542 dbus_message_iter_next(&st_iter
);
543 status
= snapper_dict_array_unpack(mem_ctx
, &st_iter
,
544 &snap_out
->num_user_data
,
545 &snap_out
->user_data
);
550 static void snapper_snap_array_print(int32_t num_snaps
,
551 struct snapper_snap
*snaps
)
555 for (i
= 0; i
< num_snaps
; i
++) {
556 DEBUG(10, ("id: %u, "
563 (unsigned int)snaps
[i
].id
,
564 (unsigned int)snaps
[i
].type
,
565 (unsigned int)snaps
[i
].pre_id
,
566 (long int)snaps
[i
].time
,
567 (unsigned int)snaps
[i
].creator_uid
,
570 snapper_dict_array_print(snaps
[i
].num_user_data
,
575 static NTSTATUS
snapper_snap_array_unpack(TALLOC_CTX
*mem_ctx
,
576 DBusMessageIter
*iter
,
577 uint32_t *num_snaps_out
,
578 struct snapper_snap
**snaps_out
)
582 struct snapper_snap
*snaps
= NULL
;
583 DBusMessageIter array_iter
;
586 status
= snapper_type_check(iter
, DBUS_TYPE_ARRAY
);
587 if (!NT_STATUS_IS_OK(status
)) {
590 dbus_message_iter_recurse(iter
, &array_iter
);
593 while (dbus_message_iter_get_arg_type(&array_iter
)
594 != DBUS_TYPE_INVALID
) {
596 snaps
= talloc_realloc(mem_ctx
, snaps
, struct snapper_snap
,
601 status
= snapper_snap_struct_unpack(snaps
, &array_iter
,
602 &snaps
[num_snaps
- 1]);
603 if (!NT_STATUS_IS_OK(status
)) {
607 dbus_message_iter_next(&array_iter
);
610 *num_snaps_out
= num_snaps
;
616 static NTSTATUS
snapper_list_snaps_unpack(TALLOC_CTX
*mem_ctx
,
617 DBusMessage
*rsp_msg
,
618 uint32_t *num_snaps_out
,
619 struct snapper_snap
**snaps_out
)
622 DBusMessageIter iter
;
625 struct snapper_snap
*snaps
;
628 msg_type
= dbus_message_get_type(rsp_msg
);
629 if (msg_type
== DBUS_MESSAGE_TYPE_ERROR
) {
630 const char *err_str
= dbus_message_get_error_name(rsp_msg
);
631 DEBUG(0, ("list_snaps error response: %s\n", err_str
));
632 return snapper_err_ntstatus_map(err_str
);
635 if (msg_type
!= DBUS_MESSAGE_TYPE_METHOD_RETURN
) {
636 DEBUG(0,("unexpected list_snaps ret type: %d\n",
638 return NT_STATUS_INVALID_PARAMETER
;
641 sig
= dbus_message_get_signature(rsp_msg
);
643 || (strcmp(sig
, SNAPPER_SIG_LIST_SNAPS_RSP
) != 0)) {
644 DEBUG(0, ("bad list snaps response sig: %s, "
646 (sig
? sig
: "NULL"),
647 SNAPPER_SIG_LIST_SNAPS_RSP
));
648 return NT_STATUS_INVALID_PARAMETER
;
651 /* read the parameters */
652 if (!dbus_message_iter_init(rsp_msg
, &iter
)) {
653 DEBUG(0, ("response has no arguments!\n"));
654 return NT_STATUS_INVALID_PARAMETER
;
657 status
= snapper_snap_array_unpack(mem_ctx
, &iter
, &num_snaps
, &snaps
);
658 if (!NT_STATUS_IS_OK(status
)) {
659 DEBUG(0, ("failed to unpack snap array\n"));
660 return NT_STATUS_INVALID_PARAMETER
;
663 snapper_snap_array_print(num_snaps
, snaps
);
665 *num_snaps_out
= num_snaps
;
671 static NTSTATUS
snapper_list_snaps_at_time_pack(const char *snapper_conf
,
674 DBusMessage
**req_msg_out
)
677 DBusMessageIter args
;
679 msg
= dbus_message_new_method_call("org.opensuse.Snapper",
680 "/org/opensuse/Snapper",
681 "org.opensuse.Snapper",
682 "ListSnapshotsAtTime");
684 DEBUG(0, ("failed to create list snaps message\n"));
685 return NT_STATUS_NO_MEMORY
;
688 dbus_message_iter_init_append(msg
, &args
);
689 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_STRING
,
691 return NT_STATUS_NO_MEMORY
;
694 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
696 return NT_STATUS_NO_MEMORY
;
699 if (!dbus_message_iter_append_basic(&args
, DBUS_TYPE_INT64
,
701 return NT_STATUS_NO_MEMORY
;
708 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
711 * Determine the snapper snapshot path given an id and base.
712 * Ideally this should be determined via a lookup.
714 static NTSTATUS
snapper_snap_id_to_path(TALLOC_CTX
*mem_ctx
,
715 const char *base_path
,
717 char **snap_path_out
)
721 snap_path
= talloc_asprintf(mem_ctx
, "%s/.snapshots/%u/snapshot",
723 if (snap_path
== NULL
) {
724 return NT_STATUS_NO_MEMORY
;
727 *snap_path_out
= snap_path
;
731 static NTSTATUS
snapper_get_conf_call(TALLOC_CTX
*mem_ctx
,
732 DBusConnection
*dconn
,
734 char **conf_name_out
,
735 char **base_path_out
)
738 DBusMessage
*req_msg
;
739 DBusMessage
*rsp_msg
;
740 uint32_t num_confs
= 0;
741 struct snapper_conf
*confs
= NULL
;
742 struct snapper_conf
*conf
;
746 status
= snapper_list_confs_pack(&req_msg
);
747 if (!NT_STATUS_IS_OK(status
)) {
751 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
752 if (!NT_STATUS_IS_OK(status
)) {
756 status
= snapper_list_confs_unpack(mem_ctx
, dconn
, rsp_msg
,
758 if (!NT_STATUS_IS_OK(status
)) {
763 * for now we only support shares where the path directly corresponds
764 * to a snapper configuration.
766 conf
= snapper_conf_array_base_find(num_confs
, confs
,
769 status
= NT_STATUS_NOT_SUPPORTED
;
773 conf_name
= talloc_strdup(mem_ctx
, conf
->name
);
774 if (conf_name
== NULL
) {
775 status
= NT_STATUS_NO_MEMORY
;
778 base_path
= talloc_strdup(mem_ctx
, conf
->mnt
);
779 if (base_path
== NULL
) {
780 status
= NT_STATUS_NO_MEMORY
;
781 goto err_conf_name_free
;
785 dbus_message_unref(rsp_msg
);
786 dbus_message_unref(req_msg
);
788 *conf_name_out
= conf_name
;
789 *base_path_out
= base_path
;
794 talloc_free(conf_name
);
798 dbus_message_unref(rsp_msg
);
800 dbus_message_unref(req_msg
);
805 /* sc_data used as parent talloc context for all labels */
806 static int snapper_get_shadow_copy_data(struct vfs_handle_struct
*handle
,
807 struct files_struct
*fsp
,
808 struct shadow_copy_data
*sc_data
,
811 DBusConnection
*dconn
;
816 DBusMessage
*req_msg
;
817 DBusMessage
*rsp_msg
;
819 struct snapper_snap
*snaps
;
823 tmp_ctx
= talloc_new(sc_data
);
824 if (tmp_ctx
== NULL
) {
825 status
= NT_STATUS_NO_MEMORY
;
829 dconn
= snapper_dbus_conn_create();
831 status
= NT_STATUS_UNSUCCESSFUL
;
832 goto err_mem_ctx_free
;
835 if (fsp
->conn
->connectpath
== NULL
) {
836 status
= NT_STATUS_INVALID_PARAMETER
;
840 status
= snapper_get_conf_call(tmp_ctx
, dconn
,
841 fsp
->conn
->connectpath
,
844 if (!NT_STATUS_IS_OK(status
)) {
848 status
= snapper_list_snaps_pack(conf_name
, &req_msg
);
849 if (!NT_STATUS_IS_OK(status
)) {
853 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
854 if (!NT_STATUS_IS_OK(status
)) {
858 status
= snapper_list_snaps_unpack(tmp_ctx
, rsp_msg
,
860 if (!NT_STATUS_IS_OK(status
)) {
863 /* we should always get at least one snapshot (current) */
864 if (num_snaps
== 0) {
865 DEBUG(1, ("zero snapshots in snap list response\n"));
866 status
= NT_STATUS_UNSUCCESSFUL
;
870 /* subtract 1, (current) snapshot is not returned */
871 sc_data
->num_volumes
= num_snaps
- 1;
872 sc_data
->labels
= NULL
;
874 if ((labels
== false) || (sc_data
->num_volumes
== 0)) {
875 /* tokens need not be added to the labels array */
879 sc_data
->labels
= talloc_array(sc_data
, SHADOW_COPY_LABEL
,
880 sc_data
->num_volumes
);
881 if (sc_data
->labels
== NULL
) {
882 status
= NT_STATUS_NO_MEMORY
;
886 /* start at end for decending order, do not include 0 (current) */
888 for (i
= num_snaps
- 1; i
> 0; i
--) {
889 char *lbl
= sc_data
->labels
[lbl_off
++];
890 struct tm gmt_snap_time
;
894 tm_ret
= gmtime_r((time_t *)&snaps
[i
].time
, &gmt_snap_time
);
895 if (tm_ret
== NULL
) {
896 status
= NT_STATUS_UNSUCCESSFUL
;
897 goto err_labels_free
;
899 str_sz
= strftime(lbl
, sizeof(SHADOW_COPY_LABEL
),
900 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time
);
902 status
= NT_STATUS_UNSUCCESSFUL
;
903 goto err_labels_free
;
908 talloc_free(tmp_ctx
);
909 dbus_message_unref(rsp_msg
);
910 dbus_message_unref(req_msg
);
911 snapper_dbus_conn_destroy(dconn
);
916 TALLOC_FREE(sc_data
->labels
);
918 dbus_message_unref(rsp_msg
);
920 dbus_message_unref(req_msg
);
922 snapper_dbus_conn_destroy(dconn
);
924 talloc_free(tmp_ctx
);
926 errno
= map_errno_from_nt_status(status
);
930 static bool snapper_gmt_strip_snapshot(TALLOC_CTX
*mem_ctx
,
931 struct vfs_handle_struct
*handle
,
941 size_t rest_len
, dst_len
;
943 p
= strstr_m(name
, "@GMT-");
947 if ((p
> name
) && (p
[-1] != '/')) {
950 q
= strptime(p
, GMT_FORMAT
, &tm
);
955 timestamp
= timegm(&tm
);
956 if (timestamp
== (time_t)-1) {
959 if ((p
== name
) && (q
[0] == '\0')) {
960 if (pstripped
!= NULL
) {
961 stripped
= talloc_strdup(mem_ctx
, "");
962 if (stripped
== NULL
) {
965 *pstripped
= stripped
;
967 *ptimestamp
= timestamp
;
975 rest_len
= strlen(q
);
976 dst_len
= (p
-name
) + rest_len
;
978 if (pstripped
!= NULL
) {
979 stripped
= talloc_array(mem_ctx
, char, dst_len
+1);
980 if (stripped
== NULL
) {
985 memcpy(stripped
, name
, p
-name
);
988 memcpy(stripped
+ (p
-name
), q
, rest_len
);
990 stripped
[dst_len
] = '\0';
991 *pstripped
= stripped
;
993 *ptimestamp
= timestamp
;
1000 static NTSTATUS
snapper_get_snap_at_time_call(TALLOC_CTX
*mem_ctx
,
1001 DBusConnection
*dconn
,
1002 const char *conf_name
,
1003 const char *base_path
,
1005 char **snap_path_out
)
1008 DBusMessage
*req_msg
;
1009 DBusMessage
*rsp_msg
;
1011 struct snapper_snap
*snaps
;
1014 status
= snapper_list_snaps_at_time_pack(conf_name
,
1018 if (!NT_STATUS_IS_OK(status
)) {
1022 status
= snapper_dbus_msg_xchng(dconn
, req_msg
, &rsp_msg
);
1023 if (!NT_STATUS_IS_OK(status
)) {
1027 status
= snapper_list_snaps_unpack(mem_ctx
, rsp_msg
,
1028 &num_snaps
, &snaps
);
1029 if (!NT_STATUS_IS_OK(status
)) {
1033 if (num_snaps
== 0) {
1034 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime
));
1035 status
= NT_STATUS_INVALID_PARAMETER
;
1036 goto err_snap_array_free
;
1037 } else if (num_snaps
> 0) {
1038 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1039 num_snaps
, snaptime
));
1042 status
= snapper_snap_id_to_path(mem_ctx
, base_path
, snaps
[0].id
,
1044 if (!NT_STATUS_IS_OK(status
)) {
1045 goto err_snap_array_free
;
1048 *snap_path_out
= snap_path
;
1049 err_snap_array_free
:
1052 dbus_message_unref(rsp_msg
);
1054 dbus_message_unref(req_msg
);
1059 static NTSTATUS
snapper_snap_path_expand(struct connection_struct
*conn
,
1060 TALLOC_CTX
*mem_ctx
,
1062 char **snap_dir_out
)
1064 DBusConnection
*dconn
;
1070 dconn
= snapper_dbus_conn_create();
1071 if (dconn
== NULL
) {
1072 status
= NT_STATUS_UNSUCCESSFUL
;
1076 if (conn
->connectpath
== NULL
) {
1077 status
= NT_STATUS_INVALID_PARAMETER
;
1081 status
= snapper_get_conf_call(mem_ctx
, dconn
,
1085 if (!NT_STATUS_IS_OK(status
)) {
1089 status
= snapper_get_snap_at_time_call(mem_ctx
, dconn
,
1090 conf_name
, base_path
, snap_time
,
1092 if (!NT_STATUS_IS_OK(status
)) {
1093 goto err_conf_name_free
;
1096 /* confirm snapshot path is nested under base path */
1097 if (strncmp(snap_path
, base_path
, strlen(base_path
)) != 0) {
1098 status
= NT_STATUS_INVALID_PARAMETER
;
1099 goto err_snap_path_free
;
1102 talloc_free(conf_name
);
1103 talloc_free(base_path
);
1104 snapper_dbus_conn_destroy(dconn
);
1105 *snap_dir_out
= snap_path
;
1107 return NT_STATUS_OK
;
1110 talloc_free(snap_path
);
1112 talloc_free(conf_name
);
1113 talloc_free(base_path
);
1115 snapper_dbus_conn_destroy(dconn
);
1120 static char *snapper_gmt_convert(TALLOC_CTX
*mem_ctx
,
1121 struct vfs_handle_struct
*handle
,
1122 const char *name
, time_t timestamp
)
1124 char *snap_path
= NULL
;
1129 status
= snapper_snap_path_expand(handle
->conn
, mem_ctx
, timestamp
,
1131 if (!NT_STATUS_IS_OK(status
)) {
1132 errno
= map_errno_from_nt_status(status
);
1136 path
= talloc_asprintf(mem_ctx
, "%s/%s", snap_path
, name
);
1139 goto err_snap_path_free
;
1142 DEBUG(10, ("converted %s/%s @ time to %s\n",
1143 handle
->conn
->connectpath
, name
, path
));
1147 saved_errno
= errno
;
1148 talloc_free(snap_path
);
1149 errno
= saved_errno
;
1154 static DIR *snapper_gmt_opendir(vfs_handle_struct
*handle
,
1165 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1166 ×tamp
, &stripped
)) {
1169 if (timestamp
== 0) {
1170 return SMB_VFS_NEXT_OPENDIR(handle
, fname
, mask
, attr
);
1172 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1173 TALLOC_FREE(stripped
);
1177 ret
= SMB_VFS_NEXT_OPENDIR(handle
, conv
, mask
, attr
);
1178 saved_errno
= errno
;
1180 errno
= saved_errno
;
1184 static int snapper_gmt_rename(vfs_handle_struct
*handle
,
1185 const struct smb_filename
*smb_fname_src
,
1186 const struct smb_filename
*smb_fname_dst
)
1188 time_t timestamp_src
, timestamp_dst
;
1190 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1191 smb_fname_src
->base_name
,
1192 ×tamp_src
, NULL
)) {
1195 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1196 smb_fname_dst
->base_name
,
1197 ×tamp_dst
, NULL
)) {
1200 if (timestamp_src
!= 0) {
1204 if (timestamp_dst
!= 0) {
1208 return SMB_VFS_NEXT_RENAME(handle
, smb_fname_src
, smb_fname_dst
);
1211 static int snapper_gmt_symlink(vfs_handle_struct
*handle
,
1212 const char *oldname
, const char *newname
)
1214 time_t timestamp_old
, timestamp_new
;
1216 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1217 ×tamp_old
, NULL
)) {
1220 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1221 ×tamp_new
, NULL
)) {
1224 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1228 return SMB_VFS_NEXT_SYMLINK(handle
, oldname
, newname
);
1231 static int snapper_gmt_link(vfs_handle_struct
*handle
,
1232 const char *oldname
, const char *newname
)
1234 time_t timestamp_old
, timestamp_new
;
1236 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, oldname
,
1237 ×tamp_old
, NULL
)) {
1240 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, newname
,
1241 ×tamp_new
, NULL
)) {
1244 if ((timestamp_old
!= 0) || (timestamp_new
!= 0)) {
1248 return SMB_VFS_NEXT_LINK(handle
, oldname
, newname
);
1251 static int snapper_gmt_stat(vfs_handle_struct
*handle
,
1252 struct smb_filename
*smb_fname
)
1255 char *stripped
, *tmp
;
1256 int ret
, saved_errno
;
1258 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1259 smb_fname
->base_name
,
1260 ×tamp
, &stripped
)) {
1263 if (timestamp
== 0) {
1264 return SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1267 tmp
= smb_fname
->base_name
;
1268 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1269 stripped
, timestamp
);
1270 TALLOC_FREE(stripped
);
1272 if (smb_fname
->base_name
== NULL
) {
1273 smb_fname
->base_name
= tmp
;
1277 ret
= SMB_VFS_NEXT_STAT(handle
, smb_fname
);
1278 saved_errno
= errno
;
1280 TALLOC_FREE(smb_fname
->base_name
);
1281 smb_fname
->base_name
= tmp
;
1283 errno
= saved_errno
;
1287 static int snapper_gmt_lstat(vfs_handle_struct
*handle
,
1288 struct smb_filename
*smb_fname
)
1291 char *stripped
, *tmp
;
1292 int ret
, saved_errno
;
1294 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1295 smb_fname
->base_name
,
1296 ×tamp
, &stripped
)) {
1299 if (timestamp
== 0) {
1300 return SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1303 tmp
= smb_fname
->base_name
;
1304 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1305 stripped
, timestamp
);
1306 TALLOC_FREE(stripped
);
1308 if (smb_fname
->base_name
== NULL
) {
1309 smb_fname
->base_name
= tmp
;
1313 ret
= SMB_VFS_NEXT_LSTAT(handle
, smb_fname
);
1314 saved_errno
= errno
;
1316 TALLOC_FREE(smb_fname
->base_name
);
1317 smb_fname
->base_name
= tmp
;
1319 errno
= saved_errno
;
1323 static int snapper_gmt_fstat(vfs_handle_struct
*handle
, files_struct
*fsp
,
1324 SMB_STRUCT_STAT
*sbuf
)
1329 ret
= SMB_VFS_NEXT_FSTAT(handle
, fsp
, sbuf
);
1333 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1334 fsp
->fsp_name
->base_name
,
1335 ×tamp
, NULL
)) {
1341 static int snapper_gmt_open(vfs_handle_struct
*handle
,
1342 struct smb_filename
*smb_fname
, files_struct
*fsp
,
1343 int flags
, mode_t mode
)
1346 char *stripped
, *tmp
;
1347 int ret
, saved_errno
;
1349 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1350 smb_fname
->base_name
,
1351 ×tamp
, &stripped
)) {
1354 if (timestamp
== 0) {
1355 return SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1358 tmp
= smb_fname
->base_name
;
1359 smb_fname
->base_name
= snapper_gmt_convert(talloc_tos(), handle
,
1360 stripped
, timestamp
);
1361 TALLOC_FREE(stripped
);
1363 if (smb_fname
->base_name
== NULL
) {
1364 smb_fname
->base_name
= tmp
;
1368 ret
= SMB_VFS_NEXT_OPEN(handle
, smb_fname
, fsp
, flags
, mode
);
1369 saved_errno
= errno
;
1371 TALLOC_FREE(smb_fname
->base_name
);
1372 smb_fname
->base_name
= tmp
;
1374 errno
= saved_errno
;
1378 static int snapper_gmt_unlink(vfs_handle_struct
*handle
,
1379 const struct smb_filename
*smb_fname
)
1383 int ret
, saved_errno
;
1384 struct smb_filename
*conv
;
1386 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1387 smb_fname
->base_name
,
1388 ×tamp
, &stripped
)) {
1391 if (timestamp
== 0) {
1392 return SMB_VFS_NEXT_UNLINK(handle
, smb_fname
);
1394 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1399 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1400 stripped
, timestamp
);
1401 TALLOC_FREE(stripped
);
1402 if (conv
->base_name
== NULL
) {
1405 ret
= SMB_VFS_NEXT_UNLINK(handle
, conv
);
1406 saved_errno
= errno
;
1408 errno
= saved_errno
;
1412 static int snapper_gmt_chmod(vfs_handle_struct
*handle
, const char *fname
,
1417 int ret
, saved_errno
;
1420 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1421 ×tamp
, &stripped
)) {
1424 if (timestamp
== 0) {
1425 return SMB_VFS_NEXT_CHMOD(handle
, fname
, mode
);
1427 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1428 TALLOC_FREE(stripped
);
1432 ret
= SMB_VFS_NEXT_CHMOD(handle
, conv
, mode
);
1433 saved_errno
= errno
;
1435 errno
= saved_errno
;
1439 static int snapper_gmt_chown(vfs_handle_struct
*handle
, const char *fname
,
1440 uid_t uid
, gid_t gid
)
1444 int ret
, saved_errno
;
1447 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1448 ×tamp
, &stripped
)) {
1451 if (timestamp
== 0) {
1452 return SMB_VFS_NEXT_CHOWN(handle
, fname
, uid
, gid
);
1454 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1455 TALLOC_FREE(stripped
);
1459 ret
= SMB_VFS_NEXT_CHOWN(handle
, conv
, uid
, gid
);
1460 saved_errno
= errno
;
1462 errno
= saved_errno
;
1466 static int snapper_gmt_chdir(vfs_handle_struct
*handle
,
1471 int ret
, saved_errno
;
1474 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1475 ×tamp
, &stripped
)) {
1478 if (timestamp
== 0) {
1479 return SMB_VFS_NEXT_CHDIR(handle
, fname
);
1481 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1482 TALLOC_FREE(stripped
);
1486 ret
= SMB_VFS_NEXT_CHDIR(handle
, conv
);
1487 saved_errno
= errno
;
1489 errno
= saved_errno
;
1493 static int snapper_gmt_ntimes(vfs_handle_struct
*handle
,
1494 const struct smb_filename
*smb_fname
,
1495 struct smb_file_time
*ft
)
1499 int ret
, saved_errno
;
1500 struct smb_filename
*conv
;
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_NTIMES(handle
, smb_fname
, ft
);
1510 conv
= cp_smb_filename(talloc_tos(), smb_fname
);
1515 conv
->base_name
= snapper_gmt_convert(conv
, handle
,
1516 stripped
, timestamp
);
1517 TALLOC_FREE(stripped
);
1518 if (conv
->base_name
== NULL
) {
1521 ret
= SMB_VFS_NEXT_NTIMES(handle
, conv
, ft
);
1522 saved_errno
= errno
;
1524 errno
= saved_errno
;
1528 static int snapper_gmt_readlink(vfs_handle_struct
*handle
,
1529 const char *fname
, char *buf
, size_t bufsiz
)
1533 int ret
, saved_errno
;
1536 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1537 ×tamp
, &stripped
)) {
1540 if (timestamp
== 0) {
1541 return SMB_VFS_NEXT_READLINK(handle
, fname
, buf
, bufsiz
);
1543 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1544 TALLOC_FREE(stripped
);
1548 ret
= SMB_VFS_NEXT_READLINK(handle
, conv
, buf
, bufsiz
);
1549 saved_errno
= errno
;
1551 errno
= saved_errno
;
1555 static int snapper_gmt_mknod(vfs_handle_struct
*handle
,
1556 const char *fname
, mode_t mode
, SMB_DEV_T dev
)
1560 int ret
, saved_errno
;
1563 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1564 ×tamp
, &stripped
)) {
1567 if (timestamp
== 0) {
1568 return SMB_VFS_NEXT_MKNOD(handle
, fname
, mode
, dev
);
1570 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1571 TALLOC_FREE(stripped
);
1575 ret
= SMB_VFS_NEXT_MKNOD(handle
, conv
, mode
, dev
);
1576 saved_errno
= errno
;
1578 errno
= saved_errno
;
1582 static char *snapper_gmt_realpath(vfs_handle_struct
*handle
,
1586 char *stripped
= NULL
;
1588 char *result
= NULL
;
1591 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1592 ×tamp
, &stripped
)) {
1595 if (timestamp
== 0) {
1596 return SMB_VFS_NEXT_REALPATH(handle
, fname
);
1599 tmp
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1604 result
= SMB_VFS_NEXT_REALPATH(handle
, tmp
);
1605 if (result
== NULL
) {
1610 saved_errno
= errno
;
1612 TALLOC_FREE(stripped
);
1613 errno
= saved_errno
;
1617 static NTSTATUS
snapper_gmt_fget_nt_acl(vfs_handle_struct
*handle
,
1618 struct files_struct
*fsp
,
1619 uint32 security_info
,
1620 TALLOC_CTX
*mem_ctx
,
1621 struct security_descriptor
**ppdesc
)
1628 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
,
1629 fsp
->fsp_name
->base_name
,
1630 ×tamp
, &stripped
)) {
1631 return map_nt_error_from_unix(errno
);
1633 if (timestamp
== 0) {
1634 return SMB_VFS_NEXT_FGET_NT_ACL(handle
, fsp
, security_info
,
1638 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1639 TALLOC_FREE(stripped
);
1641 return map_nt_error_from_unix(errno
);
1643 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1649 static NTSTATUS
snapper_gmt_get_nt_acl(vfs_handle_struct
*handle
,
1651 uint32 security_info
,
1652 TALLOC_CTX
*mem_ctx
,
1653 struct security_descriptor
**ppdesc
)
1660 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1661 ×tamp
, &stripped
)) {
1662 return map_nt_error_from_unix(errno
);
1664 if (timestamp
== 0) {
1665 return SMB_VFS_NEXT_GET_NT_ACL(handle
, fname
, security_info
,
1668 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1669 TALLOC_FREE(stripped
);
1671 return map_nt_error_from_unix(errno
);
1673 status
= SMB_VFS_NEXT_GET_NT_ACL(handle
, conv
, security_info
,
1679 static int snapper_gmt_mkdir(vfs_handle_struct
*handle
,
1680 const char *fname
, mode_t mode
)
1684 int ret
, saved_errno
;
1687 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1688 ×tamp
, &stripped
)) {
1691 if (timestamp
== 0) {
1692 return SMB_VFS_NEXT_MKDIR(handle
, fname
, mode
);
1694 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1695 TALLOC_FREE(stripped
);
1699 ret
= SMB_VFS_NEXT_MKDIR(handle
, conv
, mode
);
1700 saved_errno
= errno
;
1702 errno
= saved_errno
;
1706 static int snapper_gmt_rmdir(vfs_handle_struct
*handle
, const char *fname
)
1710 int ret
, saved_errno
;
1713 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1714 ×tamp
, &stripped
)) {
1717 if (timestamp
== 0) {
1718 return SMB_VFS_NEXT_RMDIR(handle
, fname
);
1720 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1721 TALLOC_FREE(stripped
);
1725 ret
= SMB_VFS_NEXT_RMDIR(handle
, conv
);
1726 saved_errno
= errno
;
1728 errno
= saved_errno
;
1732 static int snapper_gmt_chflags(vfs_handle_struct
*handle
, const char *fname
,
1737 int ret
, saved_errno
;
1740 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1741 ×tamp
, &stripped
)) {
1744 if (timestamp
== 0) {
1745 return SMB_VFS_NEXT_CHFLAGS(handle
, fname
, flags
);
1747 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1748 TALLOC_FREE(stripped
);
1752 ret
= SMB_VFS_NEXT_CHFLAGS(handle
, conv
, flags
);
1753 saved_errno
= errno
;
1755 errno
= saved_errno
;
1759 static ssize_t
snapper_gmt_getxattr(vfs_handle_struct
*handle
,
1760 const char *fname
, const char *aname
,
1761 void *value
, size_t size
)
1769 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1770 ×tamp
, &stripped
)) {
1773 if (timestamp
== 0) {
1774 return SMB_VFS_NEXT_GETXATTR(handle
, fname
, aname
, value
,
1777 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1778 TALLOC_FREE(stripped
);
1782 ret
= SMB_VFS_NEXT_GETXATTR(handle
, conv
, aname
, value
, size
);
1783 saved_errno
= errno
;
1785 errno
= saved_errno
;
1789 static ssize_t
snapper_gmt_listxattr(struct vfs_handle_struct
*handle
,
1791 char *list
, size_t size
)
1799 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1800 ×tamp
, &stripped
)) {
1803 if (timestamp
== 0) {
1804 return SMB_VFS_NEXT_LISTXATTR(handle
, fname
, list
, size
);
1806 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1807 TALLOC_FREE(stripped
);
1811 ret
= SMB_VFS_NEXT_LISTXATTR(handle
, conv
, list
, size
);
1812 saved_errno
= errno
;
1814 errno
= saved_errno
;
1818 static int snapper_gmt_removexattr(vfs_handle_struct
*handle
,
1819 const char *fname
, const char *aname
)
1823 int ret
, saved_errno
;
1826 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1827 ×tamp
, &stripped
)) {
1830 if (timestamp
== 0) {
1831 return SMB_VFS_NEXT_REMOVEXATTR(handle
, fname
, aname
);
1833 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1834 TALLOC_FREE(stripped
);
1838 ret
= SMB_VFS_NEXT_REMOVEXATTR(handle
, conv
, aname
);
1839 saved_errno
= errno
;
1841 errno
= saved_errno
;
1845 static int snapper_gmt_setxattr(struct vfs_handle_struct
*handle
,
1847 const char *aname
, const void *value
,
1848 size_t size
, int flags
)
1856 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1857 ×tamp
, &stripped
)) {
1860 if (timestamp
== 0) {
1861 return SMB_VFS_NEXT_SETXATTR(handle
, fname
, aname
, value
, size
,
1864 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1865 TALLOC_FREE(stripped
);
1869 ret
= SMB_VFS_NEXT_SETXATTR(handle
, conv
, aname
, value
, size
, flags
);
1870 saved_errno
= errno
;
1872 errno
= saved_errno
;
1876 static int snapper_gmt_chmod_acl(vfs_handle_struct
*handle
,
1877 const char *fname
, mode_t mode
)
1885 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, fname
,
1886 ×tamp
, &stripped
)) {
1889 if (timestamp
== 0) {
1890 return SMB_VFS_NEXT_CHMOD_ACL(handle
, fname
, mode
);
1892 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1893 TALLOC_FREE(stripped
);
1897 ret
= SMB_VFS_NEXT_CHMOD_ACL(handle
, conv
, mode
);
1898 saved_errno
= errno
;
1900 errno
= saved_errno
;
1904 static int snapper_gmt_get_real_filename(struct vfs_handle_struct
*handle
,
1907 TALLOC_CTX
*mem_ctx
,
1916 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
1917 ×tamp
, &stripped
)) {
1920 if (timestamp
== 0) {
1921 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, path
, name
,
1922 mem_ctx
, found_name
);
1924 if (stripped
[0] == '\0') {
1925 *found_name
= talloc_strdup(mem_ctx
, name
);
1926 if (*found_name
== NULL
) {
1932 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1933 TALLOC_FREE(stripped
);
1937 ret
= SMB_VFS_NEXT_GET_REAL_FILENAME(handle
, conv
, name
,
1938 mem_ctx
, found_name
);
1939 saved_errno
= errno
;
1941 errno
= saved_errno
;
1945 static uint64_t snapper_gmt_disk_free(vfs_handle_struct
*handle
,
1946 const char *path
, bool small_query
,
1947 uint64_t *bsize
, uint64_t *dfree
,
1956 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle
, path
,
1957 ×tamp
, &stripped
)) {
1960 if (timestamp
== 0) {
1961 return SMB_VFS_NEXT_DISK_FREE(handle
, path
, small_query
,
1962 bsize
, dfree
, dsize
);
1965 conv
= snapper_gmt_convert(talloc_tos(), handle
, stripped
, timestamp
);
1966 TALLOC_FREE(stripped
);
1971 ret
= SMB_VFS_NEXT_DISK_FREE(handle
, conv
, small_query
, bsize
, dfree
,
1974 saved_errno
= errno
;
1976 errno
= saved_errno
;
1982 static struct vfs_fn_pointers snapper_fns
= {
1983 .get_shadow_copy_data_fn
= snapper_get_shadow_copy_data
,
1984 .opendir_fn
= snapper_gmt_opendir
,
1985 .disk_free_fn
= snapper_gmt_disk_free
,
1986 .rename_fn
= snapper_gmt_rename
,
1987 .link_fn
= snapper_gmt_link
,
1988 .symlink_fn
= snapper_gmt_symlink
,
1989 .stat_fn
= snapper_gmt_stat
,
1990 .lstat_fn
= snapper_gmt_lstat
,
1991 .fstat_fn
= snapper_gmt_fstat
,
1992 .open_fn
= snapper_gmt_open
,
1993 .unlink_fn
= snapper_gmt_unlink
,
1994 .chmod_fn
= snapper_gmt_chmod
,
1995 .chown_fn
= snapper_gmt_chown
,
1996 .chdir_fn
= snapper_gmt_chdir
,
1997 .ntimes_fn
= snapper_gmt_ntimes
,
1998 .readlink_fn
= snapper_gmt_readlink
,
1999 .mknod_fn
= snapper_gmt_mknod
,
2000 .realpath_fn
= snapper_gmt_realpath
,
2001 .get_nt_acl_fn
= snapper_gmt_get_nt_acl
,
2002 .fget_nt_acl_fn
= snapper_gmt_fget_nt_acl
,
2003 .mkdir_fn
= snapper_gmt_mkdir
,
2004 .rmdir_fn
= snapper_gmt_rmdir
,
2005 .getxattr_fn
= snapper_gmt_getxattr
,
2006 .listxattr_fn
= snapper_gmt_listxattr
,
2007 .removexattr_fn
= snapper_gmt_removexattr
,
2008 .setxattr_fn
= snapper_gmt_setxattr
,
2009 .chmod_acl_fn
= snapper_gmt_chmod_acl
,
2010 .chflags_fn
= snapper_gmt_chflags
,
2011 .get_real_filename_fn
= snapper_gmt_get_real_filename
,
2014 NTSTATUS
vfs_snapper_init(void);
2015 NTSTATUS
vfs_snapper_init(void)
2017 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION
,
2018 "snapper", &snapper_fns
);