int setfile(struct stat *fs, int fd) { struct timespec ts[2]; int rval; rval = 0; fs->st_mode &= S_ISTXT | S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; ts[0] = fs->st_atim; ts[1] = fs->st_mtim; if (fd >= 0 ? futimens(fd, ts) : utimensat(AT_FDCWD, to.p_path, ts, AT_SYMLINK_NOFOLLOW)) { warn("update times: %s", to.p_path); rval = 1; } /* * Changing the ownership probably won't succeed, unless we're root * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting * the mode; current BSD behavior is to remove all setuid bits on * chown. If chown fails, lose setuid/setgid bits. */ if (fd >= 0 ? fchown(fd, fs->st_uid, fs->st_gid) : lchown(to.p_path, fs->st_uid, fs->st_gid)) { if (errno != EPERM) { warn("chown: %s", to.p_path); rval = 1; } fs->st_mode &= ~(S_ISTXT | S_ISUID | S_ISGID); } if (fd >= 0 ? fchmod(fd, fs->st_mode) : fchmodat(AT_FDCWD, to.p_path, fs->st_mode, AT_SYMLINK_NOFOLLOW)) { warn("chmod: %s", to.p_path); rval = 1; } /* * XXX * NFS doesn't support chflags; ignore errors unless there's reason * to believe we're losing bits. (Note, this still won't be right * if the server supports flags and we were trying to *remove* flags * on a file that we copied, i.e., that we didn't create.) */ errno = 0; if (fd >= 0 ? fchflags(fd, fs->st_flags) : chflagsat(AT_FDCWD, to.p_path, fs->st_flags, AT_SYMLINK_NOFOLLOW)) if (errno != EOPNOTSUPP || fs->st_flags != 0) { warn("chflags: %s", to.p_path); rval = 1; } return (rval); }
/* * Given a file descriptor, create a capability with specific rights and * make sure only those rights work. */ static int try_file_ops(int filefd, int dirfd, cap_rights_t rights) { struct stat sb; struct statfs sf; cap_rights_t erights; int fd_cap, fd_capcap, dfd_cap; ssize_t ssize, ssize2; off_t off; void *p; char ch; int ret, is_nfs; struct pollfd pollfd; int success = -1; REQUIRE(fstatfs(filefd, &sf)); is_nfs = (strcmp("nfs", sf.f_fstypename) == 0); REQUIRE(fd_cap = cap_new(filefd, rights)); CHECK(cap_getrights(fd_cap, &erights) == 0); CHECK(rights == erights); REQUIRE(fd_capcap = cap_new(fd_cap, rights)); CHECK(cap_getrights(fd_capcap, &erights) == 0); CHECK(rights == erights); CHECK(fd_capcap != fd_cap); REQUIRE(dfd_cap = cap_new(dirfd, rights)); CHECK(cap_getrights(dfd_cap, &erights) == 0); CHECK(rights == erights); ssize = read(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(read, CAP_READ, ssize >= 0); ssize = write(fd_cap, &ch, sizeof(ch)); CHECK_RESULT(write, CAP_WRITE, ssize >= 0); off = lseek(fd_cap, 0, SEEK_SET); CHECK_RESULT(lseek, CAP_SEEK, off >= 0); ssize = pread(fd_cap, &ch, sizeof(ch), 0); ssize2 = pread(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pread, CAP_PREAD, ssize >= 0); CHECK(ssize == ssize2); ssize = pwrite(fd_cap, &ch, sizeof(ch), 0); CHECK_RESULT(pwrite, CAP_PWRITE, ssize >= 0); p = mmap(NULL, getpagesize(), PROT_NONE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP); p = mmap(NULL, getpagesize(), PROT_READ, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_R); p = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_W); p = mmap(NULL, getpagesize(), PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_X); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RW); p = mmap(NULL, getpagesize(), PROT_READ | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RX); p = mmap(NULL, getpagesize(), PROT_EXEC | PROT_WRITE, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_WX); p = mmap(NULL, getpagesize(), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED, fd_cap, 0); CHECK_MMAP_RESULT(CAP_MMAP_RWX); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDONLY, 0600); CHECK_RESULT(openat(O_CREATE | O_RDONLY), CAP_CREATE | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY | O_APPEND, 0600); CHECK_RESULT(openat(O_CREATE | O_WRONLY | O_APPEND), CAP_CREATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR | O_APPEND, 0600); CHECK_RESULT(openat(O_CREATE | O_RDWR | O_APPEND), CAP_CREATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = fsync(fd_cap); CHECK_RESULT(fsync, CAP_FSYNC, ret == 0); ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDONLY); CHECK_RESULT(openat(O_FSYNC | O_RDONLY), CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY | O_APPEND); CHECK_RESULT(openat(O_FSYNC | O_WRONLY | O_APPEND), CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR | O_APPEND); CHECK_RESULT(openat(O_FSYNC | O_RDWR | O_APPEND), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDONLY); CHECK_RESULT(openat(O_SYNC | O_RDONLY), CAP_FSYNC | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY | O_APPEND); CHECK_RESULT(openat(O_SYNC | O_WRONLY | O_APPEND), CAP_FSYNC | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR | O_APPEND); CHECK_RESULT(openat(O_SYNC | O_RDWR | O_APPEND), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); ret = ftruncate(fd_cap, 0); CHECK_RESULT(ftruncate, CAP_FTRUNCATE, ret == 0); ret = openat(dirfd, "cap_ftruncate", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDONLY); CHECK_RESULT(openat(O_TRUNC | O_RDONLY), CAP_FTRUNCATE | CAP_READ | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_WRONLY); CHECK_RESULT(openat(O_TRUNC | O_WRONLY), CAP_FTRUNCATE | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_ftruncate", O_TRUNC | O_RDWR); CHECK_RESULT(openat(O_TRUNC | O_RDWR), CAP_FTRUNCATE | CAP_READ | CAP_WRITE | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_ftruncate", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_WRONLY, 0600); CHECK_RESULT(openat(O_CREATE | O_WRONLY), CAP_CREATE | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dfd_cap, "cap_create", O_CREAT | O_RDWR, 0600); CHECK_RESULT(openat(O_CREATE | O_RDWR), CAP_CREATE | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_create", 0) == 0); ret = openat(dirfd, "cap_fsync", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_WRONLY); CHECK_RESULT(openat(O_FSYNC | O_WRONLY), CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_FSYNC | O_RDWR); CHECK_RESULT(openat(O_FSYNC | O_RDWR), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_WRONLY); CHECK_RESULT(openat(O_SYNC | O_WRONLY), CAP_FSYNC | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); ret = openat(dfd_cap, "cap_fsync", O_SYNC | O_RDWR); CHECK_RESULT(openat(O_SYNC | O_RDWR), CAP_FSYNC | CAP_READ | CAP_WRITE | CAP_SEEK | CAP_LOOKUP, ret >= 0); CHECK(ret == -1 || close(ret) == 0); CHECK(unlinkat(dirfd, "cap_fsync", 0) == 0); /* * Note: this is not expected to work over NFS. */ ret = fchflags(fd_cap, UF_NODUMP); CHECK_RESULT(fchflags, CAP_FCHFLAGS, ret == 0 || (is_nfs && errno == EOPNOTSUPP)); ret = openat(dirfd, "cap_chflagsat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = chflagsat(dfd_cap, "cap_chflagsat", UF_NODUMP, 0); CHECK_RESULT(chflagsat, CAP_CHFLAGSAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_chflagsat", 0) == 0); ret = fchown(fd_cap, -1, -1); CHECK_RESULT(fchown, CAP_FCHOWN, ret == 0); ret = openat(dirfd, "cap_fchownat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fchownat(dfd_cap, "cap_fchownat", -1, -1, 0); CHECK_RESULT(fchownat, CAP_FCHOWN | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fchownat", 0) == 0); ret = fchmod(fd_cap, 0644); CHECK_RESULT(fchmod, CAP_FCHMOD, ret == 0); ret = openat(dirfd, "cap_fchmodat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fchmodat(dfd_cap, "cap_fchmodat", 0600, 0); CHECK_RESULT(fchmodat, CAP_FCHMOD | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fchmodat", 0) == 0); ret = fcntl(fd_cap, F_GETFL); CHECK_RESULT(fcntl(F_GETFL), CAP_FCNTL, ret >= 0); ret = fcntl(fd_cap, F_SETFL, ret); CHECK_RESULT(fcntl(F_SETFL), CAP_FCNTL, ret == 0); /* XXX flock */ ret = fstat(fd_cap, &sb); CHECK_RESULT(fstat, CAP_FSTAT, ret == 0); ret = openat(dirfd, "cap_fstatat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = fstatat(dfd_cap, "cap_fstatat", &sb, 0); CHECK_RESULT(fstatat, CAP_FSTAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_fstatat", 0) == 0); ret = fstatfs(fd_cap, &sf); CHECK_RESULT(fstatfs, CAP_FSTATFS, ret == 0); ret = fpathconf(fd_cap, _PC_NAME_MAX); CHECK_RESULT(fpathconf, CAP_FPATHCONF, ret >= 0); ret = futimes(fd_cap, NULL); CHECK_RESULT(futimes, CAP_FUTIMES, ret == 0); ret = openat(dirfd, "cap_futimesat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = futimesat(dfd_cap, "cap_futimesat", NULL); CHECK_RESULT(futimesat, CAP_FUTIMES | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_futimesat", 0) == 0); ret = openat(dirfd, "cap_linkat_src", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = linkat(dirfd, "cap_linkat_src", dfd_cap, "cap_linkat_dst", 0); CHECK_RESULT(linkat, CAP_LINKAT | CAP_LOOKUP, ret == 0); CHECK(unlinkat(dirfd, "cap_linkat_src", 0) == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_linkat_dst", 0) == 0); ret = mkdirat(dfd_cap, "cap_mkdirat", 0700); CHECK_RESULT(mkdirat, CAP_MKDIRAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mkdirat", AT_REMOVEDIR) == 0); ret = mkfifoat(dfd_cap, "cap_mkfifoat", 0600); CHECK_RESULT(mkfifoat, CAP_MKFIFOAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mkfifoat", 0) == 0); ret = mknodat(dfd_cap, "cap_mknodat", S_IFCHR | 0600, 0); CHECK_RESULT(mknodat, CAP_MKNODAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_mknodat", 0) == 0); /* TODO: renameat(2) */ ret = symlinkat("test", dfd_cap, "cap_symlinkat"); CHECK_RESULT(symlinkat, CAP_SYMLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == -1 || unlinkat(dirfd, "cap_symlinkat", 0) == 0); ret = openat(dirfd, "cap_unlinkat", O_CREAT, 0600); CHECK(ret >= 0); CHECK(close(ret) == 0); ret = unlinkat(dfd_cap, "cap_unlinkat", 0); CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", 0) == 0); ret = mkdirat(dirfd, "cap_unlinkat", 0700); CHECK(ret == 0); ret = unlinkat(dfd_cap, "cap_unlinkat", AT_REMOVEDIR); CHECK_RESULT(unlinkat, CAP_UNLINKAT | CAP_LOOKUP, ret == 0); CHECK(ret == 0 || unlinkat(dirfd, "cap_unlinkat", AT_REMOVEDIR) == 0); pollfd.fd = fd_cap; pollfd.events = POLLIN | POLLERR | POLLHUP; pollfd.revents = 0; ret = poll(&pollfd, 1, 0); if (rights & CAP_EVENT) CHECK((pollfd.revents & POLLNVAL) == 0); else CHECK((pollfd.revents & POLLNVAL) != 0); /* XXX: select, kqueue */ close(fd_cap); close(fd_capcap); if (success == -1) { fprintf(stderr, "No tests for rights 0x%jx.\n", (uintmax_t)rights); success = FAILED; } return (success); }
int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; void *set; unsigned long val; int oct; mode_t omode; int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, atflags; uid_t uid; gid_t gid; u_int32_t fclear, fset; char *ep, *mode, *cp, *flags; if (strlen(__progname) > 2) { ischown = __progname[2] == 'o'; ischgrp = __progname[2] == 'g'; ischmod = __progname[2] == 'm'; ischflags = __progname[2] == 'f'; } uid = (uid_t)-1; gid = (gid_t)-1; Hflag = Lflag = Rflag = fflag = hflag = 0; while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': /* no longer documented. */ fflag = 1; break; case 'h': hflag = 1; break; /* * If this is a symbolic mode argument rather than * an option, we are done with option processing. */ case 'g': case 'o': case 'r': case 's': case 't': case 'u': case 'w': case 'X': case 'x': if (!ischmod) usage(); /* * If getopt() moved past the argument, back up. * If the argument contains option letters before * mode letters, setmode() will catch them. */ if (optind > 1) { cp = argv[optind - 1]; if (cp[strlen(cp) - 1] == ch) --optind; } goto done; default: usage(); } done: argv += optind; argc -= optind; if (argc < 2) usage(); /* * We alter the symlink itself if doing -h or -RP, or * if doing -RH and the symlink wasn't a command line arg. */ atflags = AT_SYMLINK_NOFOLLOW; fts_options = FTS_PHYSICAL; if (Rflag) { if (hflag) errx(1, "the -R and -h options may not be specified together."); if (Hflag) fts_options |= FTS_COMFOLLOW; if (Lflag) { fts_options &= ~FTS_PHYSICAL; fts_options |= FTS_LOGICAL; atflags = 0; } } else if (!hflag) { fts_options |= FTS_COMFOLLOW; atflags = 0; } if (ischflags) { if (pledge("stdio rpath fattr", NULL) == -1) err(1, "pledge"); flags = *argv; if (*flags >= '0' && *flags <= '7') { errno = 0; val = strtoul(flags, &ep, 8); if (val > UINT_MAX) errno = ERANGE; if (errno) err(1, "invalid flags: %s", flags); if (*ep) errx(1, "invalid flags: %s", flags); fset = val; oct = 1; } else { if (strtofflags(&flags, &fset, &fclear)) errx(1, "invalid flag: %s", flags); fclear = ~fclear; oct = 0; } } else if (ischmod) { mode = *argv; if (*mode >= '0' && *mode <= '7') { errno = 0; val = strtoul(mode, &ep, 8); if (val > INT_MAX) errno = ERANGE; if (errno) err(1, "invalid file mode: %s", mode); if (*ep) errx(1, "invalid file mode: %s", mode); omode = val; oct = 1; } else { if ((set = setmode(mode)) == NULL) errx(1, "invalid file mode: %s", mode); oct = 0; } } else if (ischown) { /* Both UID and GID are given. */ if ((cp = strchr(*argv, ':')) != NULL) { *cp++ = '\0'; gid = a_gid(cp); } /* * UID and GID are separated by a dot and UID exists. * required for backwards compatibility pre-dating POSIX.2 * likely to stay here forever */ else if ((cp = strchr(*argv, '.')) != NULL && (uid = a_uid(*argv, 1)) == (uid_t)-1) { *cp++ = '\0'; gid = a_gid(cp); } if (uid == (uid_t)-1) uid = a_uid(*argv, 0); } else gid = a_gid(*argv); if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) err(1, NULL); for (rval = 0; (p = fts_read(ftsp)) != NULL;) { switch (p->fts_info) { case FTS_D: if (!Rflag) fts_set(ftsp, p, FTS_SKIP); if (ischmod) break; else continue; case FTS_DNR: /* Warn, chmod, continue. */ warnc(p->fts_errno, "%s", p->fts_path); rval = 1; break; case FTS_DP: /* Already changed at FTS_D. */ if (ischmod) continue; else break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnc(p->fts_errno, "%s", p->fts_path); rval = 1; continue; case FTS_SL: /* Ignore. */ case FTS_SLNONE: /* * The only symlinks that end up here are ones that * don't point to anything or that loop and ones * that we found doing a physical walk. */ if (!hflag && (fts_options & FTS_LOGICAL)) continue; break; default: break; } /* * For -RH, the decision of how to handle symlinks depends * on the level: follow it iff it's a command line arg. */ if (fts_options & FTS_COMFOLLOW) { atflags = p->fts_level == FTS_ROOTLEVEL ? 0 : AT_SYMLINK_NOFOLLOW; } if (ischmod) { if (!fchmodat(AT_FDCWD, p->fts_accpath, oct ? omode : getmode(set, p->fts_statp->st_mode), atflags) || fflag) continue; } else if (!ischflags) { if (!fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflags) || fflag) continue; } else { if (!chflagsat(AT_FDCWD, p->fts_accpath, oct ? fset : (p->fts_statp->st_flags | fset) & fclear, atflags)) continue; } /* error case */ warn("%s", p->fts_path); rval = 1; } if (errno) err(1, "fts_read"); fts_close(ftsp); return (rval); }
int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; u_long clear, newflags, set; long val; int Hflag, Lflag, Rflag, fflag, hflag, vflag; int ch, fts_options, oct, rval; char *flags, *ep; Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) switch (ch) { case 'H': Hflag = 1; Lflag = 0; break; case 'L': Lflag = 1; Hflag = 0; break; case 'P': Hflag = Lflag = 0; break; case 'R': Rflag = 1; break; case 'f': fflag = 1; break; case 'h': hflag = 1; break; case 'v': vflag++; break; case '?': default: usage(); } argv += optind; argc -= optind; if (argc < 2) usage(); if (Rflag) { if (hflag) errx(1, "the -R and -h options may not be " "specified together."); if (Lflag) { fts_options = FTS_LOGICAL; } else { fts_options = FTS_PHYSICAL; if (Hflag) { fts_options |= FTS_COMFOLLOW; } } } else if (hflag) { fts_options = FTS_PHYSICAL; } else { fts_options = FTS_LOGICAL; } flags = *argv; if (*flags >= '0' && *flags <= '7') { errno = 0; val = strtol(flags, &ep, 8); if (val < 0) errno = ERANGE; if (errno) err(1, "invalid flags: %s", flags); if (*ep) errx(1, "invalid flags: %s", flags); set = val; oct = 1; } else { if (strtofflags(&flags, &set, &clear)) errx(1, "invalid flag: %s", flags); clear = ~clear; oct = 0; } if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) err(1, NULL); for (rval = 0; (p = fts_read(ftsp)) != NULL;) { int atflag; if ((fts_options & FTS_LOGICAL) || ((fts_options & FTS_COMFOLLOW) && p->fts_level == FTS_ROOTLEVEL)) atflag = 0; else atflag = AT_SYMLINK_NOFOLLOW; switch (p->fts_info) { case FTS_D: /* Change it at FTS_DP if we're recursive. */ if (!Rflag) fts_set(ftsp, p, FTS_SKIP); continue; case FTS_DNR: /* Warn, chflags. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } if (oct) newflags = set; else newflags = (p->fts_statp->st_flags | set) & clear; if (newflags == p->fts_statp->st_flags) continue; if (chflagsat(AT_FDCWD, p->fts_accpath, newflags, atflag) == -1 && !fflag) { warn("%s", p->fts_path); rval = 1; } else if (vflag) { (void)printf("%s", p->fts_path); if (vflag > 1) (void)printf(": 0%lo -> 0%lo", (u_long)p->fts_statp->st_flags, newflags); (void)printf("\n"); } } if (errno) err(1, "fts_read"); exit(rval); }
static unsigned int call_syscall(struct syscall_desc *scall, char *argv[]) { struct stat64 sb; long long flags; unsigned int i; char *endp; int name, rval; union { char *str; long long num; } args[MAX_ARGS]; #ifdef HAS_FREEBSD_ACL int entry_id = ACL_FIRST_ENTRY; acl_t acl, newacl; acl_entry_t entry, newentry; #endif /* * Verify correctness of the arguments. */ for (i = 0; i < sizeof(args)/sizeof(args[0]); i++) { if (scall->sd_args[i] == TYPE_NONE) { if (argv[i] == NULL || strcmp(argv[i], ":") == 0) break; fprintf(stderr, "too many arguments [%s]\n", argv[i]); exit(1); } else { if (argv[i] == NULL || strcmp(argv[i], ":") == 0) { if (scall->sd_args[i] & TYPE_OPTIONAL) break; fprintf(stderr, "too few arguments\n"); exit(1); } if ((scall->sd_args[i] & TYPE_MASK) == TYPE_STRING) { if (strcmp(argv[i], "NULL") == 0) args[i].str = NULL; else if (strcmp(argv[i], "DEADCODE") == 0) args[i].str = (void *)0xdeadc0de; else args[i].str = argv[i]; } else if ((scall->sd_args[i] & TYPE_MASK) == TYPE_NUMBER) { args[i].num = strtoll(argv[i], &endp, 0); if (*endp != '\0' && !isspace((unsigned char)*endp)) { fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp); exit(1); } } else if ((scall->sd_args[i] & TYPE_MASK) == TYPE_DESCRIPTOR) { if (strcmp(argv[i], "AT_FDCWD") == 0) { args[i].num = AT_FDCWD; } else if (strcmp(argv[i], "BADFD") == 0) { /* In case AT_FDCWD is -1 on some systems... */ if (AT_FDCWD == -1) args[i].num = -2; else args[i].num = -1; } else { int pos; pos = strtoll(argv[i], &endp, 0); if (*endp != '\0' && !isspace((unsigned char)*endp)) { fprintf(stderr, "invalid argument %u, number expected [%s]\n", i, endp); exit(1); } args[i].num = descriptor_get(pos); } } } } /* * Call the given syscall. */ #define NUM(n) (args[(n)].num) #define STR(n) (args[(n)].str) switch (scall->sd_action) { case ACTION_OPEN: flags = str2flags(open_flags, STR(1)); if (flags & O_CREAT) { if (i == 2) { fprintf(stderr, "too few arguments\n"); exit(1); } rval = open(STR(0), (int)flags, (mode_t)NUM(2)); } else { if (i == 3) { fprintf(stderr, "too many arguments\n"); exit(1); } rval = open(STR(0), (int)flags); } if (rval >= 0) descriptor_add(rval); break; case ACTION_OPENAT: flags = str2flags(open_flags, STR(2)); if (flags & O_CREAT) { if (i == 3) { fprintf(stderr, "too few arguments\n"); exit(1); } rval = openat(NUM(0), STR(1), (int)flags, (mode_t)NUM(3)); } else { if (i == 4) { fprintf(stderr, "too many arguments\n"); exit(1); } rval = openat(NUM(0), STR(1), (int)flags); } if (rval >= 0) descriptor_add(rval); break; case ACTION_CREATE: rval = open(STR(0), O_CREAT | O_EXCL, (mode_t)NUM(1)); if (rval >= 0) close(rval); break; case ACTION_UNLINK: rval = unlink(STR(0)); break; case ACTION_UNLINKAT: rval = unlinkat(NUM(0), STR(1), (int)str2flags(unlinkat_flags, STR(2))); break; case ACTION_MKDIR: rval = mkdir(STR(0), (mode_t)NUM(1)); break; case ACTION_MKDIRAT: rval = mkdirat(NUM(0), STR(1), (mode_t)NUM(2)); break; case ACTION_RMDIR: rval = rmdir(STR(0)); break; case ACTION_LINK: rval = link(STR(0), STR(1)); break; case ACTION_LINKAT: rval = linkat(NUM(0), STR(1), NUM(2), STR(3), (int)str2flags(linkat_flags, STR(4))); break; case ACTION_SYMLINK: rval = symlink(STR(0), STR(1)); break; case ACTION_SYMLINKAT: rval = symlinkat(STR(0), NUM(1), STR(2)); break; case ACTION_RENAME: rval = rename(STR(0), STR(1)); break; case ACTION_RENAMEAT: rval = renameat(NUM(0), STR(1), NUM(2), STR(3)); break; case ACTION_MKFIFO: rval = mkfifo(STR(0), (mode_t)NUM(1)); break; case ACTION_MKFIFOAT: rval = mkfifoat(NUM(0), STR(1), (mode_t)NUM(2)); break; case ACTION_MKNOD: case ACTION_MKNODAT: { mode_t ntype; dev_t dev; int fa; switch (scall->sd_action) { case ACTION_MKNOD: fa = 0; break; case ACTION_MKNODAT: fa = 1; break; default: abort(); } dev = makedev(NUM(fa + 3), NUM(fa + 4)); if (strcmp(STR(fa + 1), "c") == 0) /* character device */ ntype = S_IFCHR; else if (strcmp(STR(fa + 1), "b") == 0) /* block device */ ntype = S_IFBLK; else if (strcmp(STR(fa + 1), "f") == 0) /* fifo special */ ntype = S_IFIFO; else if (strcmp(STR(fa + 1), "d") == 0) /* directory */ ntype = S_IFDIR; else if (strcmp(STR(fa + 1), "o") == 0) /* regular file */ ntype = S_IFREG; else { fprintf(stderr, "wrong argument 1\n"); exit(1); } switch (scall->sd_action) { case ACTION_MKNOD: rval = mknod(STR(0), ntype | NUM(2), dev); break; case ACTION_MKNODAT: rval = mknodat(NUM(0), STR(1), ntype | NUM(3), dev); break; default: abort(); } break; } case ACTION_BIND: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = bind(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #ifdef HAS_BINDAT case ACTION_BINDAT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = bindat(NUM(0), rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #endif case ACTION_CONNECT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(0), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = connect(rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #ifdef HAS_CONNECTAT case ACTION_CONNECTAT: { struct sockaddr_un sunx; sunx.sun_family = AF_UNIX; strncpy(sunx.sun_path, STR(1), sizeof(sunx.sun_path) - 1); sunx.sun_path[sizeof(sunx.sun_path) - 1] = '\0'; rval = socket(AF_UNIX, SOCK_STREAM, 0); if (rval < 0) break; rval = connectat(NUM(0), rval, (struct sockaddr *)&sunx, sizeof(sunx)); break; } #endif case ACTION_CHMOD: rval = chmod(STR(0), (mode_t)NUM(1)); break; case ACTION_FCHMOD: rval = fchmod(NUM(0), (mode_t)NUM(1)); break; #ifdef HAS_LCHMOD case ACTION_LCHMOD: rval = lchmod(STR(0), (mode_t)NUM(1)); break; #endif case ACTION_FCHMODAT: rval = fchmodat(NUM(0), STR(1), (mode_t)NUM(2), str2flags(fchmodat_flags, STR(3))); break; case ACTION_CHOWN: rval = chown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_FCHOWN: rval = fchown(NUM(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_LCHOWN: rval = lchown(STR(0), (uid_t)NUM(1), (gid_t)NUM(2)); break; case ACTION_FCHOWNAT: rval = fchownat(NUM(0), STR(1), (uid_t)NUM(2), (gid_t)NUM(3), (int)str2flags(fchownat_flags, STR(4))); break; #ifdef HAS_CHFLAGS case ACTION_CHFLAGS: rval = chflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif #ifdef HAS_FCHFLAGS case ACTION_FCHFLAGS: rval = fchflags(NUM(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif #ifdef HAS_CHFLAGSAT case ACTION_CHFLAGSAT: rval = chflagsat(NUM(0), STR(1), (unsigned long)str2flags(chflags_flags, STR(2)), (int)str2flags(chflagsat_flags, STR(3))); break; #endif #ifdef HAS_LCHFLAGS case ACTION_LCHFLAGS: rval = lchflags(STR(0), (unsigned long)str2flags(chflags_flags, STR(1))); break; #endif case ACTION_TRUNCATE: rval = truncate64(STR(0), NUM(1)); break; case ACTION_FTRUNCATE: rval = ftruncate64(NUM(0), NUM(1)); break; case ACTION_STAT: rval = stat64(STR(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_FSTAT: rval = fstat64(NUM(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_LSTAT: rval = lstat64(STR(0), &sb); if (rval == 0) { show_stats(&sb, STR(1)); return (i); } break; case ACTION_FSTATAT: rval = fstatat(NUM(0), STR(1), &sb, (int)str2flags(fstatat_flags, STR(2))); if (rval == 0) { show_stats(&sb, STR(3)); return (i); } break; case ACTION_PATHCONF: case ACTION_FPATHCONF: case ACTION_LPATHCONF: { long lrval; name = str2name(pathconf_names, STR(1)); if (name == -1) { fprintf(stderr, "unknown name %s", STR(1)); exit(1); } errno = 0; switch (scall->sd_action) { case ACTION_PATHCONF: lrval = pathconf(STR(0), name); break; case ACTION_FPATHCONF: lrval = fpathconf(NUM(0), name); break; case ACTION_LPATHCONF: lrval = lpathconf(STR(0), name); break; default: abort(); } if (lrval == -1 && errno == 0) { printf("unlimited\n"); return (i); } else if (lrval >= 0) { printf("%ld\n", lrval); return (i); } rval = -1; break; } #ifdef HAS_FREEBSD_ACL case ACTION_PREPENDACL: rval = -1; acl = acl_get_file(STR(0), ACL_TYPE_NFS4); if (acl == NULL) break; newacl = acl_from_text(STR(1)); if (acl == NULL) break; while (acl_get_entry(newacl, entry_id, &newentry) == 1) { entry_id = ACL_NEXT_ENTRY; if (acl_create_entry_np(&acl, &entry, 0)) break; if (acl_copy_entry(entry, newentry)) break; } rval = acl_set_file(STR(0), ACL_TYPE_NFS4, acl); break; case ACTION_READACL: acl = acl_get_file(STR(0), ACL_TYPE_NFS4); if (acl == NULL) rval = -1; else rval = 0; break; #endif case ACTION_WRITE: rval = write(NUM(0), STR(1), strlen(STR(1))); break; default: fprintf(stderr, "unsupported syscall\n"); exit(1); } #undef STR #undef NUM if (rval < 0) { const char *serrno; serrno = err2str(errno); fprintf(stderr, "%s returned %d\n", scall->sd_name, rval); printf("%s\n", serrno); exit(1); } printf("0\n"); return (i); }
void copymkdir(int rootfd, char const * dir, int skelfd, mode_t mode, uid_t uid, gid_t gid, int flags) { char *p, lnk[MAXPATHLEN], copybuf[4096]; int len, homefd, srcfd, destfd; ssize_t sz; struct stat st; struct dirent *e; DIR *d; if (*dir == '/') dir++; if (mkdirat(rootfd, dir, mode) != 0 && errno != EEXIST) { warn("mkdir(%s)", dir); return; } fchownat(rootfd, dir, uid, gid, AT_SYMLINK_NOFOLLOW); if (flags > 0) chflagsat(rootfd, dir, flags, AT_SYMLINK_NOFOLLOW); if (skelfd == -1) return; homefd = openat(rootfd, dir, O_DIRECTORY); if ((d = fdopendir(skelfd)) == NULL) { close(skelfd); close(homefd); return; } while ((e = readdir(d)) != NULL) { if (strcmp(e->d_name, ".") == 0 || strcmp(e->d_name, "..") == 0) continue; p = e->d_name; if (fstatat(skelfd, p, &st, AT_SYMLINK_NOFOLLOW) == -1) continue; if (strncmp(p, "dot.", 4) == 0) /* Conversion */ p += 3; if (S_ISDIR(st.st_mode)) { copymkdir(homefd, p, openat(skelfd, e->d_name, O_DIRECTORY), st.st_mode & _DEF_DIRMODE, uid, gid, st.st_flags); continue; } if (S_ISLNK(st.st_mode) && (len = readlinkat(skelfd, e->d_name, lnk, sizeof(lnk) -1)) != -1) { lnk[len] = '\0'; symlinkat(lnk, homefd, p); fchownat(homefd, p, uid, gid, AT_SYMLINK_NOFOLLOW); continue; } if (!S_ISREG(st.st_mode)) continue; if ((srcfd = openat(skelfd, e->d_name, O_RDONLY)) == -1) continue; destfd = openat(homefd, p, O_RDWR | O_CREAT | O_EXCL, st.st_mode); if (destfd == -1) { close(srcfd); continue; } while ((sz = read(srcfd, copybuf, sizeof(copybuf))) > 0) write(destfd, copybuf, sz); close(srcfd); /* * Propagate special filesystem flags */ fchown(destfd, uid, gid); fchflags(destfd, st.st_flags); close(destfd); } closedir(d); }