vfs_snapper: check for <linux/ioctl.h>
[kai/samba-autobuild/.git] / source3 / modules / vfs_snapper.c
1 /*
2  * Module for snapshot IO using snapper
3  *
4  * Copyright (C) David Disseldorp 2012-2014
5  *
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
12  *
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.
17  *
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.
22  *
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/>.
25  */
26
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"
40
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}"
44
45 struct snapper_dict {
46         char *key;
47         char *val;
48 };
49
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;
60 };
61
62 struct snapper_conf {
63         char *name;
64         char *mnt;
65         uint32_t num_attrs;
66         struct snapper_dict *attrs;
67 };
68
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 },
74 };
75
76 static NTSTATUS snapper_err_ntstatus_map(const char *snapper_err_str)
77 {
78         int i;
79
80         if (snapper_err_str == NULL) {
81                 return NT_STATUS_UNSUCCESSFUL;
82         }
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;
87                 }
88         }
89         DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str));
90
91         return NT_STATUS_UNSUCCESSFUL;
92 }
93
94 static DBusConnection *snapper_dbus_conn_create(void)
95 {
96         DBusError err;
97         DBusConnection *dconn;
98
99         dbus_error_init(&err);
100
101         /*
102          * Always create a new DBus connection, to ensure snapperd detects the
103          * correct client [E]UID. With dbus_bus_get() it does not!
104          */
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);
109         }
110         if (dconn == NULL) {
111                 return NULL;
112         }
113
114         /* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
115         dbus_connection_set_exit_on_disconnect(dconn, false);
116
117         return dconn;
118 }
119
120 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
121 {
122         if (dconn == NULL) {
123                 DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
124                 return;
125         }
126
127         dbus_connection_close(dconn);
128         dbus_connection_unref(dconn);
129 }
130
131 /*
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.
135  */
136 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
137                                        DBusMessage *send_msg,
138                                        DBusMessage **recv_msg_out)
139 {
140         DBusPendingCall *pending;
141         DBusMessage *recv_msg;
142
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;
146         }
147         if (NULL == pending) {
148                 DEBUG(0, ("dbus msg send failed\n"));
149                 return NT_STATUS_UNSUCCESSFUL;
150         }
151
152         dbus_connection_flush(dconn);
153
154         /* block until we receive a reply */
155         dbus_pending_call_block(pending);
156
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;
162         }
163         /* free the pending message handle */
164         dbus_pending_call_unref(pending);
165         *recv_msg_out = recv_msg;
166
167         return NT_STATUS_OK;
168 }
169
170 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
171                                    int expected_type)
172 {
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;
178         }
179
180         return NT_STATUS_OK;
181 }
182
183 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
184                                        int expected_type,
185                                        void *val)
186 {
187         NTSTATUS status;
188         status = snapper_type_check(iter, expected_type);
189         if (!NT_STATUS_IS_OK(status)) {
190                 return status;
191         }
192
193         dbus_message_iter_get_basic(iter, val);
194
195         return NT_STATUS_OK;
196 }
197
198 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
199                                     struct snapper_dict *dict_out)
200
201 {
202         NTSTATUS status;
203         DBusMessageIter dct_iter;
204
205         status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
206         if (!NT_STATUS_IS_OK(status)) {
207                 return status;
208         }
209         dbus_message_iter_recurse(iter, &dct_iter);
210
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;
215         }
216
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;
222         }
223
224         return NT_STATUS_OK;
225 }
226
227 static void snapper_dict_array_print(uint32_t num_dicts,
228                                      struct snapper_dict *dicts)
229 {
230         int i;
231
232         for (i = 0; i < num_dicts; i++) {
233                 DEBUG(10, ("dict (key: %s, val: %s)\n",
234                            dicts[i].key, dicts[i].val));
235         }
236 }
237
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)
242 {
243         NTSTATUS status;
244         DBusMessageIter array_iter;
245         uint32_t num_dicts;
246         struct snapper_dict *dicts = NULL;
247
248         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
249         if (!NT_STATUS_IS_OK(status)) {
250                 return status;
251         }
252         dbus_message_iter_recurse(iter, &array_iter);
253
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();
262
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;
268                 }
269                 dbus_message_iter_next(&array_iter);
270         }
271
272         *num_dicts_out = num_dicts;
273         *dicts_out = dicts;
274
275         return NT_STATUS_OK;
276 }
277
278 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
279 {
280         DBusMessage *msg;
281
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;
289         }
290
291         /* no arguments to append */
292         *req_msg_out = msg;
293
294         return NT_STATUS_OK;
295 }
296
297 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
298                                     DBusMessageIter *iter,
299                                     struct snapper_conf *conf_out)
300 {
301         NTSTATUS status;
302         DBusMessageIter st_iter;
303
304         status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
305         if (!NT_STATUS_IS_OK(status)) {
306                 return status;
307         }
308         dbus_message_iter_recurse(iter, &st_iter);
309
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;
314         }
315
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;
321         }
322
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);
327
328         return status;
329 }
330
331 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
332                                                 struct snapper_conf *confs,
333                                                          const char *base)
334 {
335         int i;
336
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];
342                 }
343         }
344         DEBUG(5, ("config for base %s not found\n", base));
345
346         return NULL;
347 }
348
349 static void snapper_conf_array_print(int32_t num_confs,
350                                      struct snapper_conf *confs)
351 {
352         int i;
353
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);
358         }
359 }
360
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)
365 {
366         uint32_t num_confs;
367         NTSTATUS status;
368         struct snapper_conf *confs = NULL;
369         DBusMessageIter array_iter;
370
371
372         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
373         if (!NT_STATUS_IS_OK(status)) {
374                 return status;
375         }
376         dbus_message_iter_recurse(iter, &array_iter);
377
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();
386
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;
392                 }
393                 dbus_message_iter_next(&array_iter);
394         }
395
396         *num_confs_out = num_confs;
397         *confs_out = confs;
398
399         return NT_STATUS_OK;
400 }
401
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)
407 {
408         NTSTATUS status;
409         DBusMessageIter iter;
410         int msg_type;
411         uint32_t num_confs;
412         struct snapper_conf *confs;
413         const char *sig;
414
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);
420         }
421
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;
426         }
427
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;
434         }
435
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;
440         }
441
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;
446         }
447
448         snapper_conf_array_print(num_confs, confs);
449
450         *num_confs_out = num_confs;
451         *confs_out = confs;
452
453         return NT_STATUS_OK;
454 }
455
456 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
457                                         DBusMessage **req_msg_out)
458 {
459         DBusMessage *msg;
460         DBusMessageIter args;
461
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;
469         }
470
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;
476         }
477
478         *req_msg_out = msg;
479
480         return NT_STATUS_OK;
481 }
482
483 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
484                                            DBusMessageIter *iter,
485                                            struct snapper_snap *snap_out)
486 {
487         NTSTATUS status;
488         DBusMessageIter st_iter;
489
490         status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
491         if (!NT_STATUS_IS_OK(status)) {
492                 return status;
493         }
494         dbus_message_iter_recurse(iter, &st_iter);
495
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;
500         }
501
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;
507         }
508
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;
514         }
515
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;
521         }
522
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;
528         }
529
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;
535         }
536
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;
542         }
543
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);
548
549         return status;
550 }
551
552 static void snapper_snap_array_print(int32_t num_snaps,
553                                      struct snapper_snap *snaps)
554 {
555         int i;
556
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);
574         }
575 }
576
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)
581 {
582         uint32_t num_snaps;
583         NTSTATUS status;
584         struct snapper_snap *snaps = NULL;
585         DBusMessageIter array_iter;
586
587
588         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
589         if (!NT_STATUS_IS_OK(status)) {
590                 return status;
591         }
592         dbus_message_iter_recurse(iter, &array_iter);
593
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();
602
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;
608                 }
609                 dbus_message_iter_next(&array_iter);
610         }
611
612         *num_snaps_out = num_snaps;
613         *snaps_out = snaps;
614
615         return NT_STATUS_OK;
616 }
617
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)
622 {
623         NTSTATUS status;
624         DBusMessageIter iter;
625         int msg_type;
626         uint32_t num_snaps;
627         struct snapper_snap *snaps;
628         const char *sig;
629
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);
635         }
636
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;
641         }
642
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;
651         }
652
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;
657         }
658
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;
663         }
664
665         snapper_snap_array_print(num_snaps, snaps);
666
667         *num_snaps_out = num_snaps;
668         *snaps_out = snaps;
669
670         return NT_STATUS_OK;
671 }
672
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)
677 {
678         DBusMessage *msg;
679         DBusMessageIter args;
680
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;
688         }
689
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;
694         }
695
696         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
697                                             &time_lower)) {
698                 return NT_STATUS_NO_MEMORY;
699         }
700
701         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
702                                             &time_upper)) {
703                 return NT_STATUS_NO_MEMORY;
704         }
705
706         *req_msg_out = msg;
707
708         return NT_STATUS_OK;
709 }
710 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
711
712 /*
713  * Determine the snapper snapshot path given an id and base.
714  * Ideally this should be determined via a lookup.
715  */
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)
720 {
721         char *snap_path;
722
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;
727         }
728
729         *snap_path_out = snap_path;
730         return NT_STATUS_OK;
731 }
732
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)
738 {
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;
747
748         status = snapper_list_confs_pack(&req_msg);
749         if (!NT_STATUS_IS_OK(status)) {
750                 goto err_out;
751         }
752
753         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
754         if (!NT_STATUS_IS_OK(status)) {
755                 goto err_req_free;
756         }
757
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;
762         }
763
764         /*
765          * for now we only support shares where the path directly corresponds
766          * to a snapper configuration.
767          */
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;
773         }
774
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;
779         }
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;
784         }
785
786         talloc_free(confs);
787         dbus_message_unref(rsp_msg);
788         dbus_message_unref(req_msg);
789
790         *conf_name_out = conf_name;
791         *base_path_out = base_path;
792
793         return NT_STATUS_OK;
794
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;
805 }
806
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)
812 {
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;
824
825         tmp_ctx = talloc_new(sc_data);
826         if (tmp_ctx == NULL) {
827                 status = NT_STATUS_NO_MEMORY;
828                 goto err_out;
829         }
830
831         dconn = snapper_dbus_conn_create();
832         if (dconn == NULL) {
833                 status = NT_STATUS_UNSUCCESSFUL;
834                 goto err_mem_ctx_free;
835         }
836
837         if (fsp->conn->connectpath == NULL) {
838                 status = NT_STATUS_INVALID_PARAMETER;
839                 goto err_conn_free;
840         }
841
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;
848         }
849
850         status = snapper_list_snaps_pack(conf_name, &req_msg);
851         if (!NT_STATUS_IS_OK(status)) {
852                 goto err_conn_free;
853         }
854
855         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
856         if (!NT_STATUS_IS_OK(status)) {
857                 goto err_req_free;
858         }
859
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;
864         }
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;
870         }
871
872         /* subtract 1, (current) snapshot is not returned */
873         sc_data->num_volumes = num_snaps - 1;
874         sc_data->labels = NULL;
875
876         if ((labels == false) || (sc_data->num_volumes == 0)) {
877                 /* tokens need not be added to the labels array */
878                 goto done;
879         }
880
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;
886         }
887
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;
895
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;
900                 }
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;
906                 }
907         }
908
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);
914
915         return 0;
916
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;
930 }
931
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)
937 {
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;
944
945         p = strstr_m(name, "@GMT-");
946         if (p == NULL) {
947                 goto no_snapshot;
948         }
949         if ((p > name) && (p[-1] != '/')) {
950                 goto no_snapshot;
951         }
952         q = strptime(p, GMT_FORMAT, &tm);
953         if (q == NULL) {
954                 goto no_snapshot;
955         }
956         tm.tm_isdst = -1;
957         timestamp = timegm(&tm);
958         if (timestamp == (time_t)-1) {
959                 goto no_snapshot;
960         }
961         if ((p == name) && (q[0] == '\0')) {
962                 if (pstripped != NULL) {
963                         stripped = talloc_strdup(mem_ctx, "");
964                         if (stripped == NULL) {
965                                 return false;
966                         }
967                         *pstripped = stripped;
968                 }
969                 *ptimestamp = timestamp;
970                 return true;
971         }
972         if (q[0] != '/') {
973                 goto no_snapshot;
974         }
975         q += 1;
976
977         rest_len = strlen(q);
978         dst_len = (p-name) + rest_len;
979
980         if (pstripped != NULL) {
981                 stripped = talloc_array(mem_ctx, char, dst_len+1);
982                 if (stripped == NULL) {
983                         errno = ENOMEM;
984                         return false;
985                 }
986                 if (p > name) {
987                         memcpy(stripped, name, p-name);
988                 }
989                 if (rest_len > 0) {
990                         memcpy(stripped + (p-name), q, rest_len);
991                 }
992                 stripped[dst_len] = '\0';
993                 *pstripped = stripped;
994         }
995         *ptimestamp = timestamp;
996         return true;
997 no_snapshot:
998         *ptimestamp = 0;
999         return true;
1000 }
1001
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)
1008 {
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;
1015
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;
1022         }
1023
1024         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1025         if (!NT_STATUS_IS_OK(status)) {
1026                 goto err_req_free;
1027         }
1028
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;
1033         }
1034
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));
1042         }
1043
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;
1048         }
1049
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;
1059 }
1060
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)
1065 {
1066         DBusConnection *dconn;
1067         NTSTATUS status;
1068         char *conf_name;
1069         char *base_path;
1070         char *snap_path;
1071
1072         dconn = snapper_dbus_conn_create();
1073         if (dconn == NULL) {
1074                 status = NT_STATUS_UNSUCCESSFUL;
1075                 goto err_out;
1076         }
1077
1078         if (conn->connectpath == NULL) {
1079                 status = NT_STATUS_INVALID_PARAMETER;
1080                 goto err_conn_free;
1081         }
1082
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;
1089         }
1090
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;
1096         }
1097
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;
1102         }
1103
1104         talloc_free(conf_name);
1105         talloc_free(base_path);
1106         snapper_dbus_conn_destroy(dconn);
1107         *snap_dir_out = snap_path;
1108
1109         return NT_STATUS_OK;
1110
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;
1120 }
1121
1122 static char *snapper_gmt_convert(TALLOC_CTX *mem_ctx,
1123                              struct vfs_handle_struct *handle,
1124                              const char *name, time_t timestamp)
1125 {
1126         char *snap_path = NULL;
1127         char *path = NULL;
1128         NTSTATUS status;
1129         int saved_errno;
1130
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;
1136         }
1137
1138         path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1139         if (path == NULL) {
1140                 errno = ENOMEM;
1141                 goto err_snap_path_free;
1142         }
1143
1144         DEBUG(10, ("converted %s/%s @ time to %s\n",
1145                    handle->conn->connectpath, name, path));
1146         return path;
1147
1148 err_snap_path_free:
1149         saved_errno = errno;
1150         talloc_free(snap_path);
1151         errno = saved_errno;
1152 err_out:
1153         return NULL;
1154 }
1155
1156 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1157                                 const char *fname,
1158                                 const char *mask,
1159                                 uint32_t attr)
1160 {
1161         time_t timestamp;
1162         char *stripped;
1163         DIR *ret;
1164         int saved_errno;
1165         char *conv;
1166
1167         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1168                                         &timestamp, &stripped)) {
1169                 return NULL;
1170         }
1171         if (timestamp == 0) {
1172                 return SMB_VFS_NEXT_OPENDIR(handle, fname, mask, attr);
1173         }
1174         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1175         TALLOC_FREE(stripped);
1176         if (conv == NULL) {
1177                 return NULL;
1178         }
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;
1184 }
1185
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)
1189 {
1190         time_t timestamp_src, timestamp_dst;
1191
1192         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1193                                         smb_fname_src->base_name,
1194                                         &timestamp_src, NULL)) {
1195                 return -1;
1196         }
1197         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1198                                         smb_fname_dst->base_name,
1199                                         &timestamp_dst, NULL)) {
1200                 return -1;
1201         }
1202         if (timestamp_src != 0) {
1203                 errno = EXDEV;
1204                 return -1;
1205         }
1206         if (timestamp_dst != 0) {
1207                 errno = EROFS;
1208                 return -1;
1209         }
1210         return SMB_VFS_NEXT_RENAME(handle, smb_fname_src, smb_fname_dst);
1211 }
1212
1213 static int snapper_gmt_symlink(vfs_handle_struct *handle,
1214                                const char *oldname, const char *newname)
1215 {
1216         time_t timestamp_old, timestamp_new;
1217
1218         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1219                                         &timestamp_old, NULL)) {
1220                 return -1;
1221         }
1222         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1223                                         &timestamp_new, NULL)) {
1224                 return -1;
1225         }
1226         if ((timestamp_old != 0) || (timestamp_new != 0)) {
1227                 errno = EROFS;
1228                 return -1;
1229         }
1230         return SMB_VFS_NEXT_SYMLINK(handle, oldname, newname);
1231 }
1232
1233 static int snapper_gmt_link(vfs_handle_struct *handle,
1234                             const char *oldname, const char *newname)
1235 {
1236         time_t timestamp_old, timestamp_new;
1237
1238         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, oldname,
1239                                         &timestamp_old, NULL)) {
1240                 return -1;
1241         }
1242         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, newname,
1243                                         &timestamp_new, NULL)) {
1244                 return -1;
1245         }
1246         if ((timestamp_old != 0) || (timestamp_new != 0)) {
1247                 errno = EROFS;
1248                 return -1;
1249         }
1250         return SMB_VFS_NEXT_LINK(handle, oldname, newname);
1251 }
1252
1253 static int snapper_gmt_stat(vfs_handle_struct *handle,
1254                             struct smb_filename *smb_fname)
1255 {
1256         time_t timestamp;
1257         char *stripped, *tmp;
1258         int ret, saved_errno;
1259
1260         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1261                                         smb_fname->base_name,
1262                                         &timestamp, &stripped)) {
1263                 return -1;
1264         }
1265         if (timestamp == 0) {
1266                 return SMB_VFS_NEXT_STAT(handle, smb_fname);
1267         }
1268
1269         tmp = smb_fname->base_name;
1270         smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1271                                                    stripped, timestamp);
1272         TALLOC_FREE(stripped);
1273
1274         if (smb_fname->base_name == NULL) {
1275                 smb_fname->base_name = tmp;
1276                 return -1;
1277         }
1278
1279         ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
1280         saved_errno = errno;
1281
1282         TALLOC_FREE(smb_fname->base_name);
1283         smb_fname->base_name = tmp;
1284
1285         errno = saved_errno;
1286         return ret;
1287 }
1288
1289 static int snapper_gmt_lstat(vfs_handle_struct *handle,
1290                              struct smb_filename *smb_fname)
1291 {
1292         time_t timestamp;
1293         char *stripped, *tmp;
1294         int ret, saved_errno;
1295
1296         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1297                                         smb_fname->base_name,
1298                                         &timestamp, &stripped)) {
1299                 return -1;
1300         }
1301         if (timestamp == 0) {
1302                 return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1303         }
1304
1305         tmp = smb_fname->base_name;
1306         smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1307                                                    stripped, timestamp);
1308         TALLOC_FREE(stripped);
1309
1310         if (smb_fname->base_name == NULL) {
1311                 smb_fname->base_name = tmp;
1312                 return -1;
1313         }
1314
1315         ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
1316         saved_errno = errno;
1317
1318         TALLOC_FREE(smb_fname->base_name);
1319         smb_fname->base_name = tmp;
1320
1321         errno = saved_errno;
1322         return ret;
1323 }
1324
1325 static int snapper_gmt_fstat(vfs_handle_struct *handle, files_struct *fsp,
1326                              SMB_STRUCT_STAT *sbuf)
1327 {
1328         time_t timestamp;
1329         int ret;
1330
1331         ret = SMB_VFS_NEXT_FSTAT(handle, fsp, sbuf);
1332         if (ret == -1) {
1333                 return ret;
1334         }
1335         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1336                                         fsp->fsp_name->base_name,
1337                                         &timestamp, NULL)) {
1338                 return 0;
1339         }
1340         return 0;
1341 }
1342
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)
1346 {
1347         time_t timestamp;
1348         char *stripped, *tmp;
1349         int ret, saved_errno;
1350
1351         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1352                                         smb_fname->base_name,
1353                                         &timestamp, &stripped)) {
1354                 return -1;
1355         }
1356         if (timestamp == 0) {
1357                 return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1358         }
1359
1360         tmp = smb_fname->base_name;
1361         smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
1362                                                    stripped, timestamp);
1363         TALLOC_FREE(stripped);
1364
1365         if (smb_fname->base_name == NULL) {
1366                 smb_fname->base_name = tmp;
1367                 return -1;
1368         }
1369
1370         ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
1371         saved_errno = errno;
1372
1373         TALLOC_FREE(smb_fname->base_name);
1374         smb_fname->base_name = tmp;
1375
1376         errno = saved_errno;
1377         return ret;
1378 }
1379
1380 static int snapper_gmt_unlink(vfs_handle_struct *handle,
1381                               const struct smb_filename *smb_fname)
1382 {
1383         time_t timestamp;
1384         char *stripped;
1385         int ret, saved_errno;
1386         struct smb_filename *conv;
1387
1388         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1389                                         smb_fname->base_name,
1390                                         &timestamp, &stripped)) {
1391                 return -1;
1392         }
1393         if (timestamp == 0) {
1394                 return SMB_VFS_NEXT_UNLINK(handle, smb_fname);
1395         }
1396         conv = cp_smb_filename(talloc_tos(), smb_fname);
1397         if (conv == NULL) {
1398                 errno = ENOMEM;
1399                 return -1;
1400         }
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;
1406         }
1407         ret = SMB_VFS_NEXT_UNLINK(handle, conv);
1408         saved_errno = errno;
1409         TALLOC_FREE(conv);
1410         errno = saved_errno;
1411         return ret;
1412 }
1413
1414 static int snapper_gmt_chmod(vfs_handle_struct *handle, const char *fname,
1415                              mode_t mode)
1416 {
1417         time_t timestamp;
1418         char *stripped;
1419         int ret, saved_errno;
1420         char *conv;
1421
1422         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1423                                         &timestamp, &stripped)) {
1424                 return -1;
1425         }
1426         if (timestamp == 0) {
1427                 return SMB_VFS_NEXT_CHMOD(handle, fname, mode);
1428         }
1429         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1430         TALLOC_FREE(stripped);
1431         if (conv == NULL) {
1432                 return -1;
1433         }
1434         ret = SMB_VFS_NEXT_CHMOD(handle, conv, mode);
1435         saved_errno = errno;
1436         TALLOC_FREE(conv);
1437         errno = saved_errno;
1438         return ret;
1439 }
1440
1441 static int snapper_gmt_chown(vfs_handle_struct *handle, const char *fname,
1442                              uid_t uid, gid_t gid)
1443 {
1444         time_t timestamp;
1445         char *stripped;
1446         int ret, saved_errno;
1447         char *conv;
1448
1449         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1450                                         &timestamp, &stripped)) {
1451                 return -1;
1452         }
1453         if (timestamp == 0) {
1454                 return SMB_VFS_NEXT_CHOWN(handle, fname, uid, gid);
1455         }
1456         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1457         TALLOC_FREE(stripped);
1458         if (conv == NULL) {
1459                 return -1;
1460         }
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;
1466 }
1467
1468 static int snapper_gmt_chdir(vfs_handle_struct *handle,
1469                              const char *fname)
1470 {
1471         time_t timestamp;
1472         char *stripped;
1473         int ret, saved_errno;
1474         char *conv;
1475
1476         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1477                                         &timestamp, &stripped)) {
1478                 return -1;
1479         }
1480         if (timestamp == 0) {
1481                 return SMB_VFS_NEXT_CHDIR(handle, fname);
1482         }
1483         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1484         TALLOC_FREE(stripped);
1485         if (conv == NULL) {
1486                 return -1;
1487         }
1488         ret = SMB_VFS_NEXT_CHDIR(handle, conv);
1489         saved_errno = errno;
1490         TALLOC_FREE(conv);
1491         errno = saved_errno;
1492         return ret;
1493 }
1494
1495 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
1496                               const struct smb_filename *smb_fname,
1497                               struct smb_file_time *ft)
1498 {
1499         time_t timestamp;
1500         char *stripped;
1501         int ret, saved_errno;
1502         struct smb_filename *conv;
1503
1504         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
1505                                         smb_fname->base_name,
1506                                         &timestamp, &stripped)) {
1507                 return -1;
1508         }
1509         if (timestamp == 0) {
1510                 return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
1511         }
1512         conv = cp_smb_filename(talloc_tos(), smb_fname);
1513         if (conv == NULL) {
1514                 errno = ENOMEM;
1515                 return -1;
1516         }
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;
1522         }
1523         ret = SMB_VFS_NEXT_NTIMES(handle, conv, ft);
1524         saved_errno = errno;
1525         TALLOC_FREE(conv);
1526         errno = saved_errno;
1527         return ret;
1528 }
1529
1530 static int snapper_gmt_readlink(vfs_handle_struct *handle,
1531                                 const char *fname, char *buf, size_t bufsiz)
1532 {
1533         time_t timestamp;
1534         char *stripped;
1535         int ret, saved_errno;
1536         char *conv;
1537
1538         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1539                                         &timestamp, &stripped)) {
1540                 return -1;
1541         }
1542         if (timestamp == 0) {
1543                 return SMB_VFS_NEXT_READLINK(handle, fname, buf, bufsiz);
1544         }
1545         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1546         TALLOC_FREE(stripped);
1547         if (conv == NULL) {
1548                 return -1;
1549         }
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;
1555 }
1556
1557 static int snapper_gmt_mknod(vfs_handle_struct *handle,
1558                              const char *fname, mode_t mode, SMB_DEV_T dev)
1559 {
1560         time_t timestamp;
1561         char *stripped;
1562         int ret, saved_errno;
1563         char *conv;
1564
1565         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1566                                         &timestamp, &stripped)) {
1567                 return -1;
1568         }
1569         if (timestamp == 0) {
1570                 return SMB_VFS_NEXT_MKNOD(handle, fname, mode, dev);
1571         }
1572         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1573         TALLOC_FREE(stripped);
1574         if (conv == NULL) {
1575                 return -1;
1576         }
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;
1582 }
1583
1584 static char *snapper_gmt_realpath(vfs_handle_struct *handle,
1585                                   const char *fname)
1586 {
1587         time_t timestamp;
1588         char *stripped = NULL;
1589         char *tmp = NULL;
1590         char *result = NULL;
1591         int saved_errno;
1592
1593         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1594                                         &timestamp, &stripped)) {
1595                 goto done;
1596         }
1597         if (timestamp == 0) {
1598                 return SMB_VFS_NEXT_REALPATH(handle, fname);
1599         }
1600
1601         tmp = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1602         if (tmp == NULL) {
1603                 goto done;
1604         }
1605
1606         result = SMB_VFS_NEXT_REALPATH(handle, tmp);
1607         if (result == NULL) {
1608                 goto done;
1609         }
1610
1611 done:
1612         saved_errno = errno;
1613         TALLOC_FREE(tmp);
1614         TALLOC_FREE(stripped);
1615         errno = saved_errno;
1616         return result;
1617 }
1618
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)
1624 {
1625         time_t timestamp;
1626         char *stripped;
1627         NTSTATUS status;
1628         char *conv;
1629
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);
1634         }
1635         if (timestamp == 0) {
1636                 return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
1637                                                 mem_ctx,
1638                                                 ppdesc);
1639         }
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);
1644         }
1645         status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1646                                          mem_ctx, ppdesc);
1647         TALLOC_FREE(conv);
1648         return status;
1649 }
1650
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)
1656 {
1657         time_t timestamp;
1658         char *stripped;
1659         NTSTATUS status;
1660         char *conv;
1661
1662         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1663                                         &timestamp, &stripped)) {
1664                 return map_nt_error_from_unix(errno);
1665         }
1666         if (timestamp == 0) {
1667                 return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
1668                                                mem_ctx, ppdesc);
1669         }
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);
1674         }
1675         status = SMB_VFS_NEXT_GET_NT_ACL(handle, conv, security_info,
1676                                          mem_ctx, ppdesc);
1677         TALLOC_FREE(conv);
1678         return status;
1679 }
1680
1681 static int snapper_gmt_mkdir(vfs_handle_struct *handle,
1682                              const char *fname, mode_t mode)
1683 {
1684         time_t timestamp;
1685         char *stripped;
1686         int ret, saved_errno;
1687         char *conv;
1688
1689         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1690                                         &timestamp, &stripped)) {
1691                 return -1;
1692         }
1693         if (timestamp == 0) {
1694                 return SMB_VFS_NEXT_MKDIR(handle, fname, mode);
1695         }
1696         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1697         TALLOC_FREE(stripped);
1698         if (conv == NULL) {
1699                 return -1;
1700         }
1701         ret = SMB_VFS_NEXT_MKDIR(handle, conv, mode);
1702         saved_errno = errno;
1703         TALLOC_FREE(conv);
1704         errno = saved_errno;
1705         return ret;
1706 }
1707
1708 static int snapper_gmt_rmdir(vfs_handle_struct *handle, const char *fname)
1709 {
1710         time_t timestamp;
1711         char *stripped;
1712         int ret, saved_errno;
1713         char *conv;
1714
1715         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1716                                         &timestamp, &stripped)) {
1717                 return -1;
1718         }
1719         if (timestamp == 0) {
1720                 return SMB_VFS_NEXT_RMDIR(handle, fname);
1721         }
1722         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1723         TALLOC_FREE(stripped);
1724         if (conv == NULL) {
1725                 return -1;
1726         }
1727         ret = SMB_VFS_NEXT_RMDIR(handle, conv);
1728         saved_errno = errno;
1729         TALLOC_FREE(conv);
1730         errno = saved_errno;
1731         return ret;
1732 }
1733
1734 static int snapper_gmt_chflags(vfs_handle_struct *handle, const char *fname,
1735                                unsigned int flags)
1736 {
1737         time_t timestamp;
1738         char *stripped;
1739         int ret, saved_errno;
1740         char *conv;
1741
1742         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1743                                         &timestamp, &stripped)) {
1744                 return -1;
1745         }
1746         if (timestamp == 0) {
1747                 return SMB_VFS_NEXT_CHFLAGS(handle, fname, flags);
1748         }
1749         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1750         TALLOC_FREE(stripped);
1751         if (conv == NULL) {
1752                 return -1;
1753         }
1754         ret = SMB_VFS_NEXT_CHFLAGS(handle, conv, flags);
1755         saved_errno = errno;
1756         TALLOC_FREE(conv);
1757         errno = saved_errno;
1758         return ret;
1759 }
1760
1761 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
1762                                     const char *fname, const char *aname,
1763                                     void *value, size_t size)
1764 {
1765         time_t timestamp;
1766         char *stripped;
1767         ssize_t ret;
1768         int saved_errno;
1769         char *conv;
1770
1771         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1772                                         &timestamp, &stripped)) {
1773                 return -1;
1774         }
1775         if (timestamp == 0) {
1776                 return SMB_VFS_NEXT_GETXATTR(handle, fname, aname, value,
1777                                              size);
1778         }
1779         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1780         TALLOC_FREE(stripped);
1781         if (conv == NULL) {
1782                 return -1;
1783         }
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;
1789 }
1790
1791 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
1792                                      const char *fname,
1793                                      char *list, size_t size)
1794 {
1795         time_t timestamp;
1796         char *stripped;
1797         ssize_t ret;
1798         int saved_errno;
1799         char *conv;
1800
1801         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1802                                         &timestamp, &stripped)) {
1803                 return -1;
1804         }
1805         if (timestamp == 0) {
1806                 return SMB_VFS_NEXT_LISTXATTR(handle, fname, list, size);
1807         }
1808         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1809         TALLOC_FREE(stripped);
1810         if (conv == NULL) {
1811                 return -1;
1812         }
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;
1818 }
1819
1820 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
1821                                    const char *fname, const char *aname)
1822 {
1823         time_t timestamp;
1824         char *stripped;
1825         int ret, saved_errno;
1826         char *conv;
1827
1828         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1829                                         &timestamp, &stripped)) {
1830                 return -1;
1831         }
1832         if (timestamp == 0) {
1833                 return SMB_VFS_NEXT_REMOVEXATTR(handle, fname, aname);
1834         }
1835         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1836         TALLOC_FREE(stripped);
1837         if (conv == NULL) {
1838                 return -1;
1839         }
1840         ret = SMB_VFS_NEXT_REMOVEXATTR(handle, conv, aname);
1841         saved_errno = errno;
1842         TALLOC_FREE(conv);
1843         errno = saved_errno;
1844         return ret;
1845 }
1846
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)
1851 {
1852         time_t timestamp;
1853         char *stripped;
1854         ssize_t ret;
1855         int saved_errno;
1856         char *conv;
1857
1858         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1859                                         &timestamp, &stripped)) {
1860                 return -1;
1861         }
1862         if (timestamp == 0) {
1863                 return SMB_VFS_NEXT_SETXATTR(handle, fname, aname, value, size,
1864                                              flags);
1865         }
1866         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1867         TALLOC_FREE(stripped);
1868         if (conv == NULL) {
1869                 return -1;
1870         }
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;
1876 }
1877
1878 static int snapper_gmt_chmod_acl(vfs_handle_struct *handle,
1879                                  const char *fname, mode_t mode)
1880 {
1881         time_t timestamp;
1882         char *stripped;
1883         ssize_t ret;
1884         int saved_errno;
1885         char *conv;
1886
1887         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname,
1888                                         &timestamp, &stripped)) {
1889                 return -1;
1890         }
1891         if (timestamp == 0) {
1892                 return SMB_VFS_NEXT_CHMOD_ACL(handle, fname, mode);
1893         }
1894         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1895         TALLOC_FREE(stripped);
1896         if (conv == NULL) {
1897                 return -1;
1898         }
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;
1904 }
1905
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)
1911 {
1912         time_t timestamp;
1913         char *stripped;
1914         ssize_t ret;
1915         int saved_errno;
1916         char *conv;
1917
1918         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1919                                         &timestamp, &stripped)) {
1920                 return -1;
1921         }
1922         if (timestamp == 0) {
1923                 return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
1924                                                       mem_ctx, found_name);
1925         }
1926         if (stripped[0] == '\0') {
1927                 *found_name = talloc_strdup(mem_ctx, name);
1928                 if (*found_name == NULL) {
1929                         errno = ENOMEM;
1930                         return -1;
1931                 }
1932                 return 0;
1933         }
1934         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1935         TALLOC_FREE(stripped);
1936         if (conv == NULL) {
1937                 return -1;
1938         }
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;
1945 }
1946
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)
1951 {
1952         time_t timestamp;
1953         char *stripped;
1954         ssize_t ret;
1955         int saved_errno;
1956         char *conv;
1957
1958         if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
1959                                         &timestamp, &stripped)) {
1960                 return -1;
1961         }
1962         if (timestamp == 0) {
1963                 return SMB_VFS_NEXT_DISK_FREE(handle, path, small_query,
1964                                               bsize, dfree, dsize);
1965         }
1966
1967         conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1968         TALLOC_FREE(stripped);
1969         if (conv == NULL) {
1970                 return -1;
1971         }
1972
1973         ret = SMB_VFS_NEXT_DISK_FREE(handle, conv, small_query, bsize, dfree,
1974                                      dsize);
1975
1976         saved_errno = errno;
1977         TALLOC_FREE(conv);
1978         errno = saved_errno;
1979
1980         return ret;
1981 }
1982
1983
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,
2014 };
2015
2016 NTSTATUS vfs_snapper_init(void);
2017 NTSTATUS vfs_snapper_init(void)
2018 {
2019         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2020                                 "snapper", &snapper_fns);
2021 }