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, ®match, 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