torture: Add a check to verify MS-SMB2 3.3.5.14.2
[Samba.git] / source3 / modules / vfs_snapper.c
blobf0f0b5c5578263babafa529067afb53c6e6d95bd
1 /*
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>
30 #include <dirent.h>
31 #include <libgen.h>
32 #include "includes.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}"
43 struct snapper_dict {
44 char *key;
45 char *val;
48 struct snapper_snap {
49 uint32_t id;
50 uint16_t type;
51 uint32_t pre_id;
52 int64_t time;
53 uint32_t creator_uid;
54 char *desc;
55 char *cleanup;
56 uint32_t num_user_data;
57 struct snapper_dict *user_data;
60 struct snapper_conf {
61 char *name;
62 char *mnt;
63 uint32_t num_attrs;
64 struct snapper_dict *attrs;
67 static const struct {
68 const char *snapper_err_str;
69 NTSTATUS status;
70 } snapper_err_map[] = {
71 { "error.no_permissions", NT_STATUS_ACCESS_DENIED },
74 static NTSTATUS snapper_err_ntstatus_map(const char *snapper_err_str)
76 int i;
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,
83 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)
94 DBusError err;
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);
108 if (dconn == NULL) {
109 return NULL;
112 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
113 dbus_connection_set_exit_on_disconnect(dconn, false);
115 return dconn;
118 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
120 if (dconn == NULL) {
121 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
122 return;
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;
165 return NT_STATUS_OK;
168 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
169 int expected_type)
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;
178 return NT_STATUS_OK;
181 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
182 int expected_type,
183 void *val)
185 NTSTATUS status;
186 status = snapper_type_check(iter, expected_type);
187 if (!NT_STATUS_IS_OK(status)) {
188 return status;
191 dbus_message_iter_get_basic(iter, val);
193 return NT_STATUS_OK;
196 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
197 struct snapper_dict *dict_out)
200 NTSTATUS status;
201 DBusMessageIter dct_iter;
203 status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
204 if (!NT_STATUS_IS_OK(status)) {
205 return status;
207 dbus_message_iter_recurse(iter, &dct_iter);
209 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
210 &dict_out->key);
211 if (!NT_STATUS_IS_OK(status)) {
212 return status;
215 dbus_message_iter_next(&dct_iter);
216 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
217 &dict_out->val);
218 if (!NT_STATUS_IS_OK(status)) {
219 return status;
222 return NT_STATUS_OK;
225 static void snapper_dict_array_print(uint32_t num_dicts,
226 struct snapper_dict *dicts)
228 int i;
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)
241 NTSTATUS status;
242 DBusMessageIter array_iter;
243 uint32_t num_dicts;
244 struct snapper_dict *dicts = NULL;
246 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
247 if (!NT_STATUS_IS_OK(status)) {
248 return status;
250 dbus_message_iter_recurse(iter, &array_iter);
252 num_dicts = 0;
253 while (dbus_message_iter_get_arg_type(&array_iter)
254 != DBUS_TYPE_INVALID) {
255 num_dicts++;
256 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
257 num_dicts);
258 if (dicts == NULL)
259 abort();
261 status = snapper_dict_unpack(&array_iter,
262 &dicts[num_dicts - 1]);
263 if (!NT_STATUS_IS_OK(status)) {
264 talloc_free(dicts);
265 return status;
267 dbus_message_iter_next(&array_iter);
270 *num_dicts_out = num_dicts;
271 *dicts_out = dicts;
273 return NT_STATUS_OK;
276 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
278 DBusMessage *msg;
280 msg = dbus_message_new_method_call("org.opensuse.Snapper",
281 "/org/opensuse/Snapper",
282 "org.opensuse.Snapper",
283 "ListConfigs");
284 if (msg == NULL) {
285 DEBUG(0, ("null msg\n"));
286 return NT_STATUS_NO_MEMORY;
289 /* no arguments to append */
290 *req_msg_out = msg;
292 return NT_STATUS_OK;
295 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
296 DBusMessageIter *iter,
297 struct snapper_conf *conf_out)
299 NTSTATUS status;
300 DBusMessageIter st_iter;
302 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
303 if (!NT_STATUS_IS_OK(status)) {
304 return status;
306 dbus_message_iter_recurse(iter, &st_iter);
308 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
309 &conf_out->name);
310 if (!NT_STATUS_IS_OK(status)) {
311 return status;
314 dbus_message_iter_next(&st_iter);
315 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
316 &conf_out->mnt);
317 if (!NT_STATUS_IS_OK(status)) {
318 return status;
321 dbus_message_iter_next(&st_iter);
322 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
323 &conf_out->num_attrs,
324 &conf_out->attrs);
326 return status;
329 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
330 struct snapper_conf *confs,
331 const char *base)
333 int i;
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));
339 return &confs[i];
342 DEBUG(5, ("config for base %s not found\n", base));
344 return NULL;
347 static void snapper_conf_array_print(int32_t num_confs,
348 struct snapper_conf *confs)
350 int i;
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)
364 uint32_t num_confs;
365 NTSTATUS status;
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)) {
372 return status;
374 dbus_message_iter_recurse(iter, &array_iter);
376 num_confs = 0;
377 while (dbus_message_iter_get_arg_type(&array_iter)
378 != DBUS_TYPE_INVALID) {
379 num_confs++;
380 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
381 num_confs);
382 if (confs == NULL)
383 abort();
385 status = snapper_conf_unpack(confs, &array_iter,
386 &confs[num_confs - 1]);
387 if (!NT_STATUS_IS_OK(status)) {
388 talloc_free(confs);
389 return status;
391 dbus_message_iter_next(&array_iter);
394 *num_confs_out = num_confs;
395 *confs_out = confs;
397 return NT_STATUS_OK;
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)
406 NTSTATUS status;
407 DBusMessageIter iter;
408 int msg_type;
409 uint32_t num_confs;
410 struct snapper_conf *confs;
411 const char *sig;
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",
422 msg_type));
423 return NT_STATUS_INVALID_PARAMETER;
426 sig = dbus_message_get_signature(rsp_msg);
427 if ((sig == NULL)
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"));
443 return status;
446 snapper_conf_array_print(num_confs, confs);
448 *num_confs_out = num_confs;
449 *confs_out = confs;
451 return NT_STATUS_OK;
454 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
455 DBusMessage **req_msg_out)
457 DBusMessage *msg;
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 */
464 if (msg == NULL) {
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,
472 &snapper_conf)) {
473 return NT_STATUS_NO_MEMORY;
476 *req_msg_out = msg;
478 return NT_STATUS_OK;
481 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
482 DBusMessageIter *iter,
483 struct snapper_snap *snap_out)
485 NTSTATUS status;
486 DBusMessageIter st_iter;
488 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
489 if (!NT_STATUS_IS_OK(status)) {
490 return status;
492 dbus_message_iter_recurse(iter, &st_iter);
494 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
495 &snap_out->id);
496 if (!NT_STATUS_IS_OK(status)) {
497 return status;
500 dbus_message_iter_next(&st_iter);
501 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
502 &snap_out->type);
503 if (!NT_STATUS_IS_OK(status)) {
504 return status;
507 dbus_message_iter_next(&st_iter);
508 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
509 &snap_out->pre_id);
510 if (!NT_STATUS_IS_OK(status)) {
511 return status;
514 dbus_message_iter_next(&st_iter);
515 status = snapper_type_check_get(&st_iter, DBUS_TYPE_INT64,
516 &snap_out->time);
517 if (!NT_STATUS_IS_OK(status)) {
518 return 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)) {
525 return status;
528 dbus_message_iter_next(&st_iter);
529 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
530 &snap_out->desc);
531 if (!NT_STATUS_IS_OK(status)) {
532 return status;
535 dbus_message_iter_next(&st_iter);
536 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
537 &snap_out->cleanup);
538 if (!NT_STATUS_IS_OK(status)) {
539 return 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);
547 return status;
550 static void snapper_snap_array_print(int32_t num_snaps,
551 struct snapper_snap *snaps)
553 int i;
555 for (i = 0; i < num_snaps; i++) {
556 DEBUG(10, ("id: %u, "
557 "type: %u, "
558 "pre_id: %u, "
559 "time: %ld, "
560 "creator_uid: %u, "
561 "desc: %s, "
562 "cleanup: %s\n",
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,
568 snaps[i].desc,
569 snaps[i].cleanup));
570 snapper_dict_array_print(snaps[i].num_user_data,
571 snaps[i].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)
580 uint32_t num_snaps;
581 NTSTATUS status;
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)) {
588 return status;
590 dbus_message_iter_recurse(iter, &array_iter);
592 num_snaps = 0;
593 while (dbus_message_iter_get_arg_type(&array_iter)
594 != DBUS_TYPE_INVALID) {
595 num_snaps++;
596 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
597 num_snaps);
598 if (snaps == NULL)
599 abort();
601 status = snapper_snap_struct_unpack(snaps, &array_iter,
602 &snaps[num_snaps - 1]);
603 if (!NT_STATUS_IS_OK(status)) {
604 talloc_free(snaps);
605 return status;
607 dbus_message_iter_next(&array_iter);
610 *num_snaps_out = num_snaps;
611 *snaps_out = snaps;
613 return NT_STATUS_OK;
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)
621 NTSTATUS status;
622 DBusMessageIter iter;
623 int msg_type;
624 uint32_t num_snaps;
625 struct snapper_snap *snaps;
626 const char *sig;
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",
637 msg_type));
638 return NT_STATUS_INVALID_PARAMETER;
641 sig = dbus_message_get_signature(rsp_msg);
642 if ((sig == NULL)
643 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
644 DEBUG(0, ("bad list snaps response sig: %s, "
645 "expected: %s\n",
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;
666 *snaps_out = snaps;
668 return NT_STATUS_OK;
671 static NTSTATUS snapper_list_snaps_at_time_pack(const char *snapper_conf,
672 time_t time_lower,
673 time_t time_upper,
674 DBusMessage **req_msg_out)
676 DBusMessage *msg;
677 DBusMessageIter args;
679 msg = dbus_message_new_method_call("org.opensuse.Snapper",
680 "/org/opensuse/Snapper",
681 "org.opensuse.Snapper",
682 "ListSnapshotsAtTime");
683 if (msg == NULL) {
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,
690 &snapper_conf)) {
691 return NT_STATUS_NO_MEMORY;
694 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
695 &time_lower)) {
696 return NT_STATUS_NO_MEMORY;
699 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
700 &time_upper)) {
701 return NT_STATUS_NO_MEMORY;
704 *req_msg_out = msg;
706 return NT_STATUS_OK;
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,
716 uint32_t snap_id,
717 char **snap_path_out)
719 char *snap_path;
721 snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
722 base_path, snap_id);
723 if (snap_path == NULL) {
724 return NT_STATUS_NO_MEMORY;
727 *snap_path_out = snap_path;
728 return NT_STATUS_OK;
731 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
732 DBusConnection *dconn,
733 const char *path,
734 char **conf_name_out,
735 char **base_path_out)
737 NTSTATUS status;
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;
743 char *conf_name;
744 char *base_path;
746 status = snapper_list_confs_pack(&req_msg);
747 if (!NT_STATUS_IS_OK(status)) {
748 goto err_out;
751 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
752 if (!NT_STATUS_IS_OK(status)) {
753 goto err_req_free;
756 status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
757 &num_confs, &confs);
758 if (!NT_STATUS_IS_OK(status)) {
759 goto err_rsp_free;
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,
767 path);
768 if (conf == NULL) {
769 status = NT_STATUS_NOT_SUPPORTED;
770 goto err_array_free;
773 conf_name = talloc_strdup(mem_ctx, conf->name);
774 if (conf_name == NULL) {
775 status = NT_STATUS_NO_MEMORY;
776 goto err_array_free;
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;
784 talloc_free(confs);
785 dbus_message_unref(rsp_msg);
786 dbus_message_unref(req_msg);
788 *conf_name_out = conf_name;
789 *base_path_out = base_path;
791 return NT_STATUS_OK;
793 err_conf_name_free:
794 talloc_free(conf_name);
795 err_array_free:
796 talloc_free(confs);
797 err_rsp_free:
798 dbus_message_unref(rsp_msg);
799 err_req_free:
800 dbus_message_unref(req_msg);
801 err_out:
802 return status;
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,
809 bool labels)
811 DBusConnection *dconn;
812 TALLOC_CTX *tmp_ctx;
813 NTSTATUS status;
814 char *conf_name;
815 char *base_path;
816 DBusMessage *req_msg;
817 DBusMessage *rsp_msg;
818 uint32_t num_snaps;
819 struct snapper_snap *snaps;
820 uint32_t i;
821 uint32_t lbl_off;
823 tmp_ctx = talloc_new(sc_data);
824 if (tmp_ctx == NULL) {
825 status = NT_STATUS_NO_MEMORY;
826 goto err_out;
829 dconn = snapper_dbus_conn_create();
830 if (dconn == NULL) {
831 status = NT_STATUS_UNSUCCESSFUL;
832 goto err_mem_ctx_free;
835 if (fsp->conn->connectpath == NULL) {
836 status = NT_STATUS_INVALID_PARAMETER;
837 goto err_conn_free;
840 status = snapper_get_conf_call(tmp_ctx, dconn,
841 fsp->conn->connectpath,
842 &conf_name,
843 &base_path);
844 if (!NT_STATUS_IS_OK(status)) {
845 goto err_conn_free;
848 status = snapper_list_snaps_pack(conf_name, &req_msg);
849 if (!NT_STATUS_IS_OK(status)) {
850 goto err_conn_free;
853 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
854 if (!NT_STATUS_IS_OK(status)) {
855 goto err_req_free;
858 status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
859 &num_snaps, &snaps);
860 if (!NT_STATUS_IS_OK(status)) {
861 goto err_rsp_free;
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;
867 goto err_rsp_free;
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 */
876 goto done;
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;
883 goto err_rsp_free;
886 /* start at end for decending order, do not include 0 (current) */
887 lbl_off = 0;
888 for (i = num_snaps - 1; i > 0; i--) {
889 char *lbl = sc_data->labels[lbl_off++];
890 struct tm gmt_snap_time;
891 struct tm *tm_ret;
892 size_t str_sz;
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);
901 if (str_sz == 0) {
902 status = NT_STATUS_UNSUCCESSFUL;
903 goto err_labels_free;
907 done:
908 talloc_free(tmp_ctx);
909 dbus_message_unref(rsp_msg);
910 dbus_message_unref(req_msg);
911 snapper_dbus_conn_destroy(dconn);
913 return 0;
915 err_labels_free:
916 TALLOC_FREE(sc_data->labels);
917 err_rsp_free:
918 dbus_message_unref(rsp_msg);
919 err_req_free:
920 dbus_message_unref(req_msg);
921 err_conn_free:
922 snapper_dbus_conn_destroy(dconn);
923 err_mem_ctx_free:
924 talloc_free(tmp_ctx);
925 err_out:
926 errno = map_errno_from_nt_status(status);
927 return -1;
930 static bool snapper_gmt_strip_snapshot(TALLOC_CTX *mem_ctx,
931 struct vfs_handle_struct *handle,
932 const char *name,
933 time_t *ptimestamp,
934 char **pstripped)
936 struct tm tm;
937 time_t timestamp;
938 const char *p;
939 char *q;
940 char *stripped;
941 size_t rest_len, dst_len;
943 p = strstr_m(name, "@GMT-");
944 if (p == NULL) {
945 goto no_snapshot;
947 if ((p > name) && (p[-1] != '/')) {
948 goto no_snapshot;
950 q = strptime(p, GMT_FORMAT, &tm);
951 if (q == NULL) {
952 goto no_snapshot;
954 tm.tm_isdst = -1;
955 timestamp = timegm(&tm);
956 if (timestamp == (time_t)-1) {
957 goto no_snapshot;
959 if ((p == name) && (q[0] == '\0')) {
960 if (pstripped != NULL) {
961 stripped = talloc_strdup(mem_ctx, "");
962 if (stripped == NULL) {
963 return false;
965 *pstripped = stripped;
967 *ptimestamp = timestamp;
968 return true;
970 if (q[0] != '/') {
971 goto no_snapshot;
973 q += 1;
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) {
981 errno = ENOMEM;
982 return false;
984 if (p > name) {
985 memcpy(stripped, name, p-name);
987 if (rest_len > 0) {
988 memcpy(stripped + (p-name), q, rest_len);
990 stripped[dst_len] = '\0';
991 *pstripped = stripped;
993 *ptimestamp = timestamp;
994 return true;
995 no_snapshot:
996 *ptimestamp = 0;
997 return true;
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,
1004 time_t snaptime,
1005 char **snap_path_out)
1007 NTSTATUS status;
1008 DBusMessage *req_msg;
1009 DBusMessage *rsp_msg;
1010 uint32_t num_snaps;
1011 struct snapper_snap *snaps;
1012 char *snap_path;
1014 status = snapper_list_snaps_at_time_pack(conf_name,
1015 snaptime,
1016 snaptime,
1017 &req_msg);
1018 if (!NT_STATUS_IS_OK(status)) {
1019 goto err_out;
1022 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1023 if (!NT_STATUS_IS_OK(status)) {
1024 goto err_req_free;
1027 status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1028 &num_snaps, &snaps);
1029 if (!NT_STATUS_IS_OK(status)) {
1030 goto err_rsp_free;
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,
1043 &snap_path);
1044 if (!NT_STATUS_IS_OK(status)) {
1045 goto err_snap_array_free;
1048 *snap_path_out = snap_path;
1049 err_snap_array_free:
1050 talloc_free(snaps);
1051 err_rsp_free:
1052 dbus_message_unref(rsp_msg);
1053 err_req_free:
1054 dbus_message_unref(req_msg);
1055 err_out:
1056 return status;
1059 static NTSTATUS snapper_snap_path_expand(struct connection_struct *conn,
1060 TALLOC_CTX *mem_ctx,
1061 time_t snap_time,
1062 char **snap_dir_out)
1064 DBusConnection *dconn;
1065 NTSTATUS status;
1066 char *conf_name;
1067 char *base_path;
1068 char *snap_path;
1070 dconn = snapper_dbus_conn_create();
1071 if (dconn == NULL) {
1072 status = NT_STATUS_UNSUCCESSFUL;
1073 goto err_out;
1076 if (conn->connectpath == NULL) {
1077 status = NT_STATUS_INVALID_PARAMETER;
1078 goto err_conn_free;
1081 status = snapper_get_conf_call(mem_ctx, dconn,
1082 conn->connectpath,
1083 &conf_name,
1084 &base_path);
1085 if (!NT_STATUS_IS_OK(status)) {
1086 goto err_conn_free;
1089 status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1090 conf_name, base_path, snap_time,
1091 &snap_path);
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;
1109 err_snap_path_free:
1110 talloc_free(snap_path);
1111 err_conf_name_free:
1112 talloc_free(conf_name);
1113 talloc_free(base_path);
1114 err_conn_free:
1115 snapper_dbus_conn_destroy(dconn);
1116 err_out:
1117 return status;
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;
1125 char *path = NULL;
1126 NTSTATUS status;
1127 int saved_errno;
1129 status = snapper_snap_path_expand(handle->conn, mem_ctx, timestamp,
1130 &snap_path);
1131 if (!NT_STATUS_IS_OK(status)) {
1132 errno = map_errno_from_nt_status(status);
1133 goto err_out;
1136 path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1137 if (path == NULL) {
1138 errno = ENOMEM;
1139 goto err_snap_path_free;
1142 DEBUG(10, ("converted %s/%s @ time to %s\n",
1143 handle->conn->connectpath, name, path));
1144 return path;
1146 err_snap_path_free:
1147 saved_errno = errno;
1148 talloc_free(snap_path);
1149 errno = saved_errno;
1150 err_out:
1151 return NULL;
1154 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1155 const char *fname,
1156 const char *mask,
1157 uint32_t attr)
1159 time_t timestamp;
1160 char *stripped;
1161 DIR *ret;
1162 int saved_errno;
1163 char *conv;
1165 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1166 &timestamp, &stripped)) {
1167 return NULL;
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);
1174 if (conv == NULL) {
1175 return NULL;
1177 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
1178 saved_errno = errno;
1179 TALLOC_FREE(conv);
1180 errno = saved_errno;
1181 return ret;
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 &timestamp_src, NULL)) {
1193 return -1;
1195 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1196 smb_fname_dst->base_name,
1197 &timestamp_dst, NULL)) {
1198 return -1;
1200 if (timestamp_src != 0) {
1201 errno = EXDEV;
1202 return -1;
1204 if (timestamp_dst != 0) {
1205 errno = EROFS;
1206 return -1;
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 &timestamp_old, NULL)) {
1218 return -1;
1220 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1221 &timestamp_new, NULL)) {
1222 return -1;
1224 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1225 errno = EROFS;
1226 return -1;
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 &timestamp_old, NULL)) {
1238 return -1;
1240 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1241 &timestamp_new, NULL)) {
1242 return -1;
1244 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1245 errno = EROFS;
1246 return -1;
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)
1254 time_t timestamp;
1255 char *stripped, *tmp;
1256 int ret, saved_errno;
1258 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1259 smb_fname->base_name,
1260 &timestamp, &stripped)) {
1261 return -1;
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;
1274 return -1;
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;
1284 return ret;
1287 static int snapper_gmt_lstat(vfs_handle_struct *handle,
1288 struct smb_filename *smb_fname)
1290 time_t timestamp;
1291 char *stripped, *tmp;
1292 int ret, saved_errno;
1294 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1295 smb_fname->base_name,
1296 &timestamp, &stripped)) {
1297 return -1;
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;
1310 return -1;
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;
1320 return ret;
1323 static int snapper_gmt_fstat(vfs_handle_struct *handle, files_struct *fsp,
1324 SMB_STRUCT_STAT *sbuf)
1326 time_t timestamp;
1327 int ret;
1329 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1330 if (ret == -1) {
1331 return ret;
1333 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1334 fsp->fsp_name->base_name,
1335 &timestamp, NULL)) {
1336 return 0;
1338 return 0;
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)
1345 time_t timestamp;
1346 char *stripped, *tmp;
1347 int ret, saved_errno;
1349 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1350 smb_fname->base_name,
1351 &timestamp, &stripped)) {
1352 return -1;
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;
1365 return -1;
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;
1375 return ret;
1378 static int snapper_gmt_unlink(vfs_handle_struct *handle,
1379 const struct smb_filename *smb_fname)
1381 time_t timestamp;
1382 char *stripped;
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 &timestamp, &stripped)) {
1389 return -1;
1391 if (timestamp == 0) {
1392 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1394 conv = cp_smb_filename(talloc_tos(), smb_fname);
1395 if (conv == NULL) {
1396 errno = ENOMEM;
1397 return -1;
1399 conv->base_name = snapper_gmt_convert(conv, handle,
1400 stripped, timestamp);
1401 TALLOC_FREE(stripped);
1402 if (conv->base_name == NULL) {
1403 return -1;
1405 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1406 saved_errno = errno;
1407 TALLOC_FREE(conv);
1408 errno = saved_errno;
1409 return ret;
1412 static int snapper_gmt_chmod(vfs_handle_struct *handle, const char *fname,
1413 mode_t mode)
1415 time_t timestamp;
1416 char *stripped;
1417 int ret, saved_errno;
1418 char *conv;
1420 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1421 &timestamp, &stripped)) {
1422 return -1;
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);
1429 if (conv == NULL) {
1430 return -1;
1432 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1433 saved_errno = errno;
1434 TALLOC_FREE(conv);
1435 errno = saved_errno;
1436 return ret;
1439 static int snapper_gmt_chown(vfs_handle_struct *handle, const char *fname,
1440 uid_t uid, gid_t gid)
1442 time_t timestamp;
1443 char *stripped;
1444 int ret, saved_errno;
1445 char *conv;
1447 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1448 &timestamp, &stripped)) {
1449 return -1;
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);
1456 if (conv == NULL) {
1457 return -1;
1459 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1460 saved_errno = errno;
1461 TALLOC_FREE(conv);
1462 errno = saved_errno;
1463 return ret;
1466 static int snapper_gmt_chdir(vfs_handle_struct *handle,
1467 const char *fname)
1469 time_t timestamp;
1470 char *stripped;
1471 int ret, saved_errno;
1472 char *conv;
1474 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1475 &timestamp, &stripped)) {
1476 return -1;
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);
1483 if (conv == NULL) {
1484 return -1;
1486 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1487 saved_errno = errno;
1488 TALLOC_FREE(conv);
1489 errno = saved_errno;
1490 return ret;
1493 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
1494 const struct smb_filename *smb_fname,
1495 struct smb_file_time *ft)
1497 time_t timestamp;
1498 char *stripped;
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 &timestamp, &stripped)) {
1505 return -1;
1507 if (timestamp == 0) {
1508 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1510 conv = cp_smb_filename(talloc_tos(), smb_fname);
1511 if (conv == NULL) {
1512 errno = ENOMEM;
1513 return -1;
1515 conv->base_name = snapper_gmt_convert(conv, handle,
1516 stripped, timestamp);
1517 TALLOC_FREE(stripped);
1518 if (conv->base_name == NULL) {
1519 return -1;
1521 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1522 saved_errno = errno;
1523 TALLOC_FREE(conv);
1524 errno = saved_errno;
1525 return ret;
1528 static int snapper_gmt_readlink(vfs_handle_struct *handle,
1529 const char *fname, char *buf, size_t bufsiz)
1531 time_t timestamp;
1532 char *stripped;
1533 int ret, saved_errno;
1534 char *conv;
1536 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1537 &timestamp, &stripped)) {
1538 return -1;
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);
1545 if (conv == NULL) {
1546 return -1;
1548 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1549 saved_errno = errno;
1550 TALLOC_FREE(conv);
1551 errno = saved_errno;
1552 return ret;
1555 static int snapper_gmt_mknod(vfs_handle_struct *handle,
1556 const char *fname, mode_t mode, SMB_DEV_T dev)
1558 time_t timestamp;
1559 char *stripped;
1560 int ret, saved_errno;
1561 char *conv;
1563 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1564 &timestamp, &stripped)) {
1565 return -1;
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);
1572 if (conv == NULL) {
1573 return -1;
1575 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1576 saved_errno = errno;
1577 TALLOC_FREE(conv);
1578 errno = saved_errno;
1579 return ret;
1582 static char *snapper_gmt_realpath(vfs_handle_struct *handle,
1583 const char *fname)
1585 time_t timestamp;
1586 char *stripped = NULL;
1587 char *tmp = NULL;
1588 char *result = NULL;
1589 int saved_errno;
1591 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1592 &timestamp, &stripped)) {
1593 goto done;
1595 if (timestamp == 0) {
1596 return SMB_VFS_NEXT_REALPATH(handle, fname);
1599 tmp = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1600 if (tmp == NULL) {
1601 goto done;
1604 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1605 if (result == NULL) {
1606 goto done;
1609 done:
1610 saved_errno = errno;
1611 TALLOC_FREE(tmp);
1612 TALLOC_FREE(stripped);
1613 errno = saved_errno;
1614 return result;
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)
1623 time_t timestamp;
1624 char *stripped;
1625 NTSTATUS status;
1626 char *conv;
1628 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1629 fsp->fsp_name->base_name,
1630 &timestamp, &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,
1635 mem_ctx,
1636 ppdesc);
1638 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1639 TALLOC_FREE(stripped);
1640 if (conv == NULL) {
1641 return map_nt_error_from_unix(errno);
1643 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1644 mem_ctx, ppdesc);
1645 TALLOC_FREE(conv);
1646 return status;
1649 static NTSTATUS snapper_gmt_get_nt_acl(vfs_handle_struct *handle,
1650 const char *fname,
1651 uint32 security_info,
1652 TALLOC_CTX *mem_ctx,
1653 struct security_descriptor **ppdesc)
1655 time_t timestamp;
1656 char *stripped;
1657 NTSTATUS status;
1658 char *conv;
1660 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1661 &timestamp, &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,
1666 mem_ctx, ppdesc);
1668 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1669 TALLOC_FREE(stripped);
1670 if (conv == NULL) {
1671 return map_nt_error_from_unix(errno);
1673 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1674 mem_ctx, ppdesc);
1675 TALLOC_FREE(conv);
1676 return status;
1679 static int snapper_gmt_mkdir(vfs_handle_struct *handle,
1680 const char *fname, mode_t mode)
1682 time_t timestamp;
1683 char *stripped;
1684 int ret, saved_errno;
1685 char *conv;
1687 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1688 &timestamp, &stripped)) {
1689 return -1;
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);
1696 if (conv == NULL) {
1697 return -1;
1699 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1700 saved_errno = errno;
1701 TALLOC_FREE(conv);
1702 errno = saved_errno;
1703 return ret;
1706 static int snapper_gmt_rmdir(vfs_handle_struct *handle, const char *fname)
1708 time_t timestamp;
1709 char *stripped;
1710 int ret, saved_errno;
1711 char *conv;
1713 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1714 &timestamp, &stripped)) {
1715 return -1;
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);
1722 if (conv == NULL) {
1723 return -1;
1725 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1726 saved_errno = errno;
1727 TALLOC_FREE(conv);
1728 errno = saved_errno;
1729 return ret;
1732 static int snapper_gmt_chflags(vfs_handle_struct *handle, const char *fname,
1733 unsigned int flags)
1735 time_t timestamp;
1736 char *stripped;
1737 int ret, saved_errno;
1738 char *conv;
1740 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1741 &timestamp, &stripped)) {
1742 return -1;
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);
1749 if (conv == NULL) {
1750 return -1;
1752 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1753 saved_errno = errno;
1754 TALLOC_FREE(conv);
1755 errno = saved_errno;
1756 return ret;
1759 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
1760 const char *fname, const char *aname,
1761 void *value, size_t size)
1763 time_t timestamp;
1764 char *stripped;
1765 ssize_t ret;
1766 int saved_errno;
1767 char *conv;
1769 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1770 &timestamp, &stripped)) {
1771 return -1;
1773 if (timestamp == 0) {
1774 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1775 size);
1777 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1778 TALLOC_FREE(stripped);
1779 if (conv == NULL) {
1780 return -1;
1782 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1783 saved_errno = errno;
1784 TALLOC_FREE(conv);
1785 errno = saved_errno;
1786 return ret;
1789 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
1790 const char *fname,
1791 char *list, size_t size)
1793 time_t timestamp;
1794 char *stripped;
1795 ssize_t ret;
1796 int saved_errno;
1797 char *conv;
1799 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1800 &timestamp, &stripped)) {
1801 return -1;
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);
1808 if (conv == NULL) {
1809 return -1;
1811 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1812 saved_errno = errno;
1813 TALLOC_FREE(conv);
1814 errno = saved_errno;
1815 return ret;
1818 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
1819 const char *fname, const char *aname)
1821 time_t timestamp;
1822 char *stripped;
1823 int ret, saved_errno;
1824 char *conv;
1826 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1827 &timestamp, &stripped)) {
1828 return -1;
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);
1835 if (conv == NULL) {
1836 return -1;
1838 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1839 saved_errno = errno;
1840 TALLOC_FREE(conv);
1841 errno = saved_errno;
1842 return ret;
1845 static int snapper_gmt_setxattr(struct vfs_handle_struct *handle,
1846 const char *fname,
1847 const char *aname, const void *value,
1848 size_t size, int flags)
1850 time_t timestamp;
1851 char *stripped;
1852 ssize_t ret;
1853 int saved_errno;
1854 char *conv;
1856 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1857 &timestamp, &stripped)) {
1858 return -1;
1860 if (timestamp == 0) {
1861 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1862 flags);
1864 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1865 TALLOC_FREE(stripped);
1866 if (conv == NULL) {
1867 return -1;
1869 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1870 saved_errno = errno;
1871 TALLOC_FREE(conv);
1872 errno = saved_errno;
1873 return ret;
1876 static int snapper_gmt_chmod_acl(vfs_handle_struct *handle,
1877 const char *fname, mode_t mode)
1879 time_t timestamp;
1880 char *stripped;
1881 ssize_t ret;
1882 int saved_errno;
1883 char *conv;
1885 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1886 &timestamp, &stripped)) {
1887 return -1;
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);
1894 if (conv == NULL) {
1895 return -1;
1897 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1898 saved_errno = errno;
1899 TALLOC_FREE(conv);
1900 errno = saved_errno;
1901 return ret;
1904 static int snapper_gmt_get_real_filename(struct vfs_handle_struct *handle,
1905 const char *path,
1906 const char *name,
1907 TALLOC_CTX *mem_ctx,
1908 char **found_name)
1910 time_t timestamp;
1911 char *stripped;
1912 ssize_t ret;
1913 int saved_errno;
1914 char *conv;
1916 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1917 &timestamp, &stripped)) {
1918 return -1;
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) {
1927 errno = ENOMEM;
1928 return -1;
1930 return 0;
1932 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1933 TALLOC_FREE(stripped);
1934 if (conv == NULL) {
1935 return -1;
1937 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1938 mem_ctx, found_name);
1939 saved_errno = errno;
1940 TALLOC_FREE(conv);
1941 errno = saved_errno;
1942 return ret;
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,
1948 uint64_t *dsize)
1950 time_t timestamp;
1951 char *stripped;
1952 ssize_t ret;
1953 int saved_errno;
1954 char *conv;
1956 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1957 &timestamp, &stripped)) {
1958 return -1;
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);
1967 if (conv == NULL) {
1968 return -1;
1971 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1972 dsize);
1974 saved_errno = errno;
1975 TALLOC_FREE(conv);
1976 errno = saved_errno;
1978 return ret;
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);