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();
}
Beispiel #2
0
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);
}