TEST(util, ReadFileWorldWiteable) { std::string s("hello"); TemporaryFile tf; ASSERT_TRUE(tf.fd != -1); EXPECT_TRUE(WriteFile(tf.path, s)) << strerror(errno); EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno); auto file_contents = ReadFile(tf.path); ASSERT_FALSE(file_contents) << strerror(errno); EXPECT_EQ("Skipping insecure file", file_contents.error_string()); }
static int do_mkdir(const std::vector<std::string>& args) { mode_t mode = 0755; int ret; /* mkdir <path> [mode] [owner] [group] */ if (args.size() >= 3) { mode = std::stoul(args[2], 0, 8); } ret = make_dir(args[1].c_str(), mode); /* chmod in case the directory already exists */ if (ret == -1 && errno == EEXIST) { ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW); } if (ret == -1) { return -errno; } if (args.size() >= 4) { uid_t uid = decode_uid(args[3].c_str()); gid_t gid = -1; if (args.size() == 5) { gid = decode_uid(args[4].c_str()); } if (lchown(args[1].c_str(), uid, gid) == -1) { return -errno; } /* chown may have cleared S_ISUID and S_ISGID, chmod again */ if (mode & (S_ISUID | S_ISGID)) { ret = fchmodat(AT_FDCWD, args[1].c_str(), mode, AT_SYMLINK_NOFOLLOW); if (ret == -1) { return -errno; } } } return e4crypt_set_directory_policy(args[1].c_str()); }
TEST(util, ReadFileWorldWiteable) { std::string s("hello"); TemporaryFile tf; std::string err; ASSERT_TRUE(tf.fd != -1); EXPECT_TRUE(WriteFile(tf.path, s, &err)) << strerror(errno); EXPECT_EQ("", err); EXPECT_NE(-1, fchmodat(AT_FDCWD, tf.path, 0602, AT_SYMLINK_NOFOLLOW)) << strerror(errno); EXPECT_FALSE(ReadFile(tf.path, &s, &err)) << strerror(errno); EXPECT_EQ("Skipping insecure file '"s + tf.path + "'", err); EXPECT_EQ("", s); // s was cleared. }
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); }
int ast_fchmodat(int cwd, const char* path, mode_t mode, int flags) { int r = -1; PATHIFY(cwd, path, 1, 0); if (path == dot && cwd >= 0) RESTART(r, fchmod(cwd, mode)); else RESTART(r, fchmodat(cwd, path, mode, flags)); PATHEND(); return r; }
int main() { struct stat st; struct stat st2; openat(AT_FDCWD, PWD "openat.txt", O_CREAT | O_WRONLY, 0644); assert(stat("openat.txt", &st) == 0); // relative path assert(faccessat(AT_FDCWD, PWD "openat.txt", F_OK, 0) == 0); assert(fstatat(AT_FDCWD, PWD "openat.txt", &st2, 0) == 0); assert(fchmodat(AT_FDCWD, PWD "openat.txt", 0777, 0) == 0); struct timeval my_times[2]; my_times[0].tv_sec = 0; my_times[0].tv_usec = 0; my_times[1].tv_sec = 0; my_times[1].tv_usec = 0; assert(futimesat(AT_FDCWD, PWD "openat.txt", my_times, 0) == 0); // see /etc/passwd, user 'pgbovine' is 508:100 assert(fchownat(AT_FDCWD, PWD "openat.txt", 508, 100, 0) == 0); assert(linkat(AT_FDCWD, PWD "openat.txt", AT_FDCWD, PWD "openat_hardlink.txt", 0) == 0); assert(stat("openat_hardlink.txt", &st) == 0); // relative path assert(symlinkat(PWD "openat.txt", AT_FDCWD, PWD "openat_symlink.txt") == 0); assert(lstat("openat_symlink.txt", &st) == 0); // relative path char res[300]; assert(readlinkat(AT_FDCWD, PWD "openat_symlink.txt", res, sizeof(res)) > 0); assert(renameat(AT_FDCWD, PWD "openat.txt", AT_FDCWD, PWD "openat_newname.txt", 0) == 0); assert(stat("openat.txt", &st) != 0); // should not exist anymore assert(stat("openat_newname.txt", &st) == 0); // relative path unlinkat(AT_FDCWD, PWD "openat_newname.txt", 0); unlinkat(AT_FDCWD, PWD "openat_hardlink.txt", 0); unlinkat(AT_FDCWD, PWD "openat_symlink.txt", 0); mknodat(AT_FDCWD, PWD "mknodat.fifo", S_IFIFO); assert(stat("mknodat.fifo", &st) == 0); // relative path unlinkat(AT_FDCWD, PWD "mknodat.fifo", 0); mkdirat(AT_FDCWD, PWD "mkdirat_dir", 0); assert(stat("mkdirat_dir", &st) == 0); // relative path unlinkat(AT_FDCWD, PWD "mkdirat_dir", AT_REMOVEDIR); // like 'rmdir' return 0; }
void psync_chmod(const char *fn, mode_t mode, int flags) { static int notsup; if (notsup) return; if (fchmodat(AT_FDCWD, fn, mode, flags) == -1) { int rc = errno; if (rc == ENOTSUP && flags & AT_SYMLINK_NOFOLLOW) notsup = 1; else psynclog_warn("chmod %s", fn); } }
static int fd_copy_node(int df, const char *from, const struct stat *st, int dt, const char *to) { int r; assert(from); assert(st); assert(to); r = mknodat(dt, to, st->st_mode, st->st_rdev); if (r < 0) return -errno; if (fchownat(dt, to, st->st_uid, st->st_gid, AT_SYMLINK_NOFOLLOW) < 0) r = -errno; if (fchmodat(dt, to, st->st_mode & 07777, 0) < 0) r = -errno; return r; }
static inline int make_file_safe(struct vfs_fsal_obj_handle *dir_hdl, const struct req_op_context *opctx, int dir_fd, const char *name, mode_t unix_mode, uid_t user, gid_t group, struct vfs_fsal_obj_handle **hdl) { int retval; struct stat stat; vfs_file_handle_t *fh; vfs_alloc_handle(fh); retval = fchownat(dir_fd, name, user, group, AT_SYMLINK_NOFOLLOW); if (retval < 0) goto fileerr; /* now that it is owned properly, set accessible mode */ retval = fchmodat(dir_fd, name, unix_mode, 0); if (retval < 0) goto fileerr; retval = vfs_name_to_handle(dir_fd, dir_hdl->obj_handle.fs, name, fh); if (retval < 0) goto fileerr; retval = fstatat(dir_fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) goto fileerr; /* allocate an obj_handle and fill it up */ *hdl = alloc_handle(dir_fd, fh, dir_hdl->obj_handle.fs, &stat, dir_hdl->handle, name, opctx->fsal_export); if (*hdl == NULL) return ENOMEM; return 0; fileerr: retval = errno; return retval; }
/* * 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); }
/** * @brief Set attributes on an object * * This function sets attributes on an object. Which attributes are * set is determined by attrib_set->mask. The FSAL must manage bypass * or not of share reservations, and a state may be passed. * * @param[in] obj_hdl File on which to operate * @param[in] state state_t to use for this operation * @param[in] attrib_set Attributes to set * * @return FSAL status. */ fsal_status_t vfs_setattr2(struct fsal_obj_handle *obj_hdl, bool bypass, struct state_t *state, struct attrlist *attrib_set) { struct vfs_fsal_obj_handle *myself; fsal_status_t status = {0, 0}; int retval = 0; fsal_openflags_t openflags = FSAL_O_ANY; bool has_lock = false; bool need_fsync = false; bool closefd = false; int my_fd; const char *func; /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode &= ~op_ctx->fsal_export->exp_ops.fs_umask(op_ctx->fsal_export); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); return fsalstat(posix2fsal_error(EXDEV), EXDEV); } #ifdef ENABLE_VFS_DEBUG_ACL #ifdef ENABLE_RFC_ACL if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE) && !FSAL_TEST_MASK(attrib_set->mask, ATTR_ACL)) { /* Set ACL from MODE */ struct attrlist attrs; fsal_prepare_attrs(&attrs, ATTR_ACL); status = obj_hdl->obj_ops.getattrs(obj_hdl, &attrs); if (FSAL_IS_ERROR(status)) return status; status = fsal_mode_to_acl(attrib_set, attrs.acl); /* Done with the attrs */ fsal_release_attrs(&attrs); } else { /* If ATTR_ACL is set, mode needs to be adjusted no matter what. * See 7530 s 6.4.1.3 */ if (!FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) attrib_set->mode = myself->mode; status = fsal_acl_to_mode(attrib_set); } if (FSAL_IS_ERROR(status)) return status; #endif /* ENABLE_RFC_ACL */ #endif /* This is yet another "you can't get there from here". If this object * is a socket (AF_UNIX), an fd on the socket s useless _period_. * If it is for a symlink, without O_PATH, you will get an ELOOP error * and (f)chmod doesn't work for a symlink anyway - not that it matters * because access checking is not done on the symlink but the final * target. * AF_UNIX sockets are also ozone material. If the socket is already * active listeners et al, you can manipulate the mode etc. If it is * just sitting there as in you made it with a mknod. * (one of those leaky abstractions...) * or the listener forgot to unlink it, it is lame duck. */ /* Test if size is being set, make sure file is regular and if so, * require a read/write file descriptor. */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { LogFullDebug(COMPONENT_FSAL, "Setting size on non-regular file"); return fsalstat(ERR_FSAL_INVAL, EINVAL); } openflags = FSAL_O_RDWR; } /* Get a usable file descriptor. Share conflict is only possible if * size is being set. */ status = find_fd(&my_fd, obj_hdl, bypass, state, openflags, &has_lock, &need_fsync, &closefd, false); if (FSAL_IS_ERROR(status)) { if (obj_hdl->type == SYMBOLIC_LINK && status.major == ERR_FSAL_PERM) { /* You cannot open_by_handle (XFS) a symlink and it * throws an EPERM error for it. open_by_handle_at * does not throw that error for symlinks so we play a * game here. Since there is not much we can do with * symlinks anyway, say that we did it * but don't actually do anything. * If you *really* want to tweek things * like owners, get a modern linux kernel... */ status = fsalstat(ERR_FSAL_NO_ERROR, 0); } LogFullDebug(COMPONENT_FSAL, "find_fd status=%s", fsal_err_txt(status)); goto out; } /** TRUNCATE **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_SIZE)) { retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { /** @todo FSF: is this still necessary? * * XXX ESXi volume creation pattern reliably * reached this point in the past, however now that we * only use the already open file descriptor if it is * open read/write, this may no longer fail. * If there is some other error from ftruncate, then * we will needlessly retry, but without more detail * of the original failure, we can't be sure. * Fortunately permission checking is done by * Ganesha before calling here, so we won't get an * EACCES since this call is done as root. We could * get EFBIG, EPERM, or EINVAL. */ /** @todo FSF: re-open if we really still need this */ retval = ftruncate(my_fd, attrib_set->filesize); if (retval != 0) { func = "truncate"; goto fileerr; } } } /** CHMOD **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MODE)) { /* The POSIX chmod call doesn't affect the symlink object, but * the entry it points to. So we must ignore it. */ if (obj_hdl->type != SYMBOLIC_LINK) { if (vfs_unopenable_type(obj_hdl->type)) retval = fchmodat( my_fd, myself->u.unopenable.name, fsal2unix_mode(attrib_set->mode), 0); else retval = fchmod( my_fd, fsal2unix_mode(attrib_set->mode)); if (retval != 0) { func = "chmod"; goto fileerr; } } } /** CHOWN **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER | ATTR_GROUP)) { uid_t user = FSAL_TEST_MASK(attrib_set->mask, ATTR_OWNER) ? (int)attrib_set->owner : -1; gid_t group = FSAL_TEST_MASK(attrib_set->mask, ATTR_GROUP) ? (int)attrib_set->group : -1; if (vfs_unopenable_type(obj_hdl->type)) retval = fchownat(my_fd, myself->u.unopenable.name, user, group, AT_SYMLINK_NOFOLLOW); else if (obj_hdl->type == SYMBOLIC_LINK) retval = fchownat(my_fd, "", user, group, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); else retval = fchown(my_fd, user, group); if (retval) { func = "chown"; goto fileerr; } } /** UTIME **/ if (FSAL_TEST_MASK(attrib_set->mask, ATTRS_SET_TIME)) { struct timespec timebuf[2]; if (obj_hdl->type == SYMBOLIC_LINK) goto out; /* Setting time on symlinks is illegal */ /* Atime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME_SERVER)) { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_ATIME)) { timebuf[0] = attrib_set->atime; } else { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_OMIT; } /* Mtime */ if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME_SERVER)) { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrib_set->mask, ATTR_MTIME)) { timebuf[1] = attrib_set->mtime; } else { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_OMIT; } if (vfs_unopenable_type(obj_hdl->type)) retval = vfs_utimesat(my_fd, myself->u.unopenable.name, timebuf, AT_SYMLINK_NOFOLLOW); else retval = vfs_utimes(my_fd, timebuf); if (retval != 0) { func = "utimes"; goto fileerr; } } /** SUBFSAL **/ if (myself->sub_ops && myself->sub_ops->setattrs) { status = myself->sub_ops->setattrs( myself, my_fd, attrib_set->mask, attrib_set); if (FSAL_IS_ERROR(status)) goto out; } errno = 0; fileerr: retval = errno; if (retval != 0) { LogDebug(COMPONENT_FSAL, "%s returned %s", func, strerror(retval)); } status = fsalstat(posix2fsal_error(retval), retval); out: if (closefd) close(my_fd); if (has_lock) PTHREAD_RWLOCK_unlock(&obj_hdl->lock); return status; }
int main(int argc, char *argv[]) { FTS *ftsp; FTSENT *p; void *set; int Hflag, Lflag, Rflag, ch, fflag; int fts_options, hflag, rval, vflag; char *mode; mode_t newmode; Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -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': /* * In System V the -h option causes chmod to change the * mode of the symbolic link. 4.4BSD's symbolic links * didn't have modes, so it was an undocumented noop. * In FreeBSD 3.0, lchmod(2) is introduced and this * option does real work. */ hflag = 1; break; /* * XXX * "-[rwx]" are valid mode commands. If they are the entire * argument, getopt has moved past them, so decrement optind. * Regardless, we're done argument processing. */ case 'g': case 'o': case 'r': case 's': case 't': case 'u': case 'w': case 'X': case 'x': if (argv[optind - 1][0] == '-' && argv[optind - 1][1] == ch && argv[optind - 1][2] == '\0') --optind; goto done; case 'v': vflag++; break; case '?': default: usage(); } done: 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; } mode = *argv; errno = 0; if ((set = setmode(mode)) == NULL) { if (!errno) errx(1, "invalid file mode: %s", mode); else /* malloc for setmode() failed */ err(1, "setmode failed"); } if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) err(1, "fts_open"); 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: if (!Rflag) fts_set(ftsp, p, FTS_SKIP); break; case FTS_DNR: /* Warn, chmod. */ warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; break; case FTS_DP: /* Already changed at FTS_D. */ continue; case FTS_ERR: /* Warn, continue. */ case FTS_NS: warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); rval = 1; continue; default: break; } newmode = getmode(set, p->fts_statp->st_mode); if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) continue; if (fchmodat(AT_FDCWD, p->fts_accpath, newmode, atflag) == -1 && !fflag) { warn("%s", p->fts_path); rval = 1; } else if (vflag) { printf("%s", p->fts_path); if (vflag > 1) { char m1[12], m2[12]; strmode(p->fts_statp->st_mode, m1); strmode((p->fts_statp->st_mode & S_IFMT) | newmode, m2); printf(": 0%o [%s] -> 0%o [%s]", p->fts_statp->st_mode, m1, (p->fts_statp->st_mode & S_IFMT) | newmode, m2); } 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); }
static int set_attrs(int fd, char *path, mode_t perm, uid_t uid, gid_t gid, const struct timespec *ats, const struct timespec *mts) { struct timeval tv[2]; struct stat st; int fdcwd; #ifdef HAVE_UTIMENSAT struct timespec times[2]; times[0] = *ats; times[1] = *mts; if (utimensat(fd, RELATIVE_PATH(path), times, AT_SYMLINK_NOFOLLOW) == -1 && errno != EOPNOTSUPP){ pkg_fatal_errno("Fail to set time on %s", path); } if (errno == EOPNOTSUPP) { #endif tv[0].tv_sec = ats->tv_sec; tv[0].tv_usec = ats->tv_nsec / 1000; tv[1].tv_sec = mts->tv_sec; tv[1].tv_usec = mts->tv_nsec / 1000; fdcwd = open(".", O_DIRECTORY|O_CLOEXEC); fchdir(fd); if (lutimes(RELATIVE_PATH(path), tv) == -1) { if (errno != ENOSYS) { pkg_fatal_errno("Fail to set time on %s", path); } else { /* Fallback to utimes */ if (utimes(RELATIVE_PATH(path), tv) == -1) { pkg_fatal_errno("Fail to set time(fallback) on " "%s", path); } } } fchdir(fdcwd); close(fdcwd); #ifdef HAVE_UTIMENSAT } #endif if (getenv("INSTALL_AS_USER") == NULL) { if (fchownat(fd, RELATIVE_PATH(path), uid, gid, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOTSUP) { if (fchownat(fd, RELATIVE_PATH(path), uid, gid, 0) == -1) { pkg_fatal_errno("Fail to chown(fallback) %s", path); } } else { pkg_fatal_errno("Fail to chown %s", path); } } } /* zfs drops the setuid on fchownat */ if (fchmodat(fd, RELATIVE_PATH(path), perm, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOTSUP) { /* * Executing fchmodat on a symbolic link results in * ENOENT (file not found) on platforms that do not * support AT_SYMLINK_NOFOLLOW. The file mode of * symlinks cannot be modified via file descriptor * reference on these systems. The lchmod function is * also not an option because it is not a posix * standard, nor is implemented everywhere. Since * symlink permissions have never been evaluated and * thus cosmetic, just skip them on these systems. */ if (fstatat(fd, RELATIVE_PATH(path), &st, AT_SYMLINK_NOFOLLOW) == -1) { pkg_fatal_errno("Fail to get file status %s", path); } if (!S_ISLNK(st.st_mode)) { if (fchmodat(fd, RELATIVE_PATH(path), perm, 0) == -1) { pkg_fatal_errno("Fail to chmod(fallback) %s", path); } } } else { pkg_fatal_errno("Fail to chmod %s", path); } } return (EPKG_OK); }
/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid, const char *socketcon) { struct sockaddr_un addr; int fd, ret, savederrno; char *filecon; if (socketcon) { if (setsockcreatecon(socketcon) == -1) { PLOG(ERROR) << "setsockcreatecon(\"" << socketcon << "\") failed"; return -1; } } fd = socket(PF_UNIX, type, 0); if (fd < 0) { PLOG(ERROR) << "Failed to open socket '" << name << "'"; return -1; } if (socketcon) setsockcreatecon(NULL); memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { PLOG(ERROR) << "Failed to unlink old socket '" << name << "'"; goto out_close; } filecon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(filecon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); savederrno = errno; setfscreatecon(NULL); freecon(filecon); if (ret) { errno = savederrno; PLOG(ERROR) << "Failed to bind socket '" << name << "'"; goto out_unlink; } ret = lchown(addr.sun_path, uid, gid); if (ret) { PLOG(ERROR) << "Failed to lchown socket '" << addr.sun_path << "'"; goto out_unlink; } ret = fchmodat(AT_FDCWD, addr.sun_path, perm, AT_SYMLINK_NOFOLLOW); if (ret) { PLOG(ERROR) << "Failed to fchmodat socket '" << addr.sun_path << "'"; goto out_unlink; } LOG(INFO) << "Created socket '" << addr.sun_path << "'" << ", mode " << std::oct << perm << std::dec << ", user " << uid << ", group " << gid; return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
static struct closefd vfs_fsal_open_and_stat(struct fsal_export *exp, struct vfs_fsal_obj_handle *myself, struct stat *stat, int open_flags, fsal_errors_t *fsal_error) { struct fsal_obj_handle *obj_hdl = &myself->obj_handle; struct closefd cfd = { .fd = -1, .close_fd = false }; int retval = 0; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); const char *func = "unknown"; struct vfs_filesystem *vfs_fs = myself->obj_handle.fs->private; switch (obj_hdl->type) { case SOCKET_FILE: case CHARACTER_FILE: case BLOCK_FILE: cfd.fd = vfs_open_by_handle(vfs_fs, myself->u.unopenable.dir, O_PATH | O_NOACCESS, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), O_PATH | O_NOACCESS); return cfd; } cfd.close_fd = true; retval = fstatat(cfd.fd, myself->u.unopenable.name, stat, AT_SYMLINK_NOFOLLOW); func = "fstatat"; break; case REGULAR_FILE: if (myself->u.file.openflags == FSAL_O_CLOSED) { /* no file open at the moment */ cfd.fd = vfs_fsal_open(myself, open_flags, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), open_flags); return cfd; } cfd.close_fd = true; } else { cfd.fd = myself->u.file.fd; } retval = fstat(cfd.fd, stat); func = "fstat"; break; case SYMBOLIC_LINK: open_flags |= (O_PATH | O_RDWR | O_NOFOLLOW); goto vfos_open; case FIFO_FILE: open_flags |= O_NONBLOCK; /* fall through */ case DIRECTORY: default: vfos_open: cfd.fd = vfs_fsal_open(myself, open_flags, fsal_error); if (cfd.fd < 0) { LogDebug(COMPONENT_FSAL, "Failed with %s open_flags 0x%08x", strerror(-cfd.fd), open_flags); return cfd; } cfd.close_fd = true; retval = vfs_stat_by_handle(cfd.fd, myself->handle, stat, open_flags); func = "vfs_stat_by_handle"; break; } if (retval < 0) { retval = errno; if (cfd.close_fd) { int rc; rc = close(cfd.fd); if (rc < 0) { rc = errno; LogDebug(COMPONENT_FSAL, "close failed with %s", strerror(rc)); } } if (retval == ENOENT) retval = ESTALE; *fsal_error = posix2fsal_error(retval); LogDebug(COMPONENT_FSAL, "%s failed with %s", func, strerror(retval)); cfd.fd = -retval; cfd.close_fd = false; return cfd; } return cfd; } static fsal_status_t getattrs(struct fsal_obj_handle *obj_hdl) { struct vfs_fsal_obj_handle *myself; struct closefd cfd = { .fd = -1, .close_fd = false }; struct stat stat; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; fsal_status_t st; int retval = 0; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s getattr for handle belonging to FSAL %s, ignoring", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); goto out; } cfd = vfs_fsal_open_and_stat(op_ctx->fsal_export, myself, &stat, O_RDONLY, &fsal_error); if (cfd.fd >= 0) { st = posix2fsal_attributes(&stat, &obj_hdl->attributes); if (cfd.close_fd) close(cfd.fd); if (FSAL_IS_ERROR(st)) { FSAL_CLEAR_MASK(obj_hdl->attributes.mask); FSAL_SET_MASK(obj_hdl->attributes.mask, ATTR_RDATTR_ERR); fsal_error = st.major; retval = st.minor; } else { obj_hdl->attributes.fsid = obj_hdl->fs->fsid; } } else { LogDebug(COMPONENT_FSAL, "Failed with %s, fsal_error %s", strerror(-cfd.fd), fsal_error == ERR_FSAL_STALE ? "ERR_FSAL_STALE" : "other"); if (obj_hdl->type == SYMBOLIC_LINK && cfd.fd == -EPERM) { /* You cannot open_by_handle (XFS on linux) a symlink * and it throws an EPERM error for it. * open_by_handle_at does not throw that error for * symlinks so we play a game here. Since there is * not much we can do with symlinks anyway, * say that we did it but don't actually * do anything. In this case, return the stat we got * at lookup time. If you *really* want to tweek things * like owners, get a modern linux kernel... */ fsal_error = ERR_FSAL_NO_ERROR; goto out; } retval = -cfd.fd; } out: return fsalstat(fsal_error, retval); } /* * NOTE: this is done under protection of the attributes rwlock * in the cache entry. */ static fsal_status_t setattrs(struct fsal_obj_handle *obj_hdl, struct attrlist *attrs) { struct vfs_fsal_obj_handle *myself; struct closefd cfd = { .fd = -1, .close_fd = false }; struct stat stat; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; int open_flags = O_RDONLY; /* apply umask, if mode attribute is to be changed */ if (FSAL_TEST_MASK(attrs->mask, ATTR_MODE)) attrs->mode &= ~op_ctx->fsal_export->exp_ops. fs_umask(op_ctx->fsal_export); myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto hdlerr; } /* This is yet another "you can't get there from here". If this object * is a socket (AF_UNIX), an fd on the socket s useless _period_. * If it is for a symlink, without O_PATH, you will get an ELOOP error * and (f)chmod doesn't work for a symlink anyway - not that it matters * because access checking is not done on the symlink but the final * target. * AF_UNIX sockets are also ozone material. If the socket is already * active listeners et al, you can manipulate the mode etc. If it is * just sitting there as in you made it with a mknod. * (one of those leaky abstractions...) * or the listener forgot to unlink it, it is lame duck. */ if (FSAL_TEST_MASK(attrs->mask, ATTR_SIZE)) open_flags = O_RDWR; cfd = vfs_fsal_open_and_stat(op_ctx->fsal_export, myself, &stat, open_flags, &fsal_error); if (cfd.fd < 0) { if (obj_hdl->type == SYMBOLIC_LINK && cfd.fd == -EPERM) { /* You cannot open_by_handle (XFS) a symlink and it * throws an EPERM error for it. open_by_handle_at * does not throw that error for symlinks so we play a * game here. Since there is not much we can do with * symlinks anyway, say that we did it * but don't actually do anything. * If you *really* want to tweek things * like owners, get a modern linux kernel... */ fsal_error = ERR_FSAL_NO_ERROR; goto out; } return fsalstat(fsal_error, cfd.fd); } /** TRUNCATE **/ if (FSAL_TEST_MASK(attrs->mask, ATTR_SIZE)) { if (obj_hdl->type != REGULAR_FILE) { fsal_error = ERR_FSAL_INVAL; goto fileerr; } retval = ftruncate(cfd.fd, attrs->filesize); if (retval != 0) { /* XXX ESXi volume creation pattern reliably * reaches this point and reliably can successfully * ftruncate on reopen. I don't know yet if fd if * we failed to handle a previous error, or what. * I don't see a prior failed op in wireshark. */ if (retval == -1 /* bad fd */) { vfs_close(obj_hdl); if (cfd.close_fd) close(cfd.fd); cfd = vfs_fsal_open_and_stat( op_ctx->fsal_export, myself, &stat, open_flags, &fsal_error); retval = ftruncate(cfd.fd, attrs->filesize); if (retval != 0) goto fileerr; } else goto fileerr; } } /** CHMOD **/ if (FSAL_TEST_MASK(attrs->mask, ATTR_MODE)) { /* The POSIX chmod call doesn't affect the symlink object, but * the entry it points to. So we must ignore it. */ if (!S_ISLNK(stat.st_mode)) { if (vfs_unopenable_type(obj_hdl->type)) retval = fchmodat(cfd.fd, myself->u.unopenable.name, fsal2unix_mode(attrs->mode), 0); else retval = fchmod(cfd.fd, fsal2unix_mode(attrs->mode)); if (retval != 0) goto fileerr; } } /** CHOWN **/ if (FSAL_TEST_MASK(attrs->mask, ATTR_OWNER | ATTR_GROUP)) { uid_t user = FSAL_TEST_MASK(attrs->mask, ATTR_OWNER) ? (int)attrs->owner : -1; gid_t group = FSAL_TEST_MASK(attrs->mask, ATTR_GROUP) ? (int)attrs->group : -1; if (vfs_unopenable_type(obj_hdl->type)) retval = fchownat(cfd.fd, myself->u.unopenable.name, user, group, AT_SYMLINK_NOFOLLOW); else if (obj_hdl->type == SYMBOLIC_LINK) retval = fchownat(cfd.fd, "", user, group, AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH); else retval = fchown(cfd.fd, user, group); if (retval) goto fileerr; } /** UTIME **/ if (FSAL_TEST_MASK (attrs->mask, ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SERVER | ATTR_MTIME_SERVER)) { struct timespec timebuf[2]; if (obj_hdl->type == SYMBOLIC_LINK) goto out; /* Setting time on symlinks is illegal */ /* Atime */ if (FSAL_TEST_MASK(attrs->mask, ATTR_ATIME_SERVER)) { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrs->mask, ATTR_ATIME)) { timebuf[0] = attrs->atime; } else { timebuf[0].tv_sec = 0; timebuf[0].tv_nsec = UTIME_OMIT; } /* Mtime */ if (FSAL_TEST_MASK(attrs->mask, ATTR_MTIME_SERVER)) { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_NOW; } else if (FSAL_TEST_MASK(attrs->mask, ATTR_MTIME)) { timebuf[1] = attrs->mtime; } else { timebuf[1].tv_sec = 0; timebuf[1].tv_nsec = UTIME_OMIT; } if (vfs_unopenable_type(obj_hdl->type)) retval = vfs_utimesat(cfd.fd, myself->u.unopenable.name, timebuf, AT_SYMLINK_NOFOLLOW); else retval = vfs_utimes(cfd.fd, timebuf); if (retval != 0) goto fileerr; } goto out; fileerr: retval = errno; fsal_error = posix2fsal_error(retval); out: if (cfd.close_fd) close(cfd.fd); hdlerr: return fsalstat(fsal_error, retval); } /* file_unlink * unlink the named file in the directory */ static fsal_status_t file_unlink(struct fsal_obj_handle *dir_hdl, const char *name) { struct vfs_fsal_obj_handle *myself; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; struct stat stat; int fd; int retval = 0; myself = container_of(dir_hdl, struct vfs_fsal_obj_handle, obj_handle); if (dir_hdl->fsal != dir_hdl->fs->fsal) { LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s, return EXDEV", dir_hdl->fsal->name, dir_hdl->fs->fsal != NULL ? dir_hdl->fs->fsal->name : "(none)"); retval = EXDEV; fsal_error = posix2fsal_error(retval); goto out; } fd = vfs_fsal_open(myself, O_PATH | O_NOACCESS, &fsal_error); if (fd < 0) { retval = -fd; goto out; } retval = fstatat(fd, name, &stat, AT_SYMLINK_NOFOLLOW); if (retval < 0) { retval = errno; LogDebug(COMPONENT_FSAL, "fstatat %s failed %s", name, strerror(retval)); if (retval == ENOENT) fsal_error = ERR_FSAL_STALE; else fsal_error = posix2fsal_error(retval); goto errout; } fsal_set_credentials(op_ctx->creds); retval = unlinkat(fd, name, (S_ISDIR(stat.st_mode)) ? AT_REMOVEDIR : 0); if (retval < 0) { retval = errno; if (retval == ENOENT) fsal_error = ERR_FSAL_STALE; else fsal_error = posix2fsal_error(retval); } fsal_restore_ganesha_credentials(); errout: close(fd); out: return fsalstat(fsal_error, retval); } /* handle_digest * fill in the opaque f/s file handle part. * we zero the buffer to length first. This MAY already be done above * at which point, remove memset here because the caller is zeroing * the whole struct. */ static fsal_status_t handle_digest(const struct fsal_obj_handle *obj_hdl, fsal_digesttype_t output_type, struct gsh_buffdesc *fh_desc) { const struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (obj_hdl->fsal != obj_hdl->fs->fsal) { /* Log, but allow digest */ LogDebug(COMPONENT_FSAL, "FSAL %s operation for handle belonging to FSAL %s", obj_hdl->fsal->name, obj_hdl->fs->fsal != NULL ? obj_hdl->fs->fsal->name : "(none)"); } switch (output_type) { case FSAL_DIGEST_NFSV3: case FSAL_DIGEST_NFSV4: if (fh_desc->len < myself->handle->handle_len) { LogMajor(COMPONENT_FSAL, "Space too small for handle. need %d, have %lu", (int) myself->handle->handle_len, fh_desc->len); return fsalstat(ERR_FSAL_TOOSMALL, 0); } memcpy(fh_desc->addr, myself->handle->handle_data, myself->handle->handle_len); break; default: return fsalstat(ERR_FSAL_SERVERFAULT, 0); } fh_desc->len = myself->handle->handle_len; return fsalstat(ERR_FSAL_NO_ERROR, 0); } /** * handle_to_key * return a handle descriptor into the handle in this object handle * @TODO reminder. make sure things like hash keys don't point here * after the handle is released. */ static void handle_to_key(struct fsal_obj_handle *obj_hdl, struct gsh_buffdesc *fh_desc) { struct vfs_fsal_obj_handle *myself; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); fh_desc->addr = myself->handle->handle_data; fh_desc->len = myself->handle->handle_len; } /* * release * release our export first so they know we are gone */ static void release(struct fsal_obj_handle *obj_hdl) { struct vfs_fsal_obj_handle *myself; object_file_type_t type = obj_hdl->type; myself = container_of(obj_hdl, struct vfs_fsal_obj_handle, obj_handle); if (type == REGULAR_FILE) { fsal_status_t st = vfs_close(obj_hdl); if (FSAL_IS_ERROR(st)) { LogCrit(COMPONENT_FSAL, "Could not close hdl 0x%p, error %s(%d)", obj_hdl, strerror(st.minor), st.minor); } } fsal_obj_handle_fini(obj_hdl); if (type == SYMBOLIC_LINK) { if (myself->u.symlink.link_content != NULL) gsh_free(myself->u.symlink.link_content); } else if (vfs_unopenable_type(type)) { if (myself->u.unopenable.name != NULL) gsh_free(myself->u.unopenable.name); if (myself->u.unopenable.dir != NULL) gsh_free(myself->u.unopenable.dir); } gsh_free(myself); } void vfs_handle_ops_init(struct fsal_obj_ops *ops) { ops->release = release; ops->lookup = lookup; ops->readdir = read_dirents; ops->create = create; ops->mkdir = makedir; ops->mknode = makenode; ops->symlink = makesymlink; ops->readlink = readsymlink; ops->test_access = fsal_test_access; ops->getattrs = getattrs; ops->setattrs = setattrs; ops->link = linkfile; ops->rename = renamefile; ops->unlink = file_unlink; ops->open = vfs_open; ops->status = vfs_status; ops->read = vfs_read; ops->write = vfs_write; ops->commit = vfs_commit; ops->lock_op = vfs_lock_op; ops->close = vfs_close; ops->lru_cleanup = vfs_lru_cleanup; ops->handle_digest = handle_digest; ops->handle_to_key = handle_to_key; /* xattr related functions */ ops->list_ext_attrs = vfs_list_ext_attrs; ops->getextattr_id_by_name = vfs_getextattr_id_by_name; ops->getextattr_value_by_name = vfs_getextattr_value_by_name; ops->getextattr_value_by_id = vfs_getextattr_value_by_id; ops->setextattr_value = vfs_setextattr_value; ops->setextattr_value_by_id = vfs_setextattr_value_by_id; ops->getextattr_attrs = vfs_getextattr_attrs; ops->remove_extattr_by_id = vfs_remove_extattr_by_id; ops->remove_extattr_by_name = vfs_remove_extattr_by_name; } /* export methods that create object handles */ /* lookup_path * modeled on old api except we don't stuff attributes. * KISS */ fsal_status_t vfs_lookup_path(struct fsal_export *exp_hdl, const char *path, struct fsal_obj_handle **handle) { int dir_fd = -1; struct stat stat; struct vfs_fsal_obj_handle *hdl; fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct fsal_filesystem *fs; struct fsal_dev__ dev; vfs_file_handle_t *fh = NULL; vfs_alloc_handle(fh); *handle = NULL; /* poison it */ dir_fd = open_dir_by_path_walk(-1, path, &stat); if (dir_fd < 0) { LogCrit(COMPONENT_FSAL, "Could not open directory for path %s", path); retval = -dir_fd; goto errout; } dev = posix2fsal_devt(stat.st_dev); fs = lookup_dev(&dev); if (fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not find file system for path %s", path); retval = ENOENT; goto errout; } if (fs->fsal != exp_hdl->fsal) { LogInfo(COMPONENT_FSAL, "File system for path %s did not belong to FSAL %s", path, exp_hdl->fsal->name); retval = EACCES; goto errout; } LogDebug(COMPONENT_FSAL, "filesystem %s for path %s", fs->path, path); retval = vfs_fd_to_handle(dir_fd, fs, fh); if (retval < 0) { retval = errno; LogCrit(COMPONENT_FSAL, "Could not get handle for path %s, error %s", path, strerror(retval)); goto errout; } /* allocate an obj_handle and fill it up */ hdl = alloc_handle(-1, fh, fs, &stat, NULL, "", exp_hdl); if (hdl == NULL) { retval = ENOMEM; LogCrit(COMPONENT_FSAL, "Could not allocate handle for path %s", path); goto errout; } close(dir_fd); *handle = &hdl->obj_handle; return fsalstat(ERR_FSAL_NO_ERROR, 0); errout: if (dir_fd >= 0) close(dir_fd); fsal_error = posix2fsal_error(retval); return fsalstat(fsal_error, retval); } fsal_status_t vfs_check_handle(struct fsal_export *exp_hdl, struct gsh_buffdesc *hdl_desc, struct fsal_filesystem **fs, vfs_file_handle_t *fh, bool *dummy) { fsal_errors_t fsal_error = ERR_FSAL_NO_ERROR; int retval = 0; struct fsal_fsid__ fsid; enum fsid_type fsid_type; *fs = NULL; if (!vfs_valid_handle(hdl_desc)) return fsalstat(ERR_FSAL_BADHANDLE, 0); memcpy(fh->handle_data, hdl_desc->addr, hdl_desc->len); fh->handle_len = hdl_desc->len; *dummy = vfs_is_dummy_handle(fh); retval = vfs_extract_fsid(fh, &fsid_type, &fsid); if (retval == 0) { *fs = lookup_fsid(&fsid, fsid_type); if (*fs == NULL) { LogInfo(COMPONENT_FSAL, "Could not map " "fsid=0x%016"PRIx64".0x%016"PRIx64 " to filesytem", fsid.major, fsid.minor); retval = ESTALE; fsal_error = posix2fsal_error(retval); goto errout; } if (((*fs)->fsal != exp_hdl->fsal) && !(*dummy)) { LogInfo(COMPONENT_FSAL, "fsid=0x%016"PRIx64".0x%016"PRIx64 " in handle not a %s filesystem", fsid.major, fsid.minor, exp_hdl->fsal->name); retval = ESTALE; fsal_error = posix2fsal_error(retval); goto errout; } LogDebug(COMPONENT_FSAL, "Found filesystem %s for handle for FSAL %s", (*fs)->path, (*fs)->fsal != NULL ? (*fs)->fsal->name : "(none)"); } else { LogDebug(COMPONENT_FSAL, "Could not map handle to fsid"); fsal_error = ERR_FSAL_BADHANDLE; goto errout; } errout: return fsalstat(fsal_error, retval); }
static int set_attrs(int fd, char *path, mode_t perm, uid_t uid, gid_t gid, const struct timespec *ats, const struct timespec *mts) { #ifdef HAVE_UTIMENSAT struct timespec times[2]; times[0] = *ats; times[1] = *mts; if (utimensat(fd, RELATIVE_PATH(path), times, AT_SYMLINK_NOFOLLOW) == -1){ pkg_emit_error("Fail to set time on %s: %s", path, strerror(errno)); return (EPKG_FATAL); } #else struct timeval tv[2]; char *saved_cwd[MAXPATHLEN]; tv[0].tv_sec = ats->tv_sec; tv[0].tv_usec = ats->tv_nsec / 1000; tv[1].tv_sec = mts->tv_sec; tv[1].tv_usec = mts->tv_nsec / 1000; memset(saved_cwd, 0, sizeof (saved_cwd)); if (getcwd(saved_cwd, sizeof(saved_cwd) - 1) == NULL) { pkg_emit_error("Fail to call getcwd: %s", strerror(errno)); return (EPKG_FATAL); } fchdir(fd); if (lutimes(RELATIVE_PATH(path), tv) == -1) { if (errno != ENOSYS) { pkg_emit_error("Fail to set time on %s: %s", path, strerror(errno)); return (EPKG_FATAL); } else { /* Fallback to utimes */ if (utimes(RELATIVE_PATH(path), tv) == -1) { pkg_emit_error("Fail to set time(fallback) on %s: %s", path, strerror(errno)); return (EPKG_FATAL); } } } chdir(saved_cwd); #endif if (getenv("INSTALL_AS_USER") == NULL) { if (fchownat(fd, RELATIVE_PATH(path), uid, gid, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOTSUP) { if (fchownat(fd, RELATIVE_PATH(path), uid, gid, 0) == -1) { pkg_emit_error("Fail to chown(fallback) %s: %s", path, strerror(errno)); return (EPKG_FATAL); } } else { pkg_emit_error("Fail to chown %s: %s", path, strerror(errno)); return (EPKG_FATAL); } } } /* zfs drops the setuid on fchownat */ if (fchmodat(fd, RELATIVE_PATH(path), perm, AT_SYMLINK_NOFOLLOW) == -1) { if (errno == ENOTSUP) { if (fchmodat(fd, RELATIVE_PATH(path), perm, 0) == -1) { pkg_emit_error("Fail to chmod(fallback) %s: %s", path, strerror(errno)); return (EPKG_FATAL); } } else { pkg_emit_error("Fail to chmod %s: %s", path, strerror(errno)); return (EPKG_FATAL); } } return (EPKG_OK); }
static int do_test (void) { /* fdopendir takes over the descriptor, make a copy. */ int dupfd = dup (dir_fd); if (dupfd == -1) { puts ("dup failed"); return 1; } if (lseek (dupfd, 0, SEEK_SET) != 0) { puts ("1st lseek failed"); return 1; } /* The directory should be empty save the . and .. files. */ DIR *dir = fdopendir (dupfd); if (dir == NULL) { puts ("fdopendir failed"); return 1; } struct dirent64 *d; while ((d = readdir64 (dir)) != NULL) if (strcmp (d->d_name, ".") != 0 && strcmp (d->d_name, "..") != 0) { printf ("temp directory contains file \"%s\"\n", d->d_name); return 1; } closedir (dir); umask (022); /* Try to create a file. */ int fd = openat (dir_fd, "some-file", O_CREAT|O_RDWR|O_EXCL, 0666); if (fd == -1) { if (errno == ENOSYS) { puts ("*at functions not supported"); return 0; } puts ("file creation failed"); return 1; } write (fd, "hello", 5); puts ("file created"); struct stat64 st1; if (fstat64 (fd, &st1) != 0) { puts ("fstat64 failed"); return 1; } /* Before closing the file, try using this file descriptor to open another file. This must fail. */ if (fchmodat (fd, "some-file", 0400, 0) != -1) { puts ("fchmodat using descriptor for normal file worked"); return 1; } if (errno != ENOTDIR) { puts ("\ error for fchmodat using descriptor for normal file not ENOTDIR "); return 1; }
int main() { int fd; fd = open("foobar",O_WRONLY|O_CREAT, 0666); //staptest// [[[[open (!!!!openat (AT_FDCWD, ]]]]"foobar", O_WRONLY|O_CREAT[[[[.O_LARGEFILE]]]]?, 0666) = NNNN chmod("foobar", 0644); //staptest// [[[[chmod (!!!!fchmodat (AT_FDCWD, ]]]]"foobar", 0644) = 0 fchmod(fd, 0444); //staptest// fchmod (NNNN, 0444) = 0 #if GLIBC_SUPPORT fchmodat(AT_FDCWD, "foobar", 0644, 0); //staptest// fchmodat (AT_FDCWD, "foobar", 0644) = 0 #endif chmod((char *)-1, 0644); #ifdef __s390__ //staptest// chmod ([7]?[f]+, 0644) = -NNNN (EFAULT) #else //staptest// [[[[chmod (!!!!fchmodat (AT_FDCWD, ]]]][f]+, 0644[[[[, 0x0]]]]?) = -NNNN (EFAULT) #endif fchmod(-1, 0644); //staptest// [[[[fchmod (!!!!fchmodat (AT_FDCWD, ]]]]-1, 0644[[[[, 0x0]]]]?) = -NNNN (EBADF) #if GLIBC_SUPPORT fchmodat(AT_FDCWD, (char *)-1, 0644, 0); #ifdef __s390__ //staptest// fchmodat (AT_FDCWD, [7]?[f]+, 0644) = -NNNN (EFAULT) #else //staptest// fchmodat (AT_FDCWD, [f]+, 0644) = -NNNN (EFAULT) #endif #endif chmod("foobar", -1); //staptest// [[[[chmod (!!!!fchmodat (AT_FDCWD, ]]]]"foobar", 037777777777) = NNNN fchmod(fd, -1); //staptest// fchmod (NNNN, 037777777777) = NNNN #if GLIBC_SUPPORT fchmodat(AT_FDCWD, "foobar", -1, 0); //staptest// fchmodat (AT_FDCWD, "foobar", 037777777777) = NNNN fchmodat(-1, "foobar", 0644, 0); //staptest// fchmodat (-1, "foobar", 0644) = -NNNN (EBADF) #endif chown((char *)-1, 5000, 5001); #ifdef __s390__ //staptest// chown ([7]?[f]+, 5000, 5001) = -NNNN (EFAULT) #else //staptest// [[[[chown (!!!!fchownat (AT_FDCWD, ]]]][f]+, 5000, 5001[[[[, 0x0]]]]?) = -NNNN (EFAULT) #endif chown("foobar", 5000, -1); //staptest// [[[[chown (!!!!fchownat (AT_FDCWD, ]]]]"foobar", 5000, -1[[[[, 0x0]]]]?) = NNNN chown("foobar", -1, 5001); //staptest// [[[[chown (!!!!fchownat (AT_FDCWD, ]]]]"foobar", -1, 5001[[[[, 0x0]]]]?) = NNNN fchown(-1, 5002, 5003); //staptest// [[[[fchown (!!!!fchownat (AT_FDCWD, ]]]]-1, 5002, 5003[[[[, 0x0]]]]?) = -NNNN (EBADF) fchown(fd, 5002, -1); //staptest// fchown (NNNN, 5002, -1) = NNNN #if GLIBC_SUPPORT fchownat(-1, "foobar", 5002, -1, 0); //staptest// fchownat (-1, "foobar", 5002, -1, 0x0) = -NNNN (EBADF) fchownat(AT_FDCWD, (char *)-1, 5002, -1, 0); #ifdef __s390__ //staptest// fchownat (AT_FDCWD, [7]?[f]+, 5002, -1, 0x0) = -NNNN (EFAULT) #else //staptest// fchownat (AT_FDCWD, [f]+, 5002, -1, 0x0) = -NNNN (EFAULT) #endif fchownat(AT_FDCWD, "foobar", -1, 5000, 0); //staptest// fchownat (AT_FDCWD, "foobar", -1, 5000, 0x0) = NNNN fchownat(AT_FDCWD, "foobar", 5002, -1, 0); //staptest// fchownat (AT_FDCWD, "foobar", 5002, -1, 0x0) = NNNN #endif fchown(fd, -1, 5003); //staptest// fchown (NNNN, -1, 5003) = NNNN lchown("foobar", 5004, -1); //staptest// [[[[lchown (!!!!fchownat (AT_FDCWD, ]]]]"foobar", 5004, -1[[[[, AT_SYMLINK_NOFOLLOW]]]]?) = NNNN lchown("foobar", -1, 5005); //staptest// [[[[lchown (!!!!fchownat (AT_FDCWD, ]]]]"foobar", -1, 5005[[[[, AT_SYMLINK_NOFOLLOW]]]]?) = NNNN lchown((char *)-1, 5005, 5006); #ifdef __s390__ //staptest// lchown ([7]?[f]+, 5005, 5006) = -NNNN (EFAULT) #else //staptest// [[[[lchown (!!!!fchownat (AT_FDCWD, ]]]][f]+, 5005, 5006[[[[, AT_SYMLINK_NOFOLLOW]]]]?) = -NNNN (EFAULT) #endif #if __WORDSIZE != 64 #ifdef SYS_chown syscall(SYS_chown, "foobar", 5000, -1); //staptest// chown[[[[16]]]]? ("foobar", 5000, -1) = NNNN syscall(SYS_chown, "foobar", -1, 5001); //staptest// chown[[[[16]]]]? ("foobar", -1, 5001) = NNNN #endif #ifdef SYS_fchown syscall(SYS_fchown, fd, 5002, -1); //staptest// fchown[[[[16]]]]? (NNNN, 5002, -1) = NNNN syscall(SYS_fchown, fd, -1, 5003); //staptest// fchown[[[[16]]]]? (NNNN, -1, 5003) = NNNN #endif #ifdef SYS_lchown syscall(SYS_lchown, "foobar", 5004, -1); //staptest// lchown[[[[16]]]]? ("foobar", 5004, -1) = NNNN syscall(SYS_lchown, "foobar", -1, 5005); //staptest// lchown[[[[16]]]]? ("foobar", -1, 5005) = NNNN #endif #endif close(fd); return 0; }