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_CREATE_SNAP_RSP "u"
44 #define SNAPPER_SIG_DEL_SNAPS_RSP ""
45 #define SNAPPER_SIG_STRING_DICT "{ss}"
46 
47 struct snapper_dict {
48 	char *key;
49 	char *val;
50 };
51 
52 struct snapper_snap {
53 	uint32_t id;
54 	uint16_t type;
55 	uint32_t pre_id;
56 	int64_t time;
57 	uint32_t creator_uid;
58 	char *desc;
59 	char *cleanup;
60 	uint32_t num_user_data;
61 	struct snapper_dict *user_data;
62 };
63 
64 struct snapper_conf {
65 	char *name;
66 	char *mnt;
67 	uint32_t num_attrs;
68 	struct snapper_dict *attrs;
69 };
70 
71 static const struct {
72 	const char *snapper_err_str;
73 	NTSTATUS status;
74 } snapper_err_map[] = {
75 	{ "error.no_permissions", NT_STATUS_ACCESS_DENIED },
76 };
77 
snapper_err_ntstatus_map(const char * snapper_err_str)78 static NTSTATUS snapper_err_ntstatus_map(const char *snapper_err_str)
79 {
80 	int i;
81 
82 	if (snapper_err_str == NULL) {
83 		return NT_STATUS_UNSUCCESSFUL;
84 	}
85 	for (i = 0; i < ARRAY_SIZE(snapper_err_map); i++) {
86 		if (!strcmp(snapper_err_map[i].snapper_err_str,
87 			    snapper_err_str)) {
88 			return snapper_err_map[i].status;
89 		}
90 	}
91 	DEBUG(2, ("no explicit mapping for dbus error: %s\n", snapper_err_str));
92 
93 	return NT_STATUS_UNSUCCESSFUL;
94 }
95 
96 /*
97  * Strings are UTF-8. Other characters must be encoded hexadecimal as "\x??".
98  * As a consequence "\" must be encoded as "\\".
99  */
snapper_dbus_str_encode(TALLOC_CTX * mem_ctx,const char * in_str,char ** _out_str)100 static NTSTATUS snapper_dbus_str_encode(TALLOC_CTX *mem_ctx, const char *in_str,
101 					char **_out_str)
102 {
103 	size_t in_len;
104 	char *out_str;
105 	int i;
106 	int out_off;
107 	int out_len;
108 
109 	if (in_str == NULL) {
110 		return NT_STATUS_INVALID_PARAMETER;
111 	}
112 
113 	in_len = strlen(in_str);
114 
115 	/* output can be max 4 times the length of @in_str, +1 for terminator */
116 	out_len = (in_len * 4) + 1;
117 
118 	out_str = talloc_array(mem_ctx, char, out_len);
119 	if (out_str == NULL) {
120 		return NT_STATUS_NO_MEMORY;
121 	}
122 
123 	out_off = 0;
124 	for (i = 0; i < in_len; i++) {
125 		size_t pushed;
126 
127 		if (in_str[i] == '\\') {
128 			pushed = snprintf(out_str + out_off, out_len - out_off,
129 					  "\\\\");
130 		} else if ((unsigned char)in_str[i] > 127) {
131 			pushed = snprintf(out_str + out_off, out_len - out_off,
132 					  "\\x%02x", (unsigned char)in_str[i]);
133 		} else {
134 			/* regular character */
135 			*(out_str + out_off) = in_str[i];
136 			pushed = sizeof(char);
137 		}
138 		if (pushed >= out_len - out_off) {
139 			/* truncated, should never happen */
140 			talloc_free(out_str);
141 			return NT_STATUS_INTERNAL_ERROR;
142 		}
143 		out_off += pushed;
144 	}
145 
146 	*(out_str + out_off) = '\0';
147 	*_out_str = out_str;
148 
149 	return NT_STATUS_OK;
150 }
151 
snapper_dbus_str_decode(TALLOC_CTX * mem_ctx,const char * in_str,char ** _out_str)152 static NTSTATUS snapper_dbus_str_decode(TALLOC_CTX *mem_ctx, const char *in_str,
153 					char **_out_str)
154 {
155 	size_t in_len;
156 	char *out_str;
157 	int i;
158 	int out_off;
159 	int out_len;
160 
161 	if (in_str == NULL) {
162 		return NT_STATUS_INVALID_PARAMETER;
163 	}
164 
165 	in_len = strlen(in_str);
166 
167 	/* output cannot be larger than input, +1 for terminator */
168 	out_len = in_len + 1;
169 
170 	out_str = talloc_array(mem_ctx, char, out_len);
171 	if (out_str == NULL) {
172 		return NT_STATUS_NO_MEMORY;
173 	}
174 
175 	out_off = 0;
176 	for (i = 0; i < in_len; i++) {
177 		int j;
178 		char hex_buf[3];
179 		unsigned int non_ascii_byte;
180 
181 		if (in_str[i] != '\\') {
182 			out_str[out_off] = in_str[i];
183 			out_off++;
184 			continue;
185 		}
186 
187 		i++;
188 		if (in_str[i] == '\\') {
189 			out_str[out_off] = '\\';
190 			out_off++;
191 			continue;
192 		} else if (in_str[i] != 'x') {
193 			goto err_invalid_src_encoding;
194 		}
195 
196 		/* non-ASCII, encoded as two hex chars */
197 		for (j = 0; j < 2; j++) {
198 			i++;
199 			if ((in_str[i] == '\0') || !isxdigit(in_str[i])) {
200 				goto err_invalid_src_encoding;
201 			}
202 			hex_buf[j] = in_str[i];
203 		}
204 		hex_buf[2] = '\0';
205 
206 		sscanf(hex_buf, "%x", &non_ascii_byte);
207 		out_str[out_off] = (unsigned char)non_ascii_byte;
208 		out_off++;
209 	}
210 
211 	out_str[out_off] = '\0';
212 	*_out_str = out_str;
213 
214 	return NT_STATUS_OK;
215 err_invalid_src_encoding:
216 	DEBUG(0, ("invalid encoding %s\n", in_str));
217 	return NT_STATUS_INVALID_PARAMETER;
218 }
219 
snapper_dbus_conn_create(void)220 static DBusConnection *snapper_dbus_conn_create(void)
221 {
222 	DBusError err;
223 	DBusConnection *dconn;
224 
225 	dbus_error_init(&err);
226 
227 	/*
228 	 * Always create a new DBus connection, to ensure snapperd detects the
229 	 * correct client [E]UID. With dbus_bus_get() it does not!
230 	 */
231 	dconn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
232 	if (dbus_error_is_set(&err)) {
233 		DEBUG(0, ("dbus connection error: %s\n", err.message));
234 		dbus_error_free(&err);
235 	}
236 	if (dconn == NULL) {
237 		return NULL;
238 	}
239 
240 	/* dbus_bus_get_private() sets exit-on-disconnect by default, undo it */
241 	dbus_connection_set_exit_on_disconnect(dconn, false);
242 
243 	return dconn;
244 }
245 
snapper_dbus_conn_destroy(DBusConnection * dconn)246 static void snapper_dbus_conn_destroy(DBusConnection *dconn)
247 {
248 	if (dconn == NULL) {
249 		DEBUG(2, ("attempt to destroy NULL dbus connection\n"));
250 		return;
251 	}
252 
253 	dbus_connection_close(dconn);
254 	dbus_connection_unref(dconn);
255 }
256 
257 /*
258  * send the message @send_msg over the dbus and wait for a response, return the
259  * responsee via @recv_msg_out.
260  * @send_msg is not freed, dbus_message_unref() must be handled by the caller.
261  */
snapper_dbus_msg_xchng(DBusConnection * dconn,DBusMessage * send_msg,DBusMessage ** recv_msg_out)262 static NTSTATUS snapper_dbus_msg_xchng(DBusConnection *dconn,
263 				       DBusMessage *send_msg,
264 				       DBusMessage **recv_msg_out)
265 {
266 	DBusPendingCall *pending;
267 	DBusMessage *recv_msg;
268 
269 	/* send message and get a handle for a reply */
270 	if (!dbus_connection_send_with_reply(dconn, send_msg, &pending, -1)) {
271 		return NT_STATUS_NO_MEMORY;
272 	}
273 	if (NULL == pending) {
274 		DEBUG(0, ("dbus msg send failed\n"));
275 		return NT_STATUS_UNSUCCESSFUL;
276 	}
277 
278 	dbus_connection_flush(dconn);
279 
280 	/* block until we receive a reply */
281 	dbus_pending_call_block(pending);
282 
283 	/* get the reply message */
284 	recv_msg = dbus_pending_call_steal_reply(pending);
285 	if (recv_msg == NULL) {
286 		DEBUG(0, ("Reply Null\n"));
287 		return NT_STATUS_UNSUCCESSFUL;
288 	}
289 	/* free the pending message handle */
290 	dbus_pending_call_unref(pending);
291 	*recv_msg_out = recv_msg;
292 
293 	return NT_STATUS_OK;
294 }
295 
snapper_type_check(DBusMessageIter * iter,int expected_type)296 static NTSTATUS snapper_type_check(DBusMessageIter *iter,
297 				   int expected_type)
298 {
299 	int type = dbus_message_iter_get_arg_type(iter);
300 	if (type != expected_type) {
301 		DEBUG(0, ("got type %d, expecting %d\n",
302 			type, expected_type));
303 		return NT_STATUS_INVALID_PARAMETER;
304 	}
305 
306 	return NT_STATUS_OK;
307 }
308 
snapper_type_check_get(DBusMessageIter * iter,int expected_type,void * val)309 static NTSTATUS snapper_type_check_get(DBusMessageIter *iter,
310 				       int expected_type,
311 				       void *val)
312 {
313 	NTSTATUS status;
314 	status = snapper_type_check(iter, expected_type);
315 	if (!NT_STATUS_IS_OK(status)) {
316 		return status;
317 	}
318 
319 	dbus_message_iter_get_basic(iter, val);
320 
321 	return NT_STATUS_OK;
322 }
323 
snapper_dict_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,struct snapper_dict * dict_out)324 static NTSTATUS snapper_dict_unpack(TALLOC_CTX *mem_ctx,
325 				    DBusMessageIter *iter,
326 				    struct snapper_dict *dict_out)
327 
328 {
329 	NTSTATUS status;
330 	DBusMessageIter dct_iter;
331 	char *key_encoded;
332 	char *val_encoded;
333 
334 	status = snapper_type_check(iter, DBUS_TYPE_DICT_ENTRY);
335 	if (!NT_STATUS_IS_OK(status)) {
336 		return status;
337 	}
338 	dbus_message_iter_recurse(iter, &dct_iter);
339 
340 	status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
341 					&key_encoded);
342 	if (!NT_STATUS_IS_OK(status)) {
343 		return status;
344 	}
345 	status = snapper_dbus_str_decode(mem_ctx, key_encoded, &dict_out->key);
346 	if (!NT_STATUS_IS_OK(status)) {
347 		return status;
348 	}
349 
350 	dbus_message_iter_next(&dct_iter);
351 	status = snapper_type_check_get(&dct_iter, DBUS_TYPE_STRING,
352 					&val_encoded);
353 	if (!NT_STATUS_IS_OK(status)) {
354 		talloc_free(dict_out->key);
355 		return status;
356 	}
357 	status = snapper_dbus_str_decode(mem_ctx, val_encoded, &dict_out->val);
358 	if (!NT_STATUS_IS_OK(status)) {
359 		talloc_free(dict_out->key);
360 		return status;
361 	}
362 
363 	return NT_STATUS_OK;
364 }
365 
snapper_dict_array_print(uint32_t num_dicts,struct snapper_dict * dicts)366 static void snapper_dict_array_print(uint32_t num_dicts,
367 				     struct snapper_dict *dicts)
368 {
369 	int i;
370 
371 	for (i = 0; i < num_dicts; i++) {
372 		DEBUG(10, ("dict (key: %s, val: %s)\n",
373 			   dicts[i].key, dicts[i].val));
374 	}
375 }
376 
snapper_dict_array_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,uint32_t * num_dicts_out,struct snapper_dict ** dicts_out)377 static NTSTATUS snapper_dict_array_unpack(TALLOC_CTX *mem_ctx,
378 					  DBusMessageIter *iter,
379 					  uint32_t *num_dicts_out,
380 					  struct snapper_dict **dicts_out)
381 {
382 	NTSTATUS status;
383 	DBusMessageIter array_iter;
384 	uint32_t num_dicts;
385 	struct snapper_dict *dicts = NULL;
386 
387 	status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
388 	if (!NT_STATUS_IS_OK(status)) {
389 		return status;
390 	}
391 	dbus_message_iter_recurse(iter, &array_iter);
392 
393 	num_dicts = 0;
394 	while (dbus_message_iter_get_arg_type(&array_iter)
395 							!= DBUS_TYPE_INVALID) {
396 		num_dicts++;
397 		dicts = talloc_realloc(mem_ctx, dicts, struct snapper_dict,
398 				       num_dicts);
399 		if (dicts == NULL)
400 			abort();
401 
402 		status = snapper_dict_unpack(mem_ctx, &array_iter,
403 					     &dicts[num_dicts - 1]);
404 		if (!NT_STATUS_IS_OK(status)) {
405 			talloc_free(dicts);
406 			return status;
407 		}
408 		dbus_message_iter_next(&array_iter);
409 	}
410 
411 	*num_dicts_out = num_dicts;
412 	*dicts_out = dicts;
413 
414 	return NT_STATUS_OK;
415 }
416 
snapper_list_confs_pack(DBusMessage ** req_msg_out)417 static NTSTATUS snapper_list_confs_pack(DBusMessage **req_msg_out)
418 {
419 	DBusMessage *msg;
420 
421 	msg = dbus_message_new_method_call("org.opensuse.Snapper",
422 					   "/org/opensuse/Snapper",
423 					   "org.opensuse.Snapper",
424 					   "ListConfigs");
425 	if (msg == NULL) {
426 		DEBUG(0, ("null msg\n"));
427 		return NT_STATUS_NO_MEMORY;
428 	}
429 
430 	/* no arguments to append */
431 	*req_msg_out = msg;
432 
433 	return NT_STATUS_OK;
434 }
435 
snapper_conf_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,struct snapper_conf * conf_out)436 static NTSTATUS snapper_conf_unpack(TALLOC_CTX *mem_ctx,
437 				    DBusMessageIter *iter,
438 				    struct snapper_conf *conf_out)
439 {
440 	NTSTATUS status;
441 	DBusMessageIter st_iter;
442 	char *name_encoded;
443 	char *mnt_encoded;
444 
445 	status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
446 	if (!NT_STATUS_IS_OK(status)) {
447 		return status;
448 	}
449 	dbus_message_iter_recurse(iter, &st_iter);
450 
451 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
452 					&name_encoded);
453 	if (!NT_STATUS_IS_OK(status)) {
454 		return status;
455 	}
456 
457 	status = snapper_dbus_str_decode(mem_ctx, name_encoded,
458 					 &conf_out->name);
459 	if (!NT_STATUS_IS_OK(status)) {
460 		return status;
461 	}
462 
463 	dbus_message_iter_next(&st_iter);
464 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
465 					&mnt_encoded);
466 	if (!NT_STATUS_IS_OK(status)) {
467 		talloc_free(conf_out->name);
468 		return status;
469 	}
470 
471 	status = snapper_dbus_str_decode(mem_ctx, mnt_encoded,
472 					 &conf_out->mnt);
473 	if (!NT_STATUS_IS_OK(status)) {
474 		talloc_free(conf_out->name);
475 		return status;
476 	}
477 
478 	dbus_message_iter_next(&st_iter);
479 	status = snapper_dict_array_unpack(mem_ctx, &st_iter,
480 					   &conf_out->num_attrs,
481 					   &conf_out->attrs);
482 	if (!NT_STATUS_IS_OK(status)) {
483 		talloc_free(conf_out->mnt);
484 		talloc_free(conf_out->name);
485 		return status;
486 	}
487 
488 	return NT_STATUS_OK;
489 }
490 
snapper_conf_array_base_find(int32_t num_confs,struct snapper_conf * confs,const char * base)491 static struct snapper_conf *snapper_conf_array_base_find(int32_t num_confs,
492 						struct snapper_conf *confs,
493 							 const char *base)
494 {
495 	int i;
496 
497 	for (i = 0; i < num_confs; i++) {
498 		if (strcmp(confs[i].mnt, base) == 0) {
499 			DEBUG(5, ("found snapper conf %s for path %s\n",
500 				  confs[i].name, base));
501 			return &confs[i];
502 		}
503 	}
504 	DEBUG(5, ("config for base %s not found\n", base));
505 
506 	return NULL;
507 }
508 
snapper_conf_array_print(int32_t num_confs,struct snapper_conf * confs)509 static void snapper_conf_array_print(int32_t num_confs,
510 				     struct snapper_conf *confs)
511 {
512 	int i;
513 
514 	for (i = 0; i < num_confs; i++) {
515 		DEBUG(10, ("name: %s, mnt: %s\n",
516 			   confs[i].name, confs[i].mnt));
517 		snapper_dict_array_print(confs[i].num_attrs, confs[i].attrs);
518 	}
519 }
520 
snapper_conf_array_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,uint32_t * num_confs_out,struct snapper_conf ** confs_out)521 static NTSTATUS snapper_conf_array_unpack(TALLOC_CTX *mem_ctx,
522 					  DBusMessageIter *iter,
523 					  uint32_t *num_confs_out,
524 					  struct snapper_conf **confs_out)
525 {
526 	uint32_t num_confs;
527 	NTSTATUS status;
528 	struct snapper_conf *confs = NULL;
529 	DBusMessageIter array_iter;
530 
531 
532 	status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
533 	if (!NT_STATUS_IS_OK(status)) {
534 		return status;
535 	}
536 	dbus_message_iter_recurse(iter, &array_iter);
537 
538 	num_confs = 0;
539 	while (dbus_message_iter_get_arg_type(&array_iter)
540 							!= DBUS_TYPE_INVALID) {
541 		num_confs++;
542 		confs = talloc_realloc(mem_ctx, confs, struct snapper_conf,
543 				       num_confs);
544 		if (confs == NULL)
545 			abort();
546 
547 		status = snapper_conf_unpack(confs, &array_iter,
548 					     &confs[num_confs - 1]);
549 		if (!NT_STATUS_IS_OK(status)) {
550 			talloc_free(confs);
551 			return status;
552 		}
553 		dbus_message_iter_next(&array_iter);
554 	}
555 
556 	*num_confs_out = num_confs;
557 	*confs_out = confs;
558 
559 	return NT_STATUS_OK;
560 }
561 
snapper_list_confs_unpack(TALLOC_CTX * mem_ctx,DBusConnection * dconn,DBusMessage * rsp_msg,uint32_t * num_confs_out,struct snapper_conf ** confs_out)562 static NTSTATUS snapper_list_confs_unpack(TALLOC_CTX *mem_ctx,
563 					  DBusConnection *dconn,
564 					  DBusMessage *rsp_msg,
565 					  uint32_t *num_confs_out,
566 					  struct snapper_conf **confs_out)
567 {
568 	NTSTATUS status;
569 	DBusMessageIter iter;
570 	int msg_type;
571 	uint32_t num_confs;
572 	struct snapper_conf *confs;
573 	const char *sig;
574 
575 	msg_type = dbus_message_get_type(rsp_msg);
576 	if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
577 		const char *err_str = dbus_message_get_error_name(rsp_msg);
578 		DEBUG(0, ("list_confs error response: %s\n", err_str));
579 		return snapper_err_ntstatus_map(err_str);
580 	}
581 
582 	if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
583 		DEBUG(0, ("unexpected list_confs ret type: %d\n",
584 			  msg_type));
585 		return NT_STATUS_INVALID_PARAMETER;
586 	}
587 
588 	sig = dbus_message_get_signature(rsp_msg);
589 	if ((sig == NULL)
590 	 || (strcmp(sig, SNAPPER_SIG_LIST_CONFS_RSP) != 0)) {
591 		DEBUG(0, ("bad list confs response sig: %s, expected: %s\n",
592 			  (sig ? sig : "NULL"), SNAPPER_SIG_LIST_CONFS_RSP));
593 		return NT_STATUS_INVALID_PARAMETER;
594 	}
595 
596 	if (!dbus_message_iter_init(rsp_msg, &iter)) {
597 		/* FIXME return empty? */
598 		DEBUG(0, ("Message has no arguments!\n"));
599 		return NT_STATUS_INVALID_PARAMETER;
600 	}
601 
602 	status = snapper_conf_array_unpack(mem_ctx, &iter, &num_confs, &confs);
603 	if (!NT_STATUS_IS_OK(status)) {
604 		DEBUG(0, ("failed to unpack conf array\n"));
605 		return status;
606 	}
607 
608 	snapper_conf_array_print(num_confs, confs);
609 
610 	*num_confs_out = num_confs;
611 	*confs_out = confs;
612 
613 	return NT_STATUS_OK;
614 }
615 
snapper_list_snaps_pack(TALLOC_CTX * mem_ctx,char * snapper_conf,DBusMessage ** req_msg_out)616 static NTSTATUS snapper_list_snaps_pack(TALLOC_CTX *mem_ctx,
617 					char *snapper_conf,
618 					DBusMessage **req_msg_out)
619 {
620 	DBusMessage *msg;
621 	DBusMessageIter args;
622 	char *conf_encoded;
623 	NTSTATUS status;
624 
625 	msg = dbus_message_new_method_call("org.opensuse.Snapper", /* target for the method call */
626 					   "/org/opensuse/Snapper", /* object to call on */
627 					   "org.opensuse.Snapper", /* interface to call on */
628 					   "ListSnapshots"); /* method name */
629 	if (msg == NULL) {
630 		DEBUG(0, ("failed to create list snaps message\n"));
631 		return NT_STATUS_NO_MEMORY;
632 	}
633 
634 	status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
635 	if (!NT_STATUS_IS_OK(status)) {
636 		dbus_message_unref(msg);
637 		return status;
638 	}
639 
640 	/* append arguments */
641 	dbus_message_iter_init_append(msg, &args);
642 	if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
643 					    &conf_encoded)) {
644 		talloc_free(conf_encoded);
645 		dbus_message_unref(msg);
646 		return NT_STATUS_NO_MEMORY;
647 	}
648 
649 	*req_msg_out = msg;
650 
651 	return NT_STATUS_OK;
652 }
653 
snapper_snap_struct_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,struct snapper_snap * snap_out)654 static NTSTATUS snapper_snap_struct_unpack(TALLOC_CTX *mem_ctx,
655 					   DBusMessageIter *iter,
656 					   struct snapper_snap *snap_out)
657 {
658 	NTSTATUS status;
659 	DBusMessageIter st_iter;
660 	char *desc_encoded;
661 	char *cleanup_encoded;
662 
663 	status = snapper_type_check(iter, DBUS_TYPE_STRUCT);
664 	if (!NT_STATUS_IS_OK(status)) {
665 		return status;
666 	}
667 	dbus_message_iter_recurse(iter, &st_iter);
668 
669 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
670 					&snap_out->id);
671 	if (!NT_STATUS_IS_OK(status)) {
672 		return status;
673 	}
674 
675 	dbus_message_iter_next(&st_iter);
676 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT16,
677 					&snap_out->type);
678 	if (!NT_STATUS_IS_OK(status)) {
679 		return status;
680 	}
681 
682 	dbus_message_iter_next(&st_iter);
683 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
684 					&snap_out->pre_id);
685 	if (!NT_STATUS_IS_OK(status)) {
686 		return status;
687 	}
688 
689 	dbus_message_iter_next(&st_iter);
690 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_INT64,
691 					&snap_out->time);
692 	if (!NT_STATUS_IS_OK(status)) {
693 		return status;
694 	}
695 
696 	dbus_message_iter_next(&st_iter);
697 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_UINT32,
698 					&snap_out->creator_uid);
699 	if (!NT_STATUS_IS_OK(status)) {
700 		return status;
701 	}
702 
703 	dbus_message_iter_next(&st_iter);
704 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
705 					&desc_encoded);
706 	if (!NT_STATUS_IS_OK(status)) {
707 		return status;
708 	}
709 
710 	status = snapper_dbus_str_decode(mem_ctx, desc_encoded,
711 					 &snap_out->desc);
712 	if (!NT_STATUS_IS_OK(status)) {
713 		return status;
714 	}
715 
716 	dbus_message_iter_next(&st_iter);
717 	status = snapper_type_check_get(&st_iter, DBUS_TYPE_STRING,
718 					&cleanup_encoded);
719 	if (!NT_STATUS_IS_OK(status)) {
720 		talloc_free(snap_out->desc);
721 		return status;
722 	}
723 
724 	status = snapper_dbus_str_decode(mem_ctx, cleanup_encoded,
725 					 &snap_out->cleanup);
726 	if (!NT_STATUS_IS_OK(status)) {
727 		talloc_free(snap_out->desc);
728 		return status;
729 	}
730 
731 	dbus_message_iter_next(&st_iter);
732 	status = snapper_dict_array_unpack(mem_ctx, &st_iter,
733 					   &snap_out->num_user_data,
734 					   &snap_out->user_data);
735 	if (!NT_STATUS_IS_OK(status)) {
736 		talloc_free(snap_out->cleanup);
737 		talloc_free(snap_out->desc);
738 		return status;
739 	}
740 
741 	return NT_STATUS_OK;
742 }
743 
snapper_snap_array_print(int32_t num_snaps,struct snapper_snap * snaps)744 static void snapper_snap_array_print(int32_t num_snaps,
745 				     struct snapper_snap *snaps)
746 {
747 	int i;
748 
749 	for (i = 0; i < num_snaps; i++) {
750 		DEBUG(10, ("id: %u, "
751 			   "type: %u, "
752 			   "pre_id: %u, "
753 			   "time: %ld, "
754 			   "creator_uid: %u, "
755 			   "desc: %s, "
756 			   "cleanup: %s\n",
757 			   (unsigned int)snaps[i].id,
758 			   (unsigned int)snaps[i].type,
759 			   (unsigned int)snaps[i].pre_id,
760 			   (long int)snaps[i].time,
761 			   (unsigned int)snaps[i].creator_uid,
762 			   snaps[i].desc,
763 			   snaps[i].cleanup));
764 		snapper_dict_array_print(snaps[i].num_user_data,
765 					 snaps[i].user_data);
766 	}
767 }
768 
snapper_snap_array_unpack(TALLOC_CTX * mem_ctx,DBusMessageIter * iter,uint32_t * num_snaps_out,struct snapper_snap ** snaps_out)769 static NTSTATUS snapper_snap_array_unpack(TALLOC_CTX *mem_ctx,
770 					  DBusMessageIter *iter,
771 					  uint32_t *num_snaps_out,
772 					  struct snapper_snap **snaps_out)
773 {
774 	uint32_t num_snaps;
775 	NTSTATUS status;
776 	struct snapper_snap *snaps = NULL;
777 	DBusMessageIter array_iter;
778 
779 
780 	status = snapper_type_check(iter, DBUS_TYPE_ARRAY);
781 	if (!NT_STATUS_IS_OK(status)) {
782 		return status;
783 	}
784 	dbus_message_iter_recurse(iter, &array_iter);
785 
786 	num_snaps = 0;
787 	while (dbus_message_iter_get_arg_type(&array_iter)
788 							!= DBUS_TYPE_INVALID) {
789 		num_snaps++;
790 		snaps = talloc_realloc(mem_ctx, snaps, struct snapper_snap,
791 				       num_snaps);
792 		if (snaps == NULL)
793 			abort();
794 
795 		status = snapper_snap_struct_unpack(snaps, &array_iter,
796 						    &snaps[num_snaps - 1]);
797 		if (!NT_STATUS_IS_OK(status)) {
798 			talloc_free(snaps);
799 			return status;
800 		}
801 		dbus_message_iter_next(&array_iter);
802 	}
803 
804 	*num_snaps_out = num_snaps;
805 	*snaps_out = snaps;
806 
807 	return NT_STATUS_OK;
808 }
809 
snapper_list_snaps_unpack(TALLOC_CTX * mem_ctx,DBusMessage * rsp_msg,uint32_t * num_snaps_out,struct snapper_snap ** snaps_out)810 static NTSTATUS snapper_list_snaps_unpack(TALLOC_CTX *mem_ctx,
811 					  DBusMessage *rsp_msg,
812 					  uint32_t *num_snaps_out,
813 					  struct snapper_snap **snaps_out)
814 {
815 	NTSTATUS status;
816 	DBusMessageIter iter;
817 	int msg_type;
818 	uint32_t num_snaps;
819 	struct snapper_snap *snaps;
820 	const char *sig;
821 
822 	msg_type = dbus_message_get_type(rsp_msg);
823 	if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
824 		const char *err_str = dbus_message_get_error_name(rsp_msg);
825 		DEBUG(0, ("list_snaps error response: %s\n", err_str));
826 		return snapper_err_ntstatus_map(err_str);
827 	}
828 
829 	if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
830 		DEBUG(0,("unexpected list_snaps ret type: %d\n",
831 			 msg_type));
832 		return NT_STATUS_INVALID_PARAMETER;
833 	}
834 
835 	sig = dbus_message_get_signature(rsp_msg);
836 	if ((sig == NULL)
837 	 || (strcmp(sig, SNAPPER_SIG_LIST_SNAPS_RSP) != 0)) {
838 		DEBUG(0, ("bad list snaps response sig: %s, "
839 			  "expected: %s\n",
840 			  (sig ? sig : "NULL"),
841 			  SNAPPER_SIG_LIST_SNAPS_RSP));
842 		return NT_STATUS_INVALID_PARAMETER;
843 	}
844 
845 	/* read the parameters */
846 	if (!dbus_message_iter_init(rsp_msg, &iter)) {
847 		DEBUG(0, ("response has no arguments!\n"));
848 		return NT_STATUS_INVALID_PARAMETER;
849 	}
850 
851 	status = snapper_snap_array_unpack(mem_ctx, &iter, &num_snaps, &snaps);
852 	if (!NT_STATUS_IS_OK(status)) {
853 		DEBUG(0, ("failed to unpack snap array\n"));
854 		return NT_STATUS_INVALID_PARAMETER;
855 	}
856 
857 	snapper_snap_array_print(num_snaps, snaps);
858 
859 	*num_snaps_out = num_snaps;
860 	*snaps_out = snaps;
861 
862 	return NT_STATUS_OK;
863 }
864 
snapper_create_snap_pack(TALLOC_CTX * mem_ctx,const char * snapper_conf,const char * desc,uint32_t num_user_data,struct snapper_dict * user_data,DBusMessage ** req_msg_out)865 static NTSTATUS snapper_create_snap_pack(TALLOC_CTX *mem_ctx,
866 					 const char *snapper_conf,
867 					 const char *desc,
868 					 uint32_t num_user_data,
869 					 struct snapper_dict *user_data,
870 					 DBusMessage **req_msg_out)
871 {
872 	DBusMessage *msg;
873 	DBusMessageIter args;
874 	DBusMessageIter array_iter;
875 	DBusMessageIter struct_iter;
876 	const char *empty = "";
877 	char *str_encoded;
878 	uint32_t i;
879 	bool ok;
880 	TALLOC_CTX *enc_ctx;
881 	NTSTATUS status;
882 
883 	DEBUG(10, ("CreateSingleSnapshot: %s, %s, %s, num user %u\n",
884 		  snapper_conf, desc, empty, num_user_data));
885 
886 	enc_ctx = talloc_new(mem_ctx);
887 	if (enc_ctx == NULL) {
888 		return NT_STATUS_NO_MEMORY;
889 	}
890 
891 	msg = dbus_message_new_method_call("org.opensuse.Snapper",
892 					   "/org/opensuse/Snapper",
893 					   "org.opensuse.Snapper",
894 					   "CreateSingleSnapshot");
895 	if (msg == NULL) {
896 		DEBUG(0, ("failed to create req msg\n"));
897 		talloc_free(enc_ctx);
898 		return NT_STATUS_NO_MEMORY;
899 	}
900 
901 	status = snapper_dbus_str_encode(enc_ctx, snapper_conf, &str_encoded);
902 	if (!NT_STATUS_IS_OK(status)) {
903 		dbus_message_unref(msg);
904 		talloc_free(enc_ctx);
905 		return status;
906 	}
907 
908 	/* append arguments */
909 	dbus_message_iter_init_append(msg, &args);
910 	ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
911 					    &str_encoded);
912 	if (!ok) {
913 		dbus_message_unref(msg);
914 		talloc_free(enc_ctx);
915 		return NT_STATUS_NO_MEMORY;
916 	}
917 
918 	status = snapper_dbus_str_encode(enc_ctx, desc, &str_encoded);
919 	if (!NT_STATUS_IS_OK(status)) {
920 		dbus_message_unref(msg);
921 		talloc_free(enc_ctx);
922 		return status;
923 	}
924 
925 	ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
926 					    &str_encoded);
927 	if (!ok) {
928 		dbus_message_unref(msg);
929 		talloc_free(enc_ctx);
930 		return NT_STATUS_NO_MEMORY;
931 	}
932 
933 	/* cleanup - no need to encode empty string */
934 	ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
935 					    &empty);
936 	if (!ok) {
937 		dbus_message_unref(msg);
938 		talloc_free(enc_ctx);
939 		return NT_STATUS_NO_MEMORY;
940 	}
941 
942 	ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
943 					      SNAPPER_SIG_STRING_DICT,
944 					      &array_iter);
945 	if (!ok) {
946 		dbus_message_unref(msg);
947 		talloc_free(enc_ctx);
948 		return NT_STATUS_NO_MEMORY;
949 	}
950 
951 	for (i = 0; i < num_user_data; i++) {
952 		ok = dbus_message_iter_open_container(&array_iter,
953 						      DBUS_TYPE_DICT_ENTRY,
954 						      NULL, &struct_iter);
955 		if (!ok) {
956 			dbus_message_unref(msg);
957 			talloc_free(enc_ctx);
958 			return NT_STATUS_NO_MEMORY;
959 		}
960 
961 		status = snapper_dbus_str_encode(enc_ctx, user_data[i].key,
962 						 &str_encoded);
963 		if (!NT_STATUS_IS_OK(status)) {
964 			dbus_message_unref(msg);
965 			talloc_free(enc_ctx);
966 			return status;
967 		}
968 
969 		ok = dbus_message_iter_append_basic(&struct_iter,
970 						    DBUS_TYPE_STRING,
971 						    &str_encoded);
972 		if (!ok) {
973 			dbus_message_unref(msg);
974 			talloc_free(enc_ctx);
975 			return NT_STATUS_NO_MEMORY;
976 		}
977 
978 		status = snapper_dbus_str_encode(enc_ctx, user_data[i].val,
979 						 &str_encoded);
980 		if (!NT_STATUS_IS_OK(status)) {
981 			dbus_message_unref(msg);
982 			talloc_free(enc_ctx);
983 			return status;
984 		}
985 
986 		ok = dbus_message_iter_append_basic(&struct_iter,
987 						    DBUS_TYPE_STRING,
988 						    &str_encoded);
989 		if (!ok) {
990 			dbus_message_unref(msg);
991 			talloc_free(enc_ctx);
992 			return NT_STATUS_NO_MEMORY;
993 		}
994 
995 		ok = dbus_message_iter_close_container(&array_iter, &struct_iter);
996 		if (!ok) {
997 			dbus_message_unref(msg);
998 			talloc_free(enc_ctx);
999 			return NT_STATUS_NO_MEMORY;
1000 		}
1001 	}
1002 
1003 	ok = dbus_message_iter_close_container(&args, &array_iter);
1004 	if (!ok) {
1005 		dbus_message_unref(msg);
1006 		talloc_free(enc_ctx);
1007 		return NT_STATUS_NO_MEMORY;
1008 	}
1009 
1010 	*req_msg_out = msg;
1011 
1012 	return NT_STATUS_OK;
1013 }
1014 
snapper_create_snap_unpack(DBusConnection * conn,DBusMessage * rsp_msg,uint32_t * snap_id_out)1015 static NTSTATUS snapper_create_snap_unpack(DBusConnection *conn,
1016 					   DBusMessage *rsp_msg,
1017 					   uint32_t *snap_id_out)
1018 {
1019 	NTSTATUS status;
1020 	DBusMessageIter iter;
1021 	int msg_type;
1022 	const char *sig;
1023 	uint32_t snap_id;
1024 
1025 	msg_type = dbus_message_get_type(rsp_msg);
1026 	if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
1027 		const char *err_str = dbus_message_get_error_name(rsp_msg);
1028 		DEBUG(0, ("create snap error response: %s, euid %d egid %d\n",
1029 			  err_str, geteuid(), getegid()));
1030 		return snapper_err_ntstatus_map(err_str);
1031 	}
1032 
1033 	if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
1034 		DEBUG(0, ("unexpected create snap ret type: %d\n",
1035 			  msg_type));
1036 		return NT_STATUS_INVALID_PARAMETER;
1037 	}
1038 
1039 	sig = dbus_message_get_signature(rsp_msg);
1040 	if ((sig == NULL)
1041 	 || (strcmp(sig, SNAPPER_SIG_CREATE_SNAP_RSP) != 0)) {
1042 		DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
1043 			  (sig ? sig : "NULL"), SNAPPER_SIG_CREATE_SNAP_RSP));
1044 		return NT_STATUS_INVALID_PARAMETER;
1045 	}
1046 
1047 	/* read the parameters */
1048 	if (!dbus_message_iter_init(rsp_msg, &iter)) {
1049 		DEBUG(0, ("response has no arguments!\n"));
1050 		return NT_STATUS_INVALID_PARAMETER;
1051 	}
1052 
1053 	status = snapper_type_check_get(&iter, DBUS_TYPE_UINT32, &snap_id);
1054 	if (!NT_STATUS_IS_OK(status)) {
1055 		return status;
1056 	}
1057 	*snap_id_out = snap_id;
1058 
1059 	return NT_STATUS_OK;
1060 }
1061 
snapper_del_snap_pack(TALLOC_CTX * mem_ctx,const char * snapper_conf,uint32_t snap_id,DBusMessage ** req_msg_out)1062 static NTSTATUS snapper_del_snap_pack(TALLOC_CTX *mem_ctx,
1063 				      const char *snapper_conf,
1064 				      uint32_t snap_id,
1065 				      DBusMessage **req_msg_out)
1066 {
1067 	DBusMessage *msg;
1068 	DBusMessageIter args;
1069 	DBusMessageIter array_iter;
1070 	char *conf_encoded;
1071 	bool ok;
1072 	NTSTATUS status;
1073 
1074 	msg = dbus_message_new_method_call("org.opensuse.Snapper",
1075 					   "/org/opensuse/Snapper",
1076 					   "org.opensuse.Snapper",
1077 					   "DeleteSnapshots");
1078 	if (msg == NULL) {
1079 		DEBUG(0, ("failed to create req msg\n"));
1080 		return NT_STATUS_NO_MEMORY;
1081 	}
1082 
1083 	status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
1084 	if (!NT_STATUS_IS_OK(status)) {
1085 		dbus_message_unref(msg);
1086 		return status;
1087 	}
1088 
1089 	/* append arguments */
1090 	dbus_message_iter_init_append(msg, &args);
1091 	ok = dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
1092 					    &conf_encoded);
1093 	if (!ok) {
1094 		talloc_free(conf_encoded);
1095 		dbus_message_unref(msg);
1096 		return NT_STATUS_NO_MEMORY;
1097 	}
1098 
1099 	ok = dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
1100 					       DBUS_TYPE_UINT32_AS_STRING,
1101 					       &array_iter);
1102 	if (!ok) {
1103 		talloc_free(conf_encoded);
1104 		dbus_message_unref(msg);
1105 		return NT_STATUS_NO_MEMORY;
1106 	}
1107 
1108 	ok = dbus_message_iter_append_basic(&array_iter,
1109 					    DBUS_TYPE_UINT32,
1110 					    &snap_id);
1111 	if (!ok) {
1112 		talloc_free(conf_encoded);
1113 		dbus_message_unref(msg);
1114 		return NT_STATUS_NO_MEMORY;
1115 	}
1116 
1117 	dbus_message_iter_close_container(&args, &array_iter);
1118 	*req_msg_out = msg;
1119 
1120 	return NT_STATUS_OK;
1121 }
1122 
snapper_del_snap_unpack(DBusConnection * conn,DBusMessage * rsp_msg)1123 static NTSTATUS snapper_del_snap_unpack(DBusConnection *conn,
1124 					DBusMessage *rsp_msg)
1125 {
1126 	int msg_type;
1127 	const char *sig;
1128 
1129 	msg_type = dbus_message_get_type(rsp_msg);
1130 	if (msg_type == DBUS_MESSAGE_TYPE_ERROR) {
1131 		const char *err_str = dbus_message_get_error_name(rsp_msg);
1132 		DEBUG(0, ("del snap error response: %s\n", err_str));
1133 		return snapper_err_ntstatus_map(err_str);
1134 	}
1135 
1136 	if (msg_type != DBUS_MESSAGE_TYPE_METHOD_RETURN) {
1137 		DEBUG(0, ("unexpected del snap ret type: %d\n",
1138 			  msg_type));
1139 		return NT_STATUS_INVALID_PARAMETER;
1140 	}
1141 
1142 	sig = dbus_message_get_signature(rsp_msg);
1143 	if ((sig == NULL)
1144 	 || (strcmp(sig, SNAPPER_SIG_DEL_SNAPS_RSP) != 0)) {
1145 		DEBUG(0, ("bad create snap response sig: %s, expected: %s\n",
1146 			  (sig ? sig : "NULL"), SNAPPER_SIG_DEL_SNAPS_RSP));
1147 		return NT_STATUS_INVALID_PARAMETER;
1148 	}
1149 
1150 	/* no parameters in response */
1151 
1152 	return NT_STATUS_OK;
1153 }
1154 
snapper_list_snaps_at_time_pack(TALLOC_CTX * mem_ctx,const char * snapper_conf,time_t time_lower,time_t time_upper,DBusMessage ** req_msg_out)1155 static NTSTATUS snapper_list_snaps_at_time_pack(TALLOC_CTX *mem_ctx,
1156 						const char *snapper_conf,
1157 						time_t time_lower,
1158 						time_t time_upper,
1159 						DBusMessage **req_msg_out)
1160 {
1161 	DBusMessage *msg;
1162 	DBusMessageIter args;
1163 	char *conf_encoded;
1164 	NTSTATUS status;
1165 
1166 	msg = dbus_message_new_method_call("org.opensuse.Snapper",
1167 					   "/org/opensuse/Snapper",
1168 					   "org.opensuse.Snapper",
1169 					   "ListSnapshotsAtTime");
1170 	if (msg == NULL) {
1171 		DEBUG(0, ("failed to create list snaps message\n"));
1172 		return NT_STATUS_NO_MEMORY;
1173 	}
1174 
1175 	status = snapper_dbus_str_encode(mem_ctx, snapper_conf, &conf_encoded);
1176 	if (!NT_STATUS_IS_OK(status)) {
1177 		dbus_message_unref(msg);
1178 		return status;
1179 	}
1180 
1181 	dbus_message_iter_init_append(msg, &args);
1182 	if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING,
1183 					    &conf_encoded)) {
1184 		talloc_free(conf_encoded);
1185 		dbus_message_unref(msg);
1186 		return NT_STATUS_NO_MEMORY;
1187 	}
1188 
1189 	if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
1190 					    &time_lower)) {
1191 		talloc_free(conf_encoded);
1192 		dbus_message_unref(msg);
1193 		return NT_STATUS_NO_MEMORY;
1194 	}
1195 
1196 	if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_INT64,
1197 					    &time_upper)) {
1198 		talloc_free(conf_encoded);
1199 		dbus_message_unref(msg);
1200 		return NT_STATUS_NO_MEMORY;
1201 	}
1202 
1203 	*req_msg_out = msg;
1204 
1205 	return NT_STATUS_OK;
1206 }
1207 /* no snapper_list_snaps_at_time_unpack, use snapper_list_snaps_unpack */
1208 
1209 /*
1210  * Determine the snapper snapshot id given a path.
1211  * Ideally this should be determined via a lookup.
1212  */
snapper_snap_path_to_id(TALLOC_CTX * mem_ctx,const char * snap_path,uint32_t * snap_id_out)1213 static NTSTATUS snapper_snap_path_to_id(TALLOC_CTX *mem_ctx,
1214 					const char *snap_path,
1215 					uint32_t *snap_id_out)
1216 {
1217 	char *path_dup;
1218 	char *str_idx;
1219 	uint32_t snap_id;
1220 	int error = 0;
1221 
1222 	path_dup = talloc_strdup(mem_ctx, snap_path);
1223 	if (path_dup == NULL) {
1224 		return NT_STATUS_NO_MEMORY;
1225 	}
1226 
1227 	/* trim trailing '/' */
1228 	str_idx = path_dup + strlen(path_dup) - 1;
1229 	while (*str_idx == '/') {
1230 		*str_idx = '\0';
1231 		str_idx--;
1232 	}
1233 
1234 	str_idx = strrchr(path_dup, '/');
1235 	if ((str_idx == NULL)
1236 	 || (strcmp(str_idx + 1, "snapshot") != 0)) {
1237 		talloc_free(path_dup);
1238 		return NT_STATUS_INVALID_PARAMETER;
1239 	}
1240 
1241 	while (*str_idx == '/') {
1242 		*str_idx = '\0';
1243 		str_idx--;
1244 	}
1245 
1246 	str_idx = strrchr(path_dup, '/');
1247 	if (str_idx == NULL) {
1248 		talloc_free(path_dup);
1249 		return NT_STATUS_INVALID_PARAMETER;
1250 	}
1251 
1252 	str_idx++;
1253 	snap_id = smb_strtoul(str_idx, NULL, 10, &error, SMB_STR_STANDARD);
1254 	if (error != 0) {
1255 		talloc_free(path_dup);
1256 		return NT_STATUS_INVALID_PARAMETER;
1257 	}
1258 
1259 	talloc_free(path_dup);
1260 	*snap_id_out = snap_id;
1261 	return NT_STATUS_OK;
1262 }
1263 
1264 /*
1265  * Determine the snapper snapshot path given an id and base.
1266  * Ideally this should be determined via a lookup.
1267  */
snapper_snap_id_to_path(TALLOC_CTX * mem_ctx,const char * base_path,uint32_t snap_id,char ** snap_path_out)1268 static NTSTATUS snapper_snap_id_to_path(TALLOC_CTX *mem_ctx,
1269 					const char *base_path,
1270 					uint32_t snap_id,
1271 					char **snap_path_out)
1272 {
1273 	char *snap_path;
1274 
1275 	snap_path = talloc_asprintf(mem_ctx, "%s/.snapshots/%u/snapshot",
1276 				    base_path, snap_id);
1277 	if (snap_path == NULL) {
1278 		return NT_STATUS_NO_MEMORY;
1279 	}
1280 
1281 	*snap_path_out = snap_path;
1282 	return NT_STATUS_OK;
1283 }
1284 
snapper_get_conf_call(TALLOC_CTX * mem_ctx,DBusConnection * dconn,const char * path,char ** conf_name_out,char ** base_path_out)1285 static NTSTATUS snapper_get_conf_call(TALLOC_CTX *mem_ctx,
1286 				      DBusConnection *dconn,
1287 				      const char *path,
1288 				      char **conf_name_out,
1289 				      char **base_path_out)
1290 {
1291 	NTSTATUS status;
1292 	DBusMessage *req_msg;
1293 	DBusMessage *rsp_msg;
1294 	uint32_t num_confs = 0;
1295 	struct snapper_conf *confs = NULL;
1296 	struct snapper_conf *conf;
1297 	char *conf_name;
1298 	char *base_path;
1299 
1300 	status = snapper_list_confs_pack(&req_msg);
1301 	if (!NT_STATUS_IS_OK(status)) {
1302 		goto err_out;
1303 	}
1304 
1305 	status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1306 	if (!NT_STATUS_IS_OK(status)) {
1307 		goto err_req_free;
1308 	}
1309 
1310 	status = snapper_list_confs_unpack(mem_ctx, dconn, rsp_msg,
1311 					   &num_confs, &confs);
1312 	if (!NT_STATUS_IS_OK(status)) {
1313 		goto err_rsp_free;
1314 	}
1315 
1316 	/*
1317 	 * for now we only support shares where the path directly corresponds
1318 	 * to a snapper configuration.
1319 	 */
1320 	conf = snapper_conf_array_base_find(num_confs, confs,
1321 					    path);
1322 	if (conf == NULL) {
1323 		status = NT_STATUS_NOT_SUPPORTED;
1324 		goto err_array_free;
1325 	}
1326 
1327 	conf_name = talloc_strdup(mem_ctx, conf->name);
1328 	if (conf_name == NULL) {
1329 		status = NT_STATUS_NO_MEMORY;
1330 		goto err_array_free;
1331 	}
1332 	base_path = talloc_strdup(mem_ctx, conf->mnt);
1333 	if (base_path == NULL) {
1334 		status = NT_STATUS_NO_MEMORY;
1335 		goto err_conf_name_free;
1336 	}
1337 
1338 	talloc_free(confs);
1339 	dbus_message_unref(rsp_msg);
1340 	dbus_message_unref(req_msg);
1341 
1342 	*conf_name_out = conf_name;
1343 	*base_path_out = base_path;
1344 
1345 	return NT_STATUS_OK;
1346 
1347 err_conf_name_free:
1348 	talloc_free(conf_name);
1349 err_array_free:
1350 	talloc_free(confs);
1351 err_rsp_free:
1352 	dbus_message_unref(rsp_msg);
1353 err_req_free:
1354 	dbus_message_unref(req_msg);
1355 err_out:
1356 	return status;
1357 }
1358 
1359 /*
1360  * Check whether a path can be shadow copied. Return the base volume, allowing
1361  * the caller to determine if multiple paths lie on the same base volume.
1362  */
snapper_snap_check_path(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,const char * service_path,char ** base_volume)1363 static NTSTATUS snapper_snap_check_path(struct vfs_handle_struct *handle,
1364 					TALLOC_CTX *mem_ctx,
1365 					const char *service_path,
1366 					char **base_volume)
1367 {
1368 	NTSTATUS status;
1369 	DBusConnection *dconn;
1370 	char *conf_name;
1371 	char *base_path;
1372 
1373 	dconn = snapper_dbus_conn_create();
1374 	if (dconn == NULL) {
1375 		return NT_STATUS_UNSUCCESSFUL;
1376 	}
1377 
1378 	status = snapper_get_conf_call(mem_ctx, dconn, service_path,
1379 				       &conf_name, &base_path);
1380 	if (!NT_STATUS_IS_OK(status)) {
1381 		goto err_conn_close;
1382 	}
1383 
1384 	talloc_free(conf_name);
1385 	*base_volume = base_path;
1386 	snapper_dbus_conn_destroy(dconn);
1387 
1388 	return NT_STATUS_OK;
1389 
1390 err_conn_close:
1391 	snapper_dbus_conn_destroy(dconn);
1392 	return status;
1393 }
1394 
snapper_create_snap_call(TALLOC_CTX * mem_ctx,DBusConnection * dconn,const char * conf_name,const char * base_path,const char * snap_desc,uint32_t num_user_data,struct snapper_dict * user_data,char ** snap_path_out)1395 static NTSTATUS snapper_create_snap_call(TALLOC_CTX *mem_ctx,
1396 					 DBusConnection *dconn,
1397 					 const char *conf_name,
1398 					 const char *base_path,
1399 					 const char *snap_desc,
1400 					 uint32_t num_user_data,
1401 					 struct snapper_dict *user_data,
1402 					 char **snap_path_out)
1403 {
1404 	NTSTATUS status;
1405 	DBusMessage *req_msg;
1406 	DBusMessage *rsp_msg;
1407 	uint32_t snap_id = 0;
1408 	char *snap_path;
1409 
1410 	status = snapper_create_snap_pack(mem_ctx,
1411 					  conf_name,
1412 					  snap_desc,
1413 					  num_user_data,
1414 					  user_data,
1415 					  &req_msg);
1416 	if (!NT_STATUS_IS_OK(status)) {
1417 		goto err_out;
1418 	}
1419 
1420 	status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1421 	if (!NT_STATUS_IS_OK(status)) {
1422 		goto err_req_free;
1423 	}
1424 
1425 	status = snapper_create_snap_unpack(dconn, rsp_msg, &snap_id);
1426 	if (!NT_STATUS_IS_OK(status)) {
1427 		goto err_rsp_free;
1428 	}
1429 
1430 	status = snapper_snap_id_to_path(mem_ctx, base_path, snap_id,
1431 					 &snap_path);
1432 	if (!NT_STATUS_IS_OK(status)) {
1433 		goto err_rsp_free;
1434 	}
1435 
1436 	dbus_message_unref(rsp_msg);
1437 	dbus_message_unref(req_msg);
1438 
1439 	DEBUG(6, ("created new snapshot %u at %s\n", snap_id, snap_path));
1440 	*snap_path_out = snap_path;
1441 
1442 	return NT_STATUS_OK;
1443 
1444 err_rsp_free:
1445 	dbus_message_unref(rsp_msg);
1446 err_req_free:
1447 	dbus_message_unref(req_msg);
1448 err_out:
1449 	return status;
1450 }
1451 
snapper_snap_create(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,const char * base_volume,time_t * tstamp,bool rw,char ** _base_path,char ** _snap_path)1452 static NTSTATUS snapper_snap_create(struct vfs_handle_struct *handle,
1453 				    TALLOC_CTX *mem_ctx,
1454 				    const char *base_volume,
1455 				    time_t *tstamp,
1456 				    bool rw,
1457 				    char **_base_path,
1458 				    char **_snap_path)
1459 {
1460 	DBusConnection *dconn;
1461 	NTSTATUS status;
1462 	char *conf_name;
1463 	char *base_path;
1464 	char *snap_path = NULL;
1465 	TALLOC_CTX *tmp_ctx;
1466 
1467 	tmp_ctx = talloc_new(mem_ctx);
1468 	if (tmp_ctx == NULL) {
1469 		return NT_STATUS_NO_MEMORY;
1470 	}
1471 
1472 	dconn = snapper_dbus_conn_create();
1473 	if (dconn == NULL) {
1474 		talloc_free(tmp_ctx);
1475 		return NT_STATUS_UNSUCCESSFUL;
1476 	}
1477 
1478 	status = snapper_get_conf_call(tmp_ctx, dconn, base_volume,
1479 				       &conf_name, &base_path);
1480 	if (!NT_STATUS_IS_OK(status)) {
1481 		snapper_dbus_conn_destroy(dconn);
1482 		talloc_free(tmp_ctx);
1483 		return status;
1484 	}
1485 
1486 	status = snapper_create_snap_call(tmp_ctx, dconn,
1487 					  conf_name, base_path,
1488 					  "Snapshot created by Samba",
1489 					  0, NULL,
1490 					  &snap_path);
1491 	if (!NT_STATUS_IS_OK(status)) {
1492 		snapper_dbus_conn_destroy(dconn);
1493 		talloc_free(tmp_ctx);
1494 		return status;
1495 	}
1496 
1497 	snapper_dbus_conn_destroy(dconn);
1498 	*_base_path = talloc_steal(mem_ctx, base_path);
1499 	*_snap_path = talloc_steal(mem_ctx, snap_path);
1500 	talloc_free(tmp_ctx);
1501 
1502 	return NT_STATUS_OK;
1503 }
1504 
snapper_delete_snap_call(TALLOC_CTX * mem_ctx,DBusConnection * dconn,const char * conf_name,uint32_t snap_id)1505 static NTSTATUS snapper_delete_snap_call(TALLOC_CTX *mem_ctx,
1506 					 DBusConnection *dconn,
1507 					 const char *conf_name,
1508 					 uint32_t snap_id)
1509 {
1510 	NTSTATUS status;
1511 	DBusMessage *req_msg = NULL;
1512 	DBusMessage *rsp_msg;
1513 
1514 	status = snapper_del_snap_pack(mem_ctx, conf_name, snap_id, &req_msg);
1515 	if (!NT_STATUS_IS_OK(status)) {
1516 		goto err_out;
1517 	}
1518 
1519 	status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1520 	if (!NT_STATUS_IS_OK(status)) {
1521 		goto err_req_free;
1522 	}
1523 
1524 	status = snapper_del_snap_unpack(dconn, rsp_msg);
1525 	if (!NT_STATUS_IS_OK(status)) {
1526 		goto err_rsp_free;
1527 	}
1528 
1529 	dbus_message_unref(rsp_msg);
1530 	dbus_message_unref(req_msg);
1531 
1532 	DEBUG(6, ("deleted snapshot %u\n", snap_id));
1533 
1534 	return NT_STATUS_OK;
1535 
1536 err_rsp_free:
1537 	dbus_message_unref(rsp_msg);
1538 err_req_free:
1539 	dbus_message_unref(req_msg);
1540 err_out:
1541 	return status;
1542 }
1543 
snapper_snap_delete(struct vfs_handle_struct * handle,TALLOC_CTX * mem_ctx,char * base_path,char * snap_path)1544 static NTSTATUS snapper_snap_delete(struct vfs_handle_struct *handle,
1545 				    TALLOC_CTX *mem_ctx,
1546 				    char *base_path,
1547 				    char *snap_path)
1548 {
1549 	DBusConnection *dconn;
1550 	NTSTATUS status;
1551 	char *conf_name;
1552 	char *snap_base_path;
1553 	uint32_t snap_id;
1554 	TALLOC_CTX *tmp_ctx;
1555 
1556 	tmp_ctx = talloc_new(mem_ctx);
1557 	if (tmp_ctx == NULL) {
1558 		return NT_STATUS_NO_MEMORY;
1559 	}
1560 
1561 	dconn = snapper_dbus_conn_create();
1562 	if (dconn == NULL) {
1563 		talloc_free(tmp_ctx);
1564 		return NT_STATUS_UNSUCCESSFUL;
1565 	}
1566 
1567 	status = snapper_get_conf_call(tmp_ctx, dconn, base_path,
1568 				       &conf_name, &snap_base_path);
1569 	if (!NT_STATUS_IS_OK(status)) {
1570 		snapper_dbus_conn_destroy(dconn);
1571 		talloc_free(tmp_ctx);
1572 		return status;
1573 	}
1574 
1575 	status = snapper_snap_path_to_id(tmp_ctx, snap_path, &snap_id);
1576 	if (!NT_STATUS_IS_OK(status)) {
1577 		snapper_dbus_conn_destroy(dconn);
1578 		talloc_free(tmp_ctx);
1579 		return status;
1580 	}
1581 
1582 	status = snapper_delete_snap_call(tmp_ctx, dconn, conf_name, snap_id);
1583 	if (!NT_STATUS_IS_OK(status)) {
1584 		snapper_dbus_conn_destroy(dconn);
1585 		talloc_free(tmp_ctx);
1586 		return status;
1587 	}
1588 
1589 	snapper_dbus_conn_destroy(dconn);
1590 	talloc_free(tmp_ctx);
1591 
1592 	return NT_STATUS_OK;
1593 }
1594 
1595 /* sc_data used as parent talloc context for all labels */
snapper_get_shadow_copy_data(struct vfs_handle_struct * handle,struct files_struct * fsp,struct shadow_copy_data * sc_data,bool labels)1596 static int snapper_get_shadow_copy_data(struct vfs_handle_struct *handle,
1597 					struct files_struct *fsp,
1598 					struct shadow_copy_data *sc_data,
1599 					bool labels)
1600 {
1601 	DBusConnection *dconn;
1602 	TALLOC_CTX *tmp_ctx;
1603 	NTSTATUS status;
1604 	char *conf_name;
1605 	char *base_path;
1606 	DBusMessage *req_msg = NULL;
1607 	DBusMessage *rsp_msg;
1608 	uint32_t num_snaps;
1609 	struct snapper_snap *snaps;
1610 	uint32_t i;
1611 	uint32_t lbl_off;
1612 
1613 	tmp_ctx = talloc_new(sc_data);
1614 	if (tmp_ctx == NULL) {
1615 		status = NT_STATUS_NO_MEMORY;
1616 		goto err_out;
1617 	}
1618 
1619 	dconn = snapper_dbus_conn_create();
1620 	if (dconn == NULL) {
1621 		status = NT_STATUS_UNSUCCESSFUL;
1622 		goto err_mem_ctx_free;
1623 	}
1624 
1625 	if (fsp->conn->connectpath == NULL) {
1626 		status = NT_STATUS_INVALID_PARAMETER;
1627 		goto err_conn_free;
1628 	}
1629 
1630 	status = snapper_get_conf_call(tmp_ctx, dconn,
1631 				       fsp->conn->connectpath,
1632 				       &conf_name,
1633 				       &base_path);
1634 	if (!NT_STATUS_IS_OK(status)) {
1635 		goto err_conn_free;
1636 	}
1637 
1638 	status = snapper_list_snaps_pack(tmp_ctx, conf_name, &req_msg);
1639 	if (!NT_STATUS_IS_OK(status)) {
1640 		goto err_conn_free;
1641 	}
1642 
1643 	status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1644 	if (!NT_STATUS_IS_OK(status)) {
1645 		goto err_req_free;
1646 	}
1647 
1648 	status = snapper_list_snaps_unpack(tmp_ctx, rsp_msg,
1649 					   &num_snaps, &snaps);
1650 	if (!NT_STATUS_IS_OK(status)) {
1651 		goto err_rsp_free;
1652 	}
1653 	/* we should always get at least one snapshot (current) */
1654 	if (num_snaps == 0) {
1655 		DEBUG(1, ("zero snapshots in snap list response\n"));
1656 		status = NT_STATUS_UNSUCCESSFUL;
1657 		goto err_rsp_free;
1658 	}
1659 
1660 	/* subtract 1, (current) snapshot is not returned */
1661 	sc_data->num_volumes = num_snaps - 1;
1662 	sc_data->labels = NULL;
1663 
1664 	if ((labels == false) || (sc_data->num_volumes == 0)) {
1665 		/* tokens need not be added to the labels array */
1666 		goto done;
1667 	}
1668 
1669 	sc_data->labels = talloc_array(sc_data, SHADOW_COPY_LABEL,
1670 				       sc_data->num_volumes);
1671 	if (sc_data->labels == NULL) {
1672 		status = NT_STATUS_NO_MEMORY;
1673 		goto err_rsp_free;
1674 	}
1675 
1676 	/* start at end for decending order, do not include 0 (current) */
1677 	lbl_off = 0;
1678 	for (i = num_snaps - 1; i > 0; i--) {
1679 		char *lbl = sc_data->labels[lbl_off++];
1680 		struct tm gmt_snap_time;
1681 		struct tm *tm_ret;
1682 		size_t str_sz;
1683 
1684 		tm_ret = gmtime_r((time_t *)&snaps[i].time, &gmt_snap_time);
1685 		if (tm_ret == NULL) {
1686 			status = NT_STATUS_UNSUCCESSFUL;
1687 			goto err_labels_free;
1688 		}
1689 		str_sz = strftime(lbl, sizeof(SHADOW_COPY_LABEL),
1690 				  "@GMT-%Y.%m.%d-%H.%M.%S", &gmt_snap_time);
1691 		if (str_sz == 0) {
1692 			status = NT_STATUS_UNSUCCESSFUL;
1693 			goto err_labels_free;
1694 		}
1695 	}
1696 
1697 done:
1698 	talloc_free(tmp_ctx);
1699 	dbus_message_unref(rsp_msg);
1700 	dbus_message_unref(req_msg);
1701 	snapper_dbus_conn_destroy(dconn);
1702 
1703 	return 0;
1704 
1705 err_labels_free:
1706 	TALLOC_FREE(sc_data->labels);
1707 err_rsp_free:
1708 	dbus_message_unref(rsp_msg);
1709 err_req_free:
1710 	dbus_message_unref(req_msg);
1711 err_conn_free:
1712 	snapper_dbus_conn_destroy(dconn);
1713 err_mem_ctx_free:
1714 	talloc_free(tmp_ctx);
1715 err_out:
1716 	errno = map_errno_from_nt_status(status);
1717 	return -1;
1718 }
1719 
snapper_gmt_strip_snapshot(TALLOC_CTX * mem_ctx,struct vfs_handle_struct * handle,const char * name,time_t * ptimestamp,char ** pstripped)1720 static bool snapper_gmt_strip_snapshot(TALLOC_CTX *mem_ctx,
1721 				       struct vfs_handle_struct *handle,
1722 				       const char *name,
1723 				       time_t *ptimestamp,
1724 				       char **pstripped)
1725 {
1726 	struct tm tm;
1727 	time_t timestamp;
1728 	const char *p;
1729 	char *q;
1730 	char *stripped;
1731 	size_t rest_len, dst_len;
1732 	ptrdiff_t len_before_gmt;
1733 
1734 	p = strstr_m(name, "@GMT-");
1735 	if (p == NULL) {
1736 		goto no_snapshot;
1737 	}
1738 	if ((p > name) && (p[-1] != '/')) {
1739 		goto no_snapshot;
1740 	}
1741 	len_before_gmt = p - name;
1742 	q = strptime(p, GMT_FORMAT, &tm);
1743 	if (q == NULL) {
1744 		goto no_snapshot;
1745 	}
1746 	tm.tm_isdst = -1;
1747 	timestamp = timegm(&tm);
1748 	if (timestamp == (time_t)-1) {
1749 		goto no_snapshot;
1750 	}
1751 	if (q[0] == '\0') {
1752 		/*
1753 		 * The name consists of only the GMT token or the GMT
1754 		 * token is at the end of the path. XP seems to send
1755 		 * @GMT- at the end under certain circumstances even
1756 		 * with a path prefix.
1757 		 */
1758 		if (pstripped != NULL) {
1759 			if (len_before_gmt > 0) {
1760 				/*
1761 				 * There is a slash before
1762 				 * the @GMT-. Remove it.
1763 				 */
1764 				len_before_gmt -= 1;
1765 			}
1766 			stripped = talloc_strndup(mem_ctx, name,
1767 					len_before_gmt);
1768 			if (stripped == NULL) {
1769 				return false;
1770 			}
1771 			*pstripped = stripped;
1772 		}
1773 		*ptimestamp = timestamp;
1774 		return true;
1775 	}
1776 	if (q[0] != '/') {
1777 		/*
1778 		 * It is not a complete path component, i.e. the path
1779 		 * component continues after the gmt-token.
1780 		 */
1781 		goto no_snapshot;
1782 	}
1783 	q += 1;
1784 
1785 	rest_len = strlen(q);
1786 	dst_len = len_before_gmt + rest_len;
1787 
1788 	if (pstripped != NULL) {
1789 		stripped = talloc_array(mem_ctx, char, dst_len+1);
1790 		if (stripped == NULL) {
1791 			errno = ENOMEM;
1792 			return false;
1793 		}
1794 		if (p > name) {
1795 			memcpy(stripped, name, len_before_gmt);
1796 		}
1797 		if (rest_len > 0) {
1798 			memcpy(stripped + len_before_gmt, q, rest_len);
1799 		}
1800 		stripped[dst_len] = '\0';
1801 		*pstripped = stripped;
1802 	}
1803 	*ptimestamp = timestamp;
1804 	return true;
1805 no_snapshot:
1806 	*ptimestamp = 0;
1807 	return true;
1808 }
1809 
snapper_get_snap_at_time_call(TALLOC_CTX * mem_ctx,DBusConnection * dconn,const char * conf_name,const char * base_path,time_t snaptime,char ** snap_path_out)1810 static NTSTATUS snapper_get_snap_at_time_call(TALLOC_CTX *mem_ctx,
1811 					      DBusConnection *dconn,
1812 					      const char *conf_name,
1813 					      const char *base_path,
1814 					      time_t snaptime,
1815 					      char **snap_path_out)
1816 {
1817 	NTSTATUS status;
1818 	DBusMessage *req_msg = NULL;
1819 	DBusMessage *rsp_msg;
1820 	uint32_t num_snaps;
1821 	struct snapper_snap *snaps;
1822 	char *snap_path;
1823 
1824 	status = snapper_list_snaps_at_time_pack(mem_ctx,
1825 						 conf_name,
1826 						 snaptime,
1827 						 snaptime,
1828 						 &req_msg);
1829 	if (!NT_STATUS_IS_OK(status)) {
1830 		goto err_out;
1831 	}
1832 
1833 	status = snapper_dbus_msg_xchng(dconn, req_msg, &rsp_msg);
1834 	if (!NT_STATUS_IS_OK(status)) {
1835 		goto err_req_free;
1836 	}
1837 
1838 	status = snapper_list_snaps_unpack(mem_ctx, rsp_msg,
1839 					   &num_snaps, &snaps);
1840 	if (!NT_STATUS_IS_OK(status)) {
1841 		goto err_rsp_free;
1842 	}
1843 
1844 	if (num_snaps == 0) {
1845 		DEBUG(4, ("no snapshots found with time: %lu\n",
1846 			  (unsigned long)snaptime));
1847 		status = NT_STATUS_INVALID_PARAMETER;
1848 		goto err_snap_array_free;
1849 	} else if (num_snaps > 0) {
1850 		DEBUG(4, ("got %u snapshots for single time %lu, using top\n",
1851 			  num_snaps, (unsigned long)snaptime));
1852 	}
1853 
1854 	status = snapper_snap_id_to_path(mem_ctx, base_path, snaps[0].id,
1855 					 &snap_path);
1856 	if (!NT_STATUS_IS_OK(status)) {
1857 		goto err_snap_array_free;
1858 	}
1859 
1860 	*snap_path_out = snap_path;
1861 err_snap_array_free:
1862 	talloc_free(snaps);
1863 err_rsp_free:
1864 	dbus_message_unref(rsp_msg);
1865 err_req_free:
1866 	dbus_message_unref(req_msg);
1867 err_out:
1868 	return status;
1869 }
1870 
snapper_snap_path_expand(struct connection_struct * conn,TALLOC_CTX * mem_ctx,time_t snap_time,char ** snap_dir_out)1871 static NTSTATUS snapper_snap_path_expand(struct connection_struct *conn,
1872 					 TALLOC_CTX *mem_ctx,
1873 					 time_t snap_time,
1874 					 char **snap_dir_out)
1875 {
1876 	DBusConnection *dconn;
1877 	NTSTATUS status;
1878 	char *conf_name;
1879 	char *base_path;
1880 	char *snap_path = NULL;
1881 
1882 	dconn = snapper_dbus_conn_create();
1883 	if (dconn == NULL) {
1884 		status = NT_STATUS_UNSUCCESSFUL;
1885 		goto err_out;
1886 	}
1887 
1888 	if (conn->connectpath == NULL) {
1889 		status = NT_STATUS_INVALID_PARAMETER;
1890 		goto err_conn_free;
1891 	}
1892 
1893 	status = snapper_get_conf_call(mem_ctx, dconn,
1894 				       conn->connectpath,
1895 				       &conf_name,
1896 				       &base_path);
1897 	if (!NT_STATUS_IS_OK(status)) {
1898 		goto err_conn_free;
1899 	}
1900 
1901 	status = snapper_get_snap_at_time_call(mem_ctx, dconn,
1902 					       conf_name, base_path, snap_time,
1903 					       &snap_path);
1904 	if (!NT_STATUS_IS_OK(status)) {
1905 		goto err_conf_name_free;
1906 	}
1907 
1908 	/* confirm snapshot path is nested under base path */
1909 	if (strncmp(snap_path, base_path, strlen(base_path)) != 0) {
1910 		status = NT_STATUS_INVALID_PARAMETER;
1911 		goto err_snap_path_free;
1912 	}
1913 
1914 	talloc_free(conf_name);
1915 	talloc_free(base_path);
1916 	snapper_dbus_conn_destroy(dconn);
1917 	*snap_dir_out = snap_path;
1918 
1919 	return NT_STATUS_OK;
1920 
1921 err_snap_path_free:
1922 	talloc_free(snap_path);
1923 err_conf_name_free:
1924 	talloc_free(conf_name);
1925 	talloc_free(base_path);
1926 err_conn_free:
1927 	snapper_dbus_conn_destroy(dconn);
1928 err_out:
1929 	return status;
1930 }
1931 
snapper_gmt_convert(TALLOC_CTX * mem_ctx,struct vfs_handle_struct * handle,const char * name,time_t timestamp)1932 static char *snapper_gmt_convert(TALLOC_CTX *mem_ctx,
1933 			     struct vfs_handle_struct *handle,
1934 			     const char *name, time_t timestamp)
1935 {
1936 	char *snap_path = NULL;
1937 	char *path = NULL;
1938 	NTSTATUS status;
1939 	int saved_errno;
1940 
1941 	status = snapper_snap_path_expand(handle->conn, mem_ctx, timestamp,
1942 					  &snap_path);
1943 	if (!NT_STATUS_IS_OK(status)) {
1944 		errno = map_errno_from_nt_status(status);
1945 		goto err_out;
1946 	}
1947 
1948 	path = talloc_asprintf(mem_ctx, "%s/%s", snap_path, name);
1949 	if (path == NULL) {
1950 		errno = ENOMEM;
1951 		goto err_snap_path_free;
1952 	}
1953 
1954 	DEBUG(10, ("converted %s/%s @ time to %s\n",
1955 		   handle->conn->connectpath, name, path));
1956 	return path;
1957 
1958 err_snap_path_free:
1959 	saved_errno = errno;
1960 	talloc_free(snap_path);
1961 	errno = saved_errno;
1962 err_out:
1963 	return NULL;
1964 }
1965 
snapper_gmt_opendir(vfs_handle_struct * handle,const struct smb_filename * smb_fname,const char * mask,uint32_t attr)1966 static DIR *snapper_gmt_opendir(vfs_handle_struct *handle,
1967 				const struct smb_filename *smb_fname,
1968 				const char *mask,
1969 				uint32_t attr)
1970 {
1971 	time_t timestamp;
1972 	char *stripped;
1973 	DIR *ret;
1974 	int saved_errno;
1975 	char *conv;
1976 	struct smb_filename *conv_smb_fname = NULL;
1977 
1978 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
1979 			handle,
1980 			smb_fname->base_name,
1981 			&timestamp,
1982 			&stripped)) {
1983 		return NULL;
1984 	}
1985 	if (timestamp == 0) {
1986 		return SMB_VFS_NEXT_OPENDIR(handle, smb_fname, mask, attr);
1987 	}
1988 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
1989 	TALLOC_FREE(stripped);
1990 	if (conv == NULL) {
1991 		return NULL;
1992 	}
1993 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
1994 					conv,
1995 					NULL,
1996 					NULL,
1997 					smb_fname->flags);
1998 	if (conv_smb_fname == NULL) {
1999 		TALLOC_FREE(conv);
2000 		errno = ENOMEM;
2001 		return NULL;
2002 	}
2003 
2004 	ret = SMB_VFS_NEXT_OPENDIR(handle, conv_smb_fname, mask, attr);
2005 	saved_errno = errno;
2006 	TALLOC_FREE(conv);
2007 	TALLOC_FREE(conv_smb_fname);
2008 	errno = saved_errno;
2009 	return ret;
2010 }
2011 
snapper_gmt_renameat(vfs_handle_struct * handle,files_struct * srcfsp,const struct smb_filename * smb_fname_src,files_struct * dstfsp,const struct smb_filename * smb_fname_dst)2012 static int snapper_gmt_renameat(vfs_handle_struct *handle,
2013 			files_struct *srcfsp,
2014 			const struct smb_filename *smb_fname_src,
2015 			files_struct *dstfsp,
2016 			const struct smb_filename *smb_fname_dst)
2017 {
2018 	time_t timestamp_src, timestamp_dst;
2019 
2020 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2021 					smb_fname_src->base_name,
2022 					&timestamp_src, NULL)) {
2023 		return -1;
2024 	}
2025 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2026 					smb_fname_dst->base_name,
2027 					&timestamp_dst, NULL)) {
2028 		return -1;
2029 	}
2030 	if (timestamp_src != 0) {
2031 		errno = EXDEV;
2032 		return -1;
2033 	}
2034 	if (timestamp_dst != 0) {
2035 		errno = EROFS;
2036 		return -1;
2037 	}
2038 	return SMB_VFS_NEXT_RENAMEAT(handle,
2039 			srcfsp,
2040 			smb_fname_src,
2041 			dstfsp,
2042 			smb_fname_dst);
2043 }
2044 
snapper_gmt_symlinkat(vfs_handle_struct * handle,const char * link_contents,struct files_struct * dirfsp,const struct smb_filename * new_smb_fname)2045 static int snapper_gmt_symlinkat(vfs_handle_struct *handle,
2046 				const char *link_contents,
2047 				struct files_struct *dirfsp,
2048 				const struct smb_filename *new_smb_fname)
2049 {
2050 	time_t timestamp_old = 0;
2051 	time_t timestamp_new = 0;
2052 
2053 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2054 				handle,
2055 				link_contents,
2056 				&timestamp_old,
2057 				NULL)) {
2058 		return -1;
2059 	}
2060 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2061 				handle,
2062 				new_smb_fname->base_name,
2063 				&timestamp_new,
2064 				NULL)) {
2065 		return -1;
2066 	}
2067 	if ((timestamp_old != 0) || (timestamp_new != 0)) {
2068 		errno = EROFS;
2069 		return -1;
2070 	}
2071 	return SMB_VFS_NEXT_SYMLINKAT(handle,
2072 			link_contents,
2073 			dirfsp,
2074 			new_smb_fname);
2075 }
2076 
snapper_gmt_linkat(vfs_handle_struct * handle,files_struct * srcfsp,const struct smb_filename * old_smb_fname,files_struct * dstfsp,const struct smb_filename * new_smb_fname,int flags)2077 static int snapper_gmt_linkat(vfs_handle_struct *handle,
2078 				files_struct *srcfsp,
2079 				const struct smb_filename *old_smb_fname,
2080 				files_struct *dstfsp,
2081 				const struct smb_filename *new_smb_fname,
2082 				int flags)
2083 {
2084 	time_t timestamp_old = 0;
2085 	time_t timestamp_new = 0;
2086 
2087 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2088 				handle,
2089 				old_smb_fname->base_name,
2090 				&timestamp_old,
2091 				NULL)) {
2092 		return -1;
2093 	}
2094 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2095 				handle,
2096 				new_smb_fname->base_name,
2097 				&timestamp_new,
2098 				NULL)) {
2099 		return -1;
2100 	}
2101 	if ((timestamp_old != 0) || (timestamp_new != 0)) {
2102 		errno = EROFS;
2103 		return -1;
2104 	}
2105 	return SMB_VFS_NEXT_LINKAT(handle,
2106 				srcfsp,
2107 				old_smb_fname,
2108 				dstfsp,
2109 				new_smb_fname,
2110 				flags);
2111 }
2112 
snapper_gmt_stat(vfs_handle_struct * handle,struct smb_filename * smb_fname)2113 static int snapper_gmt_stat(vfs_handle_struct *handle,
2114 			    struct smb_filename *smb_fname)
2115 {
2116 	time_t timestamp;
2117 	char *stripped, *tmp;
2118 	int ret, saved_errno;
2119 
2120 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2121 					smb_fname->base_name,
2122 					&timestamp, &stripped)) {
2123 		return -1;
2124 	}
2125 	if (timestamp == 0) {
2126 		return SMB_VFS_NEXT_STAT(handle, smb_fname);
2127 	}
2128 
2129 	tmp = smb_fname->base_name;
2130 	smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
2131 						   stripped, timestamp);
2132 	TALLOC_FREE(stripped);
2133 
2134 	if (smb_fname->base_name == NULL) {
2135 		smb_fname->base_name = tmp;
2136 		return -1;
2137 	}
2138 
2139 	ret = SMB_VFS_NEXT_STAT(handle, smb_fname);
2140 	saved_errno = errno;
2141 
2142 	TALLOC_FREE(smb_fname->base_name);
2143 	smb_fname->base_name = tmp;
2144 
2145 	errno = saved_errno;
2146 	return ret;
2147 }
2148 
snapper_gmt_lstat(vfs_handle_struct * handle,struct smb_filename * smb_fname)2149 static int snapper_gmt_lstat(vfs_handle_struct *handle,
2150 			     struct smb_filename *smb_fname)
2151 {
2152 	time_t timestamp;
2153 	char *stripped, *tmp;
2154 	int ret, saved_errno;
2155 
2156 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2157 					smb_fname->base_name,
2158 					&timestamp, &stripped)) {
2159 		return -1;
2160 	}
2161 	if (timestamp == 0) {
2162 		return SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2163 	}
2164 
2165 	tmp = smb_fname->base_name;
2166 	smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
2167 						   stripped, timestamp);
2168 	TALLOC_FREE(stripped);
2169 
2170 	if (smb_fname->base_name == NULL) {
2171 		smb_fname->base_name = tmp;
2172 		return -1;
2173 	}
2174 
2175 	ret = SMB_VFS_NEXT_LSTAT(handle, smb_fname);
2176 	saved_errno = errno;
2177 
2178 	TALLOC_FREE(smb_fname->base_name);
2179 	smb_fname->base_name = tmp;
2180 
2181 	errno = saved_errno;
2182 	return ret;
2183 }
2184 
snapper_gmt_open(vfs_handle_struct * handle,struct smb_filename * smb_fname,files_struct * fsp,int flags,mode_t mode)2185 static int snapper_gmt_open(vfs_handle_struct *handle,
2186 			    struct smb_filename *smb_fname, files_struct *fsp,
2187 			    int flags, mode_t mode)
2188 {
2189 	time_t timestamp;
2190 	char *stripped, *tmp;
2191 	int ret, saved_errno;
2192 
2193 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2194 					smb_fname->base_name,
2195 					&timestamp, &stripped)) {
2196 		return -1;
2197 	}
2198 	if (timestamp == 0) {
2199 		return SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
2200 	}
2201 
2202 	tmp = smb_fname->base_name;
2203 	smb_fname->base_name = snapper_gmt_convert(talloc_tos(), handle,
2204 						   stripped, timestamp);
2205 	TALLOC_FREE(stripped);
2206 
2207 	if (smb_fname->base_name == NULL) {
2208 		smb_fname->base_name = tmp;
2209 		return -1;
2210 	}
2211 
2212 	ret = SMB_VFS_NEXT_OPEN(handle, smb_fname, fsp, flags, mode);
2213 	saved_errno = errno;
2214 
2215 	TALLOC_FREE(smb_fname->base_name);
2216 	smb_fname->base_name = tmp;
2217 
2218 	errno = saved_errno;
2219 	return ret;
2220 }
2221 
snapper_gmt_unlinkat(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,int flags)2222 static int snapper_gmt_unlinkat(vfs_handle_struct *handle,
2223 			struct files_struct *dirfsp,
2224 			const struct smb_filename *smb_fname,
2225 			int flags)
2226 {
2227 	time_t timestamp = 0;
2228 
2229 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2230 					smb_fname->base_name,
2231 					&timestamp, NULL)) {
2232 		return -1;
2233 	}
2234 	if (timestamp != 0) {
2235 		errno = EROFS;
2236 		return -1;
2237 	}
2238 	return SMB_VFS_NEXT_UNLINKAT(handle,
2239 			dirfsp,
2240 			smb_fname,
2241 			flags);
2242 }
2243 
snapper_gmt_chmod(vfs_handle_struct * handle,const struct smb_filename * smb_fname,mode_t mode)2244 static int snapper_gmt_chmod(vfs_handle_struct *handle,
2245 			const struct smb_filename *smb_fname,
2246 			mode_t mode)
2247 {
2248 	time_t timestamp = 0;
2249 
2250 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2251 				handle,
2252 				smb_fname->base_name,
2253 				&timestamp,
2254 				NULL)) {
2255 		return -1;
2256 	}
2257 	if (timestamp != 0) {
2258 		errno = EROFS;
2259 		return -1;
2260 	}
2261 	return SMB_VFS_NEXT_CHMOD(handle, smb_fname, mode);
2262 }
2263 
snapper_gmt_chdir(vfs_handle_struct * handle,const struct smb_filename * smb_fname)2264 static int snapper_gmt_chdir(vfs_handle_struct *handle,
2265 			const struct smb_filename *smb_fname)
2266 {
2267 	time_t timestamp = 0;
2268 	char *stripped = NULL;
2269 	int ret;
2270 	int saved_errno = 0;
2271 	char *conv = NULL;
2272 	struct smb_filename *conv_smb_fname = NULL;
2273 
2274 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2275 				handle,
2276 				smb_fname->base_name,
2277 				&timestamp,
2278 				&stripped)) {
2279 		return -1;
2280 	}
2281 	if (timestamp == 0) {
2282 		return SMB_VFS_NEXT_CHDIR(handle, smb_fname);
2283 	}
2284 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2285 	TALLOC_FREE(stripped);
2286 	if (conv == NULL) {
2287 		return -1;
2288 	}
2289 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
2290 					conv,
2291 					NULL,
2292 					NULL,
2293 					smb_fname->flags);
2294 	if (conv_smb_fname == NULL) {
2295 		TALLOC_FREE(conv);
2296 		errno = ENOMEM;
2297 		return -1;
2298 	}
2299 	ret = SMB_VFS_NEXT_CHDIR(handle, conv_smb_fname);
2300 	if (ret == -1) {
2301 		saved_errno = errno;
2302 	}
2303 	TALLOC_FREE(conv);
2304 	TALLOC_FREE(conv_smb_fname);
2305 	if (saved_errno != 0) {
2306 		errno = saved_errno;
2307 	}
2308 	return ret;
2309 }
2310 
snapper_gmt_ntimes(vfs_handle_struct * handle,const struct smb_filename * smb_fname,struct smb_file_time * ft)2311 static int snapper_gmt_ntimes(vfs_handle_struct *handle,
2312 			      const struct smb_filename *smb_fname,
2313 			      struct smb_file_time *ft)
2314 {
2315 	time_t timestamp = 0;
2316 
2317 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2318 					smb_fname->base_name,
2319 					&timestamp, NULL)) {
2320 		return -1;
2321 	}
2322 	if (timestamp != 0) {
2323 		errno = EROFS;
2324 		return -1;
2325 	}
2326 	return SMB_VFS_NEXT_NTIMES(handle, smb_fname, ft);
2327 }
2328 
snapper_gmt_readlinkat(vfs_handle_struct * handle,files_struct * dirfsp,const struct smb_filename * smb_fname,char * buf,size_t bufsiz)2329 static int snapper_gmt_readlinkat(vfs_handle_struct *handle,
2330 				files_struct *dirfsp,
2331 				const struct smb_filename *smb_fname,
2332 				char *buf,
2333 				size_t bufsiz)
2334 {
2335 	time_t timestamp = 0;
2336 	char *stripped = NULL;
2337 	int ret;
2338 	int saved_errno = 0;
2339 	struct smb_filename *conv = NULL;
2340 
2341 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2342 					smb_fname->base_name,
2343 					&timestamp, &stripped)) {
2344 		return -1;
2345 	}
2346 	if (timestamp == 0) {
2347 		return SMB_VFS_NEXT_READLINKAT(handle,
2348 				dirfsp,
2349 				smb_fname,
2350 				buf,
2351 				bufsiz);
2352 	}
2353 	conv = cp_smb_filename(talloc_tos(), smb_fname);
2354 	if (conv == NULL) {
2355 		TALLOC_FREE(stripped);
2356 		errno = ENOMEM;
2357 		return -1;
2358 	}
2359 	conv->base_name = snapper_gmt_convert(conv, handle,
2360 					      stripped, timestamp);
2361 	TALLOC_FREE(stripped);
2362 	if (conv->base_name == NULL) {
2363 		return -1;
2364 	}
2365 	ret = SMB_VFS_NEXT_READLINKAT(handle,
2366 				dirfsp,
2367 				conv,
2368 				buf,
2369 				bufsiz);
2370 	if (ret == -1) {
2371 		saved_errno = errno;
2372 	}
2373 	TALLOC_FREE(conv);
2374 	if (saved_errno != 0) {
2375 		errno = saved_errno;
2376 	}
2377 	return ret;
2378 }
2379 
snapper_gmt_mknodat(vfs_handle_struct * handle,files_struct * dirfsp,const struct smb_filename * smb_fname,mode_t mode,SMB_DEV_T dev)2380 static int snapper_gmt_mknodat(vfs_handle_struct *handle,
2381 			files_struct *dirfsp,
2382 			const struct smb_filename *smb_fname,
2383 			mode_t mode,
2384 			SMB_DEV_T dev)
2385 {
2386 	time_t timestamp = (time_t)0;
2387 
2388 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2389 					smb_fname->base_name,
2390 					&timestamp, NULL)) {
2391 		return -1;
2392 	}
2393 	if (timestamp != 0) {
2394 		errno = EROFS;
2395 		return -1;
2396 	}
2397 	return SMB_VFS_NEXT_MKNODAT(handle,
2398 			dirfsp,
2399 			smb_fname,
2400 			mode,
2401 			dev);
2402 }
2403 
snapper_gmt_realpath(vfs_handle_struct * handle,TALLOC_CTX * ctx,const struct smb_filename * smb_fname)2404 static struct smb_filename *snapper_gmt_realpath(vfs_handle_struct *handle,
2405 				TALLOC_CTX *ctx,
2406 				const struct smb_filename *smb_fname)
2407 {
2408 	time_t timestamp = 0;
2409 	char *stripped = NULL;
2410 	struct smb_filename *result_fname = NULL;
2411 	struct smb_filename *conv_smb_fname = NULL;
2412 	int saved_errno = 0;
2413 
2414 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2415 					smb_fname->base_name,
2416 					&timestamp, &stripped)) {
2417 		goto done;
2418 	}
2419 	if (timestamp == 0) {
2420 		return SMB_VFS_NEXT_REALPATH(handle, ctx, smb_fname);
2421 	}
2422 
2423 	conv_smb_fname = cp_smb_filename(talloc_tos(), smb_fname);
2424 	if (conv_smb_fname == NULL) {
2425 		goto done;
2426 	}
2427 	conv_smb_fname->base_name = snapper_gmt_convert(conv_smb_fname, handle,
2428 					      stripped, timestamp);
2429 	if (conv_smb_fname->base_name == NULL) {
2430 		goto done;
2431 	}
2432 
2433 	result_fname = SMB_VFS_NEXT_REALPATH(handle, ctx, conv_smb_fname);
2434 
2435 done:
2436 	if (result_fname == NULL) {
2437 		saved_errno = errno;
2438 	}
2439 	TALLOC_FREE(conv_smb_fname);
2440 	TALLOC_FREE(stripped);
2441 	if (saved_errno != 0) {
2442 		errno = saved_errno;
2443 	}
2444 	return result_fname;
2445 }
2446 
snapper_gmt_fget_nt_acl(vfs_handle_struct * handle,struct files_struct * fsp,uint32_t security_info,TALLOC_CTX * mem_ctx,struct security_descriptor ** ppdesc)2447 static NTSTATUS snapper_gmt_fget_nt_acl(vfs_handle_struct *handle,
2448 					struct files_struct *fsp,
2449 					uint32_t security_info,
2450 					TALLOC_CTX *mem_ctx,
2451 					struct security_descriptor **ppdesc)
2452 {
2453 	time_t timestamp;
2454 	char *stripped;
2455 	NTSTATUS status;
2456 	char *conv;
2457 	struct smb_filename *smb_fname = NULL;
2458 
2459 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2460 					fsp->fsp_name->base_name,
2461 					&timestamp, &stripped)) {
2462 		return map_nt_error_from_unix(errno);
2463 	}
2464 	if (timestamp == 0) {
2465 		return SMB_VFS_NEXT_FGET_NT_ACL(handle, fsp, security_info,
2466 						mem_ctx,
2467 						ppdesc);
2468 	}
2469 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2470 	TALLOC_FREE(stripped);
2471 	if (conv == NULL) {
2472 		return map_nt_error_from_unix(errno);
2473 	}
2474 
2475 	smb_fname = synthetic_smb_fname(talloc_tos(),
2476 					conv,
2477 					NULL,
2478 					NULL,
2479 					fsp->fsp_name->flags);
2480 	TALLOC_FREE(conv);
2481 	if (smb_fname == NULL) {
2482 		return NT_STATUS_NO_MEMORY;
2483 	}
2484 
2485 	status = SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname, security_info,
2486 					 mem_ctx, ppdesc);
2487 	TALLOC_FREE(smb_fname);
2488 	return status;
2489 }
2490 
snapper_gmt_get_nt_acl(vfs_handle_struct * handle,const struct smb_filename * fname,uint32_t security_info,TALLOC_CTX * mem_ctx,struct security_descriptor ** ppdesc)2491 static NTSTATUS snapper_gmt_get_nt_acl(vfs_handle_struct *handle,
2492 				       const struct smb_filename *fname,
2493 				       uint32_t security_info,
2494 				       TALLOC_CTX *mem_ctx,
2495 				       struct security_descriptor **ppdesc)
2496 {
2497 	time_t timestamp;
2498 	char *stripped;
2499 	NTSTATUS status;
2500 	char *conv;
2501 	struct smb_filename *smb_fname = NULL;
2502 
2503 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname->base_name,
2504 					&timestamp, &stripped)) {
2505 		return map_nt_error_from_unix(errno);
2506 	}
2507 	if (timestamp == 0) {
2508 		return SMB_VFS_NEXT_GET_NT_ACL(handle, fname, security_info,
2509 					       mem_ctx, ppdesc);
2510 	}
2511 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2512 	TALLOC_FREE(stripped);
2513 	if (conv == NULL) {
2514 		return map_nt_error_from_unix(errno);
2515 	}
2516 	smb_fname = synthetic_smb_fname(talloc_tos(),
2517 					conv,
2518 					NULL,
2519 					NULL,
2520 					fname->flags);
2521 	TALLOC_FREE(conv);
2522 	if (smb_fname == NULL) {
2523 		return NT_STATUS_NO_MEMORY;
2524 	}
2525 
2526 	status = SMB_VFS_NEXT_GET_NT_ACL(handle, smb_fname, security_info,
2527 					 mem_ctx, ppdesc);
2528 	TALLOC_FREE(smb_fname);
2529 	return status;
2530 }
2531 
snapper_gmt_mkdirat(vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * fname,mode_t mode)2532 static int snapper_gmt_mkdirat(vfs_handle_struct *handle,
2533 				struct files_struct *dirfsp,
2534 				const struct smb_filename *fname,
2535 				mode_t mode)
2536 {
2537 	time_t timestamp = 0;
2538 
2539 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, fname->base_name,
2540 					&timestamp, NULL)) {
2541 		return -1;
2542 	}
2543 	if (timestamp != 0) {
2544 		errno = EROFS;
2545 		return -1;
2546 	}
2547 	return SMB_VFS_NEXT_MKDIRAT(handle,
2548 			dirfsp,
2549 			fname,
2550 			mode);
2551 }
2552 
snapper_gmt_chflags(vfs_handle_struct * handle,const struct smb_filename * smb_fname,unsigned int flags)2553 static int snapper_gmt_chflags(vfs_handle_struct *handle,
2554 				const struct smb_filename *smb_fname,
2555 				unsigned int flags)
2556 {
2557 	time_t timestamp = 0;
2558 
2559 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2560 				smb_fname->base_name, &timestamp, NULL)) {
2561 		return -1;
2562 	}
2563 	if (timestamp != 0) {
2564 		errno = EROFS;
2565 		return -1;
2566 	}
2567 	return SMB_VFS_NEXT_CHFLAGS(handle, smb_fname, flags);
2568 }
2569 
snapper_gmt_getxattr(vfs_handle_struct * handle,const struct smb_filename * smb_fname,const char * aname,void * value,size_t size)2570 static ssize_t snapper_gmt_getxattr(vfs_handle_struct *handle,
2571 				const struct smb_filename *smb_fname,
2572 				const char *aname,
2573 				void *value,
2574 				size_t size)
2575 {
2576 	time_t timestamp = 0;
2577 	char *stripped = NULL;
2578 	ssize_t ret;
2579 	int saved_errno = 0;
2580 	char *conv = NULL;
2581 	struct smb_filename *conv_smb_fname = NULL;
2582 
2583 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2584 					handle,
2585 					smb_fname->base_name,
2586 					&timestamp,
2587 					&stripped)) {
2588 		return -1;
2589 	}
2590 	if (timestamp == 0) {
2591 		return SMB_VFS_NEXT_GETXATTR(handle, smb_fname, aname, value,
2592 					     size);
2593 	}
2594 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2595 	TALLOC_FREE(stripped);
2596 	if (conv == NULL) {
2597 		return -1;
2598 	}
2599 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
2600 					conv,
2601 					NULL,
2602 					NULL,
2603 					smb_fname->flags);
2604 	TALLOC_FREE(conv);
2605 	if (conv_smb_fname == NULL) {
2606 		errno = ENOMEM;
2607 		return -1;
2608 	}
2609 	ret = SMB_VFS_NEXT_GETXATTR(handle, conv_smb_fname, aname, value, size);
2610 	if (ret == -1) {
2611 		saved_errno = errno;
2612 	}
2613 	TALLOC_FREE(conv_smb_fname);
2614 	TALLOC_FREE(conv);
2615 	if (saved_errno != 0) {
2616 		errno = saved_errno;
2617 	}
2618 	return ret;
2619 }
2620 
snapper_gmt_listxattr(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,char * list,size_t size)2621 static ssize_t snapper_gmt_listxattr(struct vfs_handle_struct *handle,
2622 				     const struct smb_filename *smb_fname,
2623 				     char *list, size_t size)
2624 {
2625 	time_t timestamp = 0;
2626 	char *stripped = NULL;
2627 	ssize_t ret;
2628 	int saved_errno = 0;
2629 	char *conv = NULL;
2630 	struct smb_filename *conv_smb_fname = NULL;
2631 
2632 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2633 					handle,
2634 					smb_fname->base_name,
2635 					&timestamp,
2636 					&stripped)) {
2637 		return -1;
2638 	}
2639 	if (timestamp == 0) {
2640 		return SMB_VFS_NEXT_LISTXATTR(handle, smb_fname, list, size);
2641 	}
2642 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2643 	TALLOC_FREE(stripped);
2644 	if (conv == NULL) {
2645 		return -1;
2646 	}
2647 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
2648 					conv,
2649 					NULL,
2650 					NULL,
2651 					smb_fname->flags);
2652 	TALLOC_FREE(conv);
2653 	if (conv_smb_fname == NULL) {
2654 		errno = ENOMEM;
2655 		return -1;
2656 	}
2657 	ret = SMB_VFS_NEXT_LISTXATTR(handle, conv_smb_fname, list, size);
2658 	if (ret == -1) {
2659 		saved_errno = errno;
2660 	}
2661 	TALLOC_FREE(conv_smb_fname);
2662 	TALLOC_FREE(conv);
2663 	if (saved_errno != 0) {
2664 		errno = saved_errno;
2665 	}
2666 	return ret;
2667 }
2668 
snapper_gmt_removexattr(vfs_handle_struct * handle,const struct smb_filename * smb_fname,const char * aname)2669 static int snapper_gmt_removexattr(vfs_handle_struct *handle,
2670 				const struct smb_filename *smb_fname,
2671 				const char *aname)
2672 {
2673 	time_t timestamp = 0;
2674 
2675 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2676 					handle,
2677 					smb_fname->base_name,
2678 					&timestamp,
2679 					NULL)) {
2680 		return -1;
2681 	}
2682 	if (timestamp != 0) {
2683 		errno = EROFS;
2684 		return -1;
2685 	}
2686 	return SMB_VFS_NEXT_REMOVEXATTR(handle, smb_fname, aname);
2687 }
2688 
snapper_gmt_setxattr(struct vfs_handle_struct * handle,const struct smb_filename * smb_fname,const char * aname,const void * value,size_t size,int flags)2689 static int snapper_gmt_setxattr(struct vfs_handle_struct *handle,
2690 				const struct smb_filename *smb_fname,
2691 				const char *aname, const void *value,
2692 				size_t size, int flags)
2693 {
2694 	time_t timestamp = 0;
2695 
2696 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2697 					handle,
2698 					smb_fname->base_name,
2699 					&timestamp,
2700 					NULL)) {
2701 		return -1;
2702 	}
2703 	if (timestamp != 0) {
2704 		errno = EROFS;
2705 		return -1;
2706 	}
2707 	return SMB_VFS_NEXT_SETXATTR(handle, smb_fname,
2708 				aname, value, size, flags);
2709 }
2710 
snapper_gmt_get_real_filename(struct vfs_handle_struct * handle,const char * path,const char * name,TALLOC_CTX * mem_ctx,char ** found_name)2711 static int snapper_gmt_get_real_filename(struct vfs_handle_struct *handle,
2712 					 const char *path,
2713 					 const char *name,
2714 					 TALLOC_CTX *mem_ctx,
2715 					 char **found_name)
2716 {
2717 	time_t timestamp;
2718 	char *stripped;
2719 	ssize_t ret;
2720 	int saved_errno;
2721 	char *conv;
2722 
2723 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle, path,
2724 					&timestamp, &stripped)) {
2725 		return -1;
2726 	}
2727 	if (timestamp == 0) {
2728 		return SMB_VFS_NEXT_GET_REAL_FILENAME(handle, path, name,
2729 						      mem_ctx, found_name);
2730 	}
2731 	if (stripped[0] == '\0') {
2732 		*found_name = talloc_strdup(mem_ctx, name);
2733 		if (*found_name == NULL) {
2734 			errno = ENOMEM;
2735 			return -1;
2736 		}
2737 		return 0;
2738 	}
2739 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2740 	TALLOC_FREE(stripped);
2741 	if (conv == NULL) {
2742 		return -1;
2743 	}
2744 	ret = SMB_VFS_NEXT_GET_REAL_FILENAME(handle, conv, name,
2745 					     mem_ctx, found_name);
2746 	saved_errno = errno;
2747 	TALLOC_FREE(conv);
2748 	errno = saved_errno;
2749 	return ret;
2750 }
2751 
snapper_gmt_disk_free(vfs_handle_struct * handle,const struct smb_filename * smb_fname,uint64_t * bsize,uint64_t * dfree,uint64_t * dsize)2752 static uint64_t snapper_gmt_disk_free(vfs_handle_struct *handle,
2753 				const struct smb_filename *smb_fname,
2754 				uint64_t *bsize,
2755 				uint64_t *dfree,
2756 				uint64_t *dsize)
2757 {
2758 	time_t timestamp = 0;
2759 	char *stripped = NULL;
2760 	uint64_t ret;
2761 	int saved_errno = 0;
2762 	char *conv = NULL;
2763 	struct smb_filename *conv_smb_fname = NULL;
2764 
2765 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2766 			smb_fname->base_name, &timestamp, &stripped)) {
2767 		return (uint64_t)-1;
2768 	}
2769 	if (timestamp == 0) {
2770 		return SMB_VFS_NEXT_DISK_FREE(handle, smb_fname,
2771 					      bsize, dfree, dsize);
2772 	}
2773 
2774 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2775 	TALLOC_FREE(stripped);
2776 	if (conv == NULL) {
2777 		return (uint64_t)-1;
2778 	}
2779 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
2780 					conv,
2781 					NULL,
2782 					NULL,
2783 					smb_fname->flags);
2784 	if (conv_smb_fname == NULL) {
2785 		TALLOC_FREE(conv);
2786 		errno = ENOMEM;
2787 		return (uint64_t)-1;
2788 	}
2789 
2790 	ret = SMB_VFS_NEXT_DISK_FREE(handle, conv_smb_fname,
2791 				bsize, dfree, dsize);
2792 
2793 	if (ret == (uint64_t)-1) {
2794 		saved_errno = errno;
2795 	}
2796 	TALLOC_FREE(conv_smb_fname);
2797 	if (saved_errno != 0) {
2798 		errno = saved_errno;
2799 	}
2800 	return ret;
2801 }
2802 
snapper_gmt_get_quota(vfs_handle_struct * handle,const struct smb_filename * smb_fname,enum SMB_QUOTA_TYPE qtype,unid_t id,SMB_DISK_QUOTA * dq)2803 static int snapper_gmt_get_quota(vfs_handle_struct *handle,
2804 			const struct smb_filename *smb_fname,
2805 			enum SMB_QUOTA_TYPE qtype,
2806 			unid_t id,
2807 			SMB_DISK_QUOTA *dq)
2808 {
2809 	time_t timestamp = 0;
2810 	char *stripped = NULL;
2811 	int ret;
2812 	int saved_errno = 0;
2813 	char *conv = NULL;
2814 	struct smb_filename *conv_smb_fname = NULL;
2815 
2816 	if (!snapper_gmt_strip_snapshot(talloc_tos(), handle,
2817 				smb_fname->base_name, &timestamp, &stripped)) {
2818 		return -1;
2819 	}
2820 	if (timestamp == 0) {
2821 		return SMB_VFS_NEXT_GET_QUOTA(handle, smb_fname, qtype, id, dq);
2822 	}
2823 
2824 	conv = snapper_gmt_convert(talloc_tos(), handle, stripped, timestamp);
2825 	TALLOC_FREE(stripped);
2826 	if (conv == NULL) {
2827 		return -1;
2828 	}
2829 	conv_smb_fname = synthetic_smb_fname(talloc_tos(),
2830 					conv,
2831 					NULL,
2832 					NULL,
2833 					smb_fname->flags);
2834 	TALLOC_FREE(conv);
2835 	if (conv_smb_fname == NULL) {
2836 		errno = ENOMEM;
2837 		return -1;
2838 	}
2839 
2840 	ret = SMB_VFS_NEXT_GET_QUOTA(handle, conv_smb_fname, qtype, id, dq);
2841 
2842 	if (ret == -1) {
2843 		saved_errno = errno;
2844 	}
2845 	TALLOC_FREE(conv_smb_fname);
2846 	if (saved_errno != 0) {
2847 		errno = saved_errno;
2848 	}
2849 	return ret;
2850 }
2851 
snapper_create_dfs_pathat(struct vfs_handle_struct * handle,struct files_struct * dirfsp,const struct smb_filename * smb_fname,const struct referral * reflist,size_t referral_count)2852 static NTSTATUS snapper_create_dfs_pathat(struct vfs_handle_struct *handle,
2853 				struct files_struct *dirfsp,
2854 				const struct smb_filename *smb_fname,
2855 				const struct referral *reflist,
2856 				size_t referral_count)
2857 {
2858 	time_t timestamp = 0;
2859 
2860 	if (!snapper_gmt_strip_snapshot(talloc_tos(),
2861 					handle,
2862 					smb_fname->base_name,
2863 					&timestamp,
2864 					NULL)) {
2865 		return NT_STATUS_NO_MEMORY;
2866 	}
2867 	if (timestamp != 0) {
2868 		return NT_STATUS_MEDIA_WRITE_PROTECTED;
2869 	}
2870 	return SMB_VFS_NEXT_CREATE_DFS_PATHAT(handle,
2871 			dirfsp,
2872 			smb_fname,
2873 			reflist,
2874 			referral_count);
2875 }
2876 
2877 static struct vfs_fn_pointers snapper_fns = {
2878 	.snap_check_path_fn = snapper_snap_check_path,
2879 	.snap_create_fn = snapper_snap_create,
2880 	.snap_delete_fn = snapper_snap_delete,
2881 	.get_shadow_copy_data_fn = snapper_get_shadow_copy_data,
2882 	.create_dfs_pathat_fn = snapper_create_dfs_pathat,
2883 	.opendir_fn = snapper_gmt_opendir,
2884 	.disk_free_fn = snapper_gmt_disk_free,
2885 	.get_quota_fn = snapper_gmt_get_quota,
2886 	.renameat_fn = snapper_gmt_renameat,
2887 	.linkat_fn = snapper_gmt_linkat,
2888 	.symlinkat_fn = snapper_gmt_symlinkat,
2889 	.stat_fn = snapper_gmt_stat,
2890 	.lstat_fn = snapper_gmt_lstat,
2891 	.open_fn = snapper_gmt_open,
2892 	.unlinkat_fn = snapper_gmt_unlinkat,
2893 	.chmod_fn = snapper_gmt_chmod,
2894 	.chdir_fn = snapper_gmt_chdir,
2895 	.ntimes_fn = snapper_gmt_ntimes,
2896 	.readlinkat_fn = snapper_gmt_readlinkat,
2897 	.mknodat_fn = snapper_gmt_mknodat,
2898 	.realpath_fn = snapper_gmt_realpath,
2899 	.get_nt_acl_fn = snapper_gmt_get_nt_acl,
2900 	.fget_nt_acl_fn = snapper_gmt_fget_nt_acl,
2901 	.mkdirat_fn = snapper_gmt_mkdirat,
2902 	.getxattr_fn = snapper_gmt_getxattr,
2903 	.getxattrat_send_fn = vfs_not_implemented_getxattrat_send,
2904 	.getxattrat_recv_fn = vfs_not_implemented_getxattrat_recv,
2905 	.listxattr_fn = snapper_gmt_listxattr,
2906 	.removexattr_fn = snapper_gmt_removexattr,
2907 	.setxattr_fn = snapper_gmt_setxattr,
2908 	.chflags_fn = snapper_gmt_chflags,
2909 	.get_real_filename_fn = snapper_gmt_get_real_filename,
2910 };
2911 
2912 static_decl_vfs;
vfs_snapper_init(TALLOC_CTX * ctx)2913 NTSTATUS vfs_snapper_init(TALLOC_CTX *ctx)
2914 {
2915 	return smb_register_vfs(SMB_VFS_INTERFACE_VERSION,
2916 				"snapper", &snapper_fns);
2917 }
2918