1 /*
2 
3    Copyright (c) 2005-2018 Andre Landwehr <andrel@cybernoia.de>
4 
5    This program can be distributed under the terms of the GNU LGPL.
6    See the file COPYING.
7 
8    Based on: fusexmp.c and sshfs.c by Miklos Szeredi <miklos@szeredi.hu>
9 
10    Contributions by: Niels de Vos <niels@nixpanic.net>
11                      Thomas J. Duck
12                      Andrew Brampton <me at bramp dot net>
13                      Tomáš Čech <sleep_walker at suse dot cz>
14                      Timothy Hobbs <timothyhobbs at seznam dot cz>
15                      Lee Leahu
16                      Alain Parmentier <pa at infodata.lu>
17 */
18 
19 #ifdef linux
20 /* For pread()/pwrite() */
21 #define _XOPEN_SOURCE 500
22 #endif
23 
24 #define FUSE_USE_VERSION 26
25 #define MAXBUF 4096
26 
27 #include "config.h"
28 
29 #include <fuse.h>
30 #include <fuse/fuse_lowlevel.h>
31 #include <fuse_opt.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <stdarg.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <dirent.h>
39 #include <errno.h>
40 #include <sys/statvfs.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <time.h>
44 #include <grp.h>
45 #include <pwd.h>
46 #include <utime.h>
47 #include <string.h>
48 #include <wchar.h>
49 #include <archive.h>
50 #include <archive_entry.h>
51 #include <pthread.h>
52 #include <regex.h>
53 
54 #include "archivecomp.h"
55 
56 #include "uthash.h"
57 
58   /**********/
59  /* macros */
60 /**********/
61 #ifdef NDEBUG
62 #   define log(format, ...)
63 #else
64 #   define log(format, ...) \
65 { \
66 	FILE *FH = fopen("/tmp/archivemount.log", "a"); \
67 	if (FH) { \
68 		fprintf(FH, "l. %4d: " format "\n", __LINE__, ##__VA_ARGS__); \
69 		fclose(FH); \
70 	} \
71 }
72 #endif
73 
74 
75   /*******************/
76  /* data structures */
77 /*******************/
78 
79 typedef struct node {
80 	struct node *parent;
81 	struct node *child; /* first child for directories */
82 	char *name; /* fully qualified with prepended '/' */
83 	char *basename; /* every after the last '/' */
84 	char *location; /* location on disk for new/modified files, else NULL */
85 	int namechanged; /* true when file was renamed */
86 	struct archive_entry *entry; /* libarchive header data */
87 	int modified; /* true when node was modified */
88 	UT_hash_handle hh;
89 } NODE;
90 
91 struct options {
92 	int readonly;
93 	int nobackup;
94 	int nosave;
95 	char *subtree_filter;
96 	int formatraw;
97 };
98 
99 typedef struct formatraw_cache {
100 	struct stat st;
101 	struct archive *archive;
102 	int opened;
103 	off_t offset_uncompressed;
104 } FORMATRAW_CACHE;
105 
106 enum {
107 	KEY_VERSION,
108 	KEY_HELP,
109 };
110 
111 #define AR_OPT(t, p, v) { t, offsetof(struct options, p), v }
112 
113 static struct fuse_opt ar_opts[] =
114 {
115 	AR_OPT("readonly", readonly, 1),
116 	AR_OPT("nobackup", nobackup, 1),
117 	AR_OPT("nosave"  , nosave  , 1),
118 	AR_OPT("subtree=%s", subtree_filter, 1),
119 	AR_OPT("formatraw", formatraw, 1),
120 
121 	FUSE_OPT_KEY("-V",	       KEY_VERSION),
122 	FUSE_OPT_KEY("--version",      KEY_VERSION),
123 	FUSE_OPT_KEY("-h",	       KEY_HELP),
124 	FUSE_OPT_KEY("--help",	       KEY_HELP),
125 	FUSE_OPT_END
126 };
127 
128 
129   /***********/
130  /* globals */
131 /***********/
132 
133 static int archiveFd; /* file descriptor of archive file, just to keep the
134 			 beast alive in case somebody deletes the file while
135 			 it is mounted */
136 static int archiveModified = 0;
137 static int archiveWriteable = 0;
138 static NODE *root;
139 static FORMATRAW_CACHE *rawcache;
140 struct options options;
141 char *mtpt = NULL;
142 char *archiveFile = NULL;
143 pthread_mutex_t lock; /* global node tree lock */
144 
145 /* Taken from the GNU under the GPL */
146 char *
strchrnul(const char * s,int c_in)147 strchrnul (const char *s, int c_in)
148 {
149 	char c = c_in;
150 	while (*s && (*s != c))
151 		s++;
152 
153 	return (char *) s;
154 }
155 
156   /**********************/
157  /* internal functions */
158 /**********************/
159 
160 static void
usage(const char * progname)161 usage(const char *progname)
162 {
163 	fprintf(stderr,
164 		"usage: %s archivepath mountpoint [options]\n"
165 		"\n"
166 		"general options:\n"
167 		"    -o opt,[opt...]	    mount options\n"
168 		"    -h   --help	    print help\n"
169 		"    -V   --version	    print version\n"
170 		"\n"
171 		"archivemount options:\n"
172 		"    -o readonly	    disable write support\n"
173 		"    -o nobackup	    remove archive file backups\n"
174 		"    -o nosave		    do not save changes upon unmount.\n"
175 		"			    Good if you want to change something\n"
176 		"			    and save it as a diff,\n"
177 		"			    or use a format for saving which is\n"
178 		"			    not supported by archivemount.\n"
179 		"\n"
180 		"    -o subtree=<regexp>    use only subtree matching ^\\.\\?<regexp> from archive\n"
181 		"			    it implies readonly\n"
182 		"\n"
183 		"    -o formatraw	    treat input as a single element archive\n"
184 		"			    it implies readonly\n"
185 		"\n",progname);
186 }
187 
188 static struct fuse_operations ar_oper;
189 
190 static int
ar_opt_proc(void * data,const char * arg,int key,struct fuse_args * outargs)191 ar_opt_proc(void *data, const char *arg, int key, struct fuse_args *outargs)
192 {
193 	(void) data;
194 
195 	switch(key) {
196 		case FUSE_OPT_KEY_OPT:
197 			return 1;
198 
199 		case FUSE_OPT_KEY_NONOPT:
200 			if (!archiveFile) {
201 				archiveFile = strdup(arg);
202 				return 0;
203 			} else if (!mtpt) {
204 				mtpt = strdup(arg);
205 			}
206 			return 1;
207 
208 		case KEY_HELP:
209 			usage(outargs->argv[0]);
210 			fuse_opt_add_arg(outargs, "-ho");
211 			fuse_main(outargs->argc, outargs->argv, &ar_oper, NULL);
212 			exit(1);
213 
214 		case KEY_VERSION:
215 			fprintf(stderr, "archivemount version %s\n", VERSION);
216 			fuse_opt_add_arg(outargs, "--version");
217 			fuse_main(outargs->argc, outargs->argv, &ar_oper, NULL);
218 			exit(0);
219 
220 		default:
221 			fprintf(stderr, "internal error\n");
222 			abort();
223 	}
224 }
225 
226 static NODE *
init_node()227 init_node()
228 {
229 	NODE *node;
230 
231 	if ((node = malloc(sizeof(NODE))) == NULL) {
232 		log("Out of memory");
233 		return NULL;
234 	}
235 
236 	node->parent = NULL;
237 	node->child = NULL;
238 	node->name = NULL;
239 	node->basename = NULL;
240 	node->location = NULL;
241 	node->namechanged = 0;
242 	node->entry = archive_entry_new();
243 	node->modified = 0;
244 	memset(&node->hh, 0, sizeof(node->hh));
245 
246 	if (node->entry == NULL) {
247 		log("Out of memory");
248 		free(node);
249 		return NULL;
250 	}
251 
252 	return node;
253 }
254 
255 static FORMATRAW_CACHE *
init_rawcache()256 init_rawcache()
257 {
258 	FORMATRAW_CACHE *rawcache;
259 
260 	if ((rawcache = malloc(sizeof(FORMATRAW_CACHE))) == NULL) {
261 		log("Out of memory");
262 		return NULL;
263 	}
264 
265 	memset(&rawcache->st, 0, sizeof(rawcache->st));
266 	memset(&rawcache->archive, 0, sizeof(rawcache->archive));
267 	rawcache->opened=0;
268 	rawcache->offset_uncompressed=0;
269 	return rawcache;
270 }
271 
272 static void
free_node(NODE * node)273 free_node(NODE *node)
274 {
275 	NODE *child, *tmp;
276 
277 	free(node->name);
278 	archive_entry_free(node->entry);
279 
280 	// Clean up any children
281 	HASH_ITER(hh, node->child, child, tmp) {
282 		HASH_DEL(node->child, child);
283 		free_node(child);
284 	}
285 
286 	free(node);
287 }
288 
289 /* not used now
290 #static void
291 free_rawcache(FORMATRAW_CACHE *rawcache)
292 {
293 	free(rawcache);
294 }
295 */
296 
297 
298 static void
remove_child(NODE * node)299 remove_child(NODE *node)
300 {
301 	if (node->parent) {
302 		HASH_DEL(node->parent->child, node);
303 		log("removed '%s' from parent '%s' (was first child)", node->name, node->parent->name);
304 	} else {
305 		root = NULL;
306 	}
307 }
308 
309 static void
insert_as_child(NODE * node,NODE * parent)310 insert_as_child(NODE *node, NODE *parent)
311 {
312 	node->parent = parent;
313 	HASH_ADD_KEYPTR(hh, parent->child, node->basename, strlen(node->basename), node);
314 	log("inserted '%s' as child of '%s'", node->name, parent->name);
315 }
316 
317 /*
318  * inserts "node" into tree starting at "root" according to the path
319  * specified in node->name
320  * @return 0 on success, 0-errno else (ENOENT or ENOTDIR)
321  */
322 static int
insert_by_path(NODE * root,NODE * node)323 insert_by_path(NODE *root, NODE *node)
324 {
325 	char *temp;
326 	NODE *cur = root;
327 	char *key = node->name;
328 
329 	key++;
330 	while ((temp = strchr(key, '/'))) {
331 		size_t namlen = temp - key;
332 		char nam[namlen + 1];
333 		NODE *last = cur;
334 
335 		strncpy(nam, key, namlen);
336 		nam[namlen] = '\0';
337 		if (cur != root || strcmp(cur->basename, nam) != 0) {
338 			cur = cur->child;
339 			while (cur && strcmp(cur->basename, nam) != 0)
340 			{
341 				cur = cur->hh.next;
342 			}
343 		}
344 		if (! cur) {
345 			/* parent path not found, create a temporary one */
346 			NODE *tempnode;
347 			if ((tempnode = init_node()) == NULL)
348 				return -ENOMEM;
349 
350 			if ((tempnode->name = malloc(
351 						strlen(last->name) + namlen + 2)) == NULL) {
352 				log("Out of memory");
353 				return -ENOMEM;
354 			}
355 			if (last != root) {
356 				sprintf(tempnode->name, "%s/%s", last->name, nam);
357 			} else {
358 				sprintf(tempnode->name, "/%s", nam);
359 			}
360 			tempnode->basename = strrchr(tempnode->name, '/') + 1;
361 
362 			archive_entry_free(tempnode->entry);
363 
364 			if ((tempnode->entry = archive_entry_clone(root->entry)) == NULL) {
365 				log("Out of memory");
366 				return -ENOMEM;
367 			}
368 			/* insert it recursively */
369 			insert_by_path(root, tempnode);
370 			/* now inserting node should work, correct cur for it */
371 			cur = tempnode;
372 		}
373 		/* iterate */
374 		key = temp + 1;
375 	}
376 	if (S_ISDIR(archive_entry_mode(cur->entry))) {
377 		/* check if a child of this name already exists */
378 		NODE *tempnode = NULL;
379 
380 		HASH_FIND(hh, cur->child, node->basename, strlen(node->basename), tempnode);
381 
382 		if (tempnode) {
383 			/* this is a dupe due to a temporarily inserted
384 			   node, just update the entry */
385 			archive_entry_free(node->entry);
386 			if ((node->entry = archive_entry_clone(
387 						tempnode->entry)) == NULL) {
388 				log("Out of memory");
389 				return -ENOMEM;
390 			}
391 		} else {
392 			insert_as_child(node, cur);
393 		}
394 	} else {
395 		return -ENOTDIR;
396 	}
397 	return 0;
398 }
399 
400 static int
build_tree(const char * mtpt)401 build_tree(const char *mtpt)
402 {
403 	struct archive *archive;
404 	struct stat st;
405 	int format;
406 	int compression;
407 	NODE *cur;
408 	char *subtree_filter = NULL;
409 	regex_t subtree;
410 	int regex_error;
411 	regmatch_t regmatch;
412 	char error_buffer[256];
413 
414 #define PREFIX		"^\\.\\?"
415 
416 	if (options.subtree_filter) {
417 		subtree_filter = malloc(strlen(options.subtree_filter) + strlen(PREFIX) + 1);
418 		if (!subtree_filter) {
419 			log("Not enough memory");
420 			return -ENOMEM;
421 		}
422 		strcpy(subtree_filter, PREFIX);
423 		subtree_filter = strcat(subtree_filter, options.subtree_filter);
424 		regex_error = regcomp(&subtree, subtree_filter, 0);
425 		if (regex_error) {
426 			regerror(regex_error, &subtree, error_buffer, 256);
427 			log("Regex build error: %s\n", error_buffer);
428 			return -regex_error;
429 		}
430 		options.readonly = 1;
431 	}
432 	/* open archive */
433 	if ((archive = archive_read_new()) == NULL) {
434 		log("Out of memory");
435 		return -ENOMEM;
436 	}
437 	if (archive_read_support_filter_all(archive) != ARCHIVE_OK) {
438 		fprintf(stderr, "%s\n", archive_error_string(archive));
439 		return archive_errno(archive);
440 	}
441 	if (options.formatraw) {
442 		if (archive_read_support_format_raw(archive) != ARCHIVE_OK) {
443 			fprintf(stderr, "%s\n", archive_error_string(archive));
444 			return archive_errno(archive);
445 		}
446 		options.readonly = 1;
447 	} else {
448 		if (archive_read_support_format_all(archive) != ARCHIVE_OK) {
449 			fprintf(stderr, "%s\n", archive_error_string(archive));
450 			return archive_errno(archive);
451 		}
452 	}
453 	if (archive_read_open_fd(archive, archiveFd, 10240) != ARCHIVE_OK) {
454 		fprintf(stderr, "%s\n", archive_error_string(archive));
455 		return archive_errno(archive);
456 	}
457 	/* check if format or compression prohibits writability */
458 	format = archive_format(archive);
459 	//	log("FORMAT=%s",archive_format_name(archive));
460 	compression = archive_filter_code(archive, 0);
461 	//	log("COMPRESSION=%s",archive_compression_name(archive));
462 	if (format & ARCHIVE_FORMAT_ISO9660
463 		|| format & ARCHIVE_FORMAT_ISO9660_ROCKRIDGE
464 		|| format & ARCHIVE_FORMAT_ZIP
465 		|| compression == ARCHIVE_COMPRESSION_COMPRESS)
466 	{
467 		archiveWriteable = 0;
468 	}
469 	/* create root node */
470 	if ((root = init_node()) == NULL)
471 		return -ENOMEM;
472 
473 	root->name = strdup("/");
474 	root->basename = &root->name[1];
475 
476 	/* fill root->entry */
477 	if (fstat(archiveFd, &st) != 0) {
478 		perror("Error stat'ing archiveFile");
479 		return errno;
480 	}
481 	archive_entry_set_gid(root->entry, getgid());
482 	archive_entry_set_uid(root->entry, getuid());
483 	archive_entry_set_mode(root->entry, st.st_mtime);
484 	archive_entry_set_pathname(root->entry, "/");
485 	archive_entry_set_size(root->entry, st.st_size);
486 	stat(mtpt, &st);
487 	archive_entry_set_mode(root->entry, st.st_mode);
488 
489 	if ((cur = init_node()) == NULL) {
490 		return -ENOMEM;
491 	}
492 
493 	/* read all entries in archive, create node for each */
494 	while (archive_read_next_header2(archive, cur->entry) == ARCHIVE_OK) {
495 		const char *name;
496 		/* find name of node */
497 		name = archive_entry_pathname(cur->entry);
498 		if (memcmp(name, "./\0", 3) == 0) {
499 			/* special case: the directory "./" must be skipped! */
500 			continue;
501 		}
502 		if (options.subtree_filter) {
503 			regex_error = regexec(&subtree, name, 1, &regmatch, REG_NOTEOL);
504 			if (regex_error) {
505 				if (regex_error == REG_NOMATCH)
506 					continue;
507 				regerror(regex_error, &subtree, error_buffer, 256);
508 				log("Regex match error: %s\n", error_buffer);
509 				return -regex_error;
510 			}
511 			/* strip subtree from name */
512 			name += regmatch.rm_eo;
513 		}
514 		/* create node and clone the entry */
515 		/* normalize the name to start with "/" */
516 		if (strncmp(name, "./", 2) == 0) {
517 			/* remove the "." of "./" */
518 			cur->name = strdup(name + 1);
519 		} else if (name[0] != '/') {
520 			/* prepend a '/' to name */
521 			if ((cur->name = malloc(strlen(name) + 2)) == NULL) {
522 				log("Out of memory");
523 				return -ENOMEM;
524 			}
525 			sprintf(cur->name, "/%s", name);
526 		} else {
527 			/* just set the name */
528 			cur->name = strdup(name);
529 		}
530 		int len = strlen(cur->name) - 1;
531 		if (0 < len) {
532 			/* remove trailing '/' for directories */
533 			if (cur->name[len] == '/') {
534 				cur->name[len] = '\0';
535 			}
536 			cur->basename = strrchr(cur->name, '/') + 1;
537 
538 			/* references */
539 			if (insert_by_path(root, cur) != 0) {
540 				log("ERROR: could not insert %s into tree",
541 					cur->name);
542 				return -ENOENT;
543 			}
544 		} else {
545 			/* this is the directory the subtree filter matches,
546 			   do not respect it */
547 		}
548 
549 		if ((cur = init_node()) == NULL) {
550 			return -ENOMEM;
551 		}
552 
553 		archive_read_data_skip(archive);
554 	}
555 	/* free the last unused NODE */
556 	free_node(cur);
557 
558 	/* close archive */
559 	archive_read_free(archive);
560 	lseek(archiveFd, 0, SEEK_SET);
561 	if (options.subtree_filter) {
562 		regfree(&subtree);
563 		free(subtree_filter);
564 	}
565 	return 0;
566 }
567 
568 static NODE *
find_modified_node(NODE * start)569 find_modified_node(NODE *start)
570 {
571 	NODE *ret = NULL;
572 	NODE *run = start;
573 
574 	while (run) {
575 		if (run->modified) {
576 			ret = run;
577 			break;
578 		}
579 		if (run->child) {
580 			if ((ret = find_modified_node(run->child))) {
581 				break;
582 			}
583 		}
584 		run = run->hh.next;
585 	}
586 	return ret;
587 }
588 
589 static void
correct_hardlinks_to_node(const NODE * start,const char * old_name,const char * new_name)590 correct_hardlinks_to_node(const NODE *start, const char *old_name,
591 	const char *new_name)
592 {
593 	const NODE *run = start;
594 
595 	while (run) {
596 		const char *tmp;
597 		if ((tmp = archive_entry_hardlink(run->entry))) {
598 			if (strcmp(tmp, old_name) == 0) {
599 				/* the node in "run" is a hardlink to "node",
600 				 * correct the path */
601 				//log("correcting hardlink '%s' from '%s' to '%s'", run->name, old_name, new_name);
602 				archive_entry_set_hardlink(
603 					run->entry, new_name);
604 			}
605 		}
606 		if (run->child) {
607 			correct_hardlinks_to_node(run->child, old_name, new_name);
608 		}
609 		run = run->hh.next;
610 	}
611 }
612 
613 void
correct_name_in_entry(NODE * node)614 correct_name_in_entry (NODE *node)
615 {
616 	if (root->child &&
617 		node->name[0] == '/' &&
618 		archive_entry_pathname(root->child->entry)[0] != '/')
619 	{
620 		log ("correcting name in entry to '%s'", node->name+1);
621 		archive_entry_set_pathname(node->entry, node->name + 1);
622 	} else {
623 		log ("correcting name in entry to '%s'", node->name);
624 		archive_entry_set_pathname(node->entry, node->name);
625 	}
626 }
627 
628 static NODE *
get_node_for_path(NODE * start,const char * path)629 get_node_for_path(NODE *start, const char *path)
630 {
631 	NODE *ret = NULL;
632 
633 	//log("get_node_for_path path: '%s' start: '%s'", path, start->name);
634 
635 	/* Check if start is a perfect match */
636 	if (strcmp(path, start->name + (*path=='/'?0:1)) == 0) {
637 		//log("  get_node_for_path path: '%s' start: '%s' return: '%s'", path, start->name, start->name);
638 		return start;
639 	}
640 
641 	/* Check if one of the children match */
642 	if (start->child) {
643 		const char * basename;
644 		const char * baseend;
645 
646 		/* Find the part of the path we are now looking for */
647 		basename = path + strlen(start->name) - (*path=='/'?0:1);
648 		if (*basename == '/')
649 			basename++;
650 
651 		baseend = strchrnul(basename, '/');
652 
653 		//log("get_node_for_path path: '%s' start: '%s' basename: '%s' len: %ld", path, start->name, basename, baseend - basename);
654 
655 		HASH_FIND(hh, start->child, basename, baseend - basename, ret);
656 
657 		if (ret) {
658 			ret = get_node_for_path(ret, path);
659 		}
660 	}
661 
662 	//log("  get_node_for_path path: '%s' start: '%s' return: '%s'", path, start->name, ret == NULL ? "(null)" : ret->name);
663 	return ret;
664 }
665 
666 
667 static NODE *
get_node_for_entry(NODE * start,struct archive_entry * entry)668 get_node_for_entry(NODE *start, struct archive_entry *entry)
669 {
670 	NODE *ret = NULL;
671 	NODE *run = start;
672 	const char *path = archive_entry_pathname(entry);
673 
674 	if (*path == '/') {
675 		path++;
676 	}
677 	while (run) {
678 		const char *name = archive_entry_pathname(run->entry);
679 		if (*name == '/') {
680 			name++;
681 		}
682 		if (strcmp(path, name) == 0) {
683 			ret = run;
684 			break;
685 		}
686 		if (run->child) {
687 			if ((ret = get_node_for_entry(run->child, entry))) {
688 				break;
689 			}
690 		}
691 		run = run->hh.next;
692 	}
693 	return ret;
694 }
695 
696 static int
rename_recursively(NODE * start,const char * from,const char * to)697 rename_recursively(NODE *start, const char *from, const char *to)
698 {
699 	char *individualName;
700 	char *newName;
701 	int ret = 0;
702 	NODE *node = start;
703 	/* removing and re-inserting nodes while iterating through
704 	   the hashtable is a bad idea, so we copy all node ptrs
705 	   into an array first and iterate over that instead */
706 	size_t count = HASH_COUNT(start);
707 	NODE *nodes[count];
708 	log ("%s has %u items", start->parent->name, count);
709 	NODE **dst = &nodes[0];
710 	while (node) {
711 		*dst = node;
712 		++dst;
713 		node = node->hh.next;
714 	}
715 
716 	size_t i;
717 	for (i=0; i<count; ++i) {
718 		node = nodes[i];
719 		if (node->child) {
720 			/* recurse */
721 			ret = rename_recursively(node->child, from, to);
722 		}
723 		remove_child(node);
724 		/* change node name */
725 		individualName = node->name + strlen(from);
726 		if (*to != '/') {
727 			if ((newName = (char *)malloc(strlen(to) +
728 						strlen(individualName) + 2)) == NULL) {
729 				log("Out of memory");
730 				return -ENOMEM;
731 			}
732 			sprintf(newName, "/%s%s", to, individualName);
733 		} else {
734 			if ((newName = (char *)malloc(strlen(to) +
735 						strlen(individualName) + 1)) == NULL) {
736 				log("Out of memory");
737 				return -ENOMEM;
738 			}
739 			sprintf(newName, "%s%s", to, individualName);
740 		}
741 		log ("new name: '%s'", newName);
742 		correct_hardlinks_to_node(root, node->name, newName);
743 		free(node->name);
744 		node->name = newName;
745 		node->basename = strrchr(node->name, '/') + 1;
746 		node->namechanged = 1;
747 		insert_by_path(root, node);
748 	}
749 	return ret;
750 }
751 
752 static int
get_temp_file_name(const char * path,char ** location)753 get_temp_file_name(const char *path, char **location)
754 {
755 	int fh;
756 
757 	/* create name for temp file */
758 	if ((*location = (char *)malloc(
759 				strlen(P_tmpdir) +
760 				strlen("/archivemount_XXXXXX") +
761 				1)) == NULL) {
762 		log("Out of memory");
763 		return -ENOMEM;
764 	}
765 	sprintf(*location, "%s/archivemount_XXXXXX", P_tmpdir);
766 	if ((fh = mkstemp(*location))  == -1) {
767 		log("Could not create temp file name %s: %s",
768 			*location, strerror(errno));
769 		free(*location);
770 		return 0 - errno;
771 	}
772 	close(fh);
773 	unlink(*location);
774 	return 0;
775 }
776 
777 /**
778  * Updates given nodes node->entry by stat'ing node->location. Does not update
779  * the name!
780  */
781 static int
update_entry_stat(NODE * node)782 update_entry_stat(NODE *node)
783 {
784 	struct stat st;
785 	struct passwd *pwd;
786 	struct group *grp;
787 
788 	if (lstat(node->location, &st) != 0) {
789 		return 0 - errno;
790 	}
791 	archive_entry_set_gid(node->entry, st.st_gid);
792 	archive_entry_set_uid(node->entry, st.st_uid);
793 	archive_entry_set_mtime(node->entry, st.st_mtime, 0);
794 	archive_entry_set_size(node->entry, st.st_size);
795 	archive_entry_set_mode(node->entry, st.st_mode);
796 	archive_entry_set_rdevmajor(node->entry, st.st_dev);
797 	archive_entry_set_rdevminor(node->entry, st.st_dev);
798 	pwd = getpwuid(st.st_uid);
799 	if (pwd) {
800 		archive_entry_set_uname(node->entry, strdup(pwd->pw_name));
801 	}
802 	grp = getgrgid(st.st_gid);
803 	if (grp) {
804 		archive_entry_set_gname(node->entry, strdup(grp->gr_name));
805 	}
806 	return 0;
807 }
808 
809 /*
810  * write a new or modified file to the new archive; used from save()
811  */
812 static void
write_new_modded_file(NODE * node,struct archive_entry * wentry,struct archive * newarc)813 write_new_modded_file(NODE *node, struct archive_entry *wentry,
814 	struct archive *newarc)
815 {
816 	if (node->location) {
817 		struct stat st;
818 		int fh = 0;
819 		off_t offset = 0;
820 		void *buf;
821 		ssize_t len = 0;
822 		/* copy stat info */
823 		if (lstat(node->location, &st) != 0) {
824 			log("Could not lstat temporary file %s: %s",
825 				node->location,
826 				strerror(errno));
827 			return;
828 		}
829 		archive_entry_copy_stat(wentry, &st);
830 		/* open temporary file */
831 		fh = open(node->location, O_RDONLY);
832 		if (fh == -1) {
833 			log("Fatal error opening modified file %s at "
834 				"location %s, giving up",
835 				node->name, node->location);
836 			return;
837 		}
838 		/* write header */
839 		archive_write_header(newarc, wentry);
840 		if (S_ISREG(st.st_mode)) {
841 			/* regular file, copy data */
842 			if ((buf = malloc(MAXBUF)) == NULL) {
843 				log("Out of memory");
844 				return;
845 			}
846 			while ((len = pread(fh, buf, (size_t)MAXBUF,
847 						offset)) > 0)
848 			{
849 				archive_write_data(newarc, buf, len);
850 				offset += len;
851 			}
852 			free(buf);
853 		}
854 		if (len == -1) {
855 			log("Error reading temporary file %s for file %s: %s",
856 				node->location,
857 				node->name,
858 				strerror(errno));
859 			close(fh);
860 			return;
861 		}
862 		/* clean up */
863 		close(fh);
864 		if (S_ISDIR(st.st_mode)) {
865 			if (rmdir(node->location) == -1) {
866 				log("WARNING: rmdir '%s' failed: %s",
867 					node->location, strerror(errno));
868 			}
869 		} else {
870 			if (unlink(node->location) == -1) {
871 				log("WARNING: unlinking '%s' failed: %s",
872 					node->location, strerror(errno));
873 			}
874 		}
875 	} else {
876 		/* no data, only write header (e.g. when node is a link!) */
877 		//log("writing header for file %s", archive_entry_pathname(wentry));
878 		archive_write_header(newarc, wentry);
879 	}
880 	/* mark file as written */
881 	node->modified = 0;
882 }
883 
884 static int
save(const char * archiveFile)885 save(const char *archiveFile)
886 {
887 	struct archive *oldarc;
888 	struct archive *newarc;
889 	struct archive_entry *entry;
890 	int tempfile;
891 	int format;
892 	int compression;
893 	char *oldfilename;
894 	NODE *node;
895 
896 	oldfilename = malloc(strlen(archiveFile) + 5 + 1);
897 	if (!oldfilename) {
898 		log("Could not allocate memory for oldfilename");
899 		return 0 - ENOMEM;
900 	}
901 	/* unfortunately libarchive does not support modification of
902 	 * compressed archives, so a new archive has to be written */
903 	/* rename old archive */
904 	sprintf(oldfilename, "%s.orig", archiveFile);
905 	close(archiveFd);
906 	if (rename(archiveFile, oldfilename) < 0) {
907 		int err = errno;
908 		char *buf = NULL;
909 		char *unknown = "<unknown>";
910 		if (getcwd(buf, 0) == NULL) {
911 			/* getcwd failed, set buf to sth. reasonable */
912 			buf = unknown;
913 		}
914 		log("Could not rename old archive file (%s/%s): %s",
915 			buf, archiveFile, strerror(err));
916 		free(buf);
917 		archiveFd = open(archiveFile, O_RDONLY);
918 		return 0 - err;
919 	}
920 	archiveFd = open(oldfilename, O_RDONLY);
921 	free(oldfilename);
922 	/* open old archive */
923 	if ((oldarc = archive_read_new()) == NULL) {
924 		log("Out of memory");
925 		return -ENOMEM;
926 	}
927 	if (archive_read_support_filter_all(oldarc) != ARCHIVE_OK) {
928 		log("%s", archive_error_string(oldarc));
929 		return archive_errno(oldarc);
930 	}
931 	if (archive_read_support_format_all(oldarc) != ARCHIVE_OK) {
932 		log("%s", archive_error_string(oldarc));
933 		return archive_errno(oldarc);
934 	}
935 	if (archive_read_open_fd(oldarc, archiveFd, 10240) != ARCHIVE_OK) {
936 		log("%s", archive_error_string(oldarc));
937 		return archive_errno(oldarc);
938 	}
939 	format = archive_format(oldarc);
940 	compression = archive_filter_code(oldarc, 0);
941 	/*
942 	   log("format of old archive is %s (%d)",
943 	   archive_format_name(oldarc),
944 	   format);
945 	   log("compression of old archive is %s (%d)",
946 	   archive_compression_name(oldarc),
947 	   compression);
948 	   */
949 	/* open new archive */
950 	if ((newarc = archive_write_new()) == NULL) {
951 		log("Out of memory");
952 		return -ENOMEM;
953 	}
954 	switch(compression) {
955 		case ARCHIVE_COMPRESSION_GZIP:
956 			archive_write_add_filter_gzip(newarc);
957 			break;
958 		case ARCHIVE_COMPRESSION_BZIP2:
959 			archive_write_add_filter_bzip2(newarc);
960 			break;
961 		case ARCHIVE_COMPRESSION_COMPRESS:
962 		case ARCHIVE_COMPRESSION_NONE:
963 		default:
964 			archive_write_add_filter_none(newarc);
965 			break;
966 	}
967 #if 0
968 	if (archive_write_set_format(newarc, format) != ARCHIVE_OK) {
969 		return -ENOTSUP;
970 	}
971 #endif
972 	if (format & ARCHIVE_FORMAT_CPIO
973 		|| format & ARCHIVE_FORMAT_CPIO_POSIX)
974 	{
975 		archive_write_set_format_cpio(newarc);
976 		//log("set write format to posix-cpio");
977 	} else if (format & ARCHIVE_FORMAT_SHAR
978 		|| format & ARCHIVE_FORMAT_SHAR_BASE
979 		|| format & ARCHIVE_FORMAT_SHAR_DUMP)
980 	{
981 		archive_write_set_format_shar(newarc);
982 		//log("set write format to binary shar");
983 	} else if (format & ARCHIVE_FORMAT_TAR_PAX_RESTRICTED)
984 	{
985 		archive_write_set_format_pax_restricted(newarc);
986 		//log("set write format to binary pax restricted");
987 	} else if (format & ARCHIVE_FORMAT_TAR_PAX_INTERCHANGE)
988 	{
989 		archive_write_set_format_pax(newarc);
990 		//log("set write format to binary pax interchange");
991 	} else if (format & ARCHIVE_FORMAT_TAR_USTAR
992 		|| format & ARCHIVE_FORMAT_TAR
993 		|| format & ARCHIVE_FORMAT_TAR_GNUTAR
994 		|| format == 0)
995 	{
996 		archive_write_set_format_ustar(newarc);
997 		//log("set write format to ustar");
998 	} else {
999 		log("writing archives of format %d (%s) is not "
1000 			"supported", format,
1001 			archive_format_name(oldarc));
1002 		return -ENOTSUP;
1003 	}
1004 	tempfile = open(archiveFile,
1005 		O_WRONLY | O_CREAT | O_EXCL,
1006 		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
1007 	if (tempfile == -1) {
1008 		log("could not open new archive file for writing");
1009 		return 0 - errno;
1010 	}
1011 	if (archive_write_open_fd(newarc, tempfile) != ARCHIVE_OK) {
1012 		log("%s", archive_error_string(newarc));
1013 		return archive_errno(newarc);
1014 	}
1015 	while (archive_read_next_header(oldarc, &entry) == ARCHIVE_OK) {
1016 		off_t offset;
1017 		const void *buf;
1018 		struct archive_entry *wentry;
1019 		size_t len;
1020 		const char *name;
1021 		/* find corresponding node */
1022 		name = archive_entry_pathname(entry);
1023 		node = get_node_for_entry(root, entry);
1024 		if (! node) {
1025 			log("WARNING: no such node for '%s'", name);
1026 			archive_read_data_skip(oldarc);
1027 			continue;
1028 		}
1029 		/* create new entry, copy metadata */
1030 		if ((wentry = archive_entry_new()) == NULL) {
1031 			log("Out of memory");
1032 			return -ENOMEM;
1033 		}
1034 		if (archive_entry_gname_w(node->entry)) {
1035 			archive_entry_copy_gname_w(wentry,
1036 				archive_entry_gname_w(node->entry));
1037 		}
1038 		if (archive_entry_hardlink(node->entry)) {
1039 			archive_entry_copy_hardlink(wentry,
1040 				archive_entry_hardlink(node->entry));
1041 		}
1042 		if (archive_entry_hardlink_w(node->entry)) {
1043 			archive_entry_copy_hardlink_w(wentry,
1044 				archive_entry_hardlink_w(
1045 					node->entry));
1046 		}
1047 		archive_entry_copy_stat(wentry,
1048 			archive_entry_stat(node->entry));
1049 		if (archive_entry_symlink_w(node->entry)) {
1050 			archive_entry_copy_symlink_w(wentry,
1051 				archive_entry_symlink_w(
1052 					node->entry));
1053 		}
1054 		if (archive_entry_uname_w(node->entry)) {
1055 			archive_entry_copy_uname_w(wentry,
1056 				archive_entry_uname_w(node->entry));
1057 		}
1058 		/* set correct name */
1059 		if (node->namechanged) {
1060 			if (*name == '/') {
1061 				archive_entry_set_pathname(
1062 						wentry, node->name);
1063 			} else {
1064 				archive_entry_set_pathname(
1065 						wentry, node->name + 1);
1066 			}
1067 		} else {
1068 			archive_entry_set_pathname(wentry, name);
1069 		}
1070 		/* write header and copy data */
1071 		if (node->modified) {
1072 			/* file was modified */
1073 			write_new_modded_file(node, wentry, newarc);
1074 		} else {
1075 			/* file was not modified */
1076 			archive_entry_copy_stat(wentry,
1077 				archive_entry_stat(node->entry));
1078 			archive_write_header(newarc, wentry);
1079 			while (archive_read_data_block(oldarc, &buf,
1080 					&len, &offset) == ARCHIVE_OK)
1081 			{
1082 				archive_write_data(newarc, buf, len);
1083 			}
1084 		}
1085 		/* clean up */
1086 		archive_entry_free(wentry);
1087 	} /* end: while read next header */
1088 	/* find new files to add (those do still have modified flag set */
1089 	while ((node = find_modified_node(root))) {
1090 		if (node->namechanged) {
1091 			correct_name_in_entry (node);
1092 		}
1093 		write_new_modded_file(node, node->entry, newarc);
1094 	}
1095 	/* clean up, re-open the new archive for reading */
1096 	archive_read_free(oldarc);
1097 	archive_write_free(newarc);
1098 	close(tempfile);
1099 	close(archiveFd);
1100 	archiveFd = open(archiveFile, O_RDONLY);
1101 	if (options.nobackup) {
1102 		if (remove(oldfilename) < 0) {
1103 			log("Could not remove .orig archive file (%s): %s",
1104 				oldfilename, strerror(errno));
1105 			return 0 - errno;
1106 		}
1107 	}
1108 	return 0;
1109 }
1110 
1111 
1112   /*****************/
1113  /* API functions */
1114 /*****************/
1115 
1116 static int
_ar_open_raw(void)1117 _ar_open_raw(void)
1118 	//_ar_open_raw(const char *path, struct fuse_file_info *fi)
1119 {
1120 	// open archive and search first entry
1121 
1122 	const char path[] = "/data";
1123 
1124 	int ret = -1;
1125 	const char *realpath;
1126 	NODE *node;
1127 	log("_ar_open_raw called, path: '%s'", path);
1128 
1129 
1130 	if (rawcache->opened!=0) {
1131 		log("already opened");
1132 		return 0;
1133 	}
1134 
1135 	options.readonly = 1;
1136 
1137 	/* find node */
1138 
1139 	node = get_node_for_path(root, path);
1140 	if (! node) {
1141 		log("get_node_for_path error");
1142 		return -ENOENT;
1143 	}
1144 
1145 	//	struct archive *archive;
1146 	struct archive_entry *entry;
1147 	int archive_ret;
1148 	/* search file in archive */
1149 	realpath = archive_entry_pathname(node->entry);
1150 	if ((rawcache->archive = archive_read_new()) == NULL) {
1151 		log("Out of memory");
1152 		return -ENOMEM;
1153 	}
1154 	archive_ret = archive_read_support_filter_all(rawcache->archive);
1155 	if (archive_ret != ARCHIVE_OK) {
1156 		log("archive_read_support_filter_all(): %s (%d)\n",
1157 			archive_error_string(rawcache->archive), archive_ret);
1158 		return -EIO;
1159 	}
1160 
1161 	archive_ret = archive_read_support_format_raw(rawcache->archive);
1162 	if (archive_ret != ARCHIVE_OK) {
1163 		log("archive_read_support_format_raw(): %s (%d)\n",
1164 			archive_error_string(rawcache->archive), archive_ret);
1165 		return -EIO;
1166 	}
1167 
1168 	archive_ret = archive_read_open_fd(rawcache->archive, archiveFd, 10240);
1169 	if (archive_ret != ARCHIVE_OK) {
1170 		log("archive_read_open_fd(): %s (%d)\n",
1171 			archive_error_string(rawcache->archive), archive_ret);
1172 		return -EIO;
1173 	}
1174 	/* search for file to read - "/data" must be the first entry */
1175 	while ((archive_ret = archive_read_next_header(
1176 				rawcache->archive, &entry)) == ARCHIVE_OK) {
1177 		const char *name;
1178 		name = archive_entry_pathname(entry);
1179 		if (strcmp(realpath, name) == 0) {
1180 			break;
1181 		}
1182 
1183 	}
1184 	rawcache->opened=1;
1185 	rawcache->offset_uncompressed=0;
1186 
1187 	return ret;
1188 }
1189 
1190 static int
_ar_read_raw(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)1191 _ar_read_raw(const char *path, char *buf, size_t size, off_t offset,
1192 	struct fuse_file_info *fi)
1193 {
1194 	int ret = -1;
1195 	NODE *node;
1196 	(void)fi;
1197 
1198 	log("_ar_read_raw called, path: '%s'", path);
1199 	/* find node */
1200 	node = get_node_for_path(root, path);
1201 	if (! node) {
1202 		return -ENOENT;
1203 	}
1204 
1205 	if (offset < rawcache->offset_uncompressed) {
1206 		//rewind archive
1207 
1208 		/* close archive */
1209 		archive_read_free(rawcache->archive);
1210 		lseek(archiveFd, 0, SEEK_SET);
1211 
1212 		rawcache->opened=0;
1213 
1214 		/* reopen */
1215 		_ar_open_raw();
1216 	}
1217 
1218 	void *trash;
1219 	if ((trash = malloc(MAXBUF)) == NULL) {
1220 		log("Out of memory");
1221 		return -ENOMEM;
1222 	}
1223 	/* skip offset */
1224 	offset-=rawcache->offset_uncompressed;
1225 
1226 	while (offset > 0) {
1227 		int skip = offset > MAXBUF ? MAXBUF : offset;
1228 		ret = archive_read_data(
1229 			rawcache->archive, trash, skip);
1230 		if (ret == ARCHIVE_FATAL
1231 			|| ret == ARCHIVE_WARN
1232 			|| ret == ARCHIVE_RETRY)
1233 		{
1234 			log("ar_read_raw (skipping offset): %s",
1235 				archive_error_string(rawcache->archive));
1236 			errno = archive_errno(rawcache->archive);
1237 			ret = -1;
1238 			break;
1239 		}
1240 		rawcache->offset_uncompressed+=skip;
1241 		offset -= skip;
1242 	}
1243 	free(trash);
1244 
1245 	if (offset) {
1246 		/* there was an error */
1247 		log("ar_read_raw (offset!=0)");
1248 		return -EIO;
1249 	}
1250 	/* read data */
1251 	ret = archive_read_data(rawcache->archive, buf, size);
1252 	if (ret == ARCHIVE_FATAL
1253 		|| ret == ARCHIVE_WARN
1254 		|| ret == ARCHIVE_RETRY)
1255 	{
1256 		log("ar_read_raw (reading data): %s",
1257 			archive_error_string(rawcache->archive));
1258 		errno = archive_errno(rawcache->archive);
1259 		ret = -1;
1260 	}
1261 	rawcache->offset_uncompressed +=size;
1262 	return ret;
1263 }
1264 
1265 static int
_ar_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)1266 _ar_read(const char *path, char *buf, size_t size, off_t offset,
1267 	struct fuse_file_info *fi)
1268 {
1269 	int ret = -1;
1270 	const char *realpath;
1271 	NODE *node;
1272 	(void)fi;
1273 	log("_ar_read called, path: '%s'", path);
1274 	/* find node */
1275 	node = get_node_for_path(root, path);
1276 	if (! node) {
1277 		return -ENOENT;
1278 	}
1279 	if (archive_entry_hardlink(node->entry)) {
1280 		/* file is a hardlink, recurse into it */
1281 		return _ar_read(archive_entry_hardlink(node->entry),
1282 			buf, size, offset, fi);
1283 	}
1284 	if (archive_entry_symlink(node->entry)) {
1285 		/* file is a symlink, recurse into it */
1286 		return _ar_read(archive_entry_symlink(node->entry),
1287 			buf, size, offset, fi);
1288 	}
1289 	if (node->modified) {
1290 		/* the file is new or modified, read temporary file instead */
1291 		int fh;
1292 		fh = open(node->location, O_RDONLY);
1293 		if (fh == -1) {
1294 			log("Fatal error opening modified file '%s' at "
1295 				"location '%s', giving up",
1296 				path, node->location);
1297 			return 0 - errno;
1298 		}
1299 		/* copy data */
1300 		if ((ret = pread(fh, buf, size, offset)) == -1) {
1301 			log("Error reading temporary file '%s': %s",
1302 				node->location, strerror(errno));
1303 			close(fh);
1304 			ret = 0 - errno;
1305 		}
1306 		/* clean up */
1307 		close(fh);
1308 	} else {
1309 		struct archive *archive;
1310 		struct archive_entry *entry;
1311 		int archive_ret;
1312 		/* search file in archive */
1313 		realpath = archive_entry_pathname(node->entry);
1314 		if ((archive = archive_read_new()) == NULL) {
1315 			log("Out of memory");
1316 			return -ENOMEM;
1317 		}
1318 		archive_ret = archive_read_support_filter_all(archive);
1319 		if (archive_ret != ARCHIVE_OK) {
1320 			log("archive_read_support_filter_all(): %s (%d)\n",
1321 				archive_error_string(archive), archive_ret);
1322 			return -EIO;
1323 		}
1324 		if (options.formatraw) {
1325 			archive_ret = archive_read_support_format_raw(archive);
1326 			if (archive_ret != ARCHIVE_OK) {
1327 				log("archive_read_support_format_all(): %s (%d)\n",
1328 					archive_error_string(archive), archive_ret);
1329 				return -EIO;
1330 			}
1331 			options.readonly = 1;
1332 		} else {
1333 			archive_ret = archive_read_support_format_all(archive);
1334 			if (archive_ret != ARCHIVE_OK) {
1335 				log("archive_read_support_format_all(): %s (%d)\n",
1336 					archive_error_string(archive), archive_ret);
1337 				return -EIO;
1338 			}
1339 		}
1340 		archive_ret = archive_read_open_fd(archive, archiveFd, 10240);
1341 		if (archive_ret != ARCHIVE_OK) {
1342 			log("archive_read_open_fd(): %s (%d)\n",
1343 				archive_error_string(archive), archive_ret);
1344 			return -EIO;
1345 		}
1346 		/* search for file to read */
1347 		while ((archive_ret = archive_read_next_header(
1348 					archive, &entry)) == ARCHIVE_OK) {
1349 			const char *name;
1350 			name = archive_entry_pathname(entry);
1351 			if (strcmp(realpath, name) == 0) {
1352 				void *trash;
1353 				if ((trash = malloc(MAXBUF)) == NULL) {
1354 					log("Out of memory");
1355 					return -ENOMEM;
1356 				}
1357 				/* skip offset */
1358 				while (offset > 0) {
1359 					int skip = offset > MAXBUF ? MAXBUF : offset;
1360 					ret = archive_read_data(
1361 						archive, trash, skip);
1362 					if (ret == ARCHIVE_FATAL
1363 						|| ret == ARCHIVE_WARN
1364 						|| ret == ARCHIVE_RETRY)
1365 					{
1366 						log("ar_read (skipping offset): %s",
1367 							archive_error_string(archive));
1368 						errno = archive_errno(archive);
1369 						ret = -1;
1370 						break;
1371 					}
1372 					offset -= skip;
1373 				}
1374 				free(trash);
1375 				if (offset) {
1376 					/* there was an error */
1377 					break;
1378 				}
1379 				/* read data */
1380 				ret = archive_read_data(archive, buf, size);
1381 				if (ret == ARCHIVE_FATAL
1382 					|| ret == ARCHIVE_WARN
1383 					|| ret == ARCHIVE_RETRY)
1384 				{
1385 					log("ar_read (reading data): %s",
1386 						archive_error_string(archive));
1387 					errno = archive_errno(archive);
1388 					ret = -1;
1389 				}
1390 				break;
1391 			}
1392 			archive_read_data_skip(archive);
1393 		}
1394 		/* close archive */
1395 		archive_read_free(archive);
1396 		lseek(archiveFd, 0, SEEK_SET);
1397 	}
1398 	return ret;
1399 }
1400 
1401 static int
ar_read(const char * path,char * buf,size_t size,off_t offset,struct fuse_file_info * fi)1402 ar_read(const char *path, char *buf, size_t size, off_t offset,
1403 	struct fuse_file_info *fi)
1404 {
1405 	log("ar_read called, path: '%s'", path);
1406 	int ret = pthread_mutex_lock(&lock);
1407 	if (ret) {
1408 		log("failed to get lock: %s\n", strerror(ret));
1409 		return -EIO;
1410 	} else {
1411 		if (options.formatraw) {
1412 			ret = _ar_read_raw(path, buf, size, offset, fi);
1413 		} else	{
1414 			ret = _ar_read(path, buf, size, offset, fi);
1415 		}
1416 		pthread_mutex_unlock(&lock);
1417 	}
1418 	return ret;
1419 }
1420 
1421 static off_t
_ar_getsizeraw(const char * path)1422 _ar_getsizeraw(const char *path)
1423 {
1424 	off_t offset = 0, ret;
1425 	NODE *node;
1426 	const char *realpath;
1427 
1428 	log("ar_getsizeraw called, path: '%s'", path);
1429 	/* find node */
1430 	node = get_node_for_path(root, path);
1431 	if (! node) {
1432 		return -ENOENT;
1433 	}
1434 
1435 	struct archive *archive;
1436 	struct archive_entry *entry;
1437 	int archive_ret;
1438 	/* search file in archive */
1439 	realpath = archive_entry_pathname(node->entry);
1440 
1441 	if ((archive = archive_read_new()) == NULL) {
1442 		log("Out of memory");
1443 		return -ENOMEM;
1444 	}
1445 
1446 	archive_ret = archive_read_support_filter_all(archive);
1447 
1448 	if (archive_ret != ARCHIVE_OK) {
1449 		log("archive_read_support_filter_all(): %s (%d)\n",
1450 			archive_error_string(archive), archive_ret);
1451 		return -EIO;
1452 	}
1453 
1454 	if (options.formatraw) {
1455 		archive_ret = archive_read_support_format_raw(archive);
1456 		if (archive_ret != ARCHIVE_OK) {
1457 			log("archive_read_support_format_raw(): %s (%d)\n",
1458 				archive_error_string(archive), archive_ret);
1459 			return -EIO;
1460 		}
1461 		options.readonly = 1;
1462 		options.formatraw = 1;
1463 	}
1464 
1465 	archive_ret = archive_read_open_fd(archive, archiveFd, 10240);
1466 	if (archive_ret != ARCHIVE_OK) {
1467 		log("archive_read_open_fd(): %s (%d)\n",
1468 			archive_error_string(archive), archive_ret);
1469 		return -EIO;
1470 	}
1471 
1472 	/* search for file to read */
1473 	while ((archive_ret = archive_read_next_header(
1474 				archive, &entry)) == ARCHIVE_OK)
1475 	{
1476 		const char *name;
1477 		name = archive_entry_pathname(entry);
1478 		if (strcmp(realpath, name) == 0) {
1479 			void *trash;
1480 			if ((trash = malloc(MAXBUF)) == NULL) {
1481 				log("Out of memory");
1482 				return -ENOMEM;
1483 			}
1484 			/* read until no more data */
1485 			ssize_t readed=MAXBUF;
1486 			while (readed != 0) {
1487 				ret = archive_read_data(
1488 					archive, trash, MAXBUF);
1489 				readed=ret;
1490 
1491 				if (ret == ARCHIVE_FATAL
1492 					|| ret == ARCHIVE_WARN
1493 					|| ret == ARCHIVE_RETRY)
1494 				{
1495 					log("ar_read (skipping offset): %s",
1496 						archive_error_string(archive));
1497 					errno = archive_errno(archive);
1498 					ret = -1;
1499 					break;
1500 				}
1501 				offset += ret;
1502 				// log("tmp offset =%ld (%ld)",offset,offset/1024/1024);
1503 			}
1504 			free(trash);
1505 			break;
1506 		}
1507 		archive_read_data_skip(archive);
1508 	} // end of search for file to read
1509 	/* close archive */
1510 	archive_read_free(archive);
1511 	lseek(archiveFd, 0, SEEK_SET);
1512 
1513 	return offset;
1514 }
1515 
1516 static int
_ar_getattr(const char * path,struct stat * stbuf)1517 _ar_getattr(const char *path, struct stat *stbuf)
1518 {
1519 	NODE *node;
1520 	int ret;
1521 	off_t size;
1522 
1523 	//log("_ar_getattr called, path: '%s'", path);
1524 	node = get_node_for_path(root, path);
1525 	if (! node) {
1526 		return -ENOENT;
1527 	}
1528 	if (archive_entry_hardlink(node->entry)) {
1529 		/* a hardlink, recurse into it */
1530 		ret = _ar_getattr(archive_entry_hardlink(
1531 				node->entry), stbuf);
1532 		return ret;
1533 	}
1534 	if (options.formatraw && ! node->child) {
1535 		fstat(archiveFd, stbuf);
1536 		size=rawcache->st.st_size;
1537 		if (size < 0) return -1;
1538 		stbuf->st_size = size;
1539 	} else {
1540 		memcpy(stbuf, archive_entry_stat(node->entry),
1541 			sizeof(struct stat));
1542 		if (options.formatraw && node->child)
1543 			stbuf->st_size = 4096;
1544 	}
1545 	stbuf->st_blocks  = (stbuf->st_size + 511) / 512;
1546 	stbuf->st_blksize = 4096;
1547 
1548 	if (options.readonly) {
1549 		stbuf->st_mode = stbuf->st_mode & 0777555;
1550 	}
1551 
1552 	return 0;
1553 }
1554 
1555 static int
ar_getattr(const char * path,struct stat * stbuf)1556 ar_getattr(const char *path, struct stat *stbuf)
1557 {
1558 	//log("ar_getattr called, path: '%s'", path);
1559 	int ret = pthread_mutex_lock(&lock);
1560 	if (ret) {
1561 		log("failed to get lock: %s\n", strerror(ret));
1562 		return -EIO;
1563 	} else {
1564 		ret = _ar_getattr(path, stbuf);
1565 		pthread_mutex_unlock(&lock);
1566 	}
1567 	return ret;
1568 }
1569 
1570 /*
1571  * mkdir is nearly identical to mknod...
1572  */
1573 static int
ar_mkdir(const char * path,mode_t mode)1574 ar_mkdir(const char *path, mode_t mode)
1575 {
1576 	NODE *node;
1577 	char *location;
1578 	int tmp;
1579 
1580 	log("ar_mkdir called, path '%s', mode %o", path, mode);
1581 	if (! archiveWriteable || options.readonly) {
1582 		return -EROFS;
1583 	}
1584 	pthread_mutex_lock(&lock);
1585 	/* check for existing node */
1586 	node = get_node_for_path(root, path);
1587 	if (node) {
1588 		pthread_mutex_unlock(&lock);
1589 		return -EEXIST;
1590 	}
1591 	/* create name for temp dir */
1592 	if ((tmp = get_temp_file_name(path, &location)) < 0) {
1593 		pthread_mutex_unlock(&lock);
1594 		return tmp;
1595 	}
1596 	/* create temp dir */
1597 	if (mkdir(location, mode) == -1) {
1598 		log("Could not create temporary dir %s: %s",
1599 			location, strerror(errno));
1600 		free(location);
1601 		pthread_mutex_unlock(&lock);
1602 		return 0 - errno;
1603 	}
1604 	/* build node */
1605 	if ((node = init_node()) == NULL) {
1606 		pthread_mutex_unlock(&lock);
1607 		return -ENOMEM;
1608 	}
1609 	node->location = location;
1610 	node->modified = 1;
1611 	node->name = strdup(path);
1612 	node->basename = strrchr(node->name, '/') + 1;
1613 	node->namechanged = 0;
1614 	/* build entry */
1615 	if (root->child &&
1616 		node->name[0] == '/' &&
1617 		archive_entry_pathname(root->child->entry)[0] != '/')
1618 	{
1619 		archive_entry_set_pathname(node->entry, node->name + 1);
1620 	} else {
1621 		archive_entry_set_pathname(node->entry, node->name);
1622 	}
1623 	if ((tmp = update_entry_stat(node)) < 0) {
1624 		log("mkdir: error stat'ing dir %s: %s", node->location,
1625 			strerror(0 - tmp));
1626 		rmdir(location);
1627 		free(location);
1628 		free_node(node);
1629 		pthread_mutex_unlock(&lock);
1630 		return tmp;
1631 	}
1632 	/* add node to tree */
1633 	if (insert_by_path(root, node) != 0) {
1634 		log("ERROR: could not insert %s into tree",
1635 			node->name);
1636 		rmdir(location);
1637 		free(location);
1638 		free_node(node);
1639 		pthread_mutex_unlock(&lock);
1640 		return -ENOENT;
1641 	}
1642 	/* clean up */
1643 	archiveModified = 1;
1644 	pthread_mutex_unlock(&lock);
1645 	return 0;
1646 }
1647 
1648 /*
1649  * ar_rmdir is called for directories only and does not need to do any
1650  * recursive stuff
1651  */
1652 static int
ar_rmdir(const char * path)1653 ar_rmdir(const char *path)
1654 {
1655 	NODE *node;
1656 
1657 	log("ar_rmdir called, path '%s'", path);
1658 	if (! archiveWriteable || options.readonly) {
1659 		return -EROFS;
1660 	}
1661 	pthread_mutex_lock(&lock);
1662 	node = get_node_for_path(root, path);
1663 	if (! node) {
1664 		pthread_mutex_unlock(&lock);
1665 		return -ENOENT;
1666 	}
1667 	if (node->child) {
1668 		pthread_mutex_unlock(&lock);
1669 		return -ENOTEMPTY;
1670 	}
1671 	if (node->name[strlen(node->name)-1] == '.') {
1672 		pthread_mutex_unlock(&lock);
1673 		return -EINVAL;
1674 	}
1675 	if (! S_ISDIR(archive_entry_mode(node->entry))) {
1676 		pthread_mutex_unlock(&lock);
1677 		return -ENOTDIR;
1678 	}
1679 	if (node->location) {
1680 		/* remove temp directory */
1681 		if (rmdir(node->location) == -1) {
1682 			int err = errno;
1683 			log("ERROR: removing temp directory %s failed: %s",
1684 				node->location, strerror(err));
1685 			pthread_mutex_unlock(&lock);
1686 			return err;
1687 		}
1688 		free(node->location);
1689 	}
1690 	remove_child(node);
1691 	free_node(node);
1692 	archiveModified = 1;
1693 	pthread_mutex_unlock(&lock);
1694 	return 0;
1695 }
1696 
1697 static int
ar_symlink(const char * from,const char * to)1698 ar_symlink(const char *from, const char *to)
1699 {
1700 	NODE *node;
1701 	struct stat st;
1702 	struct passwd *pwd;
1703 	struct group *grp;
1704 
1705 	log("symlink called, %s -> %s", from, to);
1706 	if (! archiveWriteable || options.readonly) {
1707 		return -EROFS;
1708 	}
1709 	pthread_mutex_lock(&lock);
1710 	/* check for existing node */
1711 	node = get_node_for_path(root, to);
1712 	if (node) {
1713 		pthread_mutex_unlock(&lock);
1714 		return -EEXIST;
1715 	}
1716 	/* build node */
1717 	if ((node = init_node()) == NULL) {
1718 		pthread_mutex_unlock(&lock);
1719 		return -ENOMEM;
1720 	}
1721 	node->name = strdup(to);
1722 	node->basename = strrchr(node->name, '/') + 1;
1723 	node->modified = 1;
1724 	/* build stat info */
1725 	st.st_dev = 0;
1726 	st.st_ino = 0;
1727 	st.st_mode = S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO;
1728 	st.st_nlink = 1;
1729 	st.st_uid = getuid();
1730 	st.st_gid = getgid();
1731 	st.st_rdev = 0;
1732 	st.st_size = strlen(from);
1733 	st.st_blksize = 4096;
1734 	st.st_blocks = 0;
1735 	st.st_atime = st.st_ctime = st.st_mtime = time(NULL);
1736 	/* build entry */
1737 	if (root->child &&
1738 		node->name[0] == '/' &&
1739 		archive_entry_pathname(root->child->entry)[0] != '/')
1740 	{
1741 		archive_entry_set_pathname(node->entry, node->name + 1);
1742 	} else {
1743 		archive_entry_set_pathname(node->entry, node->name);
1744 	}
1745 	archive_entry_copy_stat(node->entry, &st);
1746 	archive_entry_set_symlink(node->entry, strdup(from));
1747 	/* get user/group name */
1748 	pwd = getpwuid(st.st_uid);
1749 	if (pwd) {
1750 		/* a name was found for the uid */
1751 		archive_entry_set_uname(node->entry, strdup(pwd->pw_name));
1752 	} else {
1753 		if (errno == EINTR || errno == EIO || errno == EMFILE ||
1754 			errno == ENFILE || errno == ENOMEM ||
1755 			errno == ERANGE)
1756 		{
1757 			log("ERROR calling getpwuid: %s", strerror(errno));
1758 			free_node(node);
1759 			pthread_mutex_unlock(&lock);
1760 			return 0 - errno;
1761 		}
1762 		/* on other errors the uid just could
1763 		   not be resolved into a name */
1764 	}
1765 	grp = getgrgid(st.st_gid);
1766 	if (grp) {
1767 		/* a name was found for the uid */
1768 		archive_entry_set_gname(node->entry, strdup(grp->gr_name));
1769 	} else {
1770 		if (errno == EINTR || errno == EIO || errno == EMFILE ||
1771 			errno == ENFILE || errno == ENOMEM ||
1772 			errno == ERANGE)
1773 		{
1774 			log("ERROR calling getgrgid: %s", strerror(errno));
1775 			free_node(node);
1776 			pthread_mutex_unlock(&lock);
1777 			return 0 - errno;
1778 		}
1779 		/* on other errors the gid just could
1780 		   not be resolved into a name */
1781 	}
1782 	/* add node to tree */
1783 	if (insert_by_path(root, node) != 0) {
1784 		log("ERROR: could not insert symlink %s into tree",
1785 			node->name);
1786 		free_node(node);
1787 		pthread_mutex_unlock(&lock);
1788 		return -ENOENT;
1789 	}
1790 	/* clean up */
1791 	archiveModified = 1;
1792 	pthread_mutex_unlock(&lock);
1793 	return 0;
1794 }
1795 
1796 static int
ar_link(const char * from,const char * to)1797 ar_link(const char *from, const char *to)
1798 {
1799 	NODE *node;
1800 	NODE *fromnode;
1801 	struct stat st;
1802 	struct passwd *pwd;
1803 	struct group *grp;
1804 
1805 	log("ar_link called, %s -> %s", from, to);
1806 	if (! archiveWriteable || options.readonly) {
1807 		return -EROFS;
1808 	}
1809 	pthread_mutex_lock(&lock);
1810 	/* find source node */
1811 	fromnode = get_node_for_path(root, from);
1812 	if (! fromnode) {
1813 		pthread_mutex_unlock(&lock);
1814 		return -ENOENT;
1815 	}
1816 	/* check for existing target */
1817 	node = get_node_for_path(root, to);
1818 	if (node) {
1819 		pthread_mutex_unlock(&lock);
1820 		return -EEXIST;
1821 	}
1822 	/* extract originals stat info */
1823 	_ar_getattr(from, &st);
1824 	/* build new node */
1825 	if ((node = init_node()) == NULL) {
1826 		pthread_mutex_unlock(&lock);
1827 		return -ENOMEM;
1828 	}
1829 	node->name = strdup(to);
1830 	node->basename = strrchr(node->name, '/') + 1;
1831 	node->modified = 1;
1832 	/* build entry */
1833 	if (node->name[0] == '/' &&
1834 		archive_entry_pathname(fromnode->entry)[0] != '/')
1835 	{
1836 		archive_entry_set_pathname(node->entry, node->name + 1);
1837 	} else {
1838 		archive_entry_set_pathname(node->entry, node->name);
1839 	}
1840 	archive_entry_copy_stat(node->entry, &st);
1841 	archive_entry_set_hardlink(node->entry, strdup(from));
1842 	/* get user/group name */
1843 	pwd = getpwuid(st.st_uid);
1844 	if (pwd) {
1845 		/* a name was found for the uid */
1846 		archive_entry_set_uname(node->entry, strdup(pwd->pw_name));
1847 	} else {
1848 		if (errno == EINTR || errno == EIO || errno == EMFILE ||
1849 			errno == ENFILE || errno == ENOMEM ||
1850 			errno == ERANGE)
1851 		{
1852 			log("ERROR calling getpwuid: %s", strerror(errno));
1853 			free_node(node);
1854 			pthread_mutex_unlock(&lock);
1855 			return 0 - errno;
1856 		}
1857 		/* on other errors the uid just could
1858 		   not be resolved into a name */
1859 	}
1860 	grp = getgrgid(st.st_gid);
1861 	if (grp) {
1862 		/* a name was found for the uid */
1863 		archive_entry_set_gname(node->entry, strdup(grp->gr_name));
1864 	} else {
1865 		if (errno == EINTR || errno == EIO || errno == EMFILE ||
1866 			errno == ENFILE || errno == ENOMEM ||
1867 			errno == ERANGE)
1868 		{
1869 			log("ERROR calling getgrgid: %s", strerror(errno));
1870 			free_node(node);
1871 			pthread_mutex_unlock(&lock);
1872 			return 0 - errno;
1873 		}
1874 		/* on other errors the gid just could
1875 		   not be resolved into a name */
1876 	}
1877 	/* add node to tree */
1878 	if (insert_by_path(root, node) != 0) {
1879 		log("ERROR: could not insert hardlink %s into tree",
1880 			node->name);
1881 		free_node(node);
1882 		pthread_mutex_unlock(&lock);
1883 		return -ENOENT;
1884 	}
1885 	/* clean up */
1886 	archiveModified = 1;
1887 	pthread_mutex_unlock(&lock);
1888 	return 0;
1889 }
1890 
1891 static int
_ar_truncate(const char * path,off_t size)1892 _ar_truncate(const char *path, off_t size)
1893 {
1894 	NODE *node;
1895 	char *location;
1896 	int ret;
1897 	int tmp;
1898 	int fh;
1899 
1900 	log("_ar_truncate called, path '%s'", path);
1901 	if (! archiveWriteable || options.readonly) {
1902 		return -EROFS;
1903 	}
1904 	node = get_node_for_path(root, path);
1905 	if (! node) {
1906 		return -ENOENT;
1907 	}
1908 	if (archive_entry_hardlink(node->entry)) {
1909 		/* file is a hardlink, recurse into it */
1910 		return _ar_truncate(archive_entry_hardlink(
1911 				node->entry), size);
1912 	}
1913 	if (archive_entry_symlink(node->entry)) {
1914 		/* file is a symlink, recurse into it */
1915 		return _ar_truncate(archive_entry_symlink(
1916 				node->entry), size);
1917 	}
1918 	if (node->location) {
1919 		/* open existing temp file */
1920 		location = node->location;
1921 		if ((fh = open(location, O_WRONLY)) == -1) {
1922 			log("error opening temp file %s: %s",
1923 				location, strerror(errno));
1924 			unlink(location);
1925 			return 0 - errno;
1926 		}
1927 	} else {
1928 		/* create new temp file */
1929 		char *tmpbuf = NULL;
1930 		int tmpoffset = 0;
1931 		int64_t tmpsize;
1932 		struct fuse_file_info fi;
1933 		if ((tmp = get_temp_file_name(path, &location)) < 0) {
1934 			return tmp;
1935 		}
1936 		if ((fh = open(location, O_WRONLY | O_CREAT | O_EXCL,
1937 					archive_entry_mode(node->entry))) == -1)
1938 		{
1939 			log("error opening temp file %s: %s",
1940 				location, strerror(errno));
1941 			unlink(location);
1942 			return 0 - errno;
1943 		}
1944 		/* copy original file to temporary file */
1945 		tmpsize = archive_entry_size(node->entry);
1946 		if ((tmpbuf = (char *)malloc(MAXBUF)) == NULL) {
1947 			log("Out of memory");
1948 			return -ENOMEM;
1949 		}
1950 		while (tmpsize) {
1951 			int len = tmpsize > MAXBUF ? MAXBUF : tmpsize;
1952 			/* read */
1953 			if ((tmp = _ar_read(path, tmpbuf, len, tmpoffset, &fi))
1954 				< 0)
1955 			{
1956 				log("ERROR reading while copying %s to "
1957 					"temporary location %s: %s",
1958 					path, location,
1959 					strerror(0 - tmp));
1960 				close(fh);
1961 				unlink(location);
1962 				free(tmpbuf);
1963 				return tmp;
1964 			}
1965 			/* write */
1966 			if (write(fh, tmpbuf, tmp) == -1) {
1967 				tmp = 0 - errno;
1968 				log("ERROR writing while copying %s to "
1969 					"temporary location %s: %s",
1970 					path, location,
1971 					strerror(errno));
1972 				close(fh);
1973 				unlink(location);
1974 				free(tmpbuf);
1975 				return tmp;
1976 			}
1977 			tmpsize -= len;
1978 			tmpoffset += len;
1979 			if (tmpoffset >= size) {
1980 				/* copied enough, exit the loop */
1981 				break;
1982 			}
1983 		}
1984 		/* clean up */
1985 		free(tmpbuf);
1986 		lseek(fh, 0, SEEK_SET);
1987 	}
1988 	/* truncate temporary file */
1989 	if ((ret = truncate(location, size)) == -1) {
1990 		tmp = 0 - errno;
1991 		log("ERROR truncating %s (temporary location %s): %s",
1992 			path, location, strerror(errno));
1993 		close(fh);
1994 		unlink(location);
1995 		return tmp;
1996 	}
1997 	/* record location, update entry */
1998 	node->location = location;
1999 	node->modified = 1;
2000 	if ((tmp = update_entry_stat(node)) < 0) {
2001 		log("write: error stat'ing file %s: %s", node->location,
2002 			strerror(0 - tmp));
2003 		close(fh);
2004 		unlink(location);
2005 		return tmp;
2006 	}
2007 	/* clean up */
2008 	close(fh);
2009 	archiveModified = 1;
2010 	return ret;
2011 }
2012 
2013 static int
ar_truncate(const char * path,off_t size)2014 ar_truncate(const char *path, off_t size)
2015 {
2016 	int ret;
2017 	log("ar_truncate called, path '%s'", path);
2018 	pthread_mutex_lock(&lock);
2019 	ret = _ar_truncate(path, size);
2020 	pthread_mutex_unlock(&lock);
2021 	return ret;
2022 }
2023 
2024 static int
_ar_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)2025 _ar_write(const char *path, const char *buf, size_t size,
2026 	off_t offset, struct fuse_file_info *fi)
2027 {
2028 	NODE *node;
2029 	char *location;
2030 	int ret;
2031 	int tmp;
2032 	int fh;
2033 
2034 	log("_ar_write called, path '%s'", path);
2035 	if (! archiveWriteable || options.readonly) {
2036 		return -EROFS;
2037 	}
2038 	node = get_node_for_path(root, path);
2039 	if (! node) {
2040 		return -ENOENT;
2041 	}
2042 	if (S_ISLNK(archive_entry_mode(node->entry))) {
2043 		/* file is a symlink, recurse into it */
2044 		return _ar_write(archive_entry_symlink(node->entry),
2045 			buf, size, offset, fi);
2046 	}
2047 	if (archive_entry_hardlink(node->entry)) {
2048 		/* file is a hardlink, recurse into it */
2049 		return _ar_write(archive_entry_hardlink(node->entry),
2050 			buf, size, offset, fi);
2051 	}
2052 	if (archive_entry_symlink(node->entry)) {
2053 		/* file is a symlink, recurse into it */
2054 		return _ar_write(archive_entry_symlink(node->entry),
2055 			buf, size, offset, fi);
2056 	}
2057 	if (node->location) {
2058 		/* open existing temp file */
2059 		location = node->location;
2060 		if ((fh = open(location, O_WRONLY)) == -1) {
2061 			log("error opening temp file %s: %s",
2062 				location, strerror(errno));
2063 			unlink(location);
2064 			return 0 - errno;
2065 		}
2066 	} else {
2067 		/* create new temp file */
2068 		char *tmpbuf = NULL;
2069 		int tmpoffset = 0;
2070 		int64_t tmpsize;
2071 		if ((tmp = get_temp_file_name(path, &location)) < 0) {
2072 			return tmp;
2073 		}
2074 		if ((fh = open(location, O_WRONLY | O_CREAT | O_EXCL,
2075 					archive_entry_mode(node->entry))) == -1)
2076 		{
2077 			log("error opening temp file %s: %s",
2078 				location, strerror(errno));
2079 			unlink(location);
2080 			return 0 - errno;
2081 		}
2082 		/* copy original file to temporary file */
2083 		tmpsize = archive_entry_size(node->entry);
2084 		if ((tmpbuf = (char *)malloc(MAXBUF)) == NULL) {
2085 			log("Out of memory");
2086 			return -ENOMEM;
2087 		}
2088 		while (tmpsize) {
2089 			int len = tmpsize > MAXBUF ? MAXBUF : tmpsize;
2090 			/* read */
2091 			if ((tmp = _ar_read(path, tmpbuf, len, tmpoffset, fi))
2092 				< 0)
2093 			{
2094 				log("ERROR reading while copying %s to "
2095 					"temporary location %s: %s",
2096 					path, location,
2097 					strerror(0 - tmp));
2098 				close(fh);
2099 				unlink(location);
2100 				free(tmpbuf);
2101 				return tmp;
2102 			}
2103 			/* write */
2104 			if (write(fh, tmpbuf, len) == -1) {
2105 				tmp = 0 - errno;
2106 				log("ERROR writing while copying %s to "
2107 					"temporary location %s: %s",
2108 					path, location,
2109 					strerror(errno));
2110 				close(fh);
2111 				unlink(location);
2112 				free(tmpbuf);
2113 				return tmp;
2114 			}
2115 			tmpsize -= len;
2116 			tmpoffset += len;
2117 		}
2118 		/* clean up */
2119 		free(tmpbuf);
2120 		lseek(fh, 0, SEEK_SET);
2121 	}
2122 	/* write changes to temporary file */
2123 	if ((ret = pwrite(fh, buf, size, offset)) == -1) {
2124 		tmp = 0 - errno;
2125 		log("ERROR writing changes to %s (temporary "
2126 			"location %s): %s",
2127 			path, location, strerror(errno));
2128 		close(fh);
2129 		unlink(location);
2130 		return tmp;
2131 	}
2132 	/* record location, update entry */
2133 	node->location = location;
2134 	node->modified = 1;
2135 	if ((tmp = update_entry_stat(node)) < 0) {
2136 		log("write: error stat'ing file %s: %s", node->location,
2137 			strerror(0 - tmp));
2138 		close(fh);
2139 		unlink(location);
2140 		return tmp;
2141 	}
2142 	/* clean up */
2143 	close(fh);
2144 	archiveModified = 1;
2145 	return ret;
2146 }
2147 
2148 static int
ar_write(const char * path,const char * buf,size_t size,off_t offset,struct fuse_file_info * fi)2149 ar_write(const char *path, const char *buf, size_t size,
2150 	off_t offset, struct fuse_file_info *fi)
2151 {
2152 	int ret;
2153 	log("ar_write called, path '%s'", path);
2154 	pthread_mutex_lock(&lock);
2155 	ret = _ar_write(path, buf, size, offset, fi);
2156 	pthread_mutex_unlock(&lock);
2157 	return ret;
2158 }
2159 
2160 static int
ar_mknod(const char * path,mode_t mode,dev_t rdev)2161 ar_mknod(const char *path, mode_t mode, dev_t rdev)
2162 {
2163 	NODE *node;
2164 	char *location;
2165 	int tmp;
2166 
2167 	log("ar_mknod called, path %s", path);
2168 	if (! archiveWriteable || options.readonly) {
2169 		return -EROFS;
2170 	}
2171 	pthread_mutex_lock(&lock);
2172 	/* check for existing node */
2173 	node = get_node_for_path(root, path);
2174 	if (node) {
2175 		pthread_mutex_unlock(&lock);
2176 		return -EEXIST;
2177 	}
2178 	/* create name for temp file */
2179 	if ((tmp = get_temp_file_name(path, &location)) < 0) {
2180 		pthread_mutex_unlock(&lock);
2181 		return tmp;
2182 	}
2183 	/* create temp file */
2184 	if (mknod(location, mode, rdev) == -1) {
2185 		log("Could not create temporary file %s: %s",
2186 			location, strerror(errno));
2187 		free(location);
2188 		pthread_mutex_unlock(&lock);
2189 		return 0 - errno;
2190 	}
2191 	/* build node */
2192 	if ((node = init_node()) == NULL) {
2193 		pthread_mutex_unlock(&lock);
2194 		return -ENOMEM;
2195 	}
2196 	node->location = location;
2197 	node->modified = 1;
2198 	node->name = strdup(path);
2199 	node->basename = strrchr(node->name, '/') + 1;
2200 
2201 	/* build entry */
2202 	if (root->child &&
2203 		node->name[0] == '/' &&
2204 		archive_entry_pathname(root->child->entry)[0] != '/')
2205 	{
2206 		archive_entry_set_pathname(node->entry, node->name + 1);
2207 	} else {
2208 		archive_entry_set_pathname(node->entry, node->name);
2209 	}
2210 	if ((tmp = update_entry_stat(node)) < 0) {
2211 		log("mknod: error stat'ing file %s: %s", node->location,
2212 			strerror(0 - tmp));
2213 		unlink(location);
2214 		free(location);
2215 		free_node(node);
2216 		pthread_mutex_unlock(&lock);
2217 		return tmp;
2218 	}
2219 	/* add node to tree */
2220 	if (insert_by_path(root, node) != 0) {
2221 		log("ERROR: could not insert %s into tree",
2222 			node->name);
2223 		unlink(location);
2224 		free(location);
2225 		free_node(node);
2226 		pthread_mutex_unlock(&lock);
2227 		return -ENOENT;
2228 	}
2229 	/* clean up */
2230 	archiveModified = 1;
2231 	pthread_mutex_unlock(&lock);
2232 	return 0;
2233 }
2234 
2235 static int
_ar_unlink(const char * path)2236 _ar_unlink(const char *path)
2237 {
2238 	NODE *node;
2239 
2240 	log("_ar_unlink called, %s", path);
2241 	if (! archiveWriteable || options.readonly) {
2242 		return -EROFS;
2243 	}
2244 	node = get_node_for_path(root, path);
2245 	if (! node) {
2246 		return -ENOENT;
2247 	}
2248 	if (S_ISDIR(archive_entry_mode(node->entry))) {
2249 		return -EISDIR;
2250 	}
2251 	if (node->location) {
2252 		/* remove temporary file */
2253 		if (unlink(node->location) == -1) {
2254 			int err = errno;
2255 			log("ERROR: could not unlink temporary file '%s': %s",
2256 				node->location, strerror(err));
2257 			return err;
2258 		}
2259 		free(node->location);
2260 	}
2261 	remove_child(node);
2262 	free_node(node);
2263 	archiveModified = 1;
2264 	return 0;
2265 }
2266 
2267 static int
ar_unlink(const char * path)2268 ar_unlink(const char *path)
2269 {
2270 	log("ar_unlink called, path '%s'", path);
2271 	int ret;
2272 	pthread_mutex_lock(&lock);
2273 	ret = _ar_unlink(path);
2274 	pthread_mutex_unlock(&lock);
2275 	return ret;
2276 }
2277 
2278 static int
_ar_chmod(const char * path,mode_t mode)2279 _ar_chmod(const char *path, mode_t mode)
2280 {
2281 	NODE *node;
2282 
2283 	log("_ar_chmod called, path '%s', mode: %o", path, mode);
2284 	if (! archiveWriteable || options.readonly) {
2285 		return -EROFS;
2286 	}
2287 	node = get_node_for_path(root, path);
2288 	if (! node) {
2289 		return -ENOENT;
2290 	}
2291 	if (archive_entry_hardlink(node->entry)) {
2292 		/* file is a hardlink, recurse into it */
2293 		return _ar_chmod(archive_entry_hardlink(node->entry), mode);
2294 	}
2295 	if (archive_entry_symlink(node->entry)) {
2296 		/* file is a symlink, recurse into it */
2297 		return _ar_chmod(archive_entry_symlink(node->entry), mode);
2298 	}
2299 #ifdef __APPLE__
2300 	/* Make sure the full mode, including file type information, is used */
2301 	mode = (0777000 & archive_entry_mode(node->entry)) | (0000777 & mode);
2302 #endif // __APPLE__
2303 	archive_entry_set_mode(node->entry, mode);
2304 	archiveModified = 1;
2305 	return 0;
2306 }
2307 
2308 static int
ar_chmod(const char * path,mode_t mode)2309 ar_chmod(const char *path, mode_t mode)
2310 {
2311 	log("ar_chmod called, path '%s', mode: %o", path, mode);
2312 	int ret;
2313 	pthread_mutex_lock(&lock);
2314 	ret = _ar_chmod(path, mode);
2315 	pthread_mutex_unlock(&lock);
2316 	return ret;
2317 }
2318 
2319 static int
_ar_chown(const char * path,uid_t uid,gid_t gid)2320 _ar_chown(const char *path, uid_t uid, gid_t gid)
2321 {
2322 	NODE *node;
2323 
2324 	log("_ar_chown called, %s", path);
2325 	if (! archiveWriteable || options.readonly) {
2326 		return -EROFS;
2327 	}
2328 	node = get_node_for_path(root, path);
2329 	if (! node) {
2330 		return -ENOENT;
2331 	}
2332 	if (archive_entry_hardlink(node->entry)) {
2333 		/* file is a hardlink, recurse into it */
2334 		return _ar_chown(archive_entry_hardlink(node->entry),
2335 			uid, gid);
2336 	}
2337 	/* changing ownership of symlinks is allowed, however */
2338 	archive_entry_set_uid(node->entry, uid);
2339 	archive_entry_set_gid(node->entry, gid);
2340 	archiveModified = 1;
2341 	return 0;
2342 }
2343 
2344 static int
ar_chown(const char * path,uid_t uid,gid_t gid)2345 ar_chown(const char *path, uid_t uid, gid_t gid)
2346 {
2347 	log("ar_chown called, %s", path);
2348 	int ret;
2349 	pthread_mutex_lock(&lock);
2350 	ret = _ar_chown(path, uid, gid);
2351 	pthread_mutex_unlock(&lock);
2352 	return ret;
2353 }
2354 
2355 static int
_ar_utime(const char * path,struct utimbuf * buf)2356 _ar_utime(const char *path, struct utimbuf *buf)
2357 {
2358 	NODE *node;
2359 
2360 	log("_ar_utime called, %s", path);
2361 	if (! archiveWriteable || options.readonly) {
2362 		return -EROFS;
2363 	}
2364 	node = get_node_for_path(root, path);
2365 	if (! node) {
2366 		return -ENOENT;
2367 	}
2368 	if (archive_entry_hardlink(node->entry)) {
2369 		/* file is a hardlink, recurse into it */
2370 		return _ar_utime(archive_entry_hardlink(node->entry), buf);
2371 	}
2372 	if (archive_entry_symlink(node->entry)) {
2373 		/* file is a symlink, recurse into it */
2374 		return _ar_utime(archive_entry_symlink(node->entry), buf);
2375 	}
2376 	archive_entry_set_mtime(node->entry, buf->modtime, 0);
2377 	archive_entry_set_atime(node->entry, buf->actime, 0);
2378 	archiveModified = 1;
2379 	return 0;
2380 }
2381 
2382 static int
ar_utime(const char * path,struct utimbuf * buf)2383 ar_utime(const char *path, struct utimbuf *buf)
2384 {
2385 	log("ar_utime called, %s", path);
2386 	int ret;
2387 	pthread_mutex_lock(&lock);
2388 	ret = _ar_utime(path, buf);
2389 	pthread_mutex_unlock(&lock);
2390 	return ret;
2391 }
2392 
2393 static int
ar_statfs(const char * path,struct statvfs * stbuf)2394 ar_statfs(const char *path, struct statvfs *stbuf)
2395 {
2396 	(void)path;
2397 
2398 	log("ar_statfs called, %s", path);
2399 
2400 	/* Adapted the following from sshfs.c */
2401 
2402 	stbuf->f_namemax = 255;
2403 	stbuf->f_bsize = 4096;
2404 	/*
2405 	 * df seems to use f_bsize instead of f_frsize, so make them
2406 	 * the same
2407 	 */
2408 	stbuf->f_frsize = stbuf->f_bsize;
2409 	stbuf->f_blocks = stbuf->f_bfree =  stbuf->f_bavail =
2410 		1000ULL * 1024 * 1024 * 1024 / stbuf->f_frsize;
2411 	stbuf->f_files = stbuf->f_ffree = 1000000000;
2412 	return 0;
2413 }
2414 
2415 static int
ar_rename(const char * from,const char * to)2416 ar_rename(const char *from, const char *to)
2417 {
2418 	NODE *from_node;
2419 	int ret = 0;
2420 	char *old_name;
2421 	char *temp_name;
2422 
2423 	log("ar_rename called, from: '%s', to: '%s'", from, to);
2424 	if (! archiveWriteable || options.readonly) {
2425 		return -EROFS;
2426 	}
2427 	pthread_mutex_lock(&lock);
2428 	from_node = get_node_for_path(root, from);
2429 	if (! from_node) {
2430 		pthread_mutex_unlock(&lock);
2431 		return -ENOENT;
2432 	}
2433 	{
2434 		/* before actually renaming the from_node, we must remove
2435 		 * the to_node if it exists */
2436 		NODE *to_node;
2437 		to_node = get_node_for_path(root, to);
2438 		if (to_node) {
2439 			ret = _ar_unlink(to_node->name);
2440 			if (0 != ret) {
2441 				return ret;
2442 			}
2443 		}
2444 	}
2445 	/* meta data is changed in save() */
2446 	/* change from_node name */
2447 	if (*to != '/') {
2448 		if ((temp_name = malloc(strlen(to) + 2)) == NULL) {
2449 			log("Out of memory");
2450 			pthread_mutex_unlock(&lock);
2451 			return -ENOMEM;
2452 		}
2453 		sprintf(temp_name, "/%s", to);
2454 	} else {
2455 		if ((temp_name = strdup(to)) == NULL) {
2456 			log("Out of memory");
2457 			pthread_mutex_unlock(&lock);
2458 			return -ENOMEM;
2459 		}
2460 	}
2461 	remove_child(from_node);
2462 	correct_hardlinks_to_node(root, from_node->name, temp_name);
2463 	free(from_node->name);
2464 	from_node->name = temp_name;
2465 	from_node->basename = strrchr(from_node->name, '/') + 1;
2466 	from_node->namechanged = 1;
2467 	ret = insert_by_path(root, from_node);
2468 	if (0 != ret) {
2469 		log ("failed to re-insert node %s", from_node->name);
2470 	}
2471 	if (from_node->child) {
2472 		/* it is a directory, recursive change of all from_nodes
2473 		 * below it is required */
2474 		ret = rename_recursively(from_node->child, from, to);
2475 	}
2476 	archiveModified = 1;
2477 	pthread_mutex_unlock(&lock);
2478 	return ret;
2479 }
2480 
2481 static int
ar_fsync(const char * path,int isdatasync,struct fuse_file_info * fi)2482 ar_fsync(const char *path, int isdatasync, struct fuse_file_info *fi)
2483 {
2484 	/* Just a stub.  This method is optional and can safely be left
2485 	   unimplemented */
2486 	(void)path;
2487 	(void)isdatasync;
2488 	(void)fi;
2489 	return 0;
2490 }
2491 
2492 static int
ar_readlink(const char * path,char * buf,size_t size)2493 ar_readlink(const char *path, char *buf, size_t size)
2494 {
2495 	NODE *node;
2496 	const char *tmp;
2497 
2498 	log("ar_readlink called, path '%s'", path);
2499 	int ret = pthread_mutex_lock(&lock);
2500 	if (ret) {
2501 		fprintf(stderr, "could not acquire lock for archive: %s\n", strerror(ret));
2502 		return ret;
2503 	}
2504 	node = get_node_for_path(root, path);
2505 	if (! node) {
2506 		pthread_mutex_unlock(&lock);
2507 		return -ENOENT;
2508 	}
2509 	if (! S_ISLNK(archive_entry_mode(node->entry))) {
2510 		pthread_mutex_unlock(&lock);
2511 		return -ENOLINK;
2512 	}
2513 	tmp = archive_entry_symlink(node->entry);
2514 	snprintf(buf, size, "%s", tmp);
2515 	pthread_mutex_unlock(&lock);
2516 
2517 	return 0;
2518 }
2519 
2520 static int
ar_open(const char * path,struct fuse_file_info * fi)2521 ar_open(const char *path, struct fuse_file_info *fi)
2522 {
2523 	NODE *node;
2524 
2525 	log("ar_open called, path '%s'", path);
2526 	int ret = pthread_mutex_lock(&lock);
2527 	if (ret) {
2528 		fprintf(stderr, "could not acquire lock for archive: %s\n", strerror(ret));
2529 		return ret;
2530 	}
2531 	node = get_node_for_path(root, path);
2532 	if (! node) {
2533 		pthread_mutex_unlock(&lock);
2534 		return -ENOENT;
2535 	}
2536 	if (fi->flags & O_WRONLY || fi->flags & O_RDWR) {
2537 		if (! archiveWriteable) {
2538 			pthread_mutex_unlock(&lock);
2539 			return -EROFS;
2540 		}
2541 	}
2542 	/* no need to recurse into links since function doesn't do anything */
2543 	/* no need to save a handle here since archives are stream based */
2544 	fi->fh = 0;
2545 	if (options.formatraw)
2546 		_ar_open_raw();
2547 	pthread_mutex_unlock(&lock);
2548 	return 0;
2549 }
2550 
2551 static int
ar_release(const char * path,struct fuse_file_info * fi)2552 ar_release(const char *path, struct fuse_file_info *fi)
2553 {
2554 	(void)fi;
2555 	(void)path;
2556 	log("ar_release called, path '%s'", path);
2557 	return 0;
2558 }
2559 
2560 static int
ar_readdir(const char * path,void * buf,fuse_fill_dir_t filler,off_t offset,struct fuse_file_info * fi)2561 ar_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
2562 	off_t offset, struct fuse_file_info *fi)
2563 {
2564 	NODE *node;
2565 	(void) offset;
2566 	(void) fi;
2567 
2568 	//log("ar_readdir called, path: '%s' offset: %d", path, offset);
2569 	int ret = -EIO;
2570 	if (pthread_mutex_lock(&lock)) {
2571 		log("could not acquire lock for archive: %s\n", strerror(ret));
2572 		return ret;
2573 	}
2574 	node = get_node_for_path(root, path);
2575 	if (! node) {
2576 		log("path '%s' not found", path);
2577 		pthread_mutex_unlock(&lock);
2578 		return -ENOENT;
2579 	}
2580 
2581 	filler(buf, ".", NULL, 0);
2582 	filler(buf, "..", NULL, 0);
2583 
2584 	node = node->child;
2585 
2586 	while (node) {
2587 		const struct stat *st;
2588 		struct stat st_copy;
2589 		if (archive_entry_hardlink(node->entry)) {
2590 			/* file is a hardlink, stat'ing it somehow does not
2591 			 * work; stat the original instead */
2592 			NODE *orig = get_node_for_path(root, archive_entry_hardlink(node->entry));
2593 			if (! orig) {
2594 				return -ENOENT;
2595 			}
2596 			st = archive_entry_stat(orig->entry);
2597 		} else {
2598 			st = archive_entry_stat(node->entry);
2599 		}
2600 		/* Make a copy so we can set blocks/blksize. These are not
2601 		 * set by libarchive. Issue 191 */
2602 		memcpy(&st_copy, st, sizeof(st_copy));
2603 		st_copy.st_blocks  = (st_copy.st_size + 511) / 512;
2604 		st_copy.st_blksize = 4096;
2605 
2606 		if (filler(buf, node->basename, &st_copy, 0)) {
2607 			pthread_mutex_unlock(&lock);
2608 			return -ENOMEM;
2609 		}
2610 
2611 		node = node->hh.next;
2612 	}
2613 
2614 	pthread_mutex_unlock(&lock);
2615 	return 0;
2616 }
2617 
2618 
2619 static int
ar_create(const char * path,mode_t mode,struct fuse_file_info * fi)2620 ar_create(const char *path, mode_t mode, struct fuse_file_info *fi)
2621 {
2622 	NODE *node;
2623 	char *location;
2624 	int tmp;
2625 
2626 	/* the implementation of this function is mostly copy-paste from
2627 	   mknod, with the exception that the temp file is created with
2628 	   creat() instead of mknod() */
2629 	log("ar_create called, path '%s'", path);
2630 	if (! archiveWriteable || options.readonly) {
2631 		return -EROFS;
2632 	}
2633 	pthread_mutex_lock(&lock);
2634 	/* check for existing node */
2635 	node = get_node_for_path(root, path);
2636 	if (node) {
2637 		pthread_mutex_unlock(&lock);
2638 		return -EEXIST;
2639 	}
2640 	/* create name for temp file */
2641 	if ((tmp = get_temp_file_name(path, &location)) < 0) {
2642 		pthread_mutex_unlock(&lock);
2643 		return tmp;
2644 	}
2645 	/* create temp file */
2646 	if (creat(location, mode) == -1) {
2647 		log("Could not create temporary file %s: %s",
2648 			location, strerror(errno));
2649 		free(location);
2650 		pthread_mutex_unlock(&lock);
2651 		return 0 - errno;
2652 	}
2653 	/* build node */
2654 	if ((node = init_node()) == NULL) {
2655 		pthread_mutex_unlock(&lock);
2656 		return -ENOMEM;
2657 	}
2658 	node->location = location;
2659 	node->modified = 1;
2660 	node->name = strdup(path);
2661 	node->basename = strrchr(node->name, '/') + 1;
2662 
2663 	/* build entry */
2664 	correct_name_in_entry(node);
2665 	if ((tmp = update_entry_stat(node)) < 0) {
2666 		log("mknod: error stat'ing file %s: %s", node->location,
2667 			strerror(0 - tmp));
2668 		unlink(location);
2669 		free(location);
2670 		free_node(node);
2671 		pthread_mutex_unlock(&lock);
2672 		return tmp;
2673 	}
2674 	/* add node to tree */
2675 	if (insert_by_path(root, node) != 0) {
2676 		log("ERROR: could not insert %s into tree",
2677 			node->name);
2678 		unlink(location);
2679 		free(location);
2680 		free_node(node);
2681 		pthread_mutex_unlock(&lock);
2682 		return -ENOENT;
2683 	}
2684 	/* clean up */
2685 	archiveModified = 1;
2686 	pthread_mutex_unlock(&lock);
2687 	return 0;
2688 }
2689 
2690 static struct fuse_operations ar_oper = {
2691 	.getattr	= ar_getattr,
2692 	.readlink	= ar_readlink,
2693 	.mknod		= ar_mknod,
2694 	.mkdir		= ar_mkdir,
2695 	.symlink	= ar_symlink,
2696 	.unlink		= ar_unlink,
2697 	.rmdir		= ar_rmdir,
2698 	.rename		= ar_rename,
2699 	.link		= ar_link,
2700 	.chmod		= ar_chmod,
2701 	.chown		= ar_chown,
2702 	.truncate	= ar_truncate,
2703 	.utime		= ar_utime,
2704 	.open		= ar_open,
2705 	.read		= ar_read,
2706 	.write		= ar_write,
2707 	.statfs		= ar_statfs,
2708 	//.flush	  = ar_flush,  // int(*flush)(const char *, struct fuse_file_info *)
2709 	.release	= ar_release,
2710 	.fsync		= ar_fsync,
2711 /*
2712 #ifdef HAVE_SETXATTR
2713 	.setxattr	= ar_setxattr,
2714 	.getxattr	= ar_getxattr,
2715 	.listxattr	= ar_listxattr,
2716 	.removexattr	= ar_removexattr,
2717 #endif
2718 */
2719 	//.opendir	  = ar_opendir,    // int(*opendir)(const char *, struct fuse_file_info *)
2720 	.readdir	= ar_readdir,
2721 	//.releasedir	  = ar_releasedir, // int(*releasedir)(const char *, struct fuse_file_info *)
2722 	//.fsyncdir	  = ar_fsyncdir,   // int(*fsyncdir)(const char *, int, struct fuse_file_info *)
2723 	//.init		  = ar_init,	   // void *(*init)(struct fuse_conn_info *conn)
2724 	//.destroy	  = ar_destroy,    // void(*destroy)(void *)
2725 	//.access	  = ar_access,	   // int(*access)(const char *, int)
2726 	.create		= ar_create,
2727 	//.ftruncate	  = ar_ftruncate,  // int(*ftruncate)(const char *, off_t, struct fuse_file_info *)
2728 	//.fgetattr	  = ar_fgetattr,   // int(*fgetattr)(const char *, struct stat *, struct fuse_file_info *)
2729 	//.lock		  = ar_lock,	   // int(*lock)(const char *, struct fuse_file_info *, int cmd, struct flock *)
2730 	//.utimens	  = ar_utimens,    // int(*utimens)(const char *, const struct timespec tv[2])
2731 	//.bmap		  = ar_bmap,	   // int(*bmap)(const char *, size_t blocksize, uint64_t *idx)
2732 };
2733 
2734 void
showUsage()2735 showUsage()
2736 {
2737 	fprintf(stderr, "Usage: archivemount <fuse-options> <archive> <mountpoint>\n");
2738 	fprintf(stderr, "Usage:	      (-v|--version)\n");
2739 }
2740 
2741 int
main(int argc,char ** argv)2742 main(int argc, char **argv)
2743 {
2744 	struct stat st;
2745 	int oldpwd;
2746 	struct fuse_args args = FUSE_ARGS_INIT(argc, argv);
2747 
2748 	/* parse cmdline args */
2749 	memset(&options, 0, sizeof(struct options));
2750 	if (fuse_opt_parse(&args, &options, ar_opts, ar_opt_proc) == -1)
2751 		return -1;
2752 	if (archiveFile==NULL) {
2753 		fprintf(stderr, "missing archive file\n");
2754 		fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
2755 		exit(1);
2756 	}
2757 	if (mtpt==NULL) {
2758 		fprintf(stderr, "missing mount point\n");
2759 		fprintf(stderr, "see `%s -h' for usage\n", argv[0]);
2760 		exit(1);
2761 	}
2762 
2763 	/* check if mtpt is ok and writeable */
2764 	if (stat(mtpt, &st) != 0) {
2765 		perror("Error stat'ing mountpoint");
2766 		exit(EXIT_FAILURE);
2767 	}
2768 	if (! S_ISDIR(st.st_mode)) {
2769 		fprintf(stderr, "Problem with mountpoint: %s\n",
2770 			strerror(ENOTDIR));
2771 		exit(EXIT_FAILURE);
2772 	}
2773 
2774 	if (!options.readonly) {
2775 		/* check if archive is writeable */
2776 		archiveFd = open(archiveFile, O_RDWR);
2777 		if (archiveFd != -1) {
2778 			archiveWriteable = 1;
2779 			close(archiveFd);
2780 		}
2781 	}
2782 	/* We want the temporary fuser version of the archive to be writable,*/
2783 	/* despite never actually writing the changes to disk.*/
2784 	if (options.nosave) { archiveWriteable = 1; }
2785 
2786 	/* open archive and read meta data */
2787 	archiveFd = open(archiveFile, O_RDONLY);
2788 	if (archiveFd == -1) {
2789 		perror("opening archive failed");
2790 		return EXIT_FAILURE;
2791 	}
2792 	if (build_tree(mtpt) != 0) {
2793 		exit(EXIT_FAILURE);
2794 	}
2795 
2796 	if (options.formatraw) {
2797 		/* create rawcache */
2798 		if ((rawcache=init_rawcache()) == NULL)
2799 			return -ENOMEM;
2800 		fprintf(stderr,"Calculating uncompressed file size. Please wait.\n");
2801 		rawcache->st.st_size=_ar_getsizeraw("/data");
2802 		//log("cache st_size = %ld",rawcache->st.st_size);
2803 	}
2804 
2805 	/* save directory this was started from */
2806 	oldpwd = open(".", 0);
2807 
2808 	/* Initialize the node tree lock */
2809 	pthread_mutex_init(&lock, NULL);
2810 
2811 	/* always use fuse in single-threaded mode
2812 	 * multithreading is broken with libarchive :-(
2813 	 */
2814 	fuse_opt_add_arg(&args, "-s");
2815 
2816 #if FUSE_VERSION >= 26
2817 	{
2818 		struct fuse *fuse;
2819 		struct fuse_chan *ch;
2820 		char *mountpoint;
2821 		int multithreaded;
2822 		int foreground;
2823 #ifdef FUSE_NUMA
2824 		int numa;
2825 #endif
2826 		int res;
2827 
2828 		res = fuse_parse_cmdline(&args, &mountpoint, &multithreaded,
2829 #ifdef FUSE_NUMA
2830 			&foreground, &numa);
2831 #else
2832 		&foreground);
2833 #endif
2834 		if (res == -1)
2835 			exit(1);
2836 
2837 		ch = fuse_mount(mountpoint, &args);
2838 		if (!ch)
2839 			exit(1);
2840 
2841 		res = fcntl(fuse_chan_fd(ch), F_SETFD, FD_CLOEXEC);
2842 		if (res == -1)
2843 			perror("WARNING: failed to set FD_CLOEXEC on fuse device");
2844 
2845 		fuse = fuse_new(ch, &args, &ar_oper,
2846 			sizeof(struct fuse_operations), NULL);
2847 		if (fuse == NULL) {
2848 			fuse_unmount(mountpoint, ch);
2849 			exit(1);
2850 		}
2851 
2852 		/* now do the real mount */
2853 		res = fuse_daemonize(foreground);
2854 		if (res != -1)
2855 			res = fuse_set_signal_handlers(fuse_get_session(fuse));
2856 
2857 		if (res == -1) {
2858 			fuse_unmount(mountpoint, ch);
2859 			fuse_destroy(fuse);
2860 			exit(1);
2861 		}
2862 
2863 		if (multithreaded)
2864 			res = fuse_loop_mt(fuse);
2865 		else
2866 			res = fuse_loop(fuse);
2867 
2868 		if (res == -1)
2869 			res = 1;
2870 		else
2871 			res = 0;
2872 
2873 		fuse_remove_signal_handlers(fuse_get_session(fuse));
2874 		fuse_unmount(mountpoint, ch);
2875 		fuse_destroy(fuse);
2876 		free(mountpoint);
2877 	}
2878 #else
2879 	{
2880 		/* now do the real mount */
2881 		int fuse_ret;
2882 		fuse_ret = fuse_main(args.argc, args.argv, &ar_oper, NULL);
2883 	}
2884 #endif
2885 
2886 	/* go back to saved dir */
2887 	{
2888 		int fchdir_ret;
2889 		fchdir_ret = fchdir(oldpwd);
2890 		if (fchdir_ret != 0) {
2891 			log("fchdir() to old path failed\n");
2892 		}
2893 	}
2894 
2895 	/* save changes if modified */
2896 	if (archiveWriteable && !options.readonly && archiveModified && !options.nosave) {
2897 		if (save(archiveFile) != 0) {
2898 			log("Saving new archive failed\n");
2899 		}
2900 	}
2901 
2902 	/* clean up */
2903 	close(archiveFd);
2904 
2905 	return EXIT_SUCCESS;
2906 }
2907 
2908 /*
2909 vim:ts=8:softtabstop=8:sw=8:noexpandtab
2910 */
2911 
2912