heimdal: Fix CID 1273430 Double free
[Samba.git] / source3 / modules / vfs_snapper.c
blob415514aa1fd0a4613b02aa943c007b6c21b3e1ca
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;
95 * Strings are UTF-8. Other characters must be encoded hexadecimal as "\x??".
96 * As a consequence "\" must be encoded as "\\".
98 static NTSTATUS snapper_dbus_str_encode(TALLOC_CTX *mem_ctx, const char *in_str,
99 char **_out_str)
101 size_t in_len;
102 char *out_str;
103 int i;
104 int out_off;
105 int out_len;
107 if (in_str == NULL) {
108 return NT_STATUS_INVALID_PARAMETER;
111 in_len = strlen(in_str);
113 /* output can be max 4 times the length of @in_str, +1 for terminator */
114 out_len = (in_len * 4) + 1;
116 out_str = talloc_array(mem_ctx, char, out_len);
117 if (out_str == NULL) {
118 return NT_STATUS_NO_MEMORY;
121 out_off = 0;
122 for (i = 0; i < in_len; i++) {
123 size_t pushed;
125 if (in_str[i] == '\\') {
126 pushed = snprintf(out_str + out_off, out_len - out_off,
127 "\\\\");
128 } else if ((unsigned char)in_str[i] > 127) {
129 pushed = snprintf(out_str + out_off, out_len - out_off,
130 "\\x%02x", (unsigned char)in_str[i]);
131 } else {
132 /* regular character */
133 *(out_str + out_off) = in_str[i];
134 pushed = sizeof(char);
136 if (pushed >= out_len - out_off) {
137 /* truncated, should never happen */
138 talloc_free(out_str);
139 return NT_STATUS_INTERNAL_ERROR;
141 out_off += pushed;
144 *(out_str + out_off) = '\0';
145 *_out_str = out_str;
147 return NT_STATUS_OK;
150 static NTSTATUS snapper_dbus_str_decode(TALLOC_CTX *mem_ctx, const char *in_str,
151 char **_out_str)
153 size_t in_len;
154 char *out_str;
155 int i;
156 int out_off;
157 int out_len;
159 if (in_str == NULL) {
160 return NT_STATUS_INVALID_PARAMETER;
163 in_len = strlen(in_str);
165 /* output cannot be larger than input, +1 for terminator */
166 out_len = in_len + 1;
168 out_str = talloc_array(mem_ctx, char, out_len);
169 if (out_str == NULL) {
170 return NT_STATUS_NO_MEMORY;
173 out_off = 0;
174 for (i = 0; i < in_len; i++) {
175 int j;
176 char hex_buf[3];
177 unsigned int non_ascii_byte;
179 if (in_str[i] != '\\') {
180 out_str[out_off] = in_str[i];
181 out_off++;
182 continue;
185 i++;
186 if (in_str[i] == '\\') {
187 out_str[out_off] = '\\';
188 out_off++;
189 continue;
190 } else if (in_str[i] != 'x') {
191 goto err_invalid_src_encoding;
194 /* non-ASCII, encoded as two hex chars */
195 for (j = 0; j < 2; j++) {
196 i++;
197 if ((in_str[i] == '\0') || !isxdigit(in_str[i])) {
198 goto err_invalid_src_encoding;
200 hex_buf[j] = in_str[i];
202 hex_buf[2] = '\0';
204 sscanf(hex_buf, "%x", &non_ascii_byte);
205 out_str[out_off] = (unsigned char)non_ascii_byte;
206 out_off++;
209 out_str[out_off] = '\0';
210 *_out_str = out_str;
212 return NT_STATUS_OK;
213 err_invalid_src_encoding:
214 DEBUG(0, ("invalid encoding %s\n", in_str));
215 return NT_STATUS_INVALID_PARAMETER;
218 static DBusConnection *snapper_dbus_conn_create(void)
220 DBusError err;
221 DBusConnection *dconn;
223 dbus_error_init(&err);
226 * Always create a new DBus connection, to ensure snapperd detects the
227 * correct client [E]UID. With dbus_bus_get() it does not!
229 dconn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
230 if (dbus_error_is_set(&err)) {
231 DEBUG(0, ("dbus connection error: %s\n", err.message));
232 dbus_error_free(&err);
234 if (dconn == NULL) {
235 return NULL;
238 /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
239 dbus_connection_set_exit_on_disconnect(dconn, false);
241 return dconn;
244 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
246 if (dconn == NULL) {
247 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
248 return;
251 dbus_connection_close(dconn);
252 dbus_connection_unref(dconn);
256 * send the message @send_msg over the dbus and wait for a response, return the
257 * responsee via @recv_msg_out.
258 * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
260 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
261 DBusMessage *send_msg,
262 DBusMessage **recv_msg_out)
264 DBusPendingCall *pending;
265 DBusMessage *recv_msg;
267 /* send message and get a handle for a reply */
268 if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
269 return NT_STATUS_NO_MEMORY;
271 if (NULL == pending) {
272 DEBUG(0, ("dbus msg send failed\n"));
273 return NT_STATUS_UNSUCCESSFUL;
276 dbus_connection_flush(dconn);
278 /* block until we receive a reply */
279 dbus_pending_call_block(pending);
281 /* get the reply message */
282 recv_msg = dbus_pending_call_steal_reply(pending);
283 if (recv_msg == NULL) {
284 DEBUG(0, ("Reply Null\n"));
285 return NT_STATUS_UNSUCCESSFUL;
287 /* free the pending message handle */
288 dbus_pending_call_unref(pending);
289 *recv_msg_out = recv_msg;
291 return NT_STATUS_OK;
294 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
295 int expected_type)
297 int type = dbus_message_iter_get_arg_type(iter);
298 if (type != expected_type) {
299 DEBUG(0, ("got type %d, expecting %d\n",
300 type, expected_type));
301 return NT_STATUS_INVALID_PARAMETER;
304 return NT_STATUS_OK;
307 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
308 int expected_type,
309 void *val)
311 NTSTATUS status;
312 status = snapper_type_check(iter, expected_type);
313 if (!NT_STATUS_IS_OK(status)) {
314 return status;
317 dbus_message_iter_get_basic(iter, val);
319 return NT_STATUS_OK;
322 static NTSTATUS snapper_dict_unpack(TALLOC_CTX *mem_ctx,
323 DBusMessageIter *iter,
324 struct snapper_dict *dict_out)
327 NTSTATUS status;
328 DBusMessageIter dct_iter;
329 char *key_encoded;
330 char *val_encoded;
332 status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
333 if (!NT_STATUS_IS_OK(status)) {
334 return status;
336 dbus_message_iter_recurse(iter, &dct_iter);
338 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
339 &key_encoded);
340 if (!NT_STATUS_IS_OK(status)) {
341 return status;
343 status = snapper_dbus_str_decode(mem_ctx, key_encoded, &dict_out->key);
344 if (!NT_STATUS_IS_OK(status)) {
345 return status;
348 dbus_message_iter_next(&dct_iter);
349 status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
350 &val_encoded);
351 if (!NT_STATUS_IS_OK(status)) {
352 talloc_free(dict_out->key);
353 return status;
355 status = snapper_dbus_str_decode(mem_ctx, val_encoded, &dict_out->val);
356 if (!NT_STATUS_IS_OK(status)) {
357 talloc_free(dict_out->key);
358 return status;
361 return NT_STATUS_OK;
364 static void snapper_dict_array_print(uint32_t num_dicts,
365 struct snapper_dict *dicts)
367 int i;
369 for (i = 0; i < num_dicts; i++) {
370 DEBUG(10, ("dict (key: %s, val: %s)\n",
371 dicts[i].key, dicts[i].val));
375 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
376 DBusMessageIter *iter,
377 uint32_t *num_dicts_out,
378 struct snapper_dict **dicts_out)
380 NTSTATUS status;
381 DBusMessageIter array_iter;
382 uint32_t num_dicts;
383 struct snapper_dict *dicts = NULL;
385 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
386 if (!NT_STATUS_IS_OK(status)) {
387 return status;
389 dbus_message_iter_recurse(iter, &array_iter);
391 num_dicts = 0;
392 while (dbus_message_iter_get_arg_type(&array_iter)
393 != DBUS_TYPE_INVALID) {
394 num_dicts++;
395 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
396 num_dicts);
397 if (dicts == NULL)
398 abort();
400 status = snapper_dict_unpack(mem_ctx, &array_iter,
401 &dicts[num_dicts - 1]);
402 if (!NT_STATUS_IS_OK(status)) {
403 talloc_free(dicts);
404 return status;
406 dbus_message_iter_next(&array_iter);
409 *num_dicts_out = num_dicts;
410 *dicts_out = dicts;
412 return NT_STATUS_OK;
415 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
417 DBusMessage *msg;
419 msg = dbus_message_new_method_call("org.opensuse.Snapper",
420 "/org/opensuse/Snapper",
421 "org.opensuse.Snapper",
422 "ListConfigs");
423 if (msg == NULL) {
424 DEBUG(0, ("null msg\n"));
425 return NT_STATUS_NO_MEMORY;
428 /* no arguments to append */
429 *req_msg_out = msg;
431 return NT_STATUS_OK;
434 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
435 DBusMessageIter *iter,
436 struct snapper_conf *conf_out)
438 NTSTATUS status;
439 DBusMessageIter st_iter;
440 char *name_encoded;
441 char *mnt_encoded;
443 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
444 if (!NT_STATUS_IS_OK(status)) {
445 return status;
447 dbus_message_iter_recurse(iter, &st_iter);
449 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
450 &name_encoded);
451 if (!NT_STATUS_IS_OK(status)) {
452 return status;
455 status = snapper_dbus_str_decode(mem_ctx, name_encoded,
456 &conf_out->name);
457 if (!NT_STATUS_IS_OK(status)) {
458 return status;
461 dbus_message_iter_next(&st_iter);
462 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
463 &mnt_encoded);
464 if (!NT_STATUS_IS_OK(status)) {
465 talloc_free(conf_out->name);
466 return status;
469 status = snapper_dbus_str_decode(mem_ctx, mnt_encoded,
470 &conf_out->mnt);
471 if (!NT_STATUS_IS_OK(status)) {
472 talloc_free(conf_out->name);
473 return status;
476 dbus_message_iter_next(&st_iter);
477 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
478 &conf_out->num_attrs,
479 &conf_out->attrs);
480 if (!NT_STATUS_IS_OK(status)) {
481 talloc_free(conf_out->mnt);
482 talloc_free(conf_out->name);
483 return status;
486 return NT_STATUS_OK;
489 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
490 struct snapper_conf *confs,
491 const char *base)
493 int i;
495 for (i = 0; i < num_confs; i++) {
496 if (strcmp(confs[i].mnt, base) == 0) {
497 DEBUG(5, ("found snapper conf %s for path %s\n",
498 confs[i].name, base));
499 return &confs[i];
502 DEBUG(5, ("config for base %s not found\n", base));
504 return NULL;
507 static void snapper_conf_array_print(int32_t num_confs,
508 struct snapper_conf *confs)
510 int i;
512 for (i = 0; i < num_confs; i++) {
513 DEBUG(10, ("name: %s, mnt: %s\n",
514 confs[i].name, confs[i].mnt));
515 snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
519 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
520 DBusMessageIter *iter,
521 uint32_t *num_confs_out,
522 struct snapper_conf **confs_out)
524 uint32_t num_confs;
525 NTSTATUS status;
526 struct snapper_conf *confs = NULL;
527 DBusMessageIter array_iter;
530 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
531 if (!NT_STATUS_IS_OK(status)) {
532 return status;
534 dbus_message_iter_recurse(iter, &array_iter);
536 num_confs = 0;
537 while (dbus_message_iter_get_arg_type(&array_iter)
538 != DBUS_TYPE_INVALID) {
539 num_confs++;
540 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
541 num_confs);
542 if (confs == NULL)
543 abort();
545 status = snapper_conf_unpack(confs, &array_iter,
546 &confs[num_confs - 1]);
547 if (!NT_STATUS_IS_OK(status)) {
548 talloc_free(confs);
549 return status;
551 dbus_message_iter_next(&array_iter);
554 *num_confs_out = num_confs;
555 *confs_out = confs;
557 return NT_STATUS_OK;
560 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
561 DBusConnection *dconn,
562 DBusMessage *rsp_msg,
563 uint32_t *num_confs_out,
564 struct snapper_conf **confs_out)
566 NTSTATUS status;
567 DBusMessageIter iter;
568 int msg_type;
569 uint32_t num_confs;
570 struct snapper_conf *confs;
571 const char *sig;
573 msg_type = dbus_message_get_type(rsp_msg);
574 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
575 const char *err_str = dbus_message_get_error_name(rsp_msg);
576 DEBUG(0, ("list_confs error response: %s\n", err_str));
577 return snapper_err_ntstatus_map(err_str);
580 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
581 DEBUG(0, ("unexpected list_confs ret type: %d\n",
582 msg_type));
583 return NT_STATUS_INVALID_PARAMETER;
586 sig = dbus_message_get_signature(rsp_msg);
587 if ((sig == NULL)
588 || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
589 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
590 (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
591 return NT_STATUS_INVALID_PARAMETER;
594 if (!dbus_message_iter_init(rsp_msg, &iter)) {
595 /* FIXME return empty? */
596 DEBUG(0, ("Message has no arguments!\n"));
597 return NT_STATUS_INVALID_PARAMETER;
600 status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
601 if (!NT_STATUS_IS_OK(status)) {
602 DEBUG(0, ("failed to unpack conf array\n"));
603 return status;
606 snapper_conf_array_print(num_confs, confs);
608 *num_confs_out = num_confs;
609 *confs_out = confs;
611 return NT_STATUS_OK;
614 static NTSTATUS snapper_list_snaps_pack(TALLOC_CTX *mem_ctx,
615 char *snapper_conf,
616 DBusMessage **req_msg_out)
618 DBusMessage *msg;
619 DBusMessageIter args;
620 char *conf_encoded;
621 NTSTATUS status;
623 msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
624 "/org/opensuse/Snapper", /* object to call on */
625 "org.opensuse.Snapper", /* interface to call on */
626 "ListSnapshots"); /* method name */
627 if (msg == NULL) {
628 DEBUG(0, ("failed to create list snaps message\n"));
629 return NT_STATUS_NO_MEMORY;
632 status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
633 if (!NT_STATUS_IS_OK(status)) {
634 dbus_message_unref(msg);
635 return status;
638 /* append arguments */
639 dbus_message_iter_init_append(msg, &args);
640 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
641 &conf_encoded)) {
642 talloc_free(conf_encoded);
643 dbus_message_unref(msg);
644 return NT_STATUS_NO_MEMORY;
647 *req_msg_out = msg;
649 return NT_STATUS_OK;
652 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
653 DBusMessageIter *iter,
654 struct snapper_snap *snap_out)
656 NTSTATUS status;
657 DBusMessageIter st_iter;
658 char *desc_encoded;
659 char *cleanup_encoded;
661 status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
662 if (!NT_STATUS_IS_OK(status)) {
663 return status;
665 dbus_message_iter_recurse(iter, &st_iter);
667 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
668 &snap_out->id);
669 if (!NT_STATUS_IS_OK(status)) {
670 return status;
673 dbus_message_iter_next(&st_iter);
674 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
675 &snap_out->type);
676 if (!NT_STATUS_IS_OK(status)) {
677 return status;
680 dbus_message_iter_next(&st_iter);
681 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
682 &snap_out->pre_id);
683 if (!NT_STATUS_IS_OK(status)) {
684 return status;
687 dbus_message_iter_next(&st_iter);
688 status = snapper_type_check_get(&st_iter, DBUS_TYPE_INT64,
689 &snap_out->time);
690 if (!NT_STATUS_IS_OK(status)) {
691 return status;
694 dbus_message_iter_next(&st_iter);
695 status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
696 &snap_out->creator_uid);
697 if (!NT_STATUS_IS_OK(status)) {
698 return status;
701 dbus_message_iter_next(&st_iter);
702 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
703 &desc_encoded);
704 if (!NT_STATUS_IS_OK(status)) {
705 return status;
708 status = snapper_dbus_str_decode(mem_ctx, desc_encoded,
709 &snap_out->desc);
710 if (!NT_STATUS_IS_OK(status)) {
711 return status;
714 dbus_message_iter_next(&st_iter);
715 status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
716 &cleanup_encoded);
717 if (!NT_STATUS_IS_OK(status)) {
718 talloc_free(snap_out->desc);
719 return status;
722 status = snapper_dbus_str_decode(mem_ctx, cleanup_encoded,
723 &snap_out->cleanup);
724 if (!NT_STATUS_IS_OK(status)) {
725 talloc_free(snap_out->desc);
726 return status;
729 dbus_message_iter_next(&st_iter);
730 status = snapper_dict_array_unpack(mem_ctx, &st_iter,
731 &snap_out->num_user_data,
732 &snap_out->user_data);
733 if (!NT_STATUS_IS_OK(status)) {
734 talloc_free(snap_out->cleanup);
735 talloc_free(snap_out->desc);
736 return status;
739 return NT_STATUS_OK;
742 static void snapper_snap_array_print(int32_t num_snaps,
743 struct snapper_snap *snaps)
745 int i;
747 for (i = 0; i < num_snaps; i++) {
748 DEBUG(10, ("id: %u, "
749 "type: %u, "
750 "pre_id: %u, "
751 "time: %ld, "
752 "creator_uid: %u, "
753 "desc: %s, "
754 "cleanup: %s\n",
755 (unsigned int)snaps[i].id,
756 (unsigned int)snaps[i].type,
757 (unsigned int)snaps[i].pre_id,
758 (long int)snaps[i].time,
759 (unsigned int)snaps[i].creator_uid,
760 snaps[i].desc,
761 snaps[i].cleanup));
762 snapper_dict_array_print(snaps[i].num_user_data,
763 snaps[i].user_data);
767 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
768 DBusMessageIter *iter,
769 uint32_t *num_snaps_out,
770 struct snapper_snap **snaps_out)
772 uint32_t num_snaps;
773 NTSTATUS status;
774 struct snapper_snap *snaps = NULL;
775 DBusMessageIter array_iter;
778 status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
779 if (!NT_STATUS_IS_OK(status)) {
780 return status;
782 dbus_message_iter_recurse(iter, &array_iter);
784 num_snaps = 0;
785 while (dbus_message_iter_get_arg_type(&array_iter)
786 != DBUS_TYPE_INVALID) {
787 num_snaps++;
788 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
789 num_snaps);
790 if (snaps == NULL)
791 abort();
793 status = snapper_snap_struct_unpack(snaps, &array_iter,
794 &snaps[num_snaps - 1]);
795 if (!NT_STATUS_IS_OK(status)) {
796 talloc_free(snaps);
797 return status;
799 dbus_message_iter_next(&array_iter);
802 *num_snaps_out = num_snaps;
803 *snaps_out = snaps;
805 return NT_STATUS_OK;
808 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
809 DBusMessage *rsp_msg,
810 uint32_t *num_snaps_out,
811 struct snapper_snap **snaps_out)
813 NTSTATUS status;
814 DBusMessageIter iter;
815 int msg_type;
816 uint32_t num_snaps;
817 struct snapper_snap *snaps;
818 const char *sig;
820 msg_type = dbus_message_get_type(rsp_msg);
821 if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
822 const char *err_str = dbus_message_get_error_name(rsp_msg);
823 DEBUG(0, ("list_snaps error response: %s\n", err_str));
824 return snapper_err_ntstatus_map(err_str);
827 if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
828 DEBUG(0,("unexpected list_snaps ret type: %d\n",
829 msg_type));
830 return NT_STATUS_INVALID_PARAMETER;
833 sig = dbus_message_get_signature(rsp_msg);
834 if ((sig == NULL)
835 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
836 DEBUG(0, ("bad list snaps response sig: %s, "
837 "expected: %s\n",
838 (sig ? sig : "NULL"),
839 SNAPPER_SIG_LIST_SNAPS_RSP));
840 return NT_STATUS_INVALID_PARAMETER;
843 /* read the parameters */
844 if (!dbus_message_iter_init(rsp_msg, &iter)) {
845 DEBUG(0, ("response has no arguments!\n"));
846 return NT_STATUS_INVALID_PARAMETER;
849 status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
850 if (!NT_STATUS_IS_OK(status)) {
851 DEBUG(0, ("failed to unpack snap array\n"));
852 return NT_STATUS_INVALID_PARAMETER;
855 snapper_snap_array_print(num_snaps, snaps);
857 *num_snaps_out = num_snaps;
858 *snaps_out = snaps;
860 return NT_STATUS_OK;
863 static NTSTATUS snapper_list_snaps_at_time_pack(TALLOC_CTX *mem_ctx,
864 const char *snapper_conf,
865 time_t time_lower,
866 time_t time_upper,
867 DBusMessage **req_msg_out)
869 DBusMessage *msg;
870 DBusMessageIter args;
871 char *conf_encoded;
872 NTSTATUS status;
874 msg = dbus_message_new_method_call("org.opensuse.Snapper",
875 "/org/opensuse/Snapper",
876 "org.opensuse.Snapper",
877 "ListSnapshotsAtTime");
878 if (msg == NULL) {
879 DEBUG(0, ("failed to create list snaps message\n"));
880 return NT_STATUS_NO_MEMORY;
883 status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
884 if (!NT_STATUS_IS_OK(status)) {
885 dbus_message_unref(msg);
886 return status;
889 dbus_message_iter_init_append(msg, &args);
890 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
891 &conf_encoded)) {
892 talloc_free(conf_encoded);
893 dbus_message_unref(msg);
894 return NT_STATUS_NO_MEMORY;
897 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
898 &time_lower)) {
899 talloc_free(conf_encoded);
900 dbus_message_unref(msg);
901 return NT_STATUS_NO_MEMORY;
904 if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
905 &time_upper)) {
906 talloc_free(conf_encoded);
907 dbus_message_unref(msg);
908 return NT_STATUS_NO_MEMORY;
911 *req_msg_out = msg;
913 return NT_STATUS_OK;
915 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
918 * Determine the snapper snapshot path given an id and base.
919 * Ideally this should be determined via a lookup.
921 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
922 const char *base_path,
923 uint32_t snap_id,
924 char **snap_path_out)
926 char *snap_path;
928 snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
929 base_path, snap_id);
930 if (snap_path == NULL) {
931 return NT_STATUS_NO_MEMORY;
934 *snap_path_out = snap_path;
935 return NT_STATUS_OK;
938 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
939 DBusConnection *dconn,
940 const char *path,
941 char **conf_name_out,
942 char **base_path_out)
944 NTSTATUS status;
945 DBusMessage *req_msg;
946 DBusMessage *rsp_msg;
947 uint32_t num_confs = 0;
948 struct snapper_conf *confs = NULL;
949 struct snapper_conf *conf;
950 char *conf_name;
951 char *base_path;
953 status = snapper_list_confs_pack(&req_msg);
954 if (!NT_STATUS_IS_OK(status)) {
955 goto err_out;
958 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
959 if (!NT_STATUS_IS_OK(status)) {
960 goto err_req_free;
963 status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
964 &num_confs, &confs);
965 if (!NT_STATUS_IS_OK(status)) {
966 goto err_rsp_free;
970 * for now we only support shares where the path directly corresponds
971 * to a snapper configuration.
973 conf = snapper_conf_array_base_find(num_confs, confs,
974 path);
975 if (conf == NULL) {
976 status = NT_STATUS_NOT_SUPPORTED;
977 goto err_array_free;
980 conf_name = talloc_strdup(mem_ctx, conf->name);
981 if (conf_name == NULL) {
982 status = NT_STATUS_NO_MEMORY;
983 goto err_array_free;
985 base_path = talloc_strdup(mem_ctx, conf->mnt);
986 if (base_path == NULL) {
987 status = NT_STATUS_NO_MEMORY;
988 goto err_conf_name_free;
991 talloc_free(confs);
992 dbus_message_unref(rsp_msg);
993 dbus_message_unref(req_msg);
995 *conf_name_out = conf_name;
996 *base_path_out = base_path;
998 return NT_STATUS_OK;
1000 err_conf_name_free:
1001 talloc_free(conf_name);
1002 err_array_free:
1003 talloc_free(confs);
1004 err_rsp_free:
1005 dbus_message_unref(rsp_msg);
1006 err_req_free:
1007 dbus_message_unref(req_msg);
1008 err_out:
1009 return status;
1012 /* sc_data used as parent talloc context for all labels */
1013 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
1014 struct files_struct *fsp,
1015 struct shadow_copy_data *sc_data,
1016 bool labels)
1018 DBusConnection *dconn;
1019 TALLOC_CTX *tmp_ctx;
1020 NTSTATUS status;
1021 char *conf_name;
1022 char *base_path;
1023 DBusMessage *req_msg;
1024 DBusMessage *rsp_msg;
1025 uint32_t num_snaps;
1026 struct snapper_snap *snaps;
1027 uint32_t i;
1028 uint32_t lbl_off;
1030 tmp_ctx = talloc_new(sc_data);
1031 if (tmp_ctx == NULL) {
1032 status = NT_STATUS_NO_MEMORY;
1033 goto err_out;
1036 dconn = snapper_dbus_conn_create();
1037 if (dconn == NULL) {
1038 status = NT_STATUS_UNSUCCESSFUL;
1039 goto err_mem_ctx_free;
1042 if (fsp->conn->connectpath == NULL) {
1043 status = NT_STATUS_INVALID_PARAMETER;
1044 goto err_conn_free;
1047 status = snapper_get_conf_call(tmp_ctx, dconn,
1048 fsp->conn->connectpath,
1049 &conf_name,
1050 &base_path);
1051 if (!NT_STATUS_IS_OK(status)) {
1052 goto err_conn_free;
1055 status = snapper_list_snaps_pack(tmp_ctx, conf_name, &req_msg);
1056 if (!NT_STATUS_IS_OK(status)) {
1057 goto err_conn_free;
1060 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1061 if (!NT_STATUS_IS_OK(status)) {
1062 goto err_req_free;
1065 status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
1066 &num_snaps, &snaps);
1067 if (!NT_STATUS_IS_OK(status)) {
1068 goto err_rsp_free;
1070 /* we should always get at least one snapshot (current) */
1071 if (num_snaps == 0) {
1072 DEBUG(1, ("zero snapshots in snap list response\n"));
1073 status = NT_STATUS_UNSUCCESSFUL;
1074 goto err_rsp_free;
1077 /* subtract 1, (current) snapshot is not returned */
1078 sc_data->num_volumes = num_snaps - 1;
1079 sc_data->labels = NULL;
1081 if ((labels == false) || (sc_data->num_volumes == 0)) {
1082 /* tokens need not be added to the labels array */
1083 goto done;
1086 sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
1087 sc_data->num_volumes);
1088 if (sc_data->labels == NULL) {
1089 status = NT_STATUS_NO_MEMORY;
1090 goto err_rsp_free;
1093 /* start at end for decending order, do not include 0 (current) */
1094 lbl_off = 0;
1095 for (i = num_snaps - 1; i > 0; i--) {
1096 char *lbl = sc_data->labels[lbl_off++];
1097 struct tm gmt_snap_time;
1098 struct tm *tm_ret;
1099 size_t str_sz;
1101 tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
1102 if (tm_ret == NULL) {
1103 status = NT_STATUS_UNSUCCESSFUL;
1104 goto err_labels_free;
1106 str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
1107 "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
1108 if (str_sz == 0) {
1109 status = NT_STATUS_UNSUCCESSFUL;
1110 goto err_labels_free;
1114 done:
1115 talloc_free(tmp_ctx);
1116 dbus_message_unref(rsp_msg);
1117 dbus_message_unref(req_msg);
1118 snapper_dbus_conn_destroy(dconn);
1120 return 0;
1122 err_labels_free:
1123 TALLOC_FREE(sc_data->labels);
1124 err_rsp_free:
1125 dbus_message_unref(rsp_msg);
1126 err_req_free:
1127 dbus_message_unref(req_msg);
1128 err_conn_free:
1129 snapper_dbus_conn_destroy(dconn);
1130 err_mem_ctx_free:
1131 talloc_free(tmp_ctx);
1132 err_out:
1133 errno = map_errno_from_nt_status(status);
1134 return -1;
1137 static bool snapper_gmt_strip_snapshot(TALLOC_CTX *mem_ctx,
1138 struct vfs_handle_struct *handle,
1139 const char *name,
1140 time_t *ptimestamp,
1141 char **pstripped)
1143 struct tm tm;
1144 time_t timestamp;
1145 const char *p;
1146 char *q;
1147 char *stripped;
1148 size_t rest_len, dst_len;
1150 p = strstr_m(name, "@GMT-");
1151 if (p == NULL) {
1152 goto no_snapshot;
1154 if ((p > name) && (p[-1] != '/')) {
1155 goto no_snapshot;
1157 q = strptime(p, GMT_FORMAT, &tm);
1158 if (q == NULL) {
1159 goto no_snapshot;
1161 tm.tm_isdst = -1;
1162 timestamp = timegm(&tm);
1163 if (timestamp == (time_t)-1) {
1164 goto no_snapshot;
1166 if ((p == name) && (q[0] == '\0')) {
1167 if (pstripped != NULL) {
1168 stripped = talloc_strdup(mem_ctx, "");
1169 if (stripped == NULL) {
1170 return false;
1172 *pstripped = stripped;
1174 *ptimestamp = timestamp;
1175 return true;
1177 if (q[0] != '/') {
1178 goto no_snapshot;
1180 q += 1;
1182 rest_len = strlen(q);
1183 dst_len = (p-name) + rest_len;
1185 if (pstripped != NULL) {
1186 stripped = talloc_array(mem_ctx, char, dst_len+1);
1187 if (stripped == NULL) {
1188 errno = ENOMEM;
1189 return false;
1191 if (p > name) {
1192 memcpy(stripped, name, p-name);
1194 if (rest_len > 0) {
1195 memcpy(stripped + (p-name), q, rest_len);
1197 stripped[dst_len] = '\0';
1198 *pstripped = stripped;
1200 *ptimestamp = timestamp;
1201 return true;
1202 no_snapshot:
1203 *ptimestamp = 0;
1204 return true;
1207 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1208 DBusConnection *dconn,
1209 const char *conf_name,
1210 const char *base_path,
1211 time_t snaptime,
1212 char **snap_path_out)
1214 NTSTATUS status;
1215 DBusMessage *req_msg;
1216 DBusMessage *rsp_msg;
1217 uint32_t num_snaps;
1218 struct snapper_snap *snaps;
1219 char *snap_path;
1221 status = snapper_list_snaps_at_time_pack(mem_ctx,
1222 conf_name,
1223 snaptime,
1224 snaptime,
1225 &req_msg);
1226 if (!NT_STATUS_IS_OK(status)) {
1227 goto err_out;
1230 status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1231 if (!NT_STATUS_IS_OK(status)) {
1232 goto err_req_free;
1235 status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1236 &num_snaps, &snaps);
1237 if (!NT_STATUS_IS_OK(status)) {
1238 goto err_rsp_free;
1241 if (num_snaps == 0) {
1242 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime));
1243 status = NT_STATUS_INVALID_PARAMETER;
1244 goto err_snap_array_free;
1245 } else if (num_snaps > 0) {
1246 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1247 num_snaps, snaptime));
1250 status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1251 &snap_path);
1252 if (!NT_STATUS_IS_OK(status)) {
1253 goto err_snap_array_free;
1256 *snap_path_out = snap_path;
1257 err_snap_array_free:
1258 talloc_free(snaps);
1259 err_rsp_free:
1260 dbus_message_unref(rsp_msg);
1261 err_req_free:
1262 dbus_message_unref(req_msg);
1263 err_out:
1264 return status;
1267 static NTSTATUS snapper_snap_path_expand(struct connection_struct *conn,
1268 TALLOC_CTX *mem_ctx,
1269 time_t snap_time,
1270 char **snap_dir_out)
1272 DBusConnection *dconn;
1273 NTSTATUS status;
1274 char *conf_name;
1275 char *base_path;
1276 char *snap_path;
1278 dconn = snapper_dbus_conn_create();
1279 if (dconn == NULL) {
1280 status = NT_STATUS_UNSUCCESSFUL;
1281 goto err_out;
1284 if (conn->connectpath == NULL) {
1285 status = NT_STATUS_INVALID_PARAMETER;
1286 goto err_conn_free;
1289 status = snapper_get_conf_call(mem_ctx, dconn,
1290 conn->connectpath,
1291 &conf_name,
1292 &base_path);
1293 if (!NT_STATUS_IS_OK(status)) {
1294 goto err_conn_free;
1297 status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1298 conf_name, base_path, snap_time,
1299 &snap_path);
1300 if (!NT_STATUS_IS_OK(status)) {
1301 goto err_conf_name_free;
1304 /* confirm snapshot path is nested under base path */
1305 if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1306 status = NT_STATUS_INVALID_PARAMETER;
1307 goto err_snap_path_free;
1310 talloc_free(conf_name);
1311 talloc_free(base_path);
1312 snapper_dbus_conn_destroy(dconn);
1313 *snap_dir_out = snap_path;
1315 return NT_STATUS_OK;
1317 err_snap_path_free:
1318 talloc_free(snap_path);
1319 err_conf_name_free:
1320 talloc_free(conf_name);
1321 talloc_free(base_path);
1322 err_conn_free:
1323 snapper_dbus_conn_destroy(dconn);
1324 err_out:
1325 return status;
1328 static char *snapper_gmt_convert(TALLOC_CTX *mem_ctx,
1329 struct vfs_handle_struct *handle,
1330 const char *name, time_t timestamp)
1332 char *snap_path = NULL;
1333 char *path = NULL;
1334 NTSTATUS status;
1335 int saved_errno;
1337 status = snapper_snap_path_expand(handle->conn, mem_ctx, timestamp,
1338 &snap_path);
1339 if (!NT_STATUS_IS_OK(status)) {
1340 errno = map_errno_from_nt_status(status);
1341 goto err_out;
1344 path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1345 if (path == NULL) {
1346 errno = ENOMEM;
1347 goto err_snap_path_free;
1350 DEBUG(10, ("converted %s/%s @ time to %s\n",
1351 handle->conn->connectpath, name, path));
1352 return path;
1354 err_snap_path_free:
1355 saved_errno = errno;
1356 talloc_free(snap_path);
1357 errno = saved_errno;
1358 err_out:
1359 return NULL;
1362 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1363 const char *fname,
1364 const char *mask,
1365 uint32_t attr)
1367 time_t timestamp;
1368 char *stripped;
1369 DIR *ret;
1370 int saved_errno;
1371 char *conv;
1373 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1374 &timestamp, &stripped)) {
1375 return NULL;
1377 if (timestamp == 0) {
1378 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
1380 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1381 TALLOC_FREE(stripped);
1382 if (conv == NULL) {
1383 return NULL;
1385 ret = SMB_VFS_NEXT_OPENDIR(handle, conv, mask, attr);
1386 saved_errno = errno;
1387 TALLOC_FREE(conv);
1388 errno = saved_errno;
1389 return ret;
1392 static int snapper_gmt_rename(vfs_handle_struct *handle,
1393 const struct smb_filename *smb_fname_src,
1394 const struct smb_filename *smb_fname_dst)
1396 time_t timestamp_src, timestamp_dst;
1398 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1399 smb_fname_src->base_name,
1400 &timestamp_src, NULL)) {
1401 return -1;
1403 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1404 smb_fname_dst->base_name,
1405 &timestamp_dst, NULL)) {
1406 return -1;
1408 if (timestamp_src != 0) {
1409 errno = EXDEV;
1410 return -1;
1412 if (timestamp_dst != 0) {
1413 errno = EROFS;
1414 return -1;
1416 return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1419 static int snapper_gmt_symlink(vfs_handle_struct *handle,
1420 const char *oldname, const char *newname)
1422 time_t timestamp_old, timestamp_new;
1424 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1425 &timestamp_old, NULL)) {
1426 return -1;
1428 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1429 &timestamp_new, NULL)) {
1430 return -1;
1432 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1433 errno = EROFS;
1434 return -1;
1436 return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
1439 static int snapper_gmt_link(vfs_handle_struct *handle,
1440 const char *oldname, const char *newname)
1442 time_t timestamp_old, timestamp_new;
1444 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1445 &timestamp_old, NULL)) {
1446 return -1;
1448 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1449 &timestamp_new, NULL)) {
1450 return -1;
1452 if ((timestamp_old != 0) || (timestamp_new != 0)) {
1453 errno = EROFS;
1454 return -1;
1456 return SMB_VFS_NEXT_LINK(handle, oldname, newname);
1459 static int snapper_gmt_stat(vfs_handle_struct *handle,
1460 struct smb_filename *smb_fname)
1462 time_t timestamp;
1463 char *stripped, *tmp;
1464 int ret, saved_errno;
1466 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1467 smb_fname->base_name,
1468 &timestamp, &stripped)) {
1469 return -1;
1471 if (timestamp == 0) {
1472 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1475 tmp = smb_fname->base_name;
1476 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1477 stripped, timestamp);
1478 TALLOC_FREE(stripped);
1480 if (smb_fname->base_name == NULL) {
1481 smb_fname->base_name = tmp;
1482 return -1;
1485 ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1486 saved_errno = errno;
1488 TALLOC_FREE(smb_fname->base_name);
1489 smb_fname->base_name = tmp;
1491 errno = saved_errno;
1492 return ret;
1495 static int snapper_gmt_lstat(vfs_handle_struct *handle,
1496 struct smb_filename *smb_fname)
1498 time_t timestamp;
1499 char *stripped, *tmp;
1500 int ret, saved_errno;
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_LSTAT(handle, smb_fname);
1511 tmp = smb_fname->base_name;
1512 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1513 stripped, timestamp);
1514 TALLOC_FREE(stripped);
1516 if (smb_fname->base_name == NULL) {
1517 smb_fname->base_name = tmp;
1518 return -1;
1521 ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1522 saved_errno = errno;
1524 TALLOC_FREE(smb_fname->base_name);
1525 smb_fname->base_name = tmp;
1527 errno = saved_errno;
1528 return ret;
1531 static int snapper_gmt_fstat(vfs_handle_struct *handle, files_struct *fsp,
1532 SMB_STRUCT_STAT *sbuf)
1534 time_t timestamp;
1535 int ret;
1537 ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1538 if (ret == -1) {
1539 return ret;
1541 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1542 fsp->fsp_name->base_name,
1543 &timestamp, NULL)) {
1544 return 0;
1546 return 0;
1549 static int snapper_gmt_open(vfs_handle_struct *handle,
1550 struct smb_filename *smb_fname, files_struct *fsp,
1551 int flags, mode_t mode)
1553 time_t timestamp;
1554 char *stripped, *tmp;
1555 int ret, saved_errno;
1557 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1558 smb_fname->base_name,
1559 &timestamp, &stripped)) {
1560 return -1;
1562 if (timestamp == 0) {
1563 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1566 tmp = smb_fname->base_name;
1567 smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1568 stripped, timestamp);
1569 TALLOC_FREE(stripped);
1571 if (smb_fname->base_name == NULL) {
1572 smb_fname->base_name = tmp;
1573 return -1;
1576 ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1577 saved_errno = errno;
1579 TALLOC_FREE(smb_fname->base_name);
1580 smb_fname->base_name = tmp;
1582 errno = saved_errno;
1583 return ret;
1586 static int snapper_gmt_unlink(vfs_handle_struct *handle,
1587 const struct smb_filename *smb_fname)
1589 time_t timestamp;
1590 char *stripped;
1591 int ret, saved_errno;
1592 struct smb_filename *conv;
1594 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1595 smb_fname->base_name,
1596 &timestamp, &stripped)) {
1597 return -1;
1599 if (timestamp == 0) {
1600 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1602 conv = cp_smb_filename(talloc_tos(), smb_fname);
1603 if (conv == NULL) {
1604 errno = ENOMEM;
1605 return -1;
1607 conv->base_name = snapper_gmt_convert(conv, handle,
1608 stripped, timestamp);
1609 TALLOC_FREE(stripped);
1610 if (conv->base_name == NULL) {
1611 return -1;
1613 ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1614 saved_errno = errno;
1615 TALLOC_FREE(conv);
1616 errno = saved_errno;
1617 return ret;
1620 static int snapper_gmt_chmod(vfs_handle_struct *handle, const char *fname,
1621 mode_t mode)
1623 time_t timestamp;
1624 char *stripped;
1625 int ret, saved_errno;
1626 char *conv;
1628 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1629 &timestamp, &stripped)) {
1630 return -1;
1632 if (timestamp == 0) {
1633 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
1635 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1636 TALLOC_FREE(stripped);
1637 if (conv == NULL) {
1638 return -1;
1640 ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1641 saved_errno = errno;
1642 TALLOC_FREE(conv);
1643 errno = saved_errno;
1644 return ret;
1647 static int snapper_gmt_chown(vfs_handle_struct *handle, const char *fname,
1648 uid_t uid, gid_t gid)
1650 time_t timestamp;
1651 char *stripped;
1652 int ret, saved_errno;
1653 char *conv;
1655 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1656 &timestamp, &stripped)) {
1657 return -1;
1659 if (timestamp == 0) {
1660 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
1662 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1663 TALLOC_FREE(stripped);
1664 if (conv == NULL) {
1665 return -1;
1667 ret = SMB_VFS_NEXT_CHOWN(handle, conv, uid, gid);
1668 saved_errno = errno;
1669 TALLOC_FREE(conv);
1670 errno = saved_errno;
1671 return ret;
1674 static int snapper_gmt_chdir(vfs_handle_struct *handle,
1675 const char *fname)
1677 time_t timestamp;
1678 char *stripped;
1679 int ret, saved_errno;
1680 char *conv;
1682 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1683 &timestamp, &stripped)) {
1684 return -1;
1686 if (timestamp == 0) {
1687 return SMB_VFS_NEXT_CHDIR(handle, fname);
1689 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1690 TALLOC_FREE(stripped);
1691 if (conv == NULL) {
1692 return -1;
1694 ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1695 saved_errno = errno;
1696 TALLOC_FREE(conv);
1697 errno = saved_errno;
1698 return ret;
1701 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
1702 const struct smb_filename *smb_fname,
1703 struct smb_file_time *ft)
1705 time_t timestamp;
1706 char *stripped;
1707 int ret, saved_errno;
1708 struct smb_filename *conv;
1710 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1711 smb_fname->base_name,
1712 &timestamp, &stripped)) {
1713 return -1;
1715 if (timestamp == 0) {
1716 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1718 conv = cp_smb_filename(talloc_tos(), smb_fname);
1719 if (conv == NULL) {
1720 errno = ENOMEM;
1721 return -1;
1723 conv->base_name = snapper_gmt_convert(conv, handle,
1724 stripped, timestamp);
1725 TALLOC_FREE(stripped);
1726 if (conv->base_name == NULL) {
1727 return -1;
1729 ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1730 saved_errno = errno;
1731 TALLOC_FREE(conv);
1732 errno = saved_errno;
1733 return ret;
1736 static int snapper_gmt_readlink(vfs_handle_struct *handle,
1737 const char *fname, char *buf, size_t bufsiz)
1739 time_t timestamp;
1740 char *stripped;
1741 int ret, saved_errno;
1742 char *conv;
1744 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1745 &timestamp, &stripped)) {
1746 return -1;
1748 if (timestamp == 0) {
1749 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1751 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1752 TALLOC_FREE(stripped);
1753 if (conv == NULL) {
1754 return -1;
1756 ret = SMB_VFS_NEXT_READLINK(handle, conv, buf, bufsiz);
1757 saved_errno = errno;
1758 TALLOC_FREE(conv);
1759 errno = saved_errno;
1760 return ret;
1763 static int snapper_gmt_mknod(vfs_handle_struct *handle,
1764 const char *fname, mode_t mode, SMB_DEV_T dev)
1766 time_t timestamp;
1767 char *stripped;
1768 int ret, 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_MKNOD(handle, fname, mode, dev);
1778 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1779 TALLOC_FREE(stripped);
1780 if (conv == NULL) {
1781 return -1;
1783 ret = SMB_VFS_NEXT_MKNOD(handle, conv, mode, dev);
1784 saved_errno = errno;
1785 TALLOC_FREE(conv);
1786 errno = saved_errno;
1787 return ret;
1790 static char *snapper_gmt_realpath(vfs_handle_struct *handle,
1791 const char *fname)
1793 time_t timestamp;
1794 char *stripped = NULL;
1795 char *tmp = NULL;
1796 char *result = NULL;
1797 int saved_errno;
1799 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1800 &timestamp, &stripped)) {
1801 goto done;
1803 if (timestamp == 0) {
1804 return SMB_VFS_NEXT_REALPATH(handle, fname);
1807 tmp = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1808 if (tmp == NULL) {
1809 goto done;
1812 result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1813 if (result == NULL) {
1814 goto done;
1817 done:
1818 saved_errno = errno;
1819 TALLOC_FREE(tmp);
1820 TALLOC_FREE(stripped);
1821 errno = saved_errno;
1822 return result;
1825 static NTSTATUS snapper_gmt_fget_nt_acl(vfs_handle_struct *handle,
1826 struct files_struct *fsp,
1827 uint32 security_info,
1828 TALLOC_CTX *mem_ctx,
1829 struct security_descriptor **ppdesc)
1831 time_t timestamp;
1832 char *stripped;
1833 NTSTATUS status;
1834 char *conv;
1836 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1837 fsp->fsp_name->base_name,
1838 &timestamp, &stripped)) {
1839 return map_nt_error_from_unix(errno);
1841 if (timestamp == 0) {
1842 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1843 mem_ctx,
1844 ppdesc);
1846 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1847 TALLOC_FREE(stripped);
1848 if (conv == NULL) {
1849 return map_nt_error_from_unix(errno);
1851 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1852 mem_ctx, ppdesc);
1853 TALLOC_FREE(conv);
1854 return status;
1857 static NTSTATUS snapper_gmt_get_nt_acl(vfs_handle_struct *handle,
1858 const char *fname,
1859 uint32 security_info,
1860 TALLOC_CTX *mem_ctx,
1861 struct security_descriptor **ppdesc)
1863 time_t timestamp;
1864 char *stripped;
1865 NTSTATUS status;
1866 char *conv;
1868 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1869 &timestamp, &stripped)) {
1870 return map_nt_error_from_unix(errno);
1872 if (timestamp == 0) {
1873 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1874 mem_ctx, ppdesc);
1876 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1877 TALLOC_FREE(stripped);
1878 if (conv == NULL) {
1879 return map_nt_error_from_unix(errno);
1881 status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1882 mem_ctx, ppdesc);
1883 TALLOC_FREE(conv);
1884 return status;
1887 static int snapper_gmt_mkdir(vfs_handle_struct *handle,
1888 const char *fname, mode_t mode)
1890 time_t timestamp;
1891 char *stripped;
1892 int ret, saved_errno;
1893 char *conv;
1895 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1896 &timestamp, &stripped)) {
1897 return -1;
1899 if (timestamp == 0) {
1900 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1902 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1903 TALLOC_FREE(stripped);
1904 if (conv == NULL) {
1905 return -1;
1907 ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1908 saved_errno = errno;
1909 TALLOC_FREE(conv);
1910 errno = saved_errno;
1911 return ret;
1914 static int snapper_gmt_rmdir(vfs_handle_struct *handle, const char *fname)
1916 time_t timestamp;
1917 char *stripped;
1918 int ret, saved_errno;
1919 char *conv;
1921 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1922 &timestamp, &stripped)) {
1923 return -1;
1925 if (timestamp == 0) {
1926 return SMB_VFS_NEXT_RMDIR(handle, fname);
1928 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1929 TALLOC_FREE(stripped);
1930 if (conv == NULL) {
1931 return -1;
1933 ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1934 saved_errno = errno;
1935 TALLOC_FREE(conv);
1936 errno = saved_errno;
1937 return ret;
1940 static int snapper_gmt_chflags(vfs_handle_struct *handle, const char *fname,
1941 unsigned int flags)
1943 time_t timestamp;
1944 char *stripped;
1945 int ret, saved_errno;
1946 char *conv;
1948 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1949 &timestamp, &stripped)) {
1950 return -1;
1952 if (timestamp == 0) {
1953 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1955 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1956 TALLOC_FREE(stripped);
1957 if (conv == NULL) {
1958 return -1;
1960 ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1961 saved_errno = errno;
1962 TALLOC_FREE(conv);
1963 errno = saved_errno;
1964 return ret;
1967 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
1968 const char *fname, const char *aname,
1969 void *value, size_t size)
1971 time_t timestamp;
1972 char *stripped;
1973 ssize_t ret;
1974 int saved_errno;
1975 char *conv;
1977 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1978 &timestamp, &stripped)) {
1979 return -1;
1981 if (timestamp == 0) {
1982 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1983 size);
1985 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1986 TALLOC_FREE(stripped);
1987 if (conv == NULL) {
1988 return -1;
1990 ret = SMB_VFS_NEXT_GETXATTR(handle, conv, aname, value, size);
1991 saved_errno = errno;
1992 TALLOC_FREE(conv);
1993 errno = saved_errno;
1994 return ret;
1997 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
1998 const char *fname,
1999 char *list, size_t size)
2001 time_t timestamp;
2002 char *stripped;
2003 ssize_t ret;
2004 int saved_errno;
2005 char *conv;
2007 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
2008 &timestamp, &stripped)) {
2009 return -1;
2011 if (timestamp == 0) {
2012 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
2014 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2015 TALLOC_FREE(stripped);
2016 if (conv == NULL) {
2017 return -1;
2019 ret = SMB_VFS_NEXT_LISTXATTR(handle, conv, list, size);
2020 saved_errno = errno;
2021 TALLOC_FREE(conv);
2022 errno = saved_errno;
2023 return ret;
2026 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
2027 const char *fname, const char *aname)
2029 time_t timestamp;
2030 char *stripped;
2031 int ret, saved_errno;
2032 char *conv;
2034 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
2035 &timestamp, &stripped)) {
2036 return -1;
2038 if (timestamp == 0) {
2039 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
2041 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2042 TALLOC_FREE(stripped);
2043 if (conv == NULL) {
2044 return -1;
2046 ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
2047 saved_errno = errno;
2048 TALLOC_FREE(conv);
2049 errno = saved_errno;
2050 return ret;
2053 static int snapper_gmt_setxattr(struct vfs_handle_struct *handle,
2054 const char *fname,
2055 const char *aname, const void *value,
2056 size_t size, int flags)
2058 time_t timestamp;
2059 char *stripped;
2060 ssize_t ret;
2061 int saved_errno;
2062 char *conv;
2064 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
2065 &timestamp, &stripped)) {
2066 return -1;
2068 if (timestamp == 0) {
2069 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
2070 flags);
2072 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2073 TALLOC_FREE(stripped);
2074 if (conv == NULL) {
2075 return -1;
2077 ret = SMB_VFS_NEXT_SETXATTR(handle, conv, aname, value, size, flags);
2078 saved_errno = errno;
2079 TALLOC_FREE(conv);
2080 errno = saved_errno;
2081 return ret;
2084 static int snapper_gmt_chmod_acl(vfs_handle_struct *handle,
2085 const char *fname, mode_t mode)
2087 time_t timestamp;
2088 char *stripped;
2089 ssize_t ret;
2090 int saved_errno;
2091 char *conv;
2093 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
2094 &timestamp, &stripped)) {
2095 return -1;
2097 if (timestamp == 0) {
2098 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
2100 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2101 TALLOC_FREE(stripped);
2102 if (conv == NULL) {
2103 return -1;
2105 ret = SMB_VFS_NEXT_CHMOD_ACL(handle, conv, mode);
2106 saved_errno = errno;
2107 TALLOC_FREE(conv);
2108 errno = saved_errno;
2109 return ret;
2112 static int snapper_gmt_get_real_filename(struct vfs_handle_struct *handle,
2113 const char *path,
2114 const char *name,
2115 TALLOC_CTX *mem_ctx,
2116 char **found_name)
2118 time_t timestamp;
2119 char *stripped;
2120 ssize_t ret;
2121 int saved_errno;
2122 char *conv;
2124 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
2125 &timestamp, &stripped)) {
2126 return -1;
2128 if (timestamp == 0) {
2129 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
2130 mem_ctx, found_name);
2132 if (stripped[0] == '\0') {
2133 *found_name = talloc_strdup(mem_ctx, name);
2134 if (*found_name == NULL) {
2135 errno = ENOMEM;
2136 return -1;
2138 return 0;
2140 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2141 TALLOC_FREE(stripped);
2142 if (conv == NULL) {
2143 return -1;
2145 ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
2146 mem_ctx, found_name);
2147 saved_errno = errno;
2148 TALLOC_FREE(conv);
2149 errno = saved_errno;
2150 return ret;
2153 static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
2154 const char *path, uint64_t *bsize,
2155 uint64_t *dfree, uint64_t *dsize)
2157 time_t timestamp;
2158 char *stripped;
2159 ssize_t ret;
2160 int saved_errno;
2161 char *conv;
2163 if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
2164 &timestamp, &stripped)) {
2165 return -1;
2167 if (timestamp == 0) {
2168 return SMB_VFS_NEXT_DISK_FREE(handle, path,
2169 bsize, dfree, dsize);
2172 conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2173 TALLOC_FREE(stripped);
2174 if (conv == NULL) {
2175 return -1;
2178 ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, bsize, dfree, dsize);
2180 saved_errno = errno;
2181 TALLOC_FREE(conv);
2182 errno = saved_errno;
2184 return ret;
2188 static struct vfs_fn_pointers snapper_fns = {
2189 .get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
2190 .opendir_fn = snapper_gmt_opendir,
2191 .disk_free_fn = snapper_gmt_disk_free,
2192 .rename_fn = snapper_gmt_rename,
2193 .link_fn = snapper_gmt_link,
2194 .symlink_fn = snapper_gmt_symlink,
2195 .stat_fn = snapper_gmt_stat,
2196 .lstat_fn = snapper_gmt_lstat,
2197 .fstat_fn = snapper_gmt_fstat,
2198 .open_fn = snapper_gmt_open,
2199 .unlink_fn = snapper_gmt_unlink,
2200 .chmod_fn = snapper_gmt_chmod,
2201 .chown_fn = snapper_gmt_chown,
2202 .chdir_fn = snapper_gmt_chdir,
2203 .ntimes_fn = snapper_gmt_ntimes,
2204 .readlink_fn = snapper_gmt_readlink,
2205 .mknod_fn = snapper_gmt_mknod,
2206 .realpath_fn = snapper_gmt_realpath,
2207 .get_nt_acl_fn = snapper_gmt_get_nt_acl,
2208 .fget_nt_acl_fn = snapper_gmt_fget_nt_acl,
2209 .mkdir_fn = snapper_gmt_mkdir,
2210 .rmdir_fn = snapper_gmt_rmdir,
2211 .getxattr_fn = snapper_gmt_getxattr,
2212 .listxattr_fn = snapper_gmt_listxattr,
2213 .removexattr_fn = snapper_gmt_removexattr,
2214 .setxattr_fn = snapper_gmt_setxattr,
2215 .chmod_acl_fn = snapper_gmt_chmod_acl,
2216 .chflags_fn = snapper_gmt_chflags,
2217 .get_real_filename_fn = snapper_gmt_get_real_filename,
2220 NTSTATUS vfs_snapper_init(void);
2221 NTSTATUS vfs_snapper_init(void)
2223 return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2224 "snapper", &snapper_fns);