testprogs: Set functional domain level to 2003.
[Samba.git] / source3 / modules / vfs_snapper.c
blob12a3b9a16eb2fed00e49efc40ab18472df8881a1
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 #ifdef HAVE_LINUX_IOCTL_H
29 #include <linux/ioctl.h>
30 #endif
31 #include <sys/ioctl.h>
32 #include <dirent.h>
33 #include <libgen.h>
34 #include "includes.h"
35 #include "include/ntioctl.h"
36 #include "include/smb.h"
37 #include "system/filesys.h"
38 #include "smbd/smbd.h"
39 #include "lib/util/tevent_ntstatus.h"
41 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uquxussa{ss})"
42 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
43 #define SNAPPER_SIG_STRING_DICT "{ss}"
45 struct snapper_dict {
46 char *key;
47 char *val;
50 struct snapper_snap {
51 uint32_t id;
52 uint16_t type;
53 uint32_t pre_id;
54 int64_t time;
55 uint32_t creator_uid;
56 char *desc;
57 char *cleanup;
58 uint32_t num_user_data;
59 struct snapper_dict *user_data;
62 struct snapper_conf {
63 char *name;
64 char *mnt;
65 uint32_t num_attrs;
66 struct snapper_dict *attrs;
69 static const struct {
70 const char *snapper_err_str;
71 NTSTATUS status;
72 } snapper_err_map[] = {
73 { "error.no_permissions", NT_STATUS_ACCESS_DENIED },
76 static NTSTATUS snapper_err_ntstatus_map(const char *snapper_err_str)
78 int i;
80 if (snapper_err_str == NULL) {
81 return NT_STATUS_UNSUCCESSFUL;
83 for (i = 0; i < ARRAY_SIZE(snapper_err_map); i++) {
84 if (!strcmp(snapper_err_map[i].snapper_err_str,
85 snapper_err_str)) {
86 return snapper_err_map[i].status;
89 DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str));
91 return NT_STATUS_UNSUCCESSFUL;
94 static DBusConnection *snapper_dbus_conn_create(void)
96 DBusError err;
97 DBusConnection *dconn;
99 dbus_error_init(&err);
102 * Always create a new DBus connection, to ensure snapperd detects the
103 * correct client [E]UID. With dbus_bus_get() it does not!
105 dconn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
106 if (dbus_error_is_set(&err)) {
107 DEBUG(0, ("dbus connection error: %s\n", err.message));
108 dbus_error_free(&err);
110 if (dconn == NULL) {
111 return NULL;
114 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
115 dbus_connection_set_exit_on_disconnect(dconn, false);
117 return dconn;
120 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
122 if (dconn == NULL) {
123 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
124 return;
127 dbus_connection_close(dconn);
128 dbus_connection_unref(dconn);
132 * send the message @send_msg over the dbus and wait for a response, return the
133 * responsee via @recv_msg_out.
134 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
136 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
137 DBusMessage *send_msg,
138 DBusMessage **recv_msg_out)
140 DBusPendingCall *pending;
141 DBusMessage *recv_msg;
143 /* send message and get a handle for a reply */
144 if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
145 return NT_STATUS_NO_MEMORY;
147 if (NULL == pending) {
148 DEBUG(0, ("dbus msg send failed\n"));
149 return NT_STATUS_UNSUCCESSFUL;
152 dbus_connection_flush(dconn);
154 /* block until we receive a reply */
155 dbus_pending_call_block(pending);
157 /* get the reply message */
158 recv_msg = dbus_pending_call_steal_reply(pending);
159 if (recv_msg == NULL) {
160 DEBUG(0, ("Reply Null\n"));
161 return NT_STATUS_UNSUCCESSFUL;
163 /* free the pending message handle */
164 dbus_pending_call_unref(pending);
165 *recv_msg_out = recv_msg;
167 return NT_STATUS_OK;
170 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
171 int expected_type)
173 int type = dbus_message_iter_get_arg_type(iter);
174 if (type != expected_type) {
175 DEBUG(0, ("got type %d, expecting %d\n",
176 type, expected_type));
177 return NT_STATUS_INVALID_PARAMETER;
180 return NT_STATUS_OK;
183 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
184 int expected_type,
185 void *val)
187 NTSTATUS status;
188 status = snapper_type_check(iter, expected_type);
189 if (!NT_STATUS_IS_OK(status)) {
190 return status;
193 dbus_message_iter_get_basic(iter, val);
195 return NT_STATUS_OK;
198 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
199 struct snapper_dict *dict_out)
202 NTSTATUS status;
203 DBusMessageIter dct_iter;
205 status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
206 if (!NT_STATUS_IS_OK(status)) {
207 return status;
209 dbus_message_iter_recurse(iter, &dct_iter);
211 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
212 &dict_out->key);
213 if (!NT_STATUS_IS_OK(status)) {
214 return status;
217 dbus_message_iter_next(&dct_iter);
218 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
219 &dict_out->val);
220 if (!NT_STATUS_IS_OK(status)) {
221 return status;
224 return NT_STATUS_OK;
227 static void snapper_dict_array_print(uint32_t num_dicts,
228 struct snapper_dict *dicts)
230 int i;
232 for (i = 0; i < num_dicts; i++) {
233 DEBUG(10, ("dict (key: %s, val: %s)\n",
234 dicts[i].key, dicts[i].val));
238 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
239 DBusMessageIter *iter,
240 uint32_t *num_dicts_out,
241 struct snapper_dict **dicts_out)
243 NTSTATUS status;
244 DBusMessageIter array_iter;
245 uint32_t num_dicts;
246 struct snapper_dict *dicts = NULL;
248 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
249 if (!NT_STATUS_IS_OK(status)) {
250 return status;
252 dbus_message_iter_recurse(iter, &array_iter);
254 num_dicts = 0;
255 while (dbus_message_iter_get_arg_type(&array_iter)
256 != DBUS_TYPE_INVALID) {
257 num_dicts++;
258 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
259 num_dicts);
260 if (dicts == NULL)
261 abort();
263 status = snapper_dict_unpack(&array_iter,
264 &dicts[num_dicts - 1]);
265 if (!NT_STATUS_IS_OK(status)) {
266 talloc_free(dicts);
267 return status;
269 dbus_message_iter_next(&array_iter);
272 *num_dicts_out = num_dicts;
273 *dicts_out = dicts;
275 return NT_STATUS_OK;
278 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
280 DBusMessage *msg;
282 msg = dbus_message_new_method_call("org.opensuse.Snapper",
283 "/org/opensuse/Snapper",
284 "org.opensuse.Snapper",
285 "ListConfigs");
286 if (msg == NULL) {
287 DEBUG(0, ("null msg\n"));
288 return NT_STATUS_NO_MEMORY;
291 /* no arguments to append */
292 *req_msg_out = msg;
294 return NT_STATUS_OK;
297 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
298 DBusMessageIter *iter,
299 struct snapper_conf *conf_out)
301 NTSTATUS status;
302 DBusMessageIter st_iter;
304 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
305 if (!NT_STATUS_IS_OK(status)) {
306 return status;
308 dbus_message_iter_recurse(iter, &st_iter);
310 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
311 &conf_out->name);
312 if (!NT_STATUS_IS_OK(status)) {
313 return status;
316 dbus_message_iter_next(&st_iter);
317 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
318 &conf_out->mnt);
319 if (!NT_STATUS_IS_OK(status)) {
320 return status;
323 dbus_message_iter_next(&st_iter);
324 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
325 &conf_out->num_attrs,
326 &conf_out->attrs);
328 return status;
331 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
332 struct snapper_conf *confs,
333 const char *base)
335 int i;
337 for (i = 0; i < num_confs; i++) {
338 if (strcmp(confs[i].mnt, base) == 0) {
339 DEBUG(5, ("found snapper conf %s for path %s\n",
340 confs[i].name, base));
341 return &confs[i];
344 DEBUG(5, ("config for base %s not found\n", base));
346 return NULL;
349 static void snapper_conf_array_print(int32_t num_confs,
350 struct snapper_conf *confs)
352 int i;
354 for (i = 0; i < num_confs; i++) {
355 DEBUG(10, ("name: %s, mnt: %s\n",
356 confs[i].name, confs[i].mnt));
357 snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
361 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
362 DBusMessageIter *iter,
363 uint32_t *num_confs_out,
364 struct snapper_conf **confs_out)
366 uint32_t num_confs;
367 NTSTATUS status;
368 struct snapper_conf *confs = NULL;
369 DBusMessageIter array_iter;
372 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
373 if (!NT_STATUS_IS_OK(status)) {
374 return status;
376 dbus_message_iter_recurse(iter, &array_iter);
378 num_confs = 0;
379 while (dbus_message_iter_get_arg_type(&array_iter)
380 != DBUS_TYPE_INVALID) {
381 num_confs++;
382 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
383 num_confs);
384 if (confs == NULL)
385 abort();
387 status = snapper_conf_unpack(confs, &array_iter,
388 &confs[num_confs - 1]);
389 if (!NT_STATUS_IS_OK(status)) {
390 talloc_free(confs);
391 return status;
393 dbus_message_iter_next(&array_iter);
396 *num_confs_out = num_confs;
397 *confs_out = confs;
399 return NT_STATUS_OK;
402 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
403 DBusConnection *dconn,
404 DBusMessage *rsp_msg,
405 uint32_t *num_confs_out,
406 struct snapper_conf **confs_out)
408 NTSTATUS status;
409 DBusMessageIter iter;
410 int msg_type;
411 uint32_t num_confs;
412 struct snapper_conf *confs;
413 const char *sig;
415 msg_type = dbus_message_get_type(rsp_msg);
416 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
417 const char *err_str = dbus_message_get_error_name(rsp_msg);
418 DEBUG(0, ("list_confs error response: %s\n", err_str));
419 return snapper_err_ntstatus_map(err_str);
422 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
423 DEBUG(0, ("unexpected list_confs ret type: %d\n",
424 msg_type));
425 return NT_STATUS_INVALID_PARAMETER;
428 sig = dbus_message_get_signature(rsp_msg);
429 if ((sig == NULL)
430 || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
431 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
432 (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
433 return NT_STATUS_INVALID_PARAMETER;
436 if (!dbus_message_iter_init(rsp_msg, &iter)) {
437 /* FIXME return empty? */
438 DEBUG(0, ("Message has no arguments!\n"));
439 return NT_STATUS_INVALID_PARAMETER;
442 status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
443 if (!NT_STATUS_IS_OK(status)) {
444 DEBUG(0, ("failed to unpack conf array\n"));
445 return status;
448 snapper_conf_array_print(num_confs, confs);
450 *num_confs_out = num_confs;
451 *confs_out = confs;
453 return NT_STATUS_OK;
456 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
457 DBusMessage **req_msg_out)
459 DBusMessage *msg;
460 DBusMessageIter args;
462 msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
463 "/org/opensuse/Snapper", /* object to call on */
464 "org.opensuse.Snapper", /* interface to call on */
465 "ListSnapshots"); /* method name */
466 if (msg == NULL) {
467 DEBUG(0, ("failed to create list snaps message\n"));
468 return NT_STATUS_NO_MEMORY;
471 /* append arguments */
472 dbus_message_iter_init_append(msg, &args);
473 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
474 &snapper_conf)) {
475 return NT_STATUS_NO_MEMORY;
478 *req_msg_out = msg;
480 return NT_STATUS_OK;
483 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
484 DBusMessageIter *iter,
485 struct snapper_snap *snap_out)
487 NTSTATUS status;
488 DBusMessageIter st_iter;
490 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
491 if (!NT_STATUS_IS_OK(status)) {
492 return status;
494 dbus_message_iter_recurse(iter, &st_iter);
496 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
497 &snap_out->id);
498 if (!NT_STATUS_IS_OK(status)) {
499 return status;
502 dbus_message_iter_next(&st_iter);
503 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
504 &snap_out->type);
505 if (!NT_STATUS_IS_OK(status)) {
506 return status;
509 dbus_message_iter_next(&st_iter);
510 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
511 &snap_out->pre_id);
512 if (!NT_STATUS_IS_OK(status)) {
513 return status;
516 dbus_message_iter_next(&st_iter);
517 status = snapper_type_check_get(&st_iter, DBUS_TYPE_INT64,
518 &snap_out->time);
519 if (!NT_STATUS_IS_OK(status)) {
520 return status;
523 dbus_message_iter_next(&st_iter);
524 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
525 &snap_out->creator_uid);
526 if (!NT_STATUS_IS_OK(status)) {
527 return status;
530 dbus_message_iter_next(&st_iter);
531 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
532 &snap_out->desc);
533 if (!NT_STATUS_IS_OK(status)) {
534 return status;
537 dbus_message_iter_next(&st_iter);
538 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
539 &snap_out->cleanup);
540 if (!NT_STATUS_IS_OK(status)) {
541 return status;
544 dbus_message_iter_next(&st_iter);
545 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
546 &snap_out->num_user_data,
547 &snap_out->user_data);
549 return status;
552 static void snapper_snap_array_print(int32_t num_snaps,
553 struct snapper_snap *snaps)
555 int i;
557 for (i = 0; i < num_snaps; i++) {
558 DEBUG(10, ("id: %u, "
559 "type: %u, "
560 "pre_id: %u, "
561 "time: %ld, "
562 "creator_uid: %u, "
563 "desc: %s, "
564 "cleanup: %s\n",
565 (unsigned int)snaps[i].id,
566 (unsigned int)snaps[i].type,
567 (unsigned int)snaps[i].pre_id,
568 (long int)snaps[i].time,
569 (unsigned int)snaps[i].creator_uid,
570 snaps[i].desc,
571 snaps[i].cleanup));
572 snapper_dict_array_print(snaps[i].num_user_data,
573 snaps[i].user_data);
577 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
578 DBusMessageIter *iter,
579 uint32_t *num_snaps_out,
580 struct snapper_snap **snaps_out)
582 uint32_t num_snaps;
583 NTSTATUS status;
584 struct snapper_snap *snaps = NULL;
585 DBusMessageIter array_iter;
588 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
589 if (!NT_STATUS_IS_OK(status)) {
590 return status;
592 dbus_message_iter_recurse(iter, &array_iter);
594 num_snaps = 0;
595 while (dbus_message_iter_get_arg_type(&array_iter)
596 != DBUS_TYPE_INVALID) {
597 num_snaps++;
598 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
599 num_snaps);
600 if (snaps == NULL)
601 abort();
603 status = snapper_snap_struct_unpack(snaps, &array_iter,
604 &snaps[num_snaps - 1]);
605 if (!NT_STATUS_IS_OK(status)) {
606 talloc_free(snaps);
607 return status;
609 dbus_message_iter_next(&array_iter);
612 *num_snaps_out = num_snaps;
613 *snaps_out = snaps;
615 return NT_STATUS_OK;
618 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
619 DBusMessage *rsp_msg,
620 uint32_t *num_snaps_out,
621 struct snapper_snap **snaps_out)
623 NTSTATUS status;
624 DBusMessageIter iter;
625 int msg_type;
626 uint32_t num_snaps;
627 struct snapper_snap *snaps;
628 const char *sig;
630 msg_type = dbus_message_get_type(rsp_msg);
631 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
632 const char *err_str = dbus_message_get_error_name(rsp_msg);
633 DEBUG(0, ("list_snaps error response: %s\n", err_str));
634 return snapper_err_ntstatus_map(err_str);
637 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
638 DEBUG(0,("unexpected list_snaps ret type: %d\n",
639 msg_type));
640 return NT_STATUS_INVALID_PARAMETER;
643 sig = dbus_message_get_signature(rsp_msg);
644 if ((sig == NULL)
645 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
646 DEBUG(0, ("bad list snaps response sig: %s, "
647 "expected: %s\n",
648 (sig ? sig : "NULL"),
649 SNAPPER_SIG_LIST_SNAPS_RSP));
650 return NT_STATUS_INVALID_PARAMETER;
653 /* read the parameters */
654 if (!dbus_message_iter_init(rsp_msg, &iter)) {
655 DEBUG(0, ("response has no arguments!\n"));
656 return NT_STATUS_INVALID_PARAMETER;
659 status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
660 if (!NT_STATUS_IS_OK(status)) {
661 DEBUG(0, ("failed to unpack snap array\n"));
662 return NT_STATUS_INVALID_PARAMETER;
665 snapper_snap_array_print(num_snaps, snaps);
667 *num_snaps_out = num_snaps;
668 *snaps_out = snaps;
670 return NT_STATUS_OK;
673 static NTSTATUS snapper_list_snaps_at_time_pack(const char *snapper_conf,
674 time_t time_lower,
675 time_t time_upper,
676 DBusMessage **req_msg_out)
678 DBusMessage *msg;
679 DBusMessageIter args;
681 msg = dbus_message_new_method_call("org.opensuse.Snapper",
682 "/org/opensuse/Snapper",
683 "org.opensuse.Snapper",
684 "ListSnapshotsAtTime");
685 if (msg == NULL) {
686 DEBUG(0, ("failed to create list snaps message\n"));
687 return NT_STATUS_NO_MEMORY;
690 dbus_message_iter_init_append(msg, &args);
691 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
692 &snapper_conf)) {
693 return NT_STATUS_NO_MEMORY;
696 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
697 &time_lower)) {
698 return NT_STATUS_NO_MEMORY;
701 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
702 &time_upper)) {
703 return NT_STATUS_NO_MEMORY;
706 *req_msg_out = msg;
708 return NT_STATUS_OK;
710 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
713 * Determine the snapper snapshot path given an id and base.
714 * Ideally this should be determined via a lookup.
716 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
717 const char *base_path,
718 uint32_t snap_id,
719 char **snap_path_out)
721 char *snap_path;
723 snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
724 base_path, snap_id);
725 if (snap_path == NULL) {
726 return NT_STATUS_NO_MEMORY;
729 *snap_path_out = snap_path;
730 return NT_STATUS_OK;
733 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
734 DBusConnection *dconn,
735 const char *path,
736 char **conf_name_out,
737 char **base_path_out)
739 NTSTATUS status;
740 DBusMessage *req_msg;
741 DBusMessage *rsp_msg;
742 uint32_t num_confs = 0;
743 struct snapper_conf *confs = NULL;
744 struct snapper_conf *conf;
745 char *conf_name;
746 char *base_path;
748 status = snapper_list_confs_pack(&req_msg);
749 if (!NT_STATUS_IS_OK(status)) {
750 goto err_out;
753 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
754 if (!NT_STATUS_IS_OK(status)) {
755 goto err_req_free;
758 status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
759 &num_confs, &confs);
760 if (!NT_STATUS_IS_OK(status)) {
761 goto err_rsp_free;
765 * for now we only support shares where the path directly corresponds
766 * to a snapper configuration.
768 conf = snapper_conf_array_base_find(num_confs, confs,
769 path);
770 if (conf == NULL) {
771 status = NT_STATUS_NOT_SUPPORTED;
772 goto err_array_free;
775 conf_name = talloc_strdup(mem_ctx, conf->name);
776 if (conf_name == NULL) {
777 status = NT_STATUS_NO_MEMORY;
778 goto err_array_free;
780 base_path = talloc_strdup(mem_ctx, conf->mnt);
781 if (base_path == NULL) {
782 status = NT_STATUS_NO_MEMORY;
783 goto err_conf_name_free;
786 talloc_free(confs);
787 dbus_message_unref(rsp_msg);
788 dbus_message_unref(req_msg);
790 *conf_name_out = conf_name;
791 *base_path_out = base_path;
793 return NT_STATUS_OK;
795 err_conf_name_free:
796 talloc_free(conf_name);
797 err_array_free:
798 talloc_free(confs);
799 err_rsp_free:
800 dbus_message_unref(rsp_msg);
801 err_req_free:
802 dbus_message_unref(req_msg);
803 err_out:
804 return status;
807 /* sc_data used as parent talloc context for all labels */
808 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
809 struct files_struct *fsp,
810 struct shadow_copy_data *sc_data,
811 bool labels)
813 DBusConnection *dconn;
814 TALLOC_CTX *tmp_ctx;
815 NTSTATUS status;
816 char *conf_name;
817 char *base_path;
818 DBusMessage *req_msg;
819 DBusMessage *rsp_msg;
820 uint32_t num_snaps;
821 struct snapper_snap *snaps;
822 uint32_t i;
823 uint32_t lbl_off;
825 tmp_ctx = talloc_new(sc_data);
826 if (tmp_ctx == NULL) {
827 status = NT_STATUS_NO_MEMORY;
828 goto err_out;
831 dconn = snapper_dbus_conn_create();
832 if (dconn == NULL) {
833 status = NT_STATUS_UNSUCCESSFUL;
834 goto err_mem_ctx_free;
837 if (fsp->conn->connectpath == NULL) {
838 status = NT_STATUS_INVALID_PARAMETER;
839 goto err_conn_free;
842 status = snapper_get_conf_call(tmp_ctx, dconn,
843 fsp->conn->connectpath,
844 &conf_name,
845 &base_path);
846 if (!NT_STATUS_IS_OK(status)) {
847 goto err_conn_free;
850 status = snapper_list_snaps_pack(conf_name, &req_msg);
851 if (!NT_STATUS_IS_OK(status)) {
852 goto err_conn_free;
855 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
856 if (!NT_STATUS_IS_OK(status)) {
857 goto err_req_free;
860 status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
861 &num_snaps, &snaps);
862 if (!NT_STATUS_IS_OK(status)) {
863 goto err_rsp_free;
865 /* we should always get at least one snapshot (current) */
866 if (num_snaps == 0) {
867 DEBUG(1, ("zero snapshots in snap list response\n"));
868 status = NT_STATUS_UNSUCCESSFUL;
869 goto err_rsp_free;
872 /* subtract 1, (current) snapshot is not returned */
873 sc_data->num_volumes = num_snaps - 1;
874 sc_data->labels = NULL;
876 if ((labels == false) || (sc_data->num_volumes == 0)) {
877 /* tokens need not be added to the labels array */
878 goto done;
881 sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
882 sc_data->num_volumes);
883 if (sc_data->labels == NULL) {
884 status = NT_STATUS_NO_MEMORY;
885 goto err_rsp_free;
888 /* start at end for decending order, do not include 0 (current) */
889 lbl_off = 0;
890 for (i = num_snaps - 1; i > 0; i--) {
891 char *lbl = sc_data->labels[lbl_off++];
892 struct tm gmt_snap_time;
893 struct tm *tm_ret;
894 size_t str_sz;
896 tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
897 if (tm_ret == NULL) {
898 status = NT_STATUS_UNSUCCESSFUL;
899 goto err_labels_free;
901 str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
902 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
903 if (str_sz == 0) {
904 status = NT_STATUS_UNSUCCESSFUL;
905 goto err_labels_free;
909 done:
910 talloc_free(tmp_ctx);
911 dbus_message_unref(rsp_msg);
912 dbus_message_unref(req_msg);
913 snapper_dbus_conn_destroy(dconn);
915 return 0;
917 err_labels_free:
918 TALLOC_FREE(sc_data->labels);
919 err_rsp_free:
920 dbus_message_unref(rsp_msg);
921 err_req_free:
922 dbus_message_unref(req_msg);
923 err_conn_free:
924 snapper_dbus_conn_destroy(dconn);
925 err_mem_ctx_free:
926 talloc_free(tmp_ctx);
927 err_out:
928 errno = map_errno_from_nt_status(status);
929 return -1;
932 static bool snapper_gmt_strip_snapshot(TALLOC_CTX *mem_ctx,
933 struct vfs_handle_struct *handle,
934 const char *name,
935 time_t *ptimestamp,
936 char **pstripped)
938 struct tm tm;
939 time_t timestamp;
940 const char *p;
941 char *q;
942 char *stripped;
943 size_t rest_len, dst_len;
945 p = strstr_m(name, "@GMT-");
946 if (p == NULL) {
947 goto no_snapshot;
949 if ((p > name) && (p[-1] != '/')) {
950 goto no_snapshot;
952 q = strptime(p, GMT_FORMAT, &tm);
953 if (q == NULL) {
954 goto no_snapshot;
956 tm.tm_isdst = -1;
957 timestamp = timegm(&tm);
958 if (timestamp == (time_t)-1) {
959 goto no_snapshot;
961 if ((p == name) && (q[0] == '\0')) {
962 if (pstripped != NULL) {
963 stripped = talloc_strdup(mem_ctx, "");
964 if (stripped == NULL) {
965 return false;
967 *pstripped = stripped;
969 *ptimestamp = timestamp;
970 return true;
972 if (q[0] != '/') {
973 goto no_snapshot;
975 q += 1;
977 rest_len = strlen(q);
978 dst_len = (p-name) + rest_len;
980 if (pstripped != NULL) {
981 stripped = talloc_array(mem_ctx, char, dst_len+1);
982 if (stripped == NULL) {
983 errno = ENOMEM;
984 return false;
986 if (p > name) {
987 memcpy(stripped, name, p-name);
989 if (rest_len > 0) {
990 memcpy(stripped + (p-name), q, rest_len);
992 stripped[dst_len] = '\0';
993 *pstripped = stripped;
995 *ptimestamp = timestamp;
996 return true;
997 no_snapshot:
998 *ptimestamp = 0;
999 return true;
1002 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1003 DBusConnection *dconn,
1004 const char *conf_name,
1005 const char *base_path,
1006 time_t snaptime,
1007 char **snap_path_out)
1009 NTSTATUS status;
1010 DBusMessage *req_msg;
1011 DBusMessage *rsp_msg;
1012 uint32_t num_snaps;
1013 struct snapper_snap *snaps;
1014 char *snap_path;
1016 status = snapper_list_snaps_at_time_pack(conf_name,
1017 snaptime,
1018 snaptime,
1019 &req_msg);
1020 if (!NT_STATUS_IS_OK(status)) {
1021 goto err_out;
1024 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1025 if (!NT_STATUS_IS_OK(status)) {
1026 goto err_req_free;
1029 status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1030 &num_snaps, &snaps);
1031 if (!NT_STATUS_IS_OK(status)) {
1032 goto err_rsp_free;
1035 if (num_snaps == 0) {
1036 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime));
1037 status = NT_STATUS_INVALID_PARAMETER;
1038 goto err_snap_array_free;
1039 } else if (num_snaps > 0) {
1040 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1041 num_snaps, snaptime));
1044 status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1045 &snap_path);
1046 if (!NT_STATUS_IS_OK(status)) {
1047 goto err_snap_array_free;
1050 *snap_path_out = snap_path;
1051 err_snap_array_free:
1052 talloc_free(snaps);
1053 err_rsp_free:
1054 dbus_message_unref(rsp_msg);
1055 err_req_free:
1056 dbus_message_unref(req_msg);
1057 err_out:
1058 return status;
1061 static NTSTATUS snapper_snap_path_expand(struct connection_struct *conn,
1062 TALLOC_CTX *mem_ctx,
1063 time_t snap_time,
1064 char **snap_dir_out)
1066 DBusConnection *dconn;
1067 NTSTATUS status;
1068 char *conf_name;
1069 char *base_path;
1070 char *snap_path;
1072 dconn = snapper_dbus_conn_create();
1073 if (dconn == NULL) {
1074 status = NT_STATUS_UNSUCCESSFUL;
1075 goto err_out;
1078 if (conn->connectpath == NULL) {
1079 status = NT_STATUS_INVALID_PARAMETER;
1080 goto err_conn_free;
1083 status = snapper_get_conf_call(mem_ctx, dconn,
1084 conn->connectpath,
1085 &conf_name,
1086 &base_path);
1087 if (!NT_STATUS_IS_OK(status)) {
1088 goto err_conn_free;
1091 status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1092 conf_name, base_path, snap_time,
1093 &snap_path);
1094 if (!NT_STATUS_IS_OK(status)) {
1095 goto err_conf_name_free;
1098 /* confirm snapshot path is nested under base path */
1099 if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1100 status = NT_STATUS_INVALID_PARAMETER;
1101 goto err_snap_path_free;
1104 talloc_free(conf_name);
1105 talloc_free(base_path);
1106 snapper_dbus_conn_destroy(dconn);
1107 *snap_dir_out = snap_path;
1109 return NT_STATUS_OK;
1111 err_snap_path_free:
1112 talloc_free(snap_path);
1113 err_conf_name_free:
1114 talloc_free(conf_name);
1115 talloc_free(base_path);
1116 err_conn_free:
1117 snapper_dbus_conn_destroy(dconn);
1118 err_out:
1119 return status;
1122 static char *snapper_gmt_convert(TALLOC_CTX *mem_ctx,
1123 struct vfs_handle_struct *handle,
1124 const char *name, time_t timestamp)
1126 char *snap_path = NULL;
1127 char *path = NULL;
1128 NTSTATUS status;
1129 int saved_errno;
1131 status = snapper_snap_path_expand(handle->conn, mem_ctx, timestamp,
1132 &snap_path);
1133 if (!NT_STATUS_IS_OK(status)) {
1134 errno = map_errno_from_nt_status(status);
1135 goto err_out;
1138 path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1139 if (path == NULL) {
1140 errno = ENOMEM;
1141 goto err_snap_path_free;
1144 DEBUG(10, ("converted %s/%s @ time to %s\n",
1145 handle->conn->connectpath, name, path));
1146 return path;
1148 err_snap_path_free:
1149 saved_errno = errno;
1150 talloc_free(snap_path);
1151 errno = saved_errno;
1152 err_out:
1153 return NULL;
1156 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1157 const char *fname,
1158 const char *mask,
1159 uint32_t attr)
1161 time_t timestamp;
1162 char *stripped;
1163 DIR *ret;
1164 int saved_errno;
1165 char *conv;
1167 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1168 &timestamp, &stripped)) {
1169 return NULL;
1171 if (timestamp == 0) {
1172 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
1174 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1175 TALLOC_FREE(stripped);
1176 if (conv == NULL) {
1177 return NULL;
1179 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
1180 saved_errno = errno;
1181 TALLOC_FREE(conv);
1182 errno = saved_errno;
1183 return ret;
1186 static int snapper_gmt_rename(vfs_handle_struct *handle,
1187 const struct smb_filename *smb_fname_src,
1188 const struct smb_filename *smb_fname_dst)
1190 time_t timestamp_src, timestamp_dst;
1192 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1193 smb_fname_src->base_name,
1194 &timestamp_src, NULL)) {
1195 return -1;
1197 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1198 smb_fname_dst->base_name,
1199 &timestamp_dst, NULL)) {
1200 return -1;
1202 if (timestamp_src != 0) {
1203 errno = EXDEV;
1204 return -1;
1206 if (timestamp_dst != 0) {
1207 errno = EROFS;
1208 return -1;
1210 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1213 static int snapper_gmt_symlink(vfs_handle_struct *handle,
1214 const char *oldname, const char *newname)
1216 time_t timestamp_old, timestamp_new;
1218 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1219 &timestamp_old, NULL)) {
1220 return -1;
1222 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1223 &timestamp_new, NULL)) {
1224 return -1;
1226 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1227 errno = EROFS;
1228 return -1;
1230 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
1233 static int snapper_gmt_link(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 &timestamp_old, NULL)) {
1240 return -1;
1242 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1243 &timestamp_new, NULL)) {
1244 return -1;
1246 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1247 errno = EROFS;
1248 return -1;
1250 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
1253 static int snapper_gmt_stat(vfs_handle_struct *handle,
1254 struct smb_filename *smb_fname)
1256 time_t timestamp;
1257 char *stripped, *tmp;
1258 int ret, saved_errno;
1260 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1261 smb_fname->base_name,
1262 &timestamp, &stripped)) {
1263 return -1;
1265 if (timestamp == 0) {
1266 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1269 tmp = smb_fname->base_name;
1270 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1271 stripped, timestamp);
1272 TALLOC_FREE(stripped);
1274 if (smb_fname->base_name == NULL) {
1275 smb_fname->base_name = tmp;
1276 return -1;
1279 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1280 saved_errno = errno;
1282 TALLOC_FREE(smb_fname->base_name);
1283 smb_fname->base_name = tmp;
1285 errno = saved_errno;
1286 return ret;
1289 static int snapper_gmt_lstat(vfs_handle_struct *handle,
1290 struct smb_filename *smb_fname)
1292 time_t timestamp;
1293 char *stripped, *tmp;
1294 int ret, saved_errno;
1296 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1297 smb_fname->base_name,
1298 &timestamp, &stripped)) {
1299 return -1;
1301 if (timestamp == 0) {
1302 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1305 tmp = smb_fname->base_name;
1306 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1307 stripped, timestamp);
1308 TALLOC_FREE(stripped);
1310 if (smb_fname->base_name == NULL) {
1311 smb_fname->base_name = tmp;
1312 return -1;
1315 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1316 saved_errno = errno;
1318 TALLOC_FREE(smb_fname->base_name);
1319 smb_fname->base_name = tmp;
1321 errno = saved_errno;
1322 return ret;
1325 static int snapper_gmt_fstat(vfs_handle_struct *handle, files_struct *fsp,
1326 SMB_STRUCT_STAT *sbuf)
1328 time_t timestamp;
1329 int ret;
1331 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1332 if (ret == -1) {
1333 return ret;
1335 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1336 fsp->fsp_name->base_name,
1337 &timestamp, NULL)) {
1338 return 0;
1340 return 0;
1343 static int snapper_gmt_open(vfs_handle_struct *handle,
1344 struct smb_filename *smb_fname, files_struct *fsp,
1345 int flags, mode_t mode)
1347 time_t timestamp;
1348 char *stripped, *tmp;
1349 int ret, saved_errno;
1351 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1352 smb_fname->base_name,
1353 &timestamp, &stripped)) {
1354 return -1;
1356 if (timestamp == 0) {
1357 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1360 tmp = smb_fname->base_name;
1361 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1362 stripped, timestamp);
1363 TALLOC_FREE(stripped);
1365 if (smb_fname->base_name == NULL) {
1366 smb_fname->base_name = tmp;
1367 return -1;
1370 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1371 saved_errno = errno;
1373 TALLOC_FREE(smb_fname->base_name);
1374 smb_fname->base_name = tmp;
1376 errno = saved_errno;
1377 return ret;
1380 static int snapper_gmt_unlink(vfs_handle_struct *handle,
1381 const struct smb_filename *smb_fname)
1383 time_t timestamp;
1384 char *stripped;
1385 int ret, saved_errno;
1386 struct smb_filename *conv;
1388 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1389 smb_fname->base_name,
1390 &timestamp, &stripped)) {
1391 return -1;
1393 if (timestamp == 0) {
1394 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1396 conv = cp_smb_filename(talloc_tos(), smb_fname);
1397 if (conv == NULL) {
1398 errno = ENOMEM;
1399 return -1;
1401 conv->base_name = snapper_gmt_convert(conv, handle,
1402 stripped, timestamp);
1403 TALLOC_FREE(stripped);
1404 if (conv->base_name == NULL) {
1405 return -1;
1407 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1408 saved_errno = errno;
1409 TALLOC_FREE(conv);
1410 errno = saved_errno;
1411 return ret;
1414 static int snapper_gmt_chmod(vfs_handle_struct *handle, const char *fname,
1415 mode_t mode)
1417 time_t timestamp;
1418 char *stripped;
1419 int ret, saved_errno;
1420 char *conv;
1422 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1423 &timestamp, &stripped)) {
1424 return -1;
1426 if (timestamp == 0) {
1427 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
1429 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1430 TALLOC_FREE(stripped);
1431 if (conv == NULL) {
1432 return -1;
1434 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1435 saved_errno = errno;
1436 TALLOC_FREE(conv);
1437 errno = saved_errno;
1438 return ret;
1441 static int snapper_gmt_chown(vfs_handle_struct *handle, const char *fname,
1442 uid_t uid, gid_t gid)
1444 time_t timestamp;
1445 char *stripped;
1446 int ret, saved_errno;
1447 char *conv;
1449 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1450 &timestamp, &stripped)) {
1451 return -1;
1453 if (timestamp == 0) {
1454 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
1456 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1457 TALLOC_FREE(stripped);
1458 if (conv == NULL) {
1459 return -1;
1461 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1462 saved_errno = errno;
1463 TALLOC_FREE(conv);
1464 errno = saved_errno;
1465 return ret;
1468 static int snapper_gmt_chdir(vfs_handle_struct *handle,
1469 const char *fname)
1471 time_t timestamp;
1472 char *stripped;
1473 int ret, saved_errno;
1474 char *conv;
1476 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1477 &timestamp, &stripped)) {
1478 return -1;
1480 if (timestamp == 0) {
1481 return SMB_VFS_NEXT_CHDIR(handle, fname);
1483 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1484 TALLOC_FREE(stripped);
1485 if (conv == NULL) {
1486 return -1;
1488 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1489 saved_errno = errno;
1490 TALLOC_FREE(conv);
1491 errno = saved_errno;
1492 return ret;
1495 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
1496 const struct smb_filename *smb_fname,
1497 struct smb_file_time *ft)
1499 time_t timestamp;
1500 char *stripped;
1501 int ret, saved_errno;
1502 struct smb_filename *conv;
1504 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1505 smb_fname->base_name,
1506 &timestamp, &stripped)) {
1507 return -1;
1509 if (timestamp == 0) {
1510 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1512 conv = cp_smb_filename(talloc_tos(), smb_fname);
1513 if (conv == NULL) {
1514 errno = ENOMEM;
1515 return -1;
1517 conv->base_name = snapper_gmt_convert(conv, handle,
1518 stripped, timestamp);
1519 TALLOC_FREE(stripped);
1520 if (conv->base_name == NULL) {
1521 return -1;
1523 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1524 saved_errno = errno;
1525 TALLOC_FREE(conv);
1526 errno = saved_errno;
1527 return ret;
1530 static int snapper_gmt_readlink(vfs_handle_struct *handle,
1531 const char *fname, char *buf, size_t bufsiz)
1533 time_t timestamp;
1534 char *stripped;
1535 int ret, saved_errno;
1536 char *conv;
1538 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1539 &timestamp, &stripped)) {
1540 return -1;
1542 if (timestamp == 0) {
1543 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1545 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1546 TALLOC_FREE(stripped);
1547 if (conv == NULL) {
1548 return -1;
1550 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1551 saved_errno = errno;
1552 TALLOC_FREE(conv);
1553 errno = saved_errno;
1554 return ret;
1557 static int snapper_gmt_mknod(vfs_handle_struct *handle,
1558 const char *fname, mode_t mode, SMB_DEV_T dev)
1560 time_t timestamp;
1561 char *stripped;
1562 int ret, saved_errno;
1563 char *conv;
1565 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1566 &timestamp, &stripped)) {
1567 return -1;
1569 if (timestamp == 0) {
1570 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1572 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1573 TALLOC_FREE(stripped);
1574 if (conv == NULL) {
1575 return -1;
1577 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1578 saved_errno = errno;
1579 TALLOC_FREE(conv);
1580 errno = saved_errno;
1581 return ret;
1584 static char *snapper_gmt_realpath(vfs_handle_struct *handle,
1585 const char *fname)
1587 time_t timestamp;
1588 char *stripped = NULL;
1589 char *tmp = NULL;
1590 char *result = NULL;
1591 int saved_errno;
1593 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1594 &timestamp, &stripped)) {
1595 goto done;
1597 if (timestamp == 0) {
1598 return SMB_VFS_NEXT_REALPATH(handle, fname);
1601 tmp = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1602 if (tmp == NULL) {
1603 goto done;
1606 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1607 if (result == NULL) {
1608 goto done;
1611 done:
1612 saved_errno = errno;
1613 TALLOC_FREE(tmp);
1614 TALLOC_FREE(stripped);
1615 errno = saved_errno;
1616 return result;
1619 static NTSTATUS snapper_gmt_fget_nt_acl(vfs_handle_struct *handle,
1620 struct files_struct *fsp,
1621 uint32 security_info,
1622 TALLOC_CTX *mem_ctx,
1623 struct security_descriptor **ppdesc)
1625 time_t timestamp;
1626 char *stripped;
1627 NTSTATUS status;
1628 char *conv;
1630 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1631 fsp->fsp_name->base_name,
1632 &timestamp, &stripped)) {
1633 return map_nt_error_from_unix(errno);
1635 if (timestamp == 0) {
1636 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1637 mem_ctx,
1638 ppdesc);
1640 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1641 TALLOC_FREE(stripped);
1642 if (conv == NULL) {
1643 return map_nt_error_from_unix(errno);
1645 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1646 mem_ctx, ppdesc);
1647 TALLOC_FREE(conv);
1648 return status;
1651 static NTSTATUS snapper_gmt_get_nt_acl(vfs_handle_struct *handle,
1652 const char *fname,
1653 uint32 security_info,
1654 TALLOC_CTX *mem_ctx,
1655 struct security_descriptor **ppdesc)
1657 time_t timestamp;
1658 char *stripped;
1659 NTSTATUS status;
1660 char *conv;
1662 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1663 &timestamp, &stripped)) {
1664 return map_nt_error_from_unix(errno);
1666 if (timestamp == 0) {
1667 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1668 mem_ctx, ppdesc);
1670 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1671 TALLOC_FREE(stripped);
1672 if (conv == NULL) {
1673 return map_nt_error_from_unix(errno);
1675 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1676 mem_ctx, ppdesc);
1677 TALLOC_FREE(conv);
1678 return status;
1681 static int snapper_gmt_mkdir(vfs_handle_struct *handle,
1682 const char *fname, mode_t mode)
1684 time_t timestamp;
1685 char *stripped;
1686 int ret, saved_errno;
1687 char *conv;
1689 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1690 &timestamp, &stripped)) {
1691 return -1;
1693 if (timestamp == 0) {
1694 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1696 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1697 TALLOC_FREE(stripped);
1698 if (conv == NULL) {
1699 return -1;
1701 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1702 saved_errno = errno;
1703 TALLOC_FREE(conv);
1704 errno = saved_errno;
1705 return ret;
1708 static int snapper_gmt_rmdir(vfs_handle_struct *handle, const char *fname)
1710 time_t timestamp;
1711 char *stripped;
1712 int ret, saved_errno;
1713 char *conv;
1715 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1716 &timestamp, &stripped)) {
1717 return -1;
1719 if (timestamp == 0) {
1720 return SMB_VFS_NEXT_RMDIR(handle, fname);
1722 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1723 TALLOC_FREE(stripped);
1724 if (conv == NULL) {
1725 return -1;
1727 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1728 saved_errno = errno;
1729 TALLOC_FREE(conv);
1730 errno = saved_errno;
1731 return ret;
1734 static int snapper_gmt_chflags(vfs_handle_struct *handle, const char *fname,
1735 unsigned int flags)
1737 time_t timestamp;
1738 char *stripped;
1739 int ret, saved_errno;
1740 char *conv;
1742 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1743 &timestamp, &stripped)) {
1744 return -1;
1746 if (timestamp == 0) {
1747 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1749 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1750 TALLOC_FREE(stripped);
1751 if (conv == NULL) {
1752 return -1;
1754 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1755 saved_errno = errno;
1756 TALLOC_FREE(conv);
1757 errno = saved_errno;
1758 return ret;
1761 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
1762 const char *fname, const char *aname,
1763 void *value, size_t size)
1765 time_t timestamp;
1766 char *stripped;
1767 ssize_t ret;
1768 int saved_errno;
1769 char *conv;
1771 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1772 &timestamp, &stripped)) {
1773 return -1;
1775 if (timestamp == 0) {
1776 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1777 size);
1779 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1780 TALLOC_FREE(stripped);
1781 if (conv == NULL) {
1782 return -1;
1784 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1785 saved_errno = errno;
1786 TALLOC_FREE(conv);
1787 errno = saved_errno;
1788 return ret;
1791 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
1792 const char *fname,
1793 char *list, size_t size)
1795 time_t timestamp;
1796 char *stripped;
1797 ssize_t ret;
1798 int saved_errno;
1799 char *conv;
1801 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1802 &timestamp, &stripped)) {
1803 return -1;
1805 if (timestamp == 0) {
1806 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1808 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1809 TALLOC_FREE(stripped);
1810 if (conv == NULL) {
1811 return -1;
1813 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
1814 saved_errno = errno;
1815 TALLOC_FREE(conv);
1816 errno = saved_errno;
1817 return ret;
1820 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
1821 const char *fname, const char *aname)
1823 time_t timestamp;
1824 char *stripped;
1825 int ret, saved_errno;
1826 char *conv;
1828 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1829 &timestamp, &stripped)) {
1830 return -1;
1832 if (timestamp == 0) {
1833 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1835 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1836 TALLOC_FREE(stripped);
1837 if (conv == NULL) {
1838 return -1;
1840 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1841 saved_errno = errno;
1842 TALLOC_FREE(conv);
1843 errno = saved_errno;
1844 return ret;
1847 static int snapper_gmt_setxattr(struct vfs_handle_struct *handle,
1848 const char *fname,
1849 const char *aname, const void *value,
1850 size_t size, int flags)
1852 time_t timestamp;
1853 char *stripped;
1854 ssize_t ret;
1855 int saved_errno;
1856 char *conv;
1858 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1859 &timestamp, &stripped)) {
1860 return -1;
1862 if (timestamp == 0) {
1863 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1864 flags);
1866 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1867 TALLOC_FREE(stripped);
1868 if (conv == NULL) {
1869 return -1;
1871 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
1872 saved_errno = errno;
1873 TALLOC_FREE(conv);
1874 errno = saved_errno;
1875 return ret;
1878 static int snapper_gmt_chmod_acl(vfs_handle_struct *handle,
1879 const char *fname, mode_t mode)
1881 time_t timestamp;
1882 char *stripped;
1883 ssize_t ret;
1884 int saved_errno;
1885 char *conv;
1887 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1888 &timestamp, &stripped)) {
1889 return -1;
1891 if (timestamp == 0) {
1892 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1894 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1895 TALLOC_FREE(stripped);
1896 if (conv == NULL) {
1897 return -1;
1899 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
1900 saved_errno = errno;
1901 TALLOC_FREE(conv);
1902 errno = saved_errno;
1903 return ret;
1906 static int snapper_gmt_get_real_filename(struct vfs_handle_struct *handle,
1907 const char *path,
1908 const char *name,
1909 TALLOC_CTX *mem_ctx,
1910 char **found_name)
1912 time_t timestamp;
1913 char *stripped;
1914 ssize_t ret;
1915 int saved_errno;
1916 char *conv;
1918 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1919 &timestamp, &stripped)) {
1920 return -1;
1922 if (timestamp == 0) {
1923 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1924 mem_ctx, found_name);
1926 if (stripped[0] == '\0') {
1927 *found_name = talloc_strdup(mem_ctx, name);
1928 if (*found_name == NULL) {
1929 errno = ENOMEM;
1930 return -1;
1932 return 0;
1934 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1935 TALLOC_FREE(stripped);
1936 if (conv == NULL) {
1937 return -1;
1939 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
1940 mem_ctx, found_name);
1941 saved_errno = errno;
1942 TALLOC_FREE(conv);
1943 errno = saved_errno;
1944 return ret;
1947 static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
1948 const char *path, bool small_query,
1949 uint64_t *bsize, uint64_t *dfree,
1950 uint64_t *dsize)
1952 time_t timestamp;
1953 char *stripped;
1954 ssize_t ret;
1955 int saved_errno;
1956 char *conv;
1958 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1959 &timestamp, &stripped)) {
1960 return -1;
1962 if (timestamp == 0) {
1963 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1964 bsize, dfree, dsize);
1967 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1968 TALLOC_FREE(stripped);
1969 if (conv == NULL) {
1970 return -1;
1973 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1974 dsize);
1976 saved_errno = errno;
1977 TALLOC_FREE(conv);
1978 errno = saved_errno;
1980 return ret;
1984 static struct vfs_fn_pointers snapper_fns = {
1985 .get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
1986 .opendir_fn = snapper_gmt_opendir,
1987 .disk_free_fn = snapper_gmt_disk_free,
1988 .rename_fn = snapper_gmt_rename,
1989 .link_fn = snapper_gmt_link,
1990 .symlink_fn = snapper_gmt_symlink,
1991 .stat_fn = snapper_gmt_stat,
1992 .lstat_fn = snapper_gmt_lstat,
1993 .fstat_fn = snapper_gmt_fstat,
1994 .open_fn = snapper_gmt_open,
1995 .unlink_fn = snapper_gmt_unlink,
1996 .chmod_fn = snapper_gmt_chmod,
1997 .chown_fn = snapper_gmt_chown,
1998 .chdir_fn = snapper_gmt_chdir,
1999 .ntimes_fn = snapper_gmt_ntimes,
2000 .readlink_fn = snapper_gmt_readlink,
2001 .mknod_fn = snapper_gmt_mknod,
2002 .realpath_fn = snapper_gmt_realpath,
2003 .get_nt_acl_fn = snapper_gmt_get_nt_acl,
2004 .fget_nt_acl_fn = snapper_gmt_fget_nt_acl,
2005 .mkdir_fn = snapper_gmt_mkdir,
2006 .rmdir_fn = snapper_gmt_rmdir,
2007 .getxattr_fn = snapper_gmt_getxattr,
2008 .listxattr_fn = snapper_gmt_listxattr,
2009 .removexattr_fn = snapper_gmt_removexattr,
2010 .setxattr_fn = snapper_gmt_setxattr,
2011 .chmod_acl_fn = snapper_gmt_chmod_acl,
2012 .chflags_fn = snapper_gmt_chflags,
2013 .get_real_filename_fn = snapper_gmt_get_real_filename,
2016 NTSTATUS vfs_snapper_init(void);
2017 NTSTATUS vfs_snapper_init(void)
2019 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2020 "snapper", &snapper_fns);