static void cleanup() { if (!initialized) return; if (prev_win) unuse_pack(&prev_win); if (pack_data) { struct pack_window *win, *prev; for (prev = NULL, win = pack_data->windows; win; prev = win, win = win->next) { if (win != pack_win) continue; if (prev) prev->next = win->next; else pack_data->windows = win->next; break; } } if (pack_win) { free(pack_win->base); free(pack_win); } end_packfile(); if (!require_explicit_termination) dump_branches(); unkeep_all_packs(); initialized = 0; pack_report(); }
int main(int argc, char *argv[]) { struct unionfs_addbranch_args addargs; struct unionfs_rdwrbranch_args rdwrargs; unsigned long remountdata[3]; fd_set branchlist; struct stat st; int fd = -1; int ret, i; char *path, resolv_path[PATH_MAX], resolv_bp[PATH_MAX]; char *options = NULL, *actual_path = NULL; int action; char *branchpath; int branchnum; int unionpos = 1; int modepos = 2; progname = argv[0]; /* check that minimum number of args were specified */ if (argc < 3) usage(); if (argv[1][0] == '-' && argv[1][1] == '-') { modepos = 1; unionpos = 2; } else { modepos = 2; unionpos = 1; } if (realpath(argv[unionpos], resolv_path) == NULL) { perror("realpath()"); exit(EXIT_FAILURE); } path = resolv_path; if (strcmp(path, "/") && (path[strlen(path) - 1] == '/')) { path[strlen(path) - 1] = '\0'; } if (!strcmp(argv[modepos], "--add")) { action = ADD; } else if (!strcmp(argv[modepos], "--remove")) { action = REMOVE; } else if (!strcmp(argv[modepos], "--mode")) { action = MODE; } else if (!strcmp(argv[modepos], "--list")) { action = LIST; } else if (!strcmp(argv[modepos], "--query")) { action = QUERY; } else { usage(); } if (stat(path, &st) == -1) { fprintf(stderr, "stat(%s): %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } if (find_union(path, &options, &actual_path, 1) < 0) { fprintf(stderr, "%s is not a valid union.\n", path); exit(EXIT_FAILURE); } branches = parse_options(options); if (!branches) { fprintf(stderr, "Could not parse options from /proc/mounts!\n"); exit(EXIT_FAILURE); } /* open file on which ioctl will operate (that is actually any file in the union) */ if (action != REMOVE) { fd = open(path, O_RDONLY); if (fd < 0) { fprintf(stderr, "open(%s): %s\n", path, strerror(errno)); exit(EXIT_FAILURE); } } /* Parse the action's options into something usable, and do it. */ switch (action) { case ADD: if (argc < 4) usage(); /* Default values if the user leaves them unspecified. */ branchnum = 0; addargs.ab_perms = MAY_READ | MAY_WRITE; branchpath = NULL; for (i = 3; i < argc; i++) { if (argv[i][0] == '-' && argv[i][1] == '-') { if (!strcmp(argv[i], "--before")) { i++; if (i == argc) { fprintf(stderr, "%s requires an argument!\n", argv[i - 1]); usage(); } branchnum = get_branch(argv[i]); if (branchnum == -1) { fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", argv[i]); dump_branches("\t"); } } else if (!strcmp(argv[i], "--after")) { i++; if (i == argc) { fprintf(stderr, "%s requires an argument!\n", argv[i - 1]); usage(); } branchnum = get_branch(argv[i]); if (branchnum == -1) { fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", argv[i]); dump_branches("\t"); } branchnum++; } else if (!strcmp(argv[i], "--mode")) { i++; if (i == argc) { fprintf(stderr, "%s requires an argument!\n", argv[i - 1]); usage(); } if (!strcmp(argv[i], "ro")) { addargs.ab_perms = MAY_READ; } else if (!strcmp(argv[i], "rw")) { addargs.ab_perms = MAY_READ | MAY_WRITE; } else { fprintf(stderr, "Valid modes are ro and rw you specified: \"%s\"\n", argv[i]); usage(); } } else { fprintf(stderr, "Unknown option: %s\n", argv[i]); usage(); } } else { /* The options must go before the path */ if ((i + 1) != argc) { fprintf(stderr, "The path of the branch to add must go after all options.\n"); usage(); } if (branchnum != -1) { fprintf(stderr, "%s already exists in the Union\n", argv[i]); usage(); } branchpath = argv[i]; } } if (!branchpath) { fprintf(stderr, "You must specify the path to add into the union!\n"); usage(); } if (realpath(branchpath, resolv_bp) == NULL) { perror("realpath()"); exit(EXIT_FAILURE); } addargs.ab_branch = branchnum; addargs.ab_path = resolv_bp; errno = 0; ret = ioctl(fd, UNIONFS_IOCTL_ADDBRANCH, &addargs); if (ret < 0) { switch (errno) { case E2BIG: fprintf(stderr, "Unionfs supports only %d branches.\n", FD_SETSIZE); break; } fprintf(stderr, "Failed to add %s into %s: %s", branchpath, path, strerror(errno)); exit(EXIT_FAILURE); } break; case MODE: if (argc != 5) { usage(); } if (!strcmp(argv[4], "ro")) { rdwrargs.rwb_perms = MAY_READ; branchnum = 3; } else if (!strcmp(argv[4], "rw")) { rdwrargs.rwb_perms = MAY_READ | MAY_WRITE; branchnum = 3; } else if (!strcmp(argv[3], "ro")) { rdwrargs.rwb_perms = MAY_READ; branchnum = 4; } else if (!strcmp(argv[3], "rw")) { rdwrargs.rwb_perms = MAY_READ | MAY_WRITE; branchnum = 4; } else { usage(); } if (realpath(argv[branchnum], resolv_bp) == NULL) { perror("realpath()"); exit(EXIT_FAILURE); } branchpath = resolv_bp; /* Set a branches writeable status. */ rdwrargs.rwb_branch = get_branch(branchpath); if (rdwrargs.rwb_branch == -1) { fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", branchpath); dump_branches("\t"); exit(EXIT_FAILURE); } ret = ioctl(fd, UNIONFS_IOCTL_RDWRBRANCH, &rdwrargs); if (ret < 0) { fprintf(stderr, "Failed to set permissions for %s in %s: %s", branchpath, path, strerror(errno)); exit(EXIT_FAILURE); } goto out; break; case REMOVE: if (argc != 4) usage(); if (realpath(argv[3], resolv_bp) == NULL) { perror("realpath()"); exit(EXIT_FAILURE); } branchpath = resolv_bp; branchnum = get_branch(branchpath); if (branchnum == -1) { fprintf(stderr, "%s is not a valid branch.\nThe current branch configuration is:\n", branchpath); dump_branches("\t"); exit(EXIT_FAILURE); } errno = 0; remountdata[0] = UNIONFS_REMOUNT_MAGIC; remountdata[1] = UNIONFS_IOCTL_DELBRANCH; remountdata[2] = branchnum; ret = mount("unionfs", actual_path, "unionfs", MS_REMOUNT | MS_MGC_VAL, remountdata); if (ret < 0) { fprintf(stderr, "Failed to remove %s from %s: %s", branchpath, path, strerror(errno)); exit(EXIT_FAILURE); } break; case LIST: dump_branches("\t"); break; case QUERY: if (argc != 3) { usage(); } if ((fd = open(argv[unionpos], O_RDONLY)) < 0) { fprintf(stderr, "Unable to open file %s : %s", argv[unionpos], strerror(errno)); exit(EXIT_FAILURE); } ret = ioctl(fd, UNIONFS_IOCTL_QUERYFILE, &branchlist); if (ret < 0) { fprintf(stderr, "Unable to retrieve list of branches for file %s : %s\n", argv[unionpos], strerror(errno)); exit(EXIT_FAILURE); } for (i = 0; i <= ret; i++) { char r, w; r = (branchperms[i] & MAY_READ) ? 'r' : '-'; w = (branchperms[i] & MAY_WRITE) ? 'w' : '-'; if (FD_ISSET(i, &branchlist)) printf("%s\t%s (%c%c)\n", argv[unionpos], branches[i], r, w); } break; } out: if (fd != -1) close(fd); exit(EXIT_SUCCESS); }