62eecf7af8c0944d89163e35e5abdb13c987c5ea
[ddiss/samba.git] / source3 / modules / vfs_snapper.c
1 /*
2  * Module for snapshot management using snapper
3  *
4  * Copyright (C) David Disseldorp 2012
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <dbus/dbus.h>
21 #include <linux/ioctl.h>
22 #include <sys/ioctl.h>
23 #include <dirent.h>
24 #include <libgen.h>
25 #include "includes.h"
26 #include "include/ntioctl.h"
27 #include "system/filesys.h"
28 #include "smbd/smbd.h"
29 #include "lib/util/tevent_ntstatus.h"
30
31 #define SNAPPER_SIG_LIST_SNAPS_RSP "a(uqutussa{ss})"
32 #define SNAPPER_SIG_LIST_CONFS_RSP "a(ssa{ss})"
33 #define SNAPPER_SIG_CREATE_SNAP_RSP "u"
34 #define SNAPPER_SIG_DEL_SNAPS_RSP ""
35 #define SNAPPER_SIG_STRING_DICT "{ss}"
36
37 struct snapper_dict {
38         char *key;
39         char *val;
40 };
41
42 struct snapper_snap {
43         uint32_t id;
44         uint16_t type;
45         uint32_t pre_id;
46         uint64_t time;
47         uint32_t creator_uid;
48         char *desc;
49         char *cleanup;
50         uint32_t num_user_data;
51         struct snapper_dict *user_data;
52 };
53
54 struct snapper_conf {
55         char *name;
56         char *mnt;
57         uint32_t num_attrs;
58         struct snapper_dict *attrs;
59 };
60
61 static DBusConnection *snapper_dbus_conn(void)
62 {
63         DBusError err;
64         DBusConnection *dconn;
65
66         dbus_error_init(&err);
67
68         dconn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
69         if (dbus_error_is_set(&err)) {
70                 DEBUG(0, ("dbus connection error: %s\n", err.message));
71                 dbus_error_free(&err);
72         }
73         if (dconn == NULL) {
74                 return NULL;
75         }
76
77         return dconn;
78 }
79
80 /*
81  * send the message @send_msg over the dbus and wait for a response, return the
82  * responsee via @recv_msg_out.
83  * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
84  */
85 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
86                                        DBusMessage *send_msg,
87                                        DBusMessage **recv_msg_out)
88 {
89         DBusPendingCall *pending;
90         DBusMessage *recv_msg;
91
92         /* send message and get a handle for a reply */
93         if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
94                 return NT_STATUS_NO_MEMORY;
95         }
96         if (NULL == pending) {
97                 DEBUG(0, ("dbus msg send failed\n"));
98                 return NT_STATUS_UNSUCCESSFUL;
99         }
100
101         dbus_connection_flush(dconn);
102
103         /* block until we receive a reply */
104         dbus_pending_call_block(pending);
105
106         /* get the reply message */
107         recv_msg = dbus_pending_call_steal_reply(pending);
108         if (recv_msg == NULL) {
109                 DEBUG(0, ("Reply Null\n"));
110                 return NT_STATUS_UNSUCCESSFUL;
111         }
112         /* free the pending message handle */
113         dbus_pending_call_unref(pending);
114         *recv_msg_out = recv_msg;
115
116         return NT_STATUS_OK;
117 }
118
119 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
120                                    int expected_type)
121 {
122         int type = dbus_message_iter_get_arg_type(iter);
123         if (type != expected_type) {
124                 DEBUG(0, ("got type %d, expecting %d\n",
125                         type, expected_type));
126                 return NT_STATUS_INVALID_PARAMETER;
127         }
128
129         return NT_STATUS_OK;
130 }
131
132 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
133                                        int expected_type,
134                                        void *val)
135 {
136         NTSTATUS status;
137         status = snapper_type_check(iter, expected_type);
138         if (!NT_STATUS_IS_OK(status)) {
139                 return status;
140         }
141
142         dbus_message_iter_get_basic(iter, val);
143
144         return NT_STATUS_OK;
145 }
146
147 static NTSTATUS snapper_dict_unpack(DBusMessageIter *iter,
148                                     struct snapper_dict *dict_out)
149
150 {
151         NTSTATUS status;
152         DBusMessageIter dct_iter;
153
154         status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
155         if (!NT_STATUS_IS_OK(status)) {
156                 return status;
157         }
158         dbus_message_iter_recurse(iter, &dct_iter);
159
160         status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
161                                         &dict_out->key);
162         if (!NT_STATUS_IS_OK(status)) {
163                 return status;
164         }
165
166         dbus_message_iter_next(&dct_iter);
167         status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
168                                         &dict_out->val);
169         if (!NT_STATUS_IS_OK(status)) {
170                 return status;
171         }
172
173         return NT_STATUS_OK;
174 }
175
176 static void snapper_dict_array_print(uint32_t num_dicts,
177                                      struct snapper_dict *dicts)
178 {
179         int i;
180
181         for (i = 0; i < num_dicts; i++) {
182                 DEBUG(10, ("dict (key: %s, val: %s)\n",
183                            dicts[i].key, dicts[i].val));
184         }
185 }
186
187 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
188                                           DBusMessageIter *iter,
189                                           uint32_t *num_dicts_out,
190                                           struct snapper_dict **dicts_out)
191 {
192         NTSTATUS status;
193         DBusMessageIter array_iter;
194         uint32_t num_dicts;
195         struct snapper_dict *dicts = NULL;
196
197         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
198         if (!NT_STATUS_IS_OK(status)) {
199                 return status;
200         }
201         dbus_message_iter_recurse(iter, &array_iter);
202
203         num_dicts = 0;
204         while (dbus_message_iter_get_arg_type(&array_iter)
205                                                         != DBUS_TYPE_INVALID) {
206                 num_dicts++;
207                 dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
208                                        num_dicts);
209                 if (dicts == NULL)
210                         abort();
211
212                 status = snapper_dict_unpack(&array_iter,
213                                              &dicts[num_dicts - 1]);
214                 if (!NT_STATUS_IS_OK(status)) {
215                         talloc_free(dicts);
216                         return status;
217                 }
218                 dbus_message_iter_next(&array_iter);
219         }
220
221         *num_dicts_out = num_dicts;
222         *dicts_out = dicts;
223
224         return NT_STATUS_OK;
225 }
226
227 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
228 {
229         DBusMessage *msg;
230
231         msg = dbus_message_new_method_call("org.opensuse.Snapper",
232                                            "/org/opensuse/Snapper",
233                                            "org.opensuse.Snapper",
234                                            "ListConfigs");
235         if (msg == NULL) {
236                 DEBUG(0, ("null msg\n"));
237                 return NT_STATUS_NO_MEMORY;
238         }
239
240         /* no arguments to append */
241         *req_msg_out = msg;
242
243         return NT_STATUS_OK;
244 }
245
246 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
247                                     DBusMessageIter *iter,
248                                     struct snapper_conf *conf_out)
249 {
250         NTSTATUS status;
251         DBusMessageIter st_iter;
252
253         status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
254         if (!NT_STATUS_IS_OK(status)) {
255                 return status;
256         }
257         dbus_message_iter_recurse(iter, &st_iter);
258
259         status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
260                                         &conf_out->name);
261         if (!NT_STATUS_IS_OK(status)) {
262                 return status;
263         }
264
265         dbus_message_iter_next(&st_iter);
266         status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
267                                         &conf_out->mnt);
268         if (!NT_STATUS_IS_OK(status)) {
269                 return status;
270         }
271
272         dbus_message_iter_next(&st_iter);
273         status = snapper_dict_array_unpack(mem_ctx, &st_iter,
274                                            &conf_out->num_attrs,
275                                            &conf_out->attrs);
276
277         return status;
278 }
279
280 static void snapper_conf_array_free(int32_t num_confs,
281                                     struct snapper_conf *confs)
282 {
283         int i;
284
285         for (i = 0; i < num_confs; i++) {
286                 talloc_free(confs[i].attrs);
287         }
288         talloc_free(confs);
289 }
290
291 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
292                                                 struct snapper_conf *confs,
293                                                          const char *base)
294 {
295         int i;
296
297         for (i = 0; i < num_confs; i++) {
298                 if (strcmp(confs[i].mnt, base) == 0) {
299                         DEBUG(5, ("found snapper conf %s for path %s\n",
300                                   confs[i].name, base));
301                         return &confs[i];
302                 }
303         }
304         DEBUG(5, ("config for base %s not found\n", base));
305
306         return NULL;
307 }
308
309 static void snapper_conf_array_print(int32_t num_confs,
310                                      struct snapper_conf *confs)
311 {
312         int i;
313
314         for (i = 0; i < num_confs; i++) {
315                 DEBUG(10, ("name: %s, mnt: %s\n",
316                            confs[i].name, confs[i].mnt));
317                 snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
318         }
319 }
320
321 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
322                                           DBusMessageIter *iter,
323                                           uint32_t *num_confs_out,
324                                           struct snapper_conf **confs_out)
325 {
326         uint32_t num_confs;
327         NTSTATUS status;
328         struct snapper_conf *confs = NULL;
329         DBusMessageIter array_iter;
330
331
332         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
333         if (!NT_STATUS_IS_OK(status)) {
334                 return status;
335         }
336         dbus_message_iter_recurse(iter, &array_iter);
337
338         num_confs = 0;
339         while (dbus_message_iter_get_arg_type(&array_iter)
340                                                         != DBUS_TYPE_INVALID) {
341                 num_confs++;
342                 confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
343                                        num_confs);
344                 if (confs == NULL)
345                         abort();
346
347                 status = snapper_conf_unpack(mem_ctx, &array_iter,
348                                              &confs[num_confs - 1]);
349                 if (!NT_STATUS_IS_OK(status)) {
350                         talloc_free(confs);
351                         return status;
352                 }
353                 dbus_message_iter_next(&array_iter);
354         }
355
356         *num_confs_out = num_confs;
357         *confs_out = confs;
358
359         return NT_STATUS_OK;
360 }
361
362 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
363                                           DBusConnection *dconn,
364                                           DBusMessage *rsp_msg,
365                                           uint32_t *num_confs_out,
366                                           struct snapper_conf **confs_out)
367 {
368         NTSTATUS status;
369         DBusMessageIter iter;
370         int msg_type;
371         uint32_t num_confs;
372         struct snapper_conf *confs;
373         const char *sig;
374
375         msg_type = dbus_message_get_type(rsp_msg);
376         if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
377                 DEBUG(0, ("list_confs error response: %s\n",
378                           dbus_message_get_error_name(rsp_msg)));
379                 return NT_STATUS_INVALID_PARAMETER;
380         }
381
382         if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
383                 DEBUG(0, ("unexpected list_confs ret type: %d\n",
384                           msg_type));
385                 return NT_STATUS_INVALID_PARAMETER;
386         }
387
388         sig = dbus_message_get_signature(rsp_msg);
389         if ((sig == NULL)
390          || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
391                 DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
392                           (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
393                 return NT_STATUS_INVALID_PARAMETER;
394         }
395
396         if (!dbus_message_iter_init(rsp_msg, &iter)) {
397                 /* FIXME return empty? */
398                 DEBUG(0, ("Message has no arguments!\n"));
399                 return NT_STATUS_INVALID_PARAMETER;
400         }
401
402         status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
403         if (!NT_STATUS_IS_OK(status)) {
404                 DEBUG(0, ("failed to unpack conf array\n"));
405                 return status;
406         }
407
408         snapper_conf_array_print(num_confs, confs);
409
410         *num_confs_out = num_confs;
411         *confs_out = confs;
412
413         return NT_STATUS_OK;
414 }
415
416 static NTSTATUS snapper_list_snaps_pack(char *snapper_conf,
417                                         DBusMessage **req_msg_out)
418 {
419         DBusMessage *msg;
420         DBusMessageIter args;
421
422         msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
423                                            "/org/opensuse/Snapper", /* object to call on */
424                                            "org.opensuse.Snapper", /* interface to call on */
425                                            "ListSnapshots"); /* method name */
426         if (msg == NULL) {
427                 DEBUG(0, ("failed to create list snaps message\n"));
428                 return NT_STATUS_NO_MEMORY;
429         }
430
431         /* append arguments */
432         dbus_message_iter_init_append(msg, &args);
433         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
434                                             &snapper_conf)) {
435                 return NT_STATUS_NO_MEMORY;
436         }
437
438         *req_msg_out = msg;
439
440         return NT_STATUS_OK;
441 }
442
443 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
444                                            DBusMessageIter *iter,
445                                            struct snapper_snap *snap_out)
446 {
447         NTSTATUS status;
448         DBusMessageIter st_iter;
449
450         status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
451         if (!NT_STATUS_IS_OK(status)) {
452                 return status;
453         }
454         dbus_message_iter_recurse(iter, &st_iter);
455
456         status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
457                                         &snap_out->id);
458         if (!NT_STATUS_IS_OK(status)) {
459                 return status;
460         }
461
462         dbus_message_iter_next(&st_iter);
463         status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
464                                         &snap_out->type);
465         if (!NT_STATUS_IS_OK(status)) {
466                 return status;
467         }
468
469         dbus_message_iter_next(&st_iter);
470         status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
471                                         &snap_out->pre_id);
472         if (!NT_STATUS_IS_OK(status)) {
473                 return status;
474         }
475
476         dbus_message_iter_next(&st_iter);
477         status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT64,
478                                         &snap_out->time);
479         if (!NT_STATUS_IS_OK(status)) {
480                 return status;
481         }
482
483         dbus_message_iter_next(&st_iter);
484         status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
485                                         &snap_out->creator_uid);
486         if (!NT_STATUS_IS_OK(status)) {
487                 return status;
488         }
489
490         dbus_message_iter_next(&st_iter);
491         status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
492                                         &snap_out->desc);
493         if (!NT_STATUS_IS_OK(status)) {
494                 return status;
495         }
496
497         dbus_message_iter_next(&st_iter);
498         status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
499                                         &snap_out->cleanup);
500         if (!NT_STATUS_IS_OK(status)) {
501                 return status;
502         }
503
504         dbus_message_iter_next(&st_iter);
505         status = snapper_dict_array_unpack(mem_ctx, &st_iter,
506                                            &snap_out->num_user_data,
507                                            &snap_out->user_data);
508
509         return status;
510 }
511
512 static void snapper_snap_array_free(int32_t num_snaps,
513                                     struct snapper_snap *snaps)
514 {
515         int i;
516
517         for (i = 0; i < num_snaps; i++) {
518                 talloc_free(snaps[i].user_data);
519         }
520         talloc_free(snaps);
521 }
522
523 static void snapper_snap_array_print(int32_t num_snaps,
524                                      struct snapper_snap *snaps)
525 {
526         int i;
527
528         for (i = 0; i < num_snaps; i++) {
529                 DEBUG(10, ("id: %u, "
530                            "type: %u, "
531                            "pre_id: %u, "
532                            "time: %lu, "
533                            "creator_uid: %u, "
534                            "desc: %s, "
535                            "cleanup: %s\n",
536                            snaps[i].id,
537                            snaps[i].type,
538                            snaps[i].pre_id,
539                            snaps[i].time,
540                            snaps[i].creator_uid,
541                            snaps[i].desc,
542                            snaps[i].cleanup));
543                 snapper_dict_array_print(snaps[i].num_user_data,
544                                          snaps[i].user_data);
545         }
546 }
547
548 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
549                                           DBusMessageIter *iter,
550                                           uint32_t *num_snaps_out,
551                                           struct snapper_snap **snaps_out)
552 {
553         uint32_t num_snaps;
554         NTSTATUS status;
555         struct snapper_snap *snaps = NULL;
556         DBusMessageIter array_iter;
557
558
559         status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
560         if (!NT_STATUS_IS_OK(status)) {
561                 return status;
562         }
563         dbus_message_iter_recurse(iter, &array_iter);
564
565         num_snaps = 0;
566         while (dbus_message_iter_get_arg_type(&array_iter)
567                                                         != DBUS_TYPE_INVALID) {
568                 num_snaps++;
569                 snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
570                                        num_snaps);
571                 if (snaps == NULL)
572                         abort();
573
574                 status = snapper_snap_struct_unpack(mem_ctx, &array_iter,
575                                                     &snaps[num_snaps - 1]);
576                 if (!NT_STATUS_IS_OK(status)) {
577                         talloc_free(snaps);
578                         return status;
579                 }
580                 dbus_message_iter_next(&array_iter);
581         }
582
583         *num_snaps_out = num_snaps;
584         *snaps_out = snaps;
585
586         return NT_STATUS_OK;
587 }
588
589 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
590                                           DBusMessage *rsp_msg,
591                                           uint32_t *num_snaps_out,
592                                           struct snapper_snap **snaps_out)
593 {
594         NTSTATUS status;
595         DBusMessageIter iter;
596         int msg_type;
597         uint32_t num_snaps;
598         struct snapper_snap *snaps;
599         const char *sig;
600
601         msg_type = dbus_message_get_type(rsp_msg);
602         if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
603                 DEBUG(0, ("list_snaps error response: %s\n",
604                           dbus_message_get_error_name(rsp_msg)));
605                 return NT_STATUS_INVALID_PARAMETER;
606         }
607
608         if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
609                 DEBUG(0,("unexpected list_snaps ret type: %d\n",
610                          msg_type));
611                 return NT_STATUS_INVALID_PARAMETER;
612         }
613
614         sig = dbus_message_get_signature(rsp_msg);
615         if ((sig == NULL)
616          || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
617                 DEBUG(0, ("bad list snaps response sig: %s, "
618                           "expected: %s\n",
619                           (sig ? sig : "NULL"),
620                           SNAPPER_SIG_LIST_SNAPS_RSP));
621                 return NT_STATUS_INVALID_PARAMETER;
622         }
623
624         /* read the parameters */
625         if (!dbus_message_iter_init(rsp_msg, &iter)) {
626                 DEBUG(0, ("response has no arguments!\n"));
627                 return NT_STATUS_INVALID_PARAMETER;
628         }
629
630         status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
631         if (!NT_STATUS_IS_OK(status)) {
632                 DEBUG(0, ("failed to unpack snap array\n"));
633                 return NT_STATUS_INVALID_PARAMETER;
634         }
635
636         snapper_snap_array_print(num_snaps, snaps);
637
638         *num_snaps_out = num_snaps;
639         *snaps_out = snaps;
640
641         return NT_STATUS_OK;
642 }
643
644 static NTSTATUS snapper_create_snap_pack(const char *snapper_conf,
645                                          const char *desc,
646                                          uint32_t num_user_data,
647                                          struct snapper_dict *user_data,
648                                          DBusMessage **req_msg_out)
649 {
650         DBusMessage *msg;
651         DBusMessageIter args;
652         DBusMessageIter array_iter;
653         DBusMessageIter struct_iter;
654         const char *empty = "";
655         uint32_t i;
656         bool ok;
657
658         DEBUG(10, ("CreateSingleSnapshot: %s, %s, %s, num user %u\n",
659                   snapper_conf, desc, empty, num_user_data));
660
661         msg = dbus_message_new_method_call("org.opensuse.Snapper",
662                                            "/org/opensuse/Snapper",
663                                            "org.opensuse.Snapper",
664                                            "CreateSingleSnapshot");
665         if (msg == NULL) {
666                 DEBUG(0, ("failed to create req msg\n"));
667                 return NT_STATUS_NO_MEMORY;
668         }
669
670         /* append arguments */
671         dbus_message_iter_init_append(msg, &args);
672         ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
673                                             &snapper_conf);
674         if (!ok) {
675                 return NT_STATUS_NO_MEMORY;
676         }
677
678         ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
679                                             &desc);
680         if (!ok) {
681                 return NT_STATUS_NO_MEMORY;
682         }
683
684         /* cleanup */
685         ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
686                                             &empty);
687         if (!ok) {
688                 return NT_STATUS_NO_MEMORY;
689         }
690
691         ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
692                                               SNAPPER_SIG_STRING_DICT,
693                                               &array_iter);
694         if (!ok) {
695                 return NT_STATUS_NO_MEMORY;
696         }
697
698         for (i = 0; i < num_user_data; i++) {
699                 ok = dbus_message_iter_open_container(&array_iter,
700                                                       DBUS_TYPE_DICT_ENTRY,
701                                                       NULL, &struct_iter);
702                 if (!ok) {
703                         return NT_STATUS_NO_MEMORY;
704                 }
705
706                 ok = dbus_message_iter_append_basic(&struct_iter,
707                                                     DBUS_TYPE_STRING,
708                                                     &user_data[i].key);
709                 if (!ok) {
710                         return NT_STATUS_NO_MEMORY;
711                 }
712                 ok = dbus_message_iter_append_basic(&struct_iter,
713                                                     DBUS_TYPE_STRING,
714                                                     &user_data[i].val);
715                 if (!ok) {
716                         return NT_STATUS_NO_MEMORY;
717                 }
718
719                 ok = dbus_message_iter_close_container(&array_iter, &struct_iter);
720                 if (!ok) {
721                         return NT_STATUS_NO_MEMORY;
722                 }
723         }
724
725         ok = dbus_message_iter_close_container(&args, &array_iter);
726         if (!ok) {
727                 return NT_STATUS_NO_MEMORY;
728         }
729
730         *req_msg_out = msg;
731
732         return NT_STATUS_OK;
733 }
734
735 static NTSTATUS snapper_create_snap_unpack(DBusConnection *conn,
736                                            DBusMessage *rsp_msg,
737                                            uint32_t *snap_id_out)
738 {
739         NTSTATUS status;
740         DBusMessageIter iter;
741         int msg_type;
742         const char *sig;
743         uint32_t snap_id;
744
745         msg_type = dbus_message_get_type(rsp_msg);
746         if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
747                 DEBUG(0, ("create snap error response: %s\n",
748                           dbus_message_get_error_name(rsp_msg)));
749                 DEBUG(0, ("euid %d egid %d\n", geteuid(), getegid()));
750                 return NT_STATUS_INVALID_PARAMETER;
751         }
752
753         if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
754                 DEBUG(0, ("unexpected create snap ret type: %d\n",
755                           msg_type));
756                 return NT_STATUS_INVALID_PARAMETER;
757         }
758
759         sig = dbus_message_get_signature(rsp_msg);
760         if ((sig == NULL)
761          || (strcmp(sig, SNAPPER_SIG_CREATE_SNAP_RSP) != 0)) {
762                 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
763                           (sig ? sig : "NULL"), SNAPPER_SIG_CREATE_SNAP_RSP));
764                 return NT_STATUS_INVALID_PARAMETER;
765         }
766
767         /* read the parameters */
768         if (!dbus_message_iter_init(rsp_msg, &iter)) {
769                 DEBUG(0, ("response has no arguments!\n"));
770                 return NT_STATUS_INVALID_PARAMETER;
771         }
772
773         status = snapper_type_check_get(&iter, DBUS_TYPE_UINT32, &snap_id);
774         if (!NT_STATUS_IS_OK(status)) {
775                 return status;
776         }
777         *snap_id_out = snap_id;
778
779         return NT_STATUS_OK;
780 }
781
782 static NTSTATUS snapper_del_snap_pack(const char *snapper_conf,
783                                       uint32_t snap_id,
784                                       DBusMessage **req_msg_out)
785 {
786         DBusMessage *msg;
787         DBusMessageIter args;
788         DBusMessageIter array_iter;
789         bool ok;
790
791         msg = dbus_message_new_method_call("org.opensuse.Snapper",
792                                            "/org/opensuse/Snapper",
793                                            "org.opensuse.Snapper",
794                                            "DeleteSnapshots");
795         if (msg == NULL) {
796                 DEBUG(0, ("failed to create req msg\n"));
797                 return NT_STATUS_NO_MEMORY;
798         }
799
800         /* append arguments */
801         dbus_message_iter_init_append(msg, &args);
802         ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
803                                             &snapper_conf);
804         if (!ok) {
805                 return NT_STATUS_NO_MEMORY;
806         }
807
808         ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
809                                                DBUS_TYPE_UINT32_AS_STRING,
810                                                &array_iter);
811         if (!ok) {
812                 return NT_STATUS_NO_MEMORY;
813         }
814
815         ok = dbus_message_iter_append_basic(&array_iter,
816                                             DBUS_TYPE_UINT32,
817                                             &snap_id);
818         if (!ok) {
819                 return NT_STATUS_NO_MEMORY;
820         }
821
822         dbus_message_iter_close_container(&args, &array_iter);
823         *req_msg_out = msg;
824
825         return NT_STATUS_OK;
826 }
827
828 static NTSTATUS snapper_del_snap_unpack(DBusConnection *conn,
829                                         DBusMessage *rsp_msg)
830 {
831         int msg_type;
832         const char *sig;
833
834         msg_type = dbus_message_get_type(rsp_msg);
835         if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
836                 DEBUG(0, ("del snap error response: %s\n",
837                           dbus_message_get_error_name(rsp_msg)));
838                 return NT_STATUS_INVALID_PARAMETER;
839         }
840
841         if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
842                 DEBUG(0, ("unexpected del snap ret type: %d\n",
843                           msg_type));
844                 return NT_STATUS_INVALID_PARAMETER;
845         }
846
847         sig = dbus_message_get_signature(rsp_msg);
848         if ((sig == NULL)
849          || (strcmp(sig, SNAPPER_SIG_DEL_SNAPS_RSP) != 0)) {
850                 DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
851                           (sig ? sig : "NULL"), SNAPPER_SIG_DEL_SNAPS_RSP));
852                 return NT_STATUS_INVALID_PARAMETER;
853         }
854
855         /* no parameters in response */
856
857         return NT_STATUS_OK;
858 }
859
860 static NTSTATUS snapper_list_snaps_at_time_pack(const char *snapper_conf,
861                                                 time_t time_lower,
862                                                 time_t time_upper,
863                                                 DBusMessage **req_msg_out)
864 {
865         DBusMessage *msg;
866         DBusMessageIter args;
867
868         msg = dbus_message_new_method_call("org.opensuse.Snapper",
869                                            "/org/opensuse/Snapper",
870                                            "org.opensuse.Snapper",
871                                            "ListSnapshotsAtTime");
872         if (msg == NULL) {
873                 DEBUG(0, ("failed to create list snaps message\n"));
874                 return NT_STATUS_NO_MEMORY;
875         }
876
877         dbus_message_iter_init_append(msg, &args);
878         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
879                                             &snapper_conf)) {
880                 return NT_STATUS_NO_MEMORY;
881         }
882
883         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64,
884                                             &time_lower)) {
885                 return NT_STATUS_NO_MEMORY;
886         }
887
888         if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_UINT64,
889                                             &time_upper)) {
890                 return NT_STATUS_NO_MEMORY;
891         }
892
893         *req_msg_out = msg;
894
895         return NT_STATUS_OK;
896 }
897 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
898
899 /*
900  * Determine the snapper snapshot id given a path.
901  * Ideally this should be determined via a lookup.
902  */
903 static NTSTATUS snapper_snap_path_to_id(TALLOC_CTX *mem_ctx,
904                                         const char *snap_path,
905                                         uint32_t *snap_id_out)
906 {
907         char *path_dup;
908         char *str_idx;
909         char *str_end;
910         uint32_t snap_id;
911
912         path_dup = talloc_strdup(mem_ctx, snap_path);
913         if (path_dup == NULL) {
914                 return NT_STATUS_NO_MEMORY;
915         }
916
917         /* trim trailing '/' */
918         str_idx = path_dup + strlen(path_dup) - 1;
919         while (*str_idx == '/') {
920                 *str_idx = '\0';
921                 str_idx--;
922         }
923
924         str_idx = strrchr(path_dup, '/');
925         if ((str_idx == NULL)
926          || (strcmp(str_idx + 1, "snapshot") != 0)) {
927                 talloc_free(path_dup);
928                 return NT_STATUS_INVALID_PARAMETER;
929         }
930
931         while (*str_idx == '/') {
932                 *str_idx = '\0';
933                 str_idx--;
934         }
935
936         str_idx = strrchr(path_dup, '/');
937         if (str_idx == NULL) {
938                 talloc_free(path_dup);
939                 return NT_STATUS_INVALID_PARAMETER;
940         }
941
942         str_idx++;
943         snap_id = strtoul(str_idx, &str_end, 10);
944         if (str_idx == str_end) {
945                 talloc_free(path_dup);
946                 return NT_STATUS_INVALID_PARAMETER;
947         }
948
949         talloc_free(path_dup);
950         *snap_id_out = snap_id;
951         return NT_STATUS_OK;
952 }
953
954 /*
955  * Determine the snapper snapshot path given an id and base.
956  * Ideally this should be determined via a lookup.
957  */
958 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
959                                         const char *base_path,
960                                         uint32_t snap_id,
961                                         char **snap_path_out)
962 {
963         char *snap_path;
964
965         snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
966                                     base_path, snap_id);
967         if (snap_path == NULL) {
968                 return NT_STATUS_NO_MEMORY;
969         }
970
971         *snap_path_out = snap_path;
972         return NT_STATUS_OK;
973 }
974
975 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
976                                       DBusConnection *dconn,
977                                       const char *path,
978                                       char **conf_name_out,
979                                       char **base_path_out)
980 {
981         NTSTATUS status;
982         DBusMessage *req_msg;
983         DBusMessage *rsp_msg;
984         uint32_t num_confs = 0;
985         struct snapper_conf *confs = NULL;
986         struct snapper_conf *conf;
987         char *conf_name;
988         char *base_path;
989
990         status = snapper_list_confs_pack(&req_msg);
991         if (!NT_STATUS_IS_OK(status)) {
992                 goto err_out;
993         }
994
995         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
996         if (!NT_STATUS_IS_OK(status)) {
997                 goto err_req_free;
998         }
999
1000         status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
1001                                            &num_confs, &confs);
1002         if (!NT_STATUS_IS_OK(status)) {
1003                 goto err_rsp_free;
1004         }
1005
1006         /*
1007          * for now we only support shares where the path directly corresponds
1008          * to a snapper configuration.
1009          */
1010         conf = snapper_conf_array_base_find(num_confs, confs,
1011                                             path);
1012         if (conf == NULL) {
1013                 status = NT_STATUS_NOT_SUPPORTED;
1014                 goto err_array_free;
1015         }
1016
1017         conf_name = talloc_strdup(mem_ctx, conf->name);
1018         if (conf_name == NULL) {
1019                 status = NT_STATUS_NO_MEMORY;
1020                 goto err_array_free;
1021         }
1022         base_path = talloc_strdup(mem_ctx, conf->mnt);
1023         if (base_path == NULL) {
1024                 status = NT_STATUS_NO_MEMORY;
1025                 goto err_conf_name_free;
1026         }
1027
1028         snapper_conf_array_free(num_confs, confs);
1029         dbus_message_unref(rsp_msg);
1030         dbus_message_unref(req_msg);
1031
1032         *conf_name_out = conf_name;
1033         *base_path_out = base_path;
1034
1035         return NT_STATUS_OK;
1036
1037 err_conf_name_free:
1038         talloc_free(conf_name);
1039 err_array_free:
1040         snapper_conf_array_free(num_confs, confs);
1041 err_rsp_free:
1042         dbus_message_unref(rsp_msg);
1043 err_req_free:
1044         dbus_message_unref(req_msg);
1045 err_out:
1046         return status;
1047 }
1048
1049 /*
1050  * Check whether a path can be shadow copied. Return the base volume, allowing
1051  * the caller to determine if multiple paths lie on the same base volume.
1052  */
1053 static NTSTATUS snapper_snap_check_path(struct vfs_handle_struct *handle,
1054                                         TALLOC_CTX *mem_ctx,
1055                                         const char *service_path,
1056                                         char **base_volume)
1057 {
1058         NTSTATUS status;
1059         DBusConnection *dconn;
1060         char *conf_name;
1061         char *base_path;
1062
1063         dconn = snapper_dbus_conn();
1064         if (dconn == NULL) {
1065                 return NT_STATUS_UNSUCCESSFUL;
1066         }
1067
1068         status = snapper_get_conf_call(mem_ctx, dconn, service_path,
1069                                        &conf_name, &base_path);
1070         if (!NT_STATUS_IS_OK(status)) {
1071                 goto err_conn_close;
1072         }
1073
1074         talloc_free(conf_name);
1075         *base_volume = base_path;
1076         dbus_connection_unref(dconn);
1077
1078         return NT_STATUS_OK;
1079
1080 err_conn_close:
1081         dbus_connection_unref(dconn);
1082         return status;
1083 }
1084
1085 static NTSTATUS snapper_create_snap_call(TALLOC_CTX *mem_ctx,
1086                                          DBusConnection *dconn,
1087                                          const char *conf_name,
1088                                          const char *base_path,
1089                                          const char *snap_desc,
1090                                          uint32_t num_user_data,
1091                                          struct snapper_dict *user_data,
1092                                          char **snap_path_out)
1093 {
1094         NTSTATUS status;
1095         DBusMessage *req_msg;
1096         DBusMessage *rsp_msg;
1097         uint32_t snap_id;
1098         char *snap_path;
1099
1100         status = snapper_create_snap_pack(conf_name,
1101                                           snap_desc,
1102                                           num_user_data,
1103                                           user_data,
1104                                           &req_msg);
1105         if (!NT_STATUS_IS_OK(status)) {
1106                 goto err_out;
1107         }
1108
1109         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1110         if (!NT_STATUS_IS_OK(status)) {
1111                 goto err_req_free;
1112         }
1113
1114         status = snapper_create_snap_unpack(dconn, rsp_msg, &snap_id);
1115         if (!NT_STATUS_IS_OK(status)) {
1116                 goto err_rsp_free;
1117         }
1118
1119         status = snapper_snap_id_to_path(mem_ctx, base_path, snap_id,
1120                                          &snap_path);
1121         if (!NT_STATUS_IS_OK(status)) {
1122                 goto err_rsp_free;
1123         }
1124
1125         dbus_message_unref(rsp_msg);
1126         dbus_message_unref(req_msg);
1127
1128         DEBUG(6, ("created new snapshot %u at %s\n", snap_id, snap_path));
1129         *snap_path_out = snap_path;
1130
1131         return NT_STATUS_OK;
1132
1133 err_rsp_free:
1134         dbus_message_unref(rsp_msg);
1135 err_req_free:
1136         dbus_message_unref(req_msg);
1137 err_out:
1138         return status;
1139 }
1140
1141 struct snapper_snap_create_state {
1142         char *conf_name;
1143         char *base_path;
1144         char *snap_path;
1145         uint32_t snap_id;
1146 };
1147
1148 static struct tevent_req *snapper_snap_create_send(struct vfs_handle_struct *handle,
1149                                                    TALLOC_CTX *mem_ctx,
1150                                                    struct tevent_context *ev,
1151                                                    const char *base_volume,
1152                                                    time_t *tstamp,
1153                                                    bool rw)
1154 {
1155         struct tevent_req *req;
1156         DBusConnection *dconn;
1157         struct snapper_snap_create_state *create_state;
1158         NTSTATUS status;
1159
1160         req = tevent_req_create(mem_ctx, &create_state,
1161                                 struct snapper_snap_create_state);
1162         if (req == NULL) {
1163                 return NULL;
1164         }
1165
1166         dconn = snapper_dbus_conn();
1167         if (dconn == NULL) {
1168                 tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
1169                 return tevent_req_post(req, ev);
1170         }
1171
1172         status = snapper_get_conf_call(create_state, dconn, base_volume,
1173                                        &create_state->conf_name,
1174                                        &create_state->base_path);
1175         if (tevent_req_nterror(req, status)) {
1176                 dbus_connection_unref(dconn);
1177                 return tevent_req_post(req, ev);
1178         }
1179
1180         status = snapper_create_snap_call(create_state, dconn,
1181                                           create_state->conf_name,
1182                                           create_state->base_path,
1183                                           "Snapshot created by Samba",
1184                                           0, NULL,
1185                                           &create_state->snap_path);
1186         if (tevent_req_nterror(req, status)) {
1187                 dbus_connection_unref(dconn);
1188                 return tevent_req_post(req, ev);
1189         }
1190
1191         dbus_connection_unref(dconn);
1192
1193         tevent_req_done(req);
1194         return tevent_req_post(req, ev);
1195 }
1196
1197 static NTSTATUS snapper_snap_create_recv(struct vfs_handle_struct *handle,
1198                                          struct tevent_req *req,
1199                                          TALLOC_CTX *mem_ctx,
1200                                          char **base_path, char **snap_path)
1201 {
1202         struct snapper_snap_create_state *create_state = tevent_req_data(req,
1203                                                 struct snapper_snap_create_state);
1204         NTSTATUS status;
1205
1206         if (tevent_req_is_nterror(req, &status)) {
1207                 tevent_req_received(req);
1208                 return status;
1209         }
1210
1211         *base_path = talloc_steal(mem_ctx, create_state->base_path);
1212         if (*base_path == NULL) {
1213                 tevent_req_received(req);
1214                 return NT_STATUS_NO_MEMORY;
1215         }
1216         *snap_path = talloc_steal(mem_ctx, create_state->snap_path);
1217         if (*snap_path == NULL) {
1218                 tevent_req_received(req);
1219                 return NT_STATUS_NO_MEMORY;
1220         }
1221
1222         tevent_req_received(req);
1223         return NT_STATUS_OK;
1224 }
1225
1226 static NTSTATUS snapper_delete_snap_call(TALLOC_CTX *mem_ctx,
1227                                          DBusConnection *dconn,
1228                                          const char *conf_name,
1229                                          uint32_t snap_id)
1230 {
1231         NTSTATUS status;
1232         DBusMessage *req_msg;
1233         DBusMessage *rsp_msg;
1234
1235         status = snapper_del_snap_pack(conf_name, snap_id, &req_msg);
1236         if (!NT_STATUS_IS_OK(status)) {
1237                 goto err_out;
1238         }
1239
1240         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1241         if (!NT_STATUS_IS_OK(status)) {
1242                 goto err_req_free;
1243         }
1244
1245         status = snapper_del_snap_unpack(dconn, rsp_msg);
1246         if (!NT_STATUS_IS_OK(status)) {
1247                 goto err_rsp_free;
1248         }
1249
1250         dbus_message_unref(rsp_msg);
1251         dbus_message_unref(req_msg);
1252
1253         DEBUG(6, ("deleted snapshot %u\n", snap_id));
1254
1255         return NT_STATUS_OK;
1256
1257 err_rsp_free:
1258         dbus_message_unref(rsp_msg);
1259 err_req_free:
1260         dbus_message_unref(req_msg);
1261 err_out:
1262         return status;
1263 }
1264
1265 struct snapper_snap_delete_state {
1266         char *conf_name;
1267         char *base_path;
1268         char *snap_path;
1269         uint32_t snap_id;
1270 };
1271
1272 static struct tevent_req *snapper_snap_delete_send(struct vfs_handle_struct *handle,
1273                                                    TALLOC_CTX *mem_ctx,
1274                                                    struct tevent_context *ev,
1275                                                    char *base_path,
1276                                                    char *snap_path)
1277 {
1278         struct tevent_req *req;
1279         DBusConnection *dconn;
1280         struct snapper_snap_delete_state *delete_state;
1281         NTSTATUS status;
1282
1283         req = tevent_req_create(mem_ctx, &delete_state,
1284                                 struct snapper_snap_delete_state);
1285         if (req == NULL) {
1286                 return NULL;
1287         }
1288
1289         dconn = snapper_dbus_conn();
1290         if (dconn == NULL) {
1291                 tevent_req_nterror(req, NT_STATUS_UNSUCCESSFUL);
1292                 return tevent_req_post(req, ev);
1293         }
1294
1295         status = snapper_get_conf_call(delete_state, dconn, base_path,
1296                                        &delete_state->conf_name,
1297                                        &delete_state->base_path);
1298         if (tevent_req_nterror(req, status)) {
1299                 dbus_connection_unref(dconn);
1300                 return tevent_req_post(req, ev);
1301         }
1302
1303         status = snapper_snap_path_to_id(delete_state, snap_path,
1304                                          &delete_state->snap_id);
1305         if (tevent_req_nterror(req, status)) {
1306                 dbus_connection_unref(dconn);
1307                 return tevent_req_post(req, ev);
1308         }
1309
1310         status = snapper_delete_snap_call(delete_state, dconn,
1311                                           delete_state->conf_name,
1312                                           delete_state->snap_id);
1313         if (tevent_req_nterror(req, status)) {
1314                 dbus_connection_unref(dconn);
1315                 return tevent_req_post(req, ev);
1316         }
1317
1318         dbus_connection_unref(dconn);
1319
1320         tevent_req_done(req);
1321         return tevent_req_post(req, ev);
1322 }
1323
1324 static NTSTATUS snapper_snap_delete_recv(struct vfs_handle_struct *handle,
1325                                          struct tevent_req *req)
1326 {
1327         NTSTATUS status;
1328
1329         if (tevent_req_is_nterror(req, &status)) {
1330                 tevent_req_received(req);
1331                 return status;
1332         }
1333         tevent_req_received(req);
1334         return NT_STATUS_OK;
1335 }
1336
1337 /* sc_data used as parent talloc context for all labels */
1338 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
1339                                         struct files_struct *fsp,
1340                                         struct shadow_copy_data *sc_data,
1341                                         bool labels)
1342 {
1343         DBusConnection *dconn;
1344         TALLOC_CTX *tmp_ctx;
1345         NTSTATUS status;
1346         char *conf_name;
1347         char *base_path;
1348         DBusMessage *req_msg;
1349         DBusMessage *rsp_msg;
1350         uint32_t num_snaps;
1351         struct snapper_snap *snaps;
1352         uint32_t i;
1353         uint32_t lbl_off;
1354
1355         tmp_ctx = talloc_new(sc_data);
1356         if (tmp_ctx == NULL) {
1357                 status = NT_STATUS_NO_MEMORY;
1358                 goto err_out;
1359         }
1360
1361         dconn = snapper_dbus_conn();
1362         if (dconn == NULL) {
1363                 status = NT_STATUS_UNSUCCESSFUL;
1364                 goto err_mem_ctx_free;
1365         }
1366
1367         if (fsp->conn->connectpath == NULL) {
1368                 status = NT_STATUS_INVALID_PARAMETER;
1369                 goto err_conn_free;
1370         }
1371
1372         status = snapper_get_conf_call(tmp_ctx, dconn,
1373                                        fsp->conn->connectpath,
1374                                        &conf_name,
1375                                        &base_path);
1376         if (!NT_STATUS_IS_OK(status)) {
1377                 goto err_conn_free;
1378         }
1379
1380         status = snapper_list_snaps_pack(conf_name, &req_msg);
1381         if (!NT_STATUS_IS_OK(status)) {
1382                 goto err_req_free;
1383         }
1384
1385         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1386         if (!NT_STATUS_IS_OK(status)) {
1387                 goto err_req_free;
1388         }
1389
1390         status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
1391                                            &num_snaps, &snaps);
1392         if (!NT_STATUS_IS_OK(status)) {
1393                 goto err_rsp_free;
1394         }
1395         /* we should always get at least one snapshot (current) */
1396         if (num_snaps == 0) {
1397                 DEBUG(6, ("zero snapshots in snap list response\n"));
1398                 goto err_rsp_free;
1399         }
1400
1401         /* subtract 1, (current) snapshot is not returned */
1402         sc_data->num_volumes = num_snaps - 1;
1403         sc_data->labels = NULL;
1404
1405         if ((labels == false) || (sc_data->num_volumes == 0)) {
1406                 /* tokens need not be added to the labels array */
1407                 goto done;
1408         }
1409
1410         sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
1411                                        sc_data->num_volumes);
1412         if (sc_data->labels == NULL) {
1413                 status = NT_STATUS_NO_MEMORY;
1414                 goto err_rsp_free;
1415         }
1416
1417         /* start at end for decending order, do not include 0 (current) */
1418         lbl_off = 0;
1419         for (i = num_snaps - 1; i > 0; i--) {
1420                 char *lbl = sc_data->labels[lbl_off++];
1421                 struct tm gmt_snap_time;
1422                 struct tm *tm_ret;
1423                 size_t str_sz;
1424
1425                 tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
1426                 if (tm_ret == NULL) {
1427                         status = NT_STATUS_UNSUCCESSFUL;
1428                         goto err_labels_free;
1429                 }
1430                 str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
1431                                   "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
1432                 if (str_sz == 0) {
1433                         status = NT_STATUS_UNSUCCESSFUL;
1434                         goto err_labels_free;
1435                 }
1436         }
1437
1438 done:
1439         talloc_free(tmp_ctx);
1440         dbus_message_unref(rsp_msg);
1441         dbus_message_unref(req_msg);
1442         dbus_connection_unref(dconn);
1443
1444         return 0;
1445
1446 err_labels_free:
1447         TALLOC_FREE(sc_data->labels);
1448 err_rsp_free:
1449         dbus_message_unref(rsp_msg);
1450 err_req_free:
1451         dbus_message_unref(req_msg);
1452 err_conn_free:
1453         dbus_connection_unref(dconn);
1454 err_mem_ctx_free:
1455         talloc_free(tmp_ctx);
1456 err_out:
1457         /* all errors are collapsed to a -1 return code */
1458         return -1;
1459 }
1460
1461 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1462                                               DBusConnection *dconn,
1463                                               const char *conf_name,
1464                                               const char *base_path,
1465                                               time_t snaptime,
1466                                               char **snap_path_out)
1467 {
1468         NTSTATUS status;
1469         DBusMessage *req_msg;
1470         DBusMessage *rsp_msg;
1471         uint32_t num_snaps;
1472         struct snapper_snap *snaps;
1473         char *snap_path;
1474
1475         status = snapper_list_snaps_at_time_pack(conf_name,
1476                                                  snaptime,
1477                                                  snaptime,
1478                                                  &req_msg);
1479         if (!NT_STATUS_IS_OK(status)) {
1480                 goto err_out;
1481         }
1482
1483         status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1484         if (!NT_STATUS_IS_OK(status)) {
1485                 goto err_req_free;
1486         }
1487
1488         status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1489                                            &num_snaps, &snaps);
1490         if (!NT_STATUS_IS_OK(status)) {
1491                 goto err_rsp_free;
1492         }
1493
1494         if (num_snaps == 0) {
1495                 DEBUG(4, ("no snapshots found with time: %lu\n", snaptime));
1496                 status = NT_STATUS_INVALID_PARAMETER;
1497                 goto err_snap_array_free;
1498         } else if (num_snaps > 0) {
1499                 DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1500                           num_snaps, snaptime));
1501         }
1502
1503         status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1504                                          &snap_path);
1505         if (!NT_STATUS_IS_OK(status)) {
1506                 goto err_snap_array_free;
1507         }
1508
1509         snapper_snap_array_free(num_snaps, snaps);
1510         dbus_message_unref(rsp_msg);
1511         dbus_message_unref(req_msg);
1512
1513         *snap_path_out = snap_path;
1514
1515         return NT_STATUS_OK;
1516
1517 err_snap_array_free:
1518         snapper_snap_array_free(num_snaps, snaps);
1519 err_rsp_free:
1520         dbus_message_unref(rsp_msg);
1521 err_req_free:
1522         dbus_message_unref(req_msg);
1523 err_out:
1524         return status;
1525 }
1526
1527 static NTSTATUS snapper_snap_dir_expand(struct vfs_handle_struct *handle,
1528                                         TALLOC_CTX *mem_ctx,
1529                                         time_t snap_time,
1530                                         char **snap_dir_out)
1531 {
1532         DBusConnection *dconn;
1533         NTSTATUS status;
1534         char *conf_name;
1535         char *base_path;
1536         char *snap_path;
1537         char *snap_path_nobase;
1538
1539         dconn = snapper_dbus_conn();
1540         if (dconn == NULL) {
1541                 status = NT_STATUS_UNSUCCESSFUL;
1542                 goto err_out;
1543         }
1544
1545         if (handle->conn->connectpath == NULL) {
1546                 status = NT_STATUS_INVALID_PARAMETER;
1547                 goto err_conn_free;
1548         }
1549
1550         status = snapper_get_conf_call(mem_ctx, dconn,
1551                                        handle->conn->connectpath,
1552                                        &conf_name,
1553                                        &base_path);
1554         if (!NT_STATUS_IS_OK(status)) {
1555                 goto err_conn_free;
1556         }
1557
1558         status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1559                                                conf_name, base_path, snap_time,
1560                                                &snap_path);
1561         if (!NT_STATUS_IS_OK(status)) {
1562                 goto err_conf_name_free;
1563         }
1564
1565         /* strip the base path from the snap path for return */
1566         if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1567                 status = NT_STATUS_INVALID_PARAMETER;
1568                 goto err_snap_path_free;
1569         }
1570
1571         snap_path_nobase = talloc_strdup(mem_ctx, snap_path + strlen(base_path));
1572         if (snap_path_nobase == NULL) {
1573                 status = NT_STATUS_NO_MEMORY;
1574                 goto err_snap_path_free;
1575         }
1576
1577         talloc_free(snap_path);
1578         talloc_free(conf_name);
1579         talloc_free(base_path);
1580         dbus_connection_unref(dconn);
1581         *snap_dir_out = snap_path_nobase;
1582
1583         return NT_STATUS_OK;
1584
1585 err_snap_path_free:
1586         talloc_free(snap_path);
1587 err_conf_name_free:
1588         talloc_free(conf_name);
1589         talloc_free(base_path);
1590 err_conn_free:
1591         dbus_connection_unref(dconn);
1592 err_out:
1593         return status;
1594 }
1595
1596 static struct vfs_fn_pointers snapper_fns = {
1597         .snap_check_path_fn = snapper_snap_check_path,
1598         .snap_create_send_fn = snapper_snap_create_send,
1599         .snap_create_recv_fn = snapper_snap_create_recv,
1600         .snap_delete_send_fn = snapper_snap_delete_send,
1601         .snap_delete_recv_fn = snapper_snap_delete_recv,
1602         .get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
1603         .snap_dir_expand_fn = snapper_snap_dir_expand,
1604 };
1605
1606 NTSTATUS vfs_snapper_init(void);
1607 NTSTATUS vfs_snapper_init(void)
1608 {
1609         return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
1610                                 "snapper", &snapper_fns);
1611 }