void au_plink_maint(char *path) { int err; if (path) { if (dp) { errno = EINVAL; AuFin("dp is not NULL"); } dp = opendir(path); if (!dp) AuFin("%s", path); err = ioctl(dirfd(dp), AUFS_CTL_PLINK_MAINT); #ifndef DEBUG if (err) AuFin("AUFS_CTL_PLINK_MAINT"); #endif } else { err = closedir(dp); if (err) AuFin("closedir"); dp = NULL; } }
/* * Ideally the mounted aufs should be unmounted even if its mntpnt has very long * pathname. In other words, if umount.aufs cannot handle a long pathname, then * mount.aufs should reject in the beginning. * getmntent(3) in glibc reads 4096 bytes for a single mnt entry. I agree it is * large enough. And mount(8) rejects too long pathname. It is OK too. As long * as 4095 (4096 - 1) bytes pathname succeeds mounting, then it should be * unmounted flawlessly. * Testing on Debian v7 (wheezy) succeeded mounting 4095 bytes pathname, but * failed unmounting. I don't like this unbalancing. So replace getmntent() by * getmntent_r() with larger buffer. Obviously this is less important since such * long pathname is very rare. */ int au_proc_getmntent(char *mntpnt, struct mntent *rent) { int found; struct mntent *p, e; FILE *fp; char a[4096 + 1024], path[PATH_MAX], *decoded; decoded = au_decode_mntpnt(mntpnt, path, sizeof(path)); if (!decoded) AuFin("au_decode_mntpnt"); fp = setmntent(ProcMounts, "r"); if (!fp) AuFin(ProcMounts); /* find the last one */ memset(rent, 0, sizeof(*rent)); found = 0; while ((p = getmntent_r(fp, &e, a, sizeof(a)))) if (!strcmp(p->mnt_dir, decoded)) { Dpri("%s, %s, %s, %s, %d, %d\n", p->mnt_fsname, p->mnt_dir, p->mnt_type, p->mnt_opts, p->mnt_freq, p->mnt_passno); copy_ent(rent, p); found = 1; } endmntent(fp); if (!found) { errno = EINVAL; AuFin("%s, %s", mntpnt, decoded); } return 0; }
int au_plink(char cwd[], int cmd, int begin_maint, int end_maint) { int err, nbr; struct mntent ent; char **br; if (begin_maint) au_plink_maint(cwd); err = au_proc_getmntent(cwd, &ent); if (err) AuFin("no such mount point"); if (hasmntopt(&ent, "noplink")) goto out; /* success */ #ifdef DEBUG //char a[] = "a,b,br:/tmp/br0=rw:/br1=ro"; char a[] = "a,b,si=1,c"; ent.mnt_opts = a; #endif err = au_br(&br, &nbr, &ent); //printf("nbr %d\n", nbr); if (err) AuFin(NULL); err = do_plink(cwd, cmd, nbr, br); if (err) AuFin(NULL); out: if (end_maint) au_plink_maint(NULL); return err; }
int au_proc_getmntent(char *mntpnt, struct mntent *rent) { int found; struct mntent *p; FILE *fp; fp = setmntent(ProcMounts, "r"); if (!fp) AuFin(ProcMounts); /* find the last one */ memset(rent, 0, sizeof(*rent)); found = 0; while ((p = getmntent(fp))) if (!strcmp(p->mnt_dir, mntpnt)) { Dpri("%s, %s, %s, %s, %d, %d\n", p->mnt_fsname, p->mnt_dir, p->mnt_type, p->mnt_opts, p->mnt_freq, p->mnt_passno); copy_ent(rent, p); found = 1; } endmntent(fp); if (!found) { errno = EINVAL; AuFin("%s", mntpnt); } return 0; }
static void opt(int argc, char *argv[]) { int opt, i, err, need_ck, done; char *dir, *p; done = 0; dir = getenv("AUFHSM_LIST_DIR"); for (i = 1; !done && i < argc; i++) { opt = getopt_long(argc, argv, short_opts, opts, NULL); switch (opt) { case -1: done = 1; break; case 'd': dir = optarg; break; case 'v': au_opt_set(fhsmd.optflags, VERBOSE); break; case 'D': au_opt_set(fhsmd.optflags, NODAEMON); break; case 'V': printf("%s version %s\n", program_invocation_short_name, AuVersion); exit(0); case 'h': case '?': usage(); exit(0); default: //usage(); exit(EINVAL); } } if (dir) { p = realpath(dir, NULL); if (!p || !*p) AuFin("%s", dir); dir = strdup(p); if (!dir) AuFin("%s", p); need_ck = 1; } else { dir = au_list_dir_def(); if (!dir) AuFin("au_list_dir_def"); need_ck = 0; } err = au_list_dir_set(dir, need_ck); if (err) AuFin("au_list_dir_set"); /* unfree dir */ }
int main(int argc, char *argv[]) { int err; char *mntpnt; opt(argc, argv); if (optind == argc) { //usage(); errno = EINVAL; AuFin(NULL); } mntpnt = realpath(argv[optind], NULL); if (!mntpnt) AuFin("%s", mntpnt); err = chdir(mntpnt); if (err) AuFin("%s", mntpnt); if (!au_opt_test(fhsmd.optflags, NODAEMON)) { au_do_syslog = 1; openlog(program_invocation_short_name, AuFhsmd_OPTION, AuFhsmd_FACILITY); err = daemon(/*nochdir*/0, /*noclose*/0); if (err) AuFin("daemon"); } shm_names(mntpnt, fhsmd.name, sizeof(*fhsmd.name)); free(mntpnt); comm_fd(fhsmd.name[AuName_LCOPY].a, &fhsmd.fd[AuFd_FHSM], &fhsmd.fd[AuFd_MSG]); INIT_LIST_HEAD(&fhsmd.in_ope); err = au_fhsmd_load(); if (err) AuLogFin("au_fhsmd_load"); err = au_epsigfd(); if (err) AuLogFin("create_epsig"); err = au_ep_add(fhsmd.fd[AuFd_MSG], EPOLLIN | EPOLLPRI); if (err) AuLogFin("au_ep_add"); err = au_ep_add(fhsmd.fd[AuFd_FHSM], EPOLLIN | EPOLLPRI); if (err) AuLogFin("au_ep_add"); /* main loop */ err = au_fhsmd_loop(); AuDbgFhsmLog("exit %d", err); return err; }
/* todo: there are some cases which options are not changed */ static void update_mtab(FILE *fp, char *mntpnt, int do_remount, int do_verbose) { int err; long pos; FILE *ofp; struct mntent ent, *p; /* prohibit updating mount options for this mntpnt */ au_plink_maint(mntpnt); err = au_proc_getmntent(mntpnt, &ent); if (err) AuFin("no such mount point"); ofp = setmntent(MTab, "r"); if (!ofp) AuFin(MTab); if (do_remount) { /* find the last one */ pos = -1; while ((p = getmntent(ofp))) { if (!strcmp(p->mnt_dir, mntpnt)) pos = ftell(ofp); } rewind(ofp); if (pos > 0) { while ((p = getmntent(ofp))) { if (ftell(ofp) == pos) { /* replace the line */ p = &ent; pos = -1; } err = addmntent(fp, p); if (err) AuFin("addmntent"); } if (pos > 0) AuFin("internal error"); } else append_mtab(fp, ofp, &ent); } else append_mtab(fp, ofp, &ent); endmntent(ofp); /* ignore */ au_plink_maint(NULL); if (do_verbose) au_print_ent(&ent); }
static void append_mtab(FILE *fp, FILE *ofp, struct mntent *ent) { int err; struct mntent *p; while ((p = getmntent(ofp))) { err = addmntent(fp, p); if (err) AuFin("addmntent"); } err = addmntent(fp, ent); if (err) AuFin("addmntent"); }
static int build_array(char *plink_dir) { int err; DIR *dp; struct dirent *de; char *p; ino_t ino; err = access(plink_dir, F_OK); if (err) return 0; err = 0; dp = opendir(plink_dir); if (!dp) AuFin("%s", plink_dir); while ((de = readdir(dp))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; #if 0 if (de->d_type == DT_DIR) { errno = EISDIR; AuFin(de->d_name); } #endif err = na_append(plink_dir, de->d_name); if (err) break; p = strchr(de->d_name, '.'); if (!p) { errno = EINVAL; AuFin("internal error, %s", de->d_name); } *p = 0; errno = 0; ino = strtoull(de->d_name, NULL, 0); if (ino == /*ULLONG_MAX*/-1 && errno == ERANGE) AuFin("internal error, %s", de->d_name); err = ia_append(ino); if (err) break; } closedir(dp); return err; }
static int ftw_cpup(const char *fname, const struct stat *st, int flags, struct FTW *ftw) { int err; if (!strcmp(fname + ftw->base, AUFS_WH_PLINKDIR)) return FTW_SKIP_SUBTREE; if (flags == FTW_D || flags == FTW_DNR) return FTW_CONTINUE; /* * do nothing but update something harmless in order to make it copyup */ if (ia_test(st->st_ino)) { Dpri("%s\n", fname); if (!S_ISLNK(st->st_mode)) err = chown(fname, -1, -1); else err = lchown(fname, -1, -1); if (err) AuFin("%s", fname); } return FTW_CONTINUE; }
static void do_mount(char *dev, char *mntpnt, int argc, char *argv[], unsigned char flags[]) { int i; const int ac = argc + 6; char *av[ac], **a; /* todo: eliminate the duplicated options */ a = av; *a++ = "mount"; *a++ = "-i"; if (!flags[Bind] || !flags[Update]) *a++ = "-n"; if (flags[Bind] && flags[Verbose]) *a++ = "-v"; *a++ = "-t"; *a++ = AUFS_NAME; for (i = 3; i < argc; i++) if (strcmp(argv[i], "-v") && strcmp(argv[i], "-n")) *a++ = argv[i]; *a++ = dev; *a++ = mntpnt; *a++ = NULL; #ifdef DEBUG for (i = 0; av[i] && i < ac; i++) puts(av[i]); exit(0); #endif execvp("mount", av); AuFin("mount"); }
static void copy_ent(struct mntent *dst, struct mntent *src) { free(dst->mnt_opts); free(dst->mnt_type); free(dst->mnt_dir); free(dst->mnt_fsname); dst->mnt_dir = NULL; dst->mnt_type = NULL; dst->mnt_opts = NULL; dst->mnt_fsname = strdup(src->mnt_fsname); if (dst->mnt_fsname) dst->mnt_dir = strdup(src->mnt_dir); if (dst->mnt_dir) dst->mnt_type = strdup(src->mnt_type); if (dst->mnt_type) dst->mnt_opts = strdup(src->mnt_opts); if (dst->mnt_opts) { dst->mnt_freq = src->mnt_freq; dst->mnt_passno = src->mnt_passno; } else AuFin("strdup"); }
static void test_opts(char opts[], unsigned char flags[]) { int c; char *p, *o, *val, *pat[] = { [Remount] = "remount", [Bind] = "bind", NULL }; o = strdup(opts); if (!o) AuFin("stdup"); p = o; while (*p) { c = getsubopt(&p, pat, &val); switch (c) { case Remount: flags[Remount] = 1; break; case Bind: flags[Bind] = 1; break; } } free(o); }
static void unlock_mtab(void) { int err; err = rename(MTab "~", MTab); if (err) AuFin(MTab); }
void au_clean_plink(void) { int err; err = ioctl(dirfd(dp), AUFS_CTL_PLINK_CLEAN); #ifndef DEBUG if (err) AuFin("AUFS_CTL_PLINK_CLEAN"); #endif }
static void lock_mtab(char *pid_file) { int err, i; for (i = 0; i < 5; i++) { err = link(pid_file, MTab "~"); if (!err) break; sleep(1); } if (err) AuFin(MTab "~"); }
static int test_flush(char opts[]) { int err, i; regex_t preg; char *p, *o; const char *pat = "^((add|ins|append|prepend|del)[:=]" "|(mod|imod)[:=][^,]*=ro" "|(noplink|ro)$)"; o = strdup(opts); if (!o) AuFin("stdup"); p = o; i = 1; while ((p = strchr(p, ','))) { i++; *p++ = 0; } /* todo: try getsubopt(3)? */ err = regcomp(&preg, pat, REG_EXTENDED | REG_NOSUB); if (err) AuFin("regcomp"); p = o; while (i--) { if (!regexec(&preg, p, 0, NULL, 0)) { err = 1; break; } else p += strlen(p) + 1; } regfree(&preg); free(o); return err; }
static int ia_append(ino_t ino) { int sz; char *p; const int cur = ia.p - ia.o; sz = na.bytes + sizeof(ino_t); p = realloc(ia.o, sz); if (!p) AuFin("realloc"); ia.o = p; ia.bytes = sz; ia.p = p + cur; *ia.cur++ = ino; ia.nino++; return 0; }
static int na_append(char *plink_dir, char *name) { int l, sz; char *p; const int cur = na.cur - na.o; l = strlen(plink_dir) + strlen(name) + 2; sz = na.bytes + l; p = realloc(na.o, sz); if (!p) AuFin("realloc"); na.o = p; na.bytes = sz; na.cur = p + cur; na.cur += sprintf(na.cur, "%s/%s", plink_dir, name) + 1; na.nname++; return 0; }
int au_update_mtab(char *mntpnt, int do_remount, int do_verbose) { int err, fd, status, e2; pid_t pid; ino_t ino; struct stat st; struct statfs stfs; struct flock flock = { .l_type = F_WRLCK, .l_whence = SEEK_SET, .l_start = 0, .l_len = 0 }; char pid_file[sizeof(MTab "~.") + 20]; FILE *fp; err = statfs(MTab, &stfs); if (stfs.f_type == PROC_SUPER_MAGIC) return 0; snprintf(pid_file, sizeof(pid_file), MTab "~.%d", getpid()); fd = open(pid_file, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) AuFin("%s", pid_file); err = fcntl(fd, F_SETLK, &flock); if (err) AuFin("%s", pid_file); fp = fdopen(fd, "r+"); if (!fp) AuFin("%s", pid_file); pid = fork(); if (!pid) { lock_mtab(pid_file); update_mtab(fp, mntpnt, do_remount, do_verbose); unlock_mtab(); return 0; } else if (pid < 0) AuFin("fork"); err = fstat(fd, &st); if (err) perror(pid_file); ino = st.st_ino; err = waitpid(pid, &status, 0); if (err < 0) { perror(pid_file); goto out; } err = !WIFEXITED(status); if (!err) err = WEXITSTATUS(status); e2 = unlink(pid_file); if (e2 && errno != ENOENT) perror(pid_file); e2 = stat(MTab "~", &st); if (!e2) { if (st.st_ino == ino) { /* * The inode number is same, * it means it is we who made the file. * If someone else removed our file between stat(2) and * unlink(2), it is a breakage of the rule. */ e2 = unlink(MTab "~"); if (e2) perror(MTab); } } else if (errno != ENOENT) perror(MTab "~"); fclose(fp); out: return err; }
static int do_plink(char *cwd, int cmd, int nbr, char *br[]) { int err, i, l; struct rlimit rlim; __nftw_func_t func; char *p; err = 0; switch (cmd) { case AuPlink_FLUSH: /*FALLTHROUGH*/ case AuPlink_CPUP: func = ftw_cpup; break; case AuPlink_LIST: func = ftw_list; break; default: errno = EINVAL; AuFin(NULL); func = NULL; /* never reach here */ } for (i = 0; i < nbr; i++) { //puts(br[i]); p = strchr(br[i], '='); if (strcmp(p + 1, AUFS_BRPERM_RW) && strcmp(p + 1, AUFS_BRPERM_RWNLWH)) continue; *p = 0; l = strlen(br[i]); p = malloc(l + sizeof(AUFS_WH_PLINKDIR) + 2); if (!p) AuFin("malloc"); sprintf(p, "%s/%s", br[i], AUFS_WH_PLINKDIR); //puts(p); err = build_array(p); if (err) AuFin("build_array"); free(p); } if (!ia.nino) goto out; if (cmd == AuPlink_LIST) { ia.p = ia.o; for (i = 0; i < ia.nino; i++) printf("%llu ", (unsigned long long)*ia.cur++); putchar('\n'); } err = getrlimit(RLIMIT_NOFILE, &rlim); if (err) AuFin("getrlimit"); nftw(cwd, func, rlim.rlim_cur - 10, FTW_PHYS | FTW_MOUNT | FTW_ACTIONRETVAL); /* ignore */ if (cmd == AuPlink_FLUSH) { au_clean_plink(); na.cur = na.o; for (i = 0; i < na.nname; i++) { Dpri("%s\n", na.cur); err = unlink(na.cur); if (err) AuFin("%s", na.cur); na.cur += strlen(na.cur) + 1; } } out: free(ia.o); free(na.o); return err; }
int main(int argc, char *argv[]) { int err, c, status; pid_t pid; unsigned char flags[LastOpt]; struct mntent ent; char *dev, *mntpnt, *opts, *cwd; DIR *cur; if (argc < 3) { errno = EINVAL; AuFin(NULL); } memset(flags, 0, sizeof(flags)); flags[Update] = 1; opts = NULL; /* mount(8) always passes the arguments in this order */ dev = argv[1]; mntpnt = argv[2]; while ((c = getopt(argc - 2, argv + 2, "nvo:")) != -1) { switch (c) { case 'n': flags[Update] = 0; break; case 'v': flags[Verbose] = 1; break; case 'o': opts = optarg; break; case '?': case ':': errno = EINVAL; AuFin("internal error"); } } cur = opendir("."); if (!cur) AuFin("."); err = chdir(mntpnt); if (err) AuFin(mntpnt); cwd = getcwd(NULL, 0); /* glibc */ if (!cwd) AuFin("getcwd"); err = fchdir(dirfd(cur)); if (err) AuFin("fchdir"); closedir(cur); /* ignore */ if (opts) test_opts(opts, flags); if (!flags[Bind] && flags[Update]) { err = access(MTab, R_OK | W_OK); if (err) AuFin(MTab); } if (flags[Remount]) { errno = EINVAL; if (flags[Bind]) AuFin("both of remount and bind are specified"); flags[AuFlush] = test_flush(opts); if (flags[AuFlush]) { err = au_plink(cwd, AuPlink_FLUSH, 1, 1); if (err) AuFin(NULL); } } pid = fork(); if (!pid) { /* actual mount operation */ do_mount(dev, mntpnt, argc, argv, flags); return 0; } else if (pid < 0) AuFin("fork"); err = waitpid(pid, &status, 0); if (err < 0) AuFin("child process"); err = !WIFEXITED(status); if (!err) err = WEXITSTATUS(status); if (!err && !flags[Bind]) { if (flags[Update]) err = au_update_mtab(cwd, flags[Remount], flags[Verbose]); else if (flags[Verbose]) { /* withoug blocking plink */ err = au_proc_getmntent(cwd, &ent); if (!err) au_print_ent(&ent); else AuFin("internal error"); } } return err; }