int _gst_set_file_access_times (const char *name, long new_atime, long new_mtime) { int result; #if defined HAVE_UTIMES struct timeval times[2]; times[0].tv_sec = new_atime + 86400 * 10957; times[1].tv_sec = new_mtime + 86400 * 10957; times[0].tv_usec = times[1].tv_usec = 0; result = utimes (name, times); #elif defined HAVE_UTIME struct utimbuf utb; utb.actime = new_atime + 86400 * 10957; utb.modtime = new_mtime + 86400 * 10957; result = utime (name, &utb); #else #warning neither utime nor utimes are available. errno = ENOSYS; result = -1; #endif if (!result) errno = 0; return (result); }
int c_utimes(const char *uri, const struct timeval *times) { mbchar_t *wuri = c_utf8_path_to_locale(uri); int ret = utimes(wuri, times); c_free_locale_string(wuri); return ret; }
int extractfile(char *name) { int flags; uid_t uid; gid_t gid; mode_t mode; int extsize; struct timeval mtimep[2], ctimep[2]; struct entry *ep; char *buf; curfile.name = name; curfile.action = USING; mtimep[0].tv_sec = curfile.atime_sec; mtimep[0].tv_usec = curfile.atime_nsec / 1000; mtimep[1].tv_sec = curfile.mtime_sec; mtimep[1].tv_usec = curfile.mtime_nsec / 1000; ctimep[0].tv_sec = curfile.atime_sec; ctimep[0].tv_usec = curfile.atime_nsec / 1000; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_usec = curfile.birthtime_nsec / 1000; extsize = curfile.extsize; uid = getuid(); if (uid == 0) uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; flags = curfile.file_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: lnkbuf[0] = '\0'; pathlen = 0; buf = setupextattr(extsize); getfile(xtrlnkfile, xtrattr, xtrlnkskip); if (pathlen == 0) { vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == GOOD) { if (extsize > 0) set_extattr_link(name, buf, extsize); (void) lchown(name, uid, gid); (void) lchmod(name, mode); (void) lutimes(name, ctimep); (void) lutimes(name, mtimep); (void) lchflags(name, flags); return (GOOD); } return (FAIL); case IFIFO: vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mkfifo(name, 0600) < 0) { fprintf(stderr, "%s: cannot create fifo: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); (void) utimes(name, mtimep); (void) chflags(name, flags); return (GOOD); case IFCHR: case IFBLK: vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if (mknod(name, (mode & (IFCHR | IFBLK)) | 0600, (int)curfile.rdev) < 0) { fprintf(stderr, "%s: cannot create special file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } if (extsize == 0) { skipfile(); } else { buf = setupextattr(extsize); getfile(xtrnull, xtrattr, xtrnull); set_extattr_file(name, buf, extsize); } (void) chown(name, uid, gid); (void) chmod(name, mode); (void) utimes(name, ctimep); (void) utimes(name, mtimep); (void) chflags(name, flags); return (GOOD); case IFREG: vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (uflag) (void) unlink(name); if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) { fprintf(stderr, "%s: cannot create file: %s\n", name, strerror(errno)); skipfile(); return (FAIL); } buf = setupextattr(extsize); getfile(xtrfile, xtrattr, xtrskip); if (extsize > 0) set_extattr_fd(ofile, name, buf, extsize); (void) fchown(ofile, uid, gid); (void) fchmod(ofile, mode); (void) futimes(ofile, ctimep); (void) futimes(ofile, mtimep); (void) fchflags(ofile, flags); (void) close(ofile); return (GOOD); } /* NOTREACHED */ }
static void process_fsetstat(u_int32_t id) { Attrib a; int handle, fd, r; int status = SSH2_FX_OK; if ((r = get_handle(iqueue, &handle)) != 0 || (r = decode_attrib(iqueue, &a)) != 0) fatal("%s: buffer error: %s", __func__, ssh_err(r)); debug("request %u: fsetstat handle %d", id, handle); fd = handle_to_fd(handle); if (fd < 0) status = SSH2_FX_FAILURE; else { char *name = handle_to_name(handle); if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { logit("set \"%s\" size %llu", name, (unsigned long long)a.size); r = ftruncate(fd, a.size); if (r == -1) status = errno_to_portable(errno); } if (a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { logit("set \"%s\" mode %04o", name, a.perm); #ifdef HAVE_FCHMOD r = fchmod(fd, a.perm & 07777); #else r = chmod(name, a.perm & 07777); #endif if (r == -1) status = errno_to_portable(errno); } if (a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { char buf[64]; time_t t = a.mtime; strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", localtime(&t)); logit("set \"%s\" modtime %s", name, buf); #ifdef HAVE_FUTIMES r = futimes(fd, attrib_to_tv(&a)); #else r = utimes(name, attrib_to_tv(&a)); #endif if (r == -1) status = errno_to_portable(errno); } if (a.flags & SSH2_FILEXFER_ATTR_UIDGID) { logit("set \"%s\" owner %lu group %lu", name, (u_long)a.uid, (u_long)a.gid); #ifdef HAVE_FCHOWN r = fchown(fd, a.uid, a.gid); #else r = chown(name, a.uid, a.gid); #endif if (r == -1) status = errno_to_portable(errno); } } send_status(id, status); }
int setfile(struct stat *fs, int fd) { static struct timeval tv[2]; struct stat ts; int rval, gotstat, islink, fdval; rval = 0; fdval = fd != -1; islink = !fdval && S_ISLNK(fs->st_mode); fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); if (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv)) { warn("%sutimes: %s", islink ? "l" : "", to.p_path); rval = 1; } if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) : stat(to.p_path, &ts))) gotstat = 0; else { gotstat = 1; ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; } /* * 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 (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ? lchown(to.p_path, fs->st_uid, fs->st_gid) : chown(to.p_path, fs->st_uid, fs->st_gid))) { if (errno != EPERM) { warn("chown: %s", to.p_path); rval = 1; } fs->st_mode &= ~(S_ISUID | S_ISGID); } if (!gotstat || fs->st_mode != ts.st_mode) if (fdval ? fchmod(fd, fs->st_mode) : (islink ? lchmod(to.p_path, fs->st_mode) : chmod(to.p_path, fs->st_mode))) { warn("chmod: %s", to.p_path); rval = 1; } if (!gotstat || fs->st_flags != ts.st_flags) if (fdval ? fchflags(fd, fs->st_flags) : (islink ? lchflags(to.p_path, fs->st_flags) : chflags(to.p_path, fs->st_flags))) { warn("chflags: %s", to.p_path); rval = 1; } return (rval); }
static void copy_file_attributes(int infd, int outfd, const char *outfile) { #ifdef WIN32 BY_HANDLE_FILE_INFORMATION fi; BOOL bOk; bOk = GetFileInformationByHandle((HANDLE)_get_osfhandle(infd), &fi); trace("GetFileInformationByHandle(...) => %s\n", bOk ? "TRUE" : "FALSE"); if (bOk) { bOk = SetFileTime((HANDLE)_get_osfhandle(outfd), NULL, &fi.ftLastAccessTime, &fi.ftLastWriteTime); trace("SetFileTime(...) => %s\n", bOk ? "TRUE" : "FALSE"); bOk = SetFileAttributesA(outfile, fi.dwFileAttributes); trace("SetFileAttributesA(...) => %s\n", bOk ? "TRUE" : "FALSE"); } #else struct stat sbuf; #ifdef HAVE_FUTIMENS struct timespec times[2]; #else struct timeval times[2]; #endif int rv; if ((rv = fstat(infd, &sbuf)) != 0) { trace("fstat(%d, &sbuf) => %d (errno = %d)\n", infd, rv, errno); return; } /* copy file times. */ #ifdef HAVE_FUTIMENS times[0].tv_sec = sbuf.st_atime; times[0].tv_nsec = SNZ_ST_TIME_NSEC(sbuf, a); times[1].tv_sec = sbuf.st_mtime; times[1].tv_nsec = SNZ_ST_TIME_NSEC(sbuf, m); rv = futimens(outfd, times); trace("futimens(%d, [{%ld, %ld}, {%ld, %ld}]) => %d\n", outfd, times[0].tv_sec, times[0].tv_nsec, times[1].tv_sec, times[1].tv_nsec, rv); #else /* HAVE_FUTIMENS */ times[0].tv_sec = sbuf.st_atime; times[0].tv_usec = SNZ_ST_TIME_NSEC(sbuf, a) / 1000; times[1].tv_sec = sbuf.st_mtime; times[1].tv_usec = SNZ_ST_TIME_NSEC(sbuf, m) / 1000; #ifdef HAVE_FUTIMES rv = futimes(outfd, times); trace("futimes(%d, [{%ld, %ld}, {%ld, %ld}]) => %d\n", outfd, times[0].tv_sec, times[0].tv_usec, times[1].tv_sec, times[1].tv_usec, rv); #else /* HAVE_FUTIMES */ rv = utimes(outfile, times); trace("utimes(\"%s\", [{%ld, %ld}, {%ld, %ld}]) => %d\n", outfile, times[0].tv_sec, times[0].tv_usec, times[1].tv_sec, times[1].tv_usec, rv); #endif /* HAVE_FUTIMES */ #endif /* HAVE_FUTIMENS */ /* copy other attributes */ rv = fchown(outfd, sbuf.st_uid, sbuf.st_gid); trace("fchown(%d, %d, %d) => %d\n", outfd, sbuf.st_uid, sbuf.st_gid, rv); rv = fchmod(outfd, sbuf.st_mode); trace("fchmod(%d, 0%o) => %d\n", outfd, sbuf.st_mode, rv); #endif }
int Sg_Utimes(SgString *path, SgObject atime, SgObject mtime) { SgObject absolutePath; char *c_path; if (!Sg_AbsolutePathP(path)) { absolutePath = Sg_AbsolutePath(path); if (SG_FALSEP(absolutePath)) { return FALSE; /* file not exists? */ } } else { absolutePath = SG_OBJ(path); } if (!Sg_FileExistP(absolutePath)) return FALSE; /* okay file not exists */ c_path = Sg_Utf32sToUtf8s(absolutePath); #if defined(HAVE_UTIMENSAT) { #define set_time(ts, time) \ do { \ if (SG_TIMEP(time) || SG_REALP(time)) { \ Sg_GetTimeSpec(time, ts); \ } else if (SG_FALSEP(time)) { \ (ts)->tv_sec = 0; \ (ts)->tv_nsec = UTIME_OMIT; \ } else { \ (ts)->tv_sec = 0; \ (ts)->tv_nsec = UTIME_NOW; \ } \ } while (0) struct timespec times[2]; set_time(×[0], atime); set_time(×[1], mtime); return utimensat(0, c_path, times, AT_SYMLINK_NOFOLLOW) == 0; #undef set_time } #elif defined(HAVE_UTIMES) { #define set_time(tv, time, m) \ do { \ if (SG_TIMEP(time) || SG_REALP(time)) { \ struct timespec ts; \ Sg_GetTimeSpec(time, &ts); \ (tv)->tv_sec = ts.tv_sec; \ (tv)->tv_usec = ts.tv_nsec / 1000; \ } else if (SG_FALSEP(time)) { \ /* emulate UTIME_OMIT */ \ struct stat st; \ if (stat(c_path, &st) == 0) { \ (tv)->tv_sec = st. m; \ (tv)->tv_usec = 0; \ } else { \ /* sorry */ \ return FALSE; \ } \ } else { \ /* emulate UTIME_NOW */ \ unsigned long sec, usec; \ Sg_GetTimeOfDay(&sec, &usec); \ (tv)->tv_sec = sec; \ (tv)->tv_usec = usec; \ } \ } while (0) struct timeval times[2]; set_time(×[0], atime, st_atime); set_time(×[1], mtime, st_mtime); return utimes(c_path, times) == 0; #undef set_time } #else /* just indicate failed to change */ return FALSE; #endif }
int run_attr_tests (char *testfile) { int ret = -1; char *res = NULL; struct stat buf; struct statfs sbuf; struct statvfs svbuf; assert (testfile); fprintf (stdout, "Testing chmod"); ret = chmod (testfile, 0); check_err (ret, "chmod", 2); fprintf (stdout, "Testing chown"); ret = chown (testfile, 0, 0); check_err (ret, "chown", 2); fprintf (stdout, "Testing link"); ret = link (testfile, testfile); check_err (ret, "link", 2); fprintf (stdout, "Testing rename"); ret = rename (testfile, testfile); check_err (ret, "rename", 2); fprintf (stdout, "Testing utimes"); ret = utimes (testfile, NULL); check_err (ret, "utimes", 2); fprintf (stdout, "Testing utime"); ret = utime (testfile, NULL); check_err (ret, "utime", 2); fprintf (stdout, "Testing unlink"); ret = unlink (testfile); check_err (ret, "unlink", 2); fprintf (stdout, "Testing symlink"); ret = symlink (testfile, testfile); check_err (ret, "symlink", 2); fprintf (stdout, "Testing readlink"); ret = readlink (testfile, testfile, 0); check_err (ret, "readlink", 2); fprintf (stdout, "Testing realpath"); ret = 0; res = realpath ((const char *)testfile, testfile); if (!res) ret = -1; check_err (ret, "realpath", 2); fprintf (stdout, "Testing stat"); ret = stat (testfile, &buf); check_err (ret, "stat", 1); fprintf (stdout, "Testing lstat"); ret = lstat (testfile, &buf); check_err (ret, "lstat", 1); fprintf (stdout, "Testing statfs"); ret = statfs (testfile, &sbuf); check_err (ret, "statfs", 2); fprintf (stdout, "Testing statvfs"); ret = statvfs (testfile, &svbuf); check_err (ret, "statvfs", 1); fprintf (stdout, "Testing getxattr"); ret = getxattr (testfile, NULL, NULL, 0); check_err (ret, "getxattr", 2); fprintf (stdout, "Testing lgetxattr"); ret = lgetxattr (testfile, NULL, NULL, 0); check_err (ret, "lgetxattr", 1); fprintf (stdout, "Testing lchown"); ret = lchown (testfile, 0, 0); check_err (ret, "lchown", 2); return 0; }
void SFTP::process_setstat(void) { Attrib a; u_int32_t id; char *utf8_name; int status = SSH2_FX_OK; WIN32_FILE_ATTRIBUTE_DATA st; try { id = get_int(); utf8_name = (char*) get_string(NULL); a = get_attrib(this->iqueue); debug("request %u: setstat name \"%s\"", id, utf8_name); const SFTPFilePath path = pathFact.create_path (utf8_name); attrib_to_stat (&a, &st); if (a.flags & SSH2_FILEXFER_ATTR_SIZE) { #if 0 logit("set \"%s\" size %llu", utf8_name, (unsigned long long)a->size); LARGE_INTEGER largeOffset; largeOffset.QuadPart = a->size; if (!::SetFilePointerEx (fh, largeOffset, NULL, FILE_BEGIN) || !::SetEndOfFile (fh) ) { status = errno_to_portable(::GetLastError ()); } #endif status = SSH2_FX_OP_UNSUPPORTED; } if (status == SSH2_FX_OK && a.flags & SSH2_FILEXFER_ATTR_ACMODTIME) { #if 0 char buf[64]; time_t t = a->mtime; strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", localtime(&t)); logit("set \"%s\" modtime %s", name, buf); ret = utimes(name, attrib_to_tv(a)); if (ret == -1) status = errno_to_portable(::GetLastError ()); #endif status = SSH2_FX_OP_UNSUPPORTED; } if (status == SSH2_FX_OK && a.flags & SSH2_FILEXFER_ATTR_UIDGID) { #if 0 logit("set \"%s\" owner %lu group %lu", name, (u_long)a->uid, (u_long)a->gid); ret = chown(name, a->uid, a->gid); if (ret == -1) status = errno_to_portable(::GetLastError ()); #endif status = SSH2_FX_OP_UNSUPPORTED; } // access premissions if (status == SSH2_FX_OK && a.flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { logit("set \"%s\" mode %04o", utf8_name, a.perm); if (!::SetFileAttributesW (path.get_for_call ().c_str (), st.dwFileAttributes) ) { status = errno_to_portable(::GetLastError ()); } } } catch (Path::InvalidPath&) { //logit status = SSH2_FX_FAILURE; // TODO return the reason } catch (...) { status = SSH2_FX_FAILURE; error ("unhandled exception in %s", __FUNCTION__); } send_status(id, status); if (utf8_name) xfree(utf8_name); }
int extractfile(char *name) { u_int flags; uid_t uid; gid_t gid; mode_t mode; struct timeval mtimep[2], ctimep[2]; struct entry *ep; int setbirth; curfile.name = name; curfile.action = USING; mtimep[0].tv_sec = curfile.atime_sec; mtimep[0].tv_usec = curfile.atime_nsec / 1000; mtimep[1].tv_sec = curfile.mtime_sec; mtimep[1].tv_usec = curfile.mtime_nsec / 1000; setbirth = curfile.birthtime_sec != 0; if (setbirth) { ctimep[0].tv_sec = curfile.atime_sec; ctimep[0].tv_usec = curfile.atime_nsec / 1000; ctimep[1].tv_sec = curfile.birthtime_sec; ctimep[1].tv_usec = curfile.birthtime_nsec / 1000; } uid = curfile.uid; gid = curfile.gid; mode = curfile.mode; flags = curfile.file_flags; switch (mode & IFMT) { default: fprintf(stderr, "%s: unknown file mode 0%o\n", name, mode); skipfile(); return (FAIL); case IFSOCK: Vprintf(stdout, "skipped socket %s\n", name); skipfile(); return (GOOD); case IFDIR: if (mflag) { ep = lookupname(name); if (ep == NULL || ep->e_flags & EXTRACT) panic("unextracted directory %s\n", name); skipfile(); return (GOOD); } Vprintf(stdout, "extract file %s\n", name); return (genliteraldir(name, curfile.ino)); case IFLNK: { lnkbuf[0] = '\0'; pathlen = 0; getfile(xtrlnkfile, xtrlnkskip); if (pathlen == 0) { Vprintf(stdout, "%s: zero length symbolic link (ignored)\n", name); return (GOOD); } if (linkit(lnkbuf, name, SYMLINK) == FAIL) return (FAIL); (void)lchown(name, uid, gid); return (GOOD); } case IFCHR: case IFBLK: Vprintf(stdout, "extract special file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (mknod(name, mode, (int)curfile.rdev) < 0) { warn("%s: cannot create special file", name); skipfile(); return (FAIL); } (void)chown(name, uid, gid); (void)chmod(name, mode); (void)chflags(name, flags); skipfile(); if (setbirth) (void)utimes(name, ctimep); (void)utimes(name, mtimep); return (GOOD); case IFIFO: Vprintf(stdout, "extract fifo %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if (mkfifo(name, mode) < 0) { warn("%s: cannot create fifo", name); skipfile(); return (FAIL); } (void)chown(name, uid, gid); (void)chmod(name, mode); (void)chflags(name, flags); skipfile(); if (setbirth) (void)utimes(name, ctimep); (void)utimes(name, mtimep); return (GOOD); case IFREG: Vprintf(stdout, "extract file %s\n", name); if (Nflag) { skipfile(); return (GOOD); } if ((ofile = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0) { warn("%s: cannot create file", name); skipfile(); return (FAIL); } (void)fchown(ofile, curfile.uid, curfile.gid); (void)fchmod(ofile, mode); (void)fchflags(ofile, flags); getfile(xtrfile, xtrskip); (void)close(ofile); if (setbirth) (void)utimes(name, ctimep); (void)utimes(name, mtimep); return (GOOD); } /* NOTREACHED */ }
static int rcsdiff_rev(RCSFILE *file, RCSNUM *rev1, RCSNUM *rev2, int dflags) { struct timeval tv[2], tv2[2]; BUF *b1, *b2; int ret; char *path1, *path2, rbuf1[RCS_REV_BUFSZ], rbuf2[RCS_REV_BUFSZ]; ret = D_ERROR; b1 = b2 = NULL; memset(&tv, 0, sizeof(tv)); memset(&tv2, 0, sizeof(tv2)); diff_rev1 = rev1; diff_rev2 = rev2; path1 = path2 = NULL; rcsnum_tostr(rev1, rbuf1, sizeof(rbuf1)); if (!quiet) fprintf(stderr, "retrieving revision %s\n", rbuf1); if ((b1 = rcs_getrev(file, rev1)) == NULL) { warnx("failed to retrieve revision %s", rbuf1); goto out; } b1 = rcs_kwexp_buf(b1, file, rev1); tv[0].tv_sec = (long)rcs_rev_getdate(file, rev1); tv[1].tv_sec = tv[0].tv_sec; rcsnum_tostr(rev2, rbuf2, sizeof(rbuf2)); if (!quiet) fprintf(stderr, "retrieving revision %s\n", rbuf2); if ((b2 = rcs_getrev(file, rev2)) == NULL) { warnx("failed to retrieve revision %s", rbuf2); goto out; } b2 = rcs_kwexp_buf(b2, file, rev2); tv2[0].tv_sec = (long)rcs_rev_getdate(file, rev2); tv2[1].tv_sec = tv2[0].tv_sec; if (!quiet) fprintf(stderr, "%s -r%s -r%s\n", diffargs, rbuf1, rbuf2); (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); buf_write_stmp(b1, path1); buf_free(b1); b1 = NULL; if (utimes(path1, (const struct timeval *)&tv) < 0) warn("utimes"); (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); buf_write_stmp(b2, path2); buf_free(b2); b2 = NULL; if (utimes(path2, (const struct timeval *)&tv2) < 0) warn("utimes"); ret = diffreg(path1, path2, NULL, dflags); out: if (b1 != NULL) buf_free(b1); if (b2 != NULL) buf_free(b2); if (path1 != NULL) xfree(path1); if (path2 != NULL) xfree(path2); return (ret); }
static int rcsdiff_file(RCSFILE *file, RCSNUM *rev, const char *filename, int dflags) { int ret, fd; time_t t; struct stat st; char *path1, *path2; BUF *b1, *b2; char rbuf[RCS_REV_BUFSZ]; struct tm *tb; struct timeval tv[2], tv2[2]; memset(&tv, 0, sizeof(tv)); memset(&tv2, 0, sizeof(tv2)); ret = D_ERROR; b1 = b2 = NULL; diff_rev1 = rev; diff_rev2 = NULL; path1 = path2 = NULL; if ((fd = open(filename, O_RDONLY)) == -1) { warn("%s", filename); goto out; } rcsnum_tostr(rev, rbuf, sizeof(rbuf)); if (!quiet) { fprintf(stderr, "retrieving revision %s\n", rbuf); fprintf(stderr, "%s -r%s %s\n", diffargs, rbuf, filename); } if ((b1 = rcs_getrev(file, rev)) == NULL) { warnx("failed to retrieve revision %s", rbuf); goto out; } b1 = rcs_kwexp_buf(b1, file, rev); tv[0].tv_sec = (long)rcs_rev_getdate(file, rev); tv[1].tv_sec = tv[0].tv_sec; if ((b2 = buf_load(filename)) == NULL) { warnx("failed to load file: `%s'", filename); goto out; } /* XXX - GNU uses GMT */ if (fstat(fd, &st) == -1) err(D_ERROR, "%s", filename); tb = gmtime(&st.st_mtime); t = mktime(tb); tv2[0].tv_sec = t; tv2[1].tv_sec = t; (void)xasprintf(&path1, "%s/diff1.XXXXXXXXXX", rcs_tmpdir); buf_write_stmp(b1, path1); buf_free(b1); b1 = NULL; if (utimes(path1, (const struct timeval *)&tv) < 0) warn("utimes"); (void)xasprintf(&path2, "%s/diff2.XXXXXXXXXX", rcs_tmpdir); buf_write_stmp(b2, path2); buf_free(b2); b2 = NULL; if (utimes(path2, (const struct timeval *)&tv2) < 0) warn("utimes"); ret = diffreg(path1, path2, NULL, dflags); out: if (fd != -1) (void)close(fd); if (b1 != NULL) buf_free(b1); if (b2 != NULL) buf_free(b2); if (path1 != NULL) xfree(path1); if (path2 != NULL) xfree(path2); return (ret); }
/* For regular file, we're going to: * Find file with latest modified time * copy it to work directory on target * move it to proper raid location * update file times to match original */ static int __replication_copy_regular_file(const char *relative_path, RaidVolume_t *to_vol) { EXLog(REPL, DBG, " > __replication_copy_regular_file [%s > %s]", relative_path, to_vol->basepath); RaidVolume_t *from_vol; char fullpath_from[PATH_MAX]; struct stat stbuf; /* Find volume w/ latest modified time */ int not_found = volume_most_recently_modified_instance(relative_path, &from_vol, fullpath_from, &stbuf); if (not_found || ((stbuf.st_mode & S_IFMT) != S_IFREG)) { EXLog(REPL, DBG, " > Path is not a regular file"); return 0; } /* Get target work path */ char workpath[PATH_MAX]; if (!volume_avaialble_work_path(to_vol, workpath)) { EXLog(REPL, DBG, " > Volume does not have an available work path"); __replication_queue_volume_check(to_vol); return 0; } /* Copy source -> dest */ #define REGFILE_COPY_CHUNK_SIZE 32768 char copy_buffer[REGFILE_COPY_CHUNK_SIZE]; int from_fd = open(fullpath_from, O_RDONLY); if (from_fd < 0) { EXLog(REPL, DBG, " > Could not open path for reading [%s]", fullpath_from); __replication_queue_volume_check(from_vol); return 0; } int to_fd = open(workpath, O_CREAT | O_WRONLY | O_APPEND, stbuf.st_mode); if (to_fd < 0) { close(from_fd); EXLog(REPL, DBG, " > Could not open path for writing [%s]", workpath); __replication_queue_volume_check(to_vol); return 0; } VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Reading %s", relative_path); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Writing %s", relative_path); /* Both files are open */ while (1) { ssize_t bytes_read = read(from_fd, copy_buffer, (size_t)REGFILE_COPY_CHUNK_SIZE); if (bytes_read < 0) { /* Shit, there was an error. Shut it all down */ close(from_fd); close(to_fd); unlink(workpath); EXLog(REPL, DBG, " > Read error on path [%s]", fullpath_from); __replication_queue_volume_check(from_vol); VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Idle"); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Idle"); return 0; } if (bytes_read == 0) { /* We hit the end of the file, wrap it up! */ close(from_fd); close(to_fd); char topath[PATH_MAX]; volume_full_path_for_raid_path(to_vol, relative_path, topath); rename(workpath, topath); struct timeval _times[2]; #ifdef __APPLE__ _times[0].tv_sec = stbuf.st_atimespec.tv_sec; _times[0].tv_usec = 0; _times[1].tv_sec = stbuf.st_mtimespec.tv_sec; _times[1].tv_usec = 0; #else _times[0].tv_sec = stbuf.st_atime; _times[0].tv_usec = 0; _times[1].tv_sec = stbuf.st_mtime; _times[1].tv_usec = 0; #endif utimes(topath, _times); VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Idle"); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Idle"); return 1; } /* Otherwise write to workpath */ ssize_t bytes_left = bytes_read; char *bufptr = copy_buffer; while (bytes_left) { /* Check for halting */ if (s_replication_halt_replication_of_file) { s_replication_halt_replication_of_file = 0; pthread_mutex_lock(&s_replication_halt_replication_mutex); if (s_replication_halt_replication_of_file_emergency || !strncmp(s_replication_halt_replication_of_file_path, relative_path, PATH_MAX)) { /* Shut it down! */ close(from_fd); close(to_fd); unlink(workpath); EXLog(REPL, DBG, " > Shutting down because path [%s] was opened by the OS", s_replication_halt_replication_of_file_path); /* On emergency, delay by 1 second for other threads to activate */ if (s_replication_halt_replication_of_file_emergency) { s_replication_halt_replication_of_file_emergency = 0; usleep(1000); } pthread_mutex_unlock(&s_replication_halt_replication_mutex); VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Idle"); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Idle"); return 0; } pthread_mutex_unlock(&s_replication_halt_replication_mutex); } ssize_t bytes_written = write(to_fd, bufptr, (size_t)bytes_left); if (bytes_written < 0) { /* Shut it all down! */ close(from_fd); close(to_fd); unlink(workpath); EXLog(REPL, DBG, " > Write error on path [%s]", workpath); __replication_queue_volume_check(to_vol); VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Idle"); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Idle"); return 0; } /* Otherwise ok */ bytes_left -= bytes_written; bufptr += bytes_written; } } VOLUME_UPDATE_REPLICATION_STATUS_STRING(from_vol, "Idle"); VOLUME_UPDATE_REPLICATION_STATUS_STRING(to_vol, "Idle"); return 0; }
/* * copy_file - copy a file * * Copy a file from src to dst. * * statp, mt, old_uid, new_uid, old_gid, and new_gid are used to set * the access and modification and the access rights. * * Return 0 on success, -1 on error. */ static int copy_file (const char *src, const char *dst, bool reset_selinux, const struct stat *statp, const struct timeval mt[], uid_t old_uid, uid_t new_uid, gid_t old_gid, gid_t new_gid) { int err = 0; int ifd; int ofd; char buf[1024]; ssize_t cnt; ifd = open (src, O_RDONLY); if (ifd < 0) { return -1; } #ifdef WITH_SELINUX if (set_selinux_file_context (dst) != 0) { return -1; } #endif /* WITH_SELINUX */ ofd = open (dst, O_WRONLY | O_CREAT | O_TRUNC, statp->st_mode & 07777); if ( (ofd < 0) || (fchown_if_needed (ofd, statp, old_uid, new_uid, old_gid, new_gid) != 0) #ifdef WITH_ACL || ( (perm_copy_fd (src, ifd, dst, ofd, &ctx) != 0) && (errno != 0)) #else /* !WITH_ACL */ || (fchmod (ofd, statp->st_mode & 07777) != 0) #endif /* !WITH_ACL */ #ifdef WITH_ATTR /* * If the third parameter is NULL, all extended attributes * except those that define Access Control Lists are copied. * ACLs are excluded by default because copying them between * file systems with and without ACL support needs some * additional logic so that no unexpected permissions result. */ || ( !reset_selinux && (attr_copy_fd (src, ifd, dst, ofd, NULL, &ctx) != 0) && (errno != 0)) #endif /* WITH_ATTR */ ) { (void) close (ifd); return -1; } while ((cnt = read (ifd, buf, sizeof buf)) > 0) { if (write (ofd, buf, (size_t)cnt) != cnt) { (void) close (ifd); return -1; } } (void) close (ifd); #ifdef HAVE_FUTIMES if (futimes (ofd, mt) != 0) { return -1; } #endif /* HAVE_FUTIMES */ if (close (ofd) != 0) { return -1; } #ifndef HAVE_FUTIMES if (utimes(dst, mt) != 0) { return -1; } #endif /* !HAVE_FUTIMES */ return err; }
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 set_attr(const char *file, struct hostfs_iattr *attrs, int fd) { struct hostfs_stat st; struct timeval times[2]; int err, ma; if (attrs->ia_valid & HOSTFS_ATTR_MODE) { if (fd >= 0) { if (fchmod(fd, attrs->ia_mode) != 0) return -errno; } else if (chmod(file, attrs->ia_mode) != 0) { return -errno; } } if (attrs->ia_valid & HOSTFS_ATTR_UID) { if (fd >= 0) { if (fchown(fd, attrs->ia_uid, -1)) return -errno; } else if (chown(file, attrs->ia_uid, -1)) { return -errno; } } if (attrs->ia_valid & HOSTFS_ATTR_GID) { if (fd >= 0) { if (fchown(fd, -1, attrs->ia_gid)) return -errno; } else if (chown(file, -1, attrs->ia_gid)) { return -errno; } } if (attrs->ia_valid & HOSTFS_ATTR_SIZE) { if (fd >= 0) { if (ftruncate(fd, attrs->ia_size)) return -errno; } else if (truncate(file, attrs->ia_size)) { return -errno; } } /* * Update accessed and/or modified time, in two parts: first set * times according to the changes to perform, and then call futimes() * or utimes() to apply them. */ ma = (HOSTFS_ATTR_ATIME_SET | HOSTFS_ATTR_MTIME_SET); if (attrs->ia_valid & ma) { err = stat_file(file, &st, fd); if (err != 0) return err; times[0].tv_sec = st.atime.tv_sec; times[0].tv_usec = st.atime.tv_nsec / 1000; times[1].tv_sec = st.mtime.tv_sec; times[1].tv_usec = st.mtime.tv_nsec / 1000; if (attrs->ia_valid & HOSTFS_ATTR_ATIME_SET) { times[0].tv_sec = attrs->ia_atime.tv_sec; times[0].tv_usec = attrs->ia_atime.tv_nsec / 1000; } if (attrs->ia_valid & HOSTFS_ATTR_MTIME_SET) { times[1].tv_sec = attrs->ia_mtime.tv_sec; times[1].tv_usec = attrs->ia_mtime.tv_nsec / 1000; } if (fd >= 0) { if (futimes(fd, times) != 0) return -errno; } else if (utimes(file, times) != 0) { return -errno; } } /* Note: ctime is not handled */ if (attrs->ia_valid & (HOSTFS_ATTR_ATIME | HOSTFS_ATTR_MTIME)) { err = stat_file(file, &st, fd); attrs->ia_atime = st.atime; attrs->ia_mtime = st.mtime; if (err != 0) return err; } return 0; }
static void process_fsetstat(u_int32_t id) { Attrib *a; int handle, fd, ret; int status = SSH2_FX_OK; handle = get_handle(); a = get_attrib(); debug("request %u: fsetstat handle %d", id, handle); fd = handle_to_fd(handle); if (fd < 0) status = SSH2_FX_FAILURE; else { char *name = handle_to_name(handle); if (a->flags & SSH2_FILEXFER_ATTR_SIZE) { logit("set \"%s\" size %llu", name, (unsigned long long)a->size); ret = ftruncate(fd, a->size); if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_PERMISSIONS) { logit("set \"%s\" mode %04o", name, a->perm); #ifdef HAVE_FCHMOD ret = fchmod(fd, a->perm & 07777); #else ret = chmod(name, a->perm & 07777); #endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_ACMODTIME) { char buf[64]; time_t t = a->mtime; strftime(buf, sizeof(buf), "%Y%m%d-%H:%M:%S", localtime(&t)); logit("set \"%s\" modtime %s", name, buf); #ifdef HAVE_FUTIMES ret = futimes(fd, attrib_to_tv(a)); #else ret = utimes(name, attrib_to_tv(a)); #endif if (ret == -1) status = errno_to_portable(errno); } if (a->flags & SSH2_FILEXFER_ATTR_UIDGID) { logit("set \"%s\" owner %lu group %lu", name, (u_long)a->uid, (u_long)a->gid); #ifdef HAVE_FCHOWN ret = fchown(fd, a->uid, a->gid); #else ret = chown(name, a->uid, a->gid); #endif if (ret == -1) status = errno_to_portable(errno); } } send_status(id, status); }
BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input) { char* s = NULL; mode_t m; UINT64 size; int status; char* fullpath; struct STAT st; struct timeval tv[2]; UINT64 LastWriteTime; UINT32 FileAttributes; UINT32 FileNameLength; m = 0; switch (FsInformationClass) { case FileBasicInformation: /* http://msdn.microsoft.com/en-us/library/cc232094.aspx */ Stream_Seek_UINT64(input); /* CreationTime */ Stream_Seek_UINT64(input); /* LastAccessTime */ Stream_Read_UINT64(input, LastWriteTime); Stream_Seek_UINT64(input); /* ChangeTime */ Stream_Read_UINT32(input, FileAttributes); if (FSTAT(file->fd, &st) != 0) return FALSE; tv[0].tv_sec = st.st_atime; tv[0].tv_usec = 0; tv[1].tv_sec = (LastWriteTime > 0 ? FILE_TIME_RDP_TO_SYSTEM(LastWriteTime) : st.st_mtime); tv[1].tv_usec = 0; #ifndef WIN32 /* TODO on win32 */ #ifdef ANDROID utimes(file->fullpath, tv); #else futimes(file->fd, tv); #endif if (FileAttributes > 0) { m = st.st_mode; if ((FileAttributes & FILE_ATTRIBUTE_READONLY) == 0) m |= S_IWUSR; else m &= ~S_IWUSR; if (m != st.st_mode) fchmod(file->fd, m); } #endif break; case FileEndOfFileInformation: /* http://msdn.microsoft.com/en-us/library/cc232067.aspx */ case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ Stream_Read_UINT64(input, size); if (ftruncate(file->fd, size) != 0) return FALSE; break; case FileDispositionInformation: /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */ if (Length) Stream_Read_UINT8(input, file->delete_pending); else file->delete_pending = 1; if (file->delete_pending && file->is_dir) { /* mstsc causes this to FAIL if the directory is not empty, * and that's what the server is expecting. If we wait for * the close to flag a failure, cut and paste of a folder * will lose the folder's contents. */ int status; status = rmdir(file->fullpath); if (status == 0) { /* Put it back so the normal pending delete will work. */ mkdir(file->fullpath, 0755); } else { return FALSE; } } break; case FileRenameInformation: /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */ Stream_Seek_UINT8(input); /* ReplaceIfExists */ Stream_Seek_UINT8(input); /* RootDirectory */ Stream_Read_UINT32(input, FileNameLength); status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(input), FileNameLength / 2, &s, 0, NULL, NULL); if (status < 1) s = (char*) calloc(1, 1); fullpath = drive_file_combine_fullpath(file->basepath, s); free(s); /* TODO rename does not work on win32 */ if (rename(file->fullpath, fullpath) == 0) { DEBUG_SVC("renamed %s to %s", file->fullpath, fullpath); drive_file_set_fullpath(file, fullpath); } else { DEBUG_WARN("rename %s to %s failed, errno = %d", file->fullpath, fullpath, errno); free(fullpath); return FALSE; } break; default: DEBUG_WARN("invalid FsInformationClass %d", FsInformationClass); return FALSE; } return TRUE; }
/* * Periodically test network connection. On signals, determine what * happened or what to do with child. Return as necessary for exit * or restart of child. */ int ssh_watch(int sock) { int r; int val; static int secs_left; int my_poll_time = first_poll_time; time_t now; double secs_to_shutdown; #if defined(HAVE_SETPROCTITLE) setproctitle("parent of %d (%d)", (int)cchild, start_count); #endif for (;;) { if (restart_ssh) { errlog(LOG_INFO, "signalled to kill and restart ssh"); ssh_kill(); return P_RESTART; } if ((val = sigsetjmp(jumpbuf, 1)) == 0) { errlog(LOG_DEBUG, "check on child %d", cchild); /* poll for expired child */ r = ssh_wait(WNOHANG); if (r != P_CONTINUE) { errlog(LOG_DEBUG, "expired child, returning %d", r); return r; } secs_left = alarm(0); if (secs_left == 0) secs_left = my_poll_time; my_poll_time = poll_time; if (max_lifetime != 0) { time(&now); secs_to_shutdown = max_lifetime - difftime(now,pid_start_time); if (secs_to_shutdown < poll_time) secs_left = secs_to_shutdown; } errlog(LOG_DEBUG, "set alarm for %d secs", secs_left); alarm(secs_left); dolongjmp = 1; pause(); } else { switch(val) { case SIGINT: case SIGTERM: case SIGQUIT: case SIGABRT: errlog(LOG_INFO, "received signal to exit (%d)", val); ssh_kill(); return P_EXIT; break; case SIGALRM: if (exceeded_lifetime()) { ssh_kill(); return P_EXIT; } if (writep && sock != -1 && !conn_test(sock, mhost, writep)) { errlog(LOG_INFO, "port down, restarting ssh"); ssh_kill(); return P_RESTART; } #ifdef TOUCH_PIDFILE /* * utimes() with a NULL time argument sets * file access and modification times to * the current time */ if (pid_file_name && utimes(pid_file_name, NULL) != 0) { errlog(LOG_ERR, "could not touch pid file: %s", strerror(errno)); } #endif break; default: break; } } } }
int copy_file_recursive(const char *source, const char *dest) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; int retval = 0; int dest_exists = 0; if (strcmp(source, ".lock") == 0) goto skip; if (stat(source, &source_stat) < 0) { perror_msg("Can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { perror_msg("Can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } if (S_ISDIR(source_stat.st_mode)) { DIR *dp; struct dirent *d; if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { error_msg("Target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode = source_stat.st_mode; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { perror_msg("Can't create directory '%s'", dest); return -1; } } /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto ret; } while (retval == 0 && (d = readdir(dp)) != NULL) { char *new_source, *new_dest; if (dot_or_dotdot(d->d_name)) continue; new_source = concat_path_file(source, d->d_name); new_dest = concat_path_file(dest, d->d_name); if (copy_file_recursive(new_source, new_dest) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); goto ret; } if (S_ISREG(source_stat.st_mode)) { int src_fd; int dst_fd; mode_t new_mode; src_fd = open(source, O_RDONLY); if (src_fd < 0) { perror_msg("Can't open '%s'", source); return -1; } /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; // security problem versus (sym)link attacks // dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } if (copyfd_eof(src_fd, dst_fd, COPYFD_SPARSE) == -1) retval = -1; close(src_fd); /* Careful: do check that buffered writes succeeded... */ if (close(dst_fd) < 0) { perror_msg("Error writing to '%s'", dest); retval = -1; } else { /* (Try to) copy atime and mtime */ struct timeval atime_mtime[2]; atime_mtime[0].tv_sec = source_stat.st_atime; // note: if "st_atim.tv_nsec" doesn't compile, try "st_atimensec": atime_mtime[0].tv_usec = source_stat.st_atim.tv_nsec / 1000; atime_mtime[1].tv_sec = source_stat.st_mtime; atime_mtime[1].tv_usec = source_stat.st_mtim.tv_nsec / 1000; // note: can use utimensat when it is more widely supported: utimes(dest, atime_mtime); } goto ret; } /* Neither dir not regular file: skip */ skip: log_warning("Skipping '%s'", source); ret: return retval; }
int fastcopy(char *from, char *to, struct stat *sbp) { struct timeval tval[2]; static u_int32_t blen; static char *bp; int nread, from_fd, to_fd; int badchown = 0, serrno = 0; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if ((to_fd = open(to, O_CREAT | O_TRUNC | O_WRONLY, 0600)) < 0) { warn("%s", to); (void)close(from_fd); return (1); } if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { serrno = errno; badchown = 1; } (void) fchmod(to_fd, sbp->st_mode & ~(S_ISUID|S_ISGID)); if (!blen) { blen = sbp->st_blksize; if ((bp = malloc(blen)) == NULL) { warn(NULL); blen = 0; return (1); } } while ((nread = read(from_fd, bp, blen)) > 0) if (write(to_fd, bp, nread) != nread) { warn("%s", to); goto err; } if (nread < 0) { warn("%s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } (void)close(from_fd); if (badchown) { errno = serrno; if ((sbp->st_mode & (S_ISUID|S_ISGID))) { warn("%s: set owner/group; not setting setuid/setgid", to); sbp->st_mode &= ~(S_ISUID|S_ISGID); } else if (!fflg) warn("%s: set owner/group", to); } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode", to); /* * 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 (fchflags(to_fd, sbp->st_flags)) if (errno != EOPNOTSUPP || sbp->st_flags != 0) warn("%s: set flags", to); TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); if (utimes(to, tval)) warn("%s: set times", to); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } return (0); }
/* Save a control message received from an agent * read_contromsg (other thread) is going to deal with it * (only if message changed) */ void save_controlmsg(unsigned int agentid, char *r_msg) { char msg_ack[OS_FLSIZE + 1]; /* Reply to the agent */ snprintf(msg_ack, OS_FLSIZE, "%s%s", CONTROL_HEADER, HC_ACK); send_msg(agentid, msg_ack); /* Check if there is a keep alive already for this agent */ if (_keep_alive[agentid] && _msg[agentid] && (strcmp(_msg[agentid], r_msg) == 0)) { utimes(_keep_alive[agentid], NULL); } else if (strcmp(r_msg, HC_STARTUP) == 0) { return; } else { FILE *fp; char *uname = r_msg; char *random_leftovers; /* Lock mutex */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } /* Update rmsg */ if (_msg[agentid]) { free(_msg[agentid]); } os_strdup(r_msg, _msg[agentid]); /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } r_msg = strchr(r_msg, '\n'); if (!r_msg) { merror("%s: WARN: Invalid message from agent id: '%d'(uname)", ARGV0, agentid); return; } *r_msg = '\0'; random_leftovers = strchr(r_msg, '\n'); if (random_leftovers) { *random_leftovers = '\0'; } /* Update the keep alive */ if (!_keep_alive[agentid]) { char agent_file[OS_SIZE_1024 + 1]; agent_file[OS_SIZE_1024] = '\0'; /* Write to the agent file */ snprintf(agent_file, OS_SIZE_1024, "%s/%s-%s", AGENTINFO_DIR, keys.keyentries[agentid]->name, keys.keyentries[agentid]->ip->ip); os_strdup(agent_file, _keep_alive[agentid]); } /* Write to the file */ fp = fopen(_keep_alive[agentid], "w"); if (fp) { fprintf(fp, "%s\n", uname); fclose(fp); } } /* Lock now to notify of change */ if (pthread_mutex_lock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } /* Assign new values */ _changed[agentid] = 1; modified_agentid = (int) agentid; /* Signal that new data is available */ pthread_cond_signal(&awake_mutex); /* Unlock mutex */ if (pthread_mutex_unlock(&lastmsg_mutex) != 0) { merror(MUTEX_ERROR, ARGV0); return; } return; }
/* Build supermin appliance from supermin_path to $TMPDIR/.guestfs-$UID. * * Returns: * 0 = built * -1 = error (aborts launch) */ static int build_supermin_appliance (guestfs_h *g, const char *supermin_path, uid_t uid, char **kernel, char **dtb, char **initrd, char **appliance) { CLEANUP_FREE char *tmpdir = guestfs_get_cachedir (g); struct stat statbuf; size_t len; /* len must be longer than the length of any pathname we can * generate in this function. */ len = strlen (tmpdir) + 128; char cachedir[len]; snprintf (cachedir, len, "%s/.guestfs-%ju", tmpdir, (uintmax_t) uid); char lockfile[len]; snprintf (lockfile, len, "%s/lock", cachedir); char appliancedir[len]; snprintf (appliancedir, len, "%s/appliance.d", cachedir); ignore_value (mkdir (cachedir, 0755)); ignore_value (chmod (cachedir, 0755)); /* RHBZ#921292 */ /* See if the cache directory exists and passes some simple checks * to make sure it has not been tampered with. */ if (lstat (cachedir, &statbuf) == -1) return 0; if (statbuf.st_uid != uid) { error (g, _("security: cached appliance %s is not owned by UID %ju"), cachedir, (uintmax_t) uid); return -1; } if (!S_ISDIR (statbuf.st_mode)) { error (g, _("security: cached appliance %s is not a directory (mode %o)"), cachedir, statbuf.st_mode); return -1; } if ((statbuf.st_mode & 0022) != 0) { error (g, _("security: cached appliance %s is writable by group or other (mode %o)"), cachedir, statbuf.st_mode); return -1; } (void) utimes (cachedir, NULL); if (g->verbose) guestfs_int_print_timestamped_message (g, "begin building supermin appliance"); /* Build the appliance if it needs to be built. */ if (g->verbose) guestfs_int_print_timestamped_message (g, "run supermin"); if (run_supermin_build (g, lockfile, appliancedir, supermin_path) == -1) return -1; if (g->verbose) guestfs_int_print_timestamped_message (g, "finished building supermin appliance"); /* Return the appliance filenames. */ *kernel = safe_malloc (g, len); #ifdef DTB_WILDCARD *dtb = safe_malloc (g, len); #else *dtb = NULL; #endif *initrd = safe_malloc (g, len); *appliance = safe_malloc (g, len); snprintf (*kernel, len, "%s/kernel", appliancedir); #ifdef DTB_WILDCARD snprintf (*dtb, len, "%s/dtb", appliancedir); #endif snprintf (*initrd, len, "%s/initrd", appliancedir); snprintf (*appliance, len, "%s/root", appliancedir); /* Touch the files so they don't get deleted (as they are in /var/tmp). */ (void) utimes (*kernel, NULL); #ifdef DTB_WILDCARD (void) utimes (*dtb, NULL); #endif (void) utimes (*initrd, NULL); /* Checking backend != "uml" is a big hack. UML encodes the mtime * of the original backing file (in this case, the appliance) in the * COW file, and checks it when adding it to the VM. If there are * multiple threads running and one touches the appliance here, it * will disturb the mtime and UML will give an error. * * We can get rid of this hack as soon as UML fixes the * ubdN=cow,original parsing bug, since we won't need to run * uml_mkcow separately, so there is no possible race. * * XXX */ if (STRNEQ (g->backend, "uml")) (void) utimes (*appliance, NULL); return 0; }
void sink(int argc, char **argv) { static BUF buffer; struct stat stb; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i; size_t j, count; int amt, exists, first, ofd; mode_t mode, omode, mask; off_t size, statbytes; unsigned long long ull; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *why, *vect[1], buf[2048]; struct timeval tv[2]; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); if (!pflag) (void) umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); (void) atomicio(vwrite, remout, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (atomicio(read, remin, cp, 1) != 1) return; if (*cp++ == '\n') SCREWUP("unexpected <newline>"); do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[sizeof(buf) - 1] && ch != '\n'); *cp = 0; if (verbose_mode) fprintf(stderr, "Sink: %s", buf); if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void) atomicio(vwrite, STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void) atomicio(vwrite, remout, "", 1); return; } if (ch == '\n') *--cp = 0; cp = buf; if (*cp == 'T') { setimes++; cp++; if (!isdigit((unsigned char)*cp)) SCREWUP("mtime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); if ((time_t)ull < 0 || (unsigned long long)(time_t)ull != ull) setimes = 0; /* out of range */ mtime.tv_sec = ull; mtime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ' || mtime.tv_usec < 0 || mtime.tv_usec > 999999) SCREWUP("mtime.usec not delimited"); if (!isdigit((unsigned char)*cp)) SCREWUP("atime.sec not present"); ull = strtoull(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); if ((time_t)ull < 0 || (unsigned long long)(time_t)ull != ull) setimes = 0; /* out of range */ atime.tv_sec = ull; atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0' || atime.tv_usec < 0 || atime.tv_usec > 999999) SCREWUP("atime.usec not delimited"); (void) atomicio(vwrite, remout, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); for (size = 0; isdigit((unsigned char)*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if ((strchr(cp, '/') != NULL) || (strcmp(cp, "..") == 0)) { run_err("error: unexpected filename: %s", cp); exit(1); } if (targisdir) { static char *namebuf; static size_t cursize; size_t need; need = strlen(targ) + strlen(cp) + 250; if (need > cursize) { free(namebuf); namebuf = xmalloc(need); cursize = need; } (void) snprintf(namebuf, need, "%s%s%s", targ, strcmp(targ, "/") ? "/" : "", cp); np = namebuf; } else np = targ; curfile = cp; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (!iamrecursive) SCREWUP("received directory without -r"); if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void) chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } vect[0] = xstrdup(np); sink(1, vect); if (setimes) { setimes = 0; if (utimes(vect[0], tv) < 0) run_err("%s: set times: %s", vect[0], strerror(errno)); } if (mod_flag) (void) chmod(vect[0], mode); free(vect[0]); continue; } omode = mode; mode |= S_IWUSR; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void) atomicio(vwrite, remout, "", 1); if ((bp = allocbuf(&buffer, ofd, COPY_BUFLEN)) == NULL) { (void) close(ofd); continue; } cp = bp->buf; wrerr = NO; statbytes = 0; if (showprogress) start_progress_meter(curfile, size, &statbytes); set_nonblock(remin); for (count = i = 0; i < size; i += bp->cnt) { amt = bp->cnt; if (i + amt > size) amt = size - i; count += amt; do { j = atomicio6(read, remin, cp, amt, scpio, &statbytes); if (j == 0) { run_err("%s", j != EPIPE ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { if (atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } } count = 0; cp = bp->buf; } } unset_nonblock(remin); if (showprogress) stop_progress_meter(); if (count != 0 && wrerr == NO && atomicio(vwrite, ofd, bp->buf, count) != count) { wrerr = YES; wrerrno = errno; } if (wrerr == NO && (!exists || S_ISREG(stb.st_mode)) && ftruncate(ofd, size) != 0) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } if (pflag) { if (exists || omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } else { if (!exists && omode != mode) #ifdef HAVE_FCHMOD if (fchmod(ofd, omode & ~mask)) { #else /* HAVE_FCHMOD */ if (chmod(np, omode & ~mask)) { #endif /* HAVE_FCHMOD */ run_err("%s: set mode: %s", np, strerror(errno)); wrerr = DISPLAYED; } } if (close(ofd) == -1) { wrerr = YES; wrerrno = errno; } (void) response(); if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { run_err("%s: set times: %s", np, strerror(errno)); wrerr = DISPLAYED; } } switch (wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: (void) atomicio(vwrite, remout, "", 1); break; case DISPLAYED: break; } } screwup: run_err("protocol error: %s", why); exit(1); } int response(void) { char ch, *cp, resp, rbuf[2048]; if (atomicio(read, remin, &resp, sizeof(resp)) != sizeof(resp)) lostconn(0); cp = rbuf; switch (resp) { case 0: /* ok */ return (0); default: *cp++ = resp; /* FALLTHROUGH */ case 1: /* error, followed by error msg */ case 2: /* fatal error, "" */ do { if (atomicio(read, remin, &ch, sizeof(ch)) != sizeof(ch)) lostconn(0); *cp++ = ch; } while (cp < &rbuf[sizeof(rbuf) - 1] && ch != '\n'); if (!iamremote) (void) atomicio(vwrite, STDERR_FILENO, rbuf, cp - rbuf); ++errs; if (resp == 1) return (-1); exit(1); } /* NOTREACHED */ } void usage(void) { (void) fprintf(stderr, "usage: scp [-12346BCpqrv] [-c cipher] [-F ssh_config] [-i identity_file]\n" " [-l limit] [-o ssh_option] [-P port] [-S program]\n" " [[user@]host1:]file1 ... [[user@]host2:]file2\n"); exit(1); }
static int fastcopy(char *from, char *to, struct stat *sbp) { #if defined(__NetBSD__) struct timespec ts[2]; #else struct timeval tval[2]; #endif static blksize_t blen; static char *bp; int nread, from_fd, to_fd; if ((from_fd = open(from, O_RDONLY, 0)) < 0) { warn("%s", from); return (1); } if ((to_fd = open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { warn("%s", to); (void)close(from_fd); return (1); } if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { warn(NULL); blen = 0; (void)close(from_fd); (void)close(to_fd); return (1); } while ((nread = read(from_fd, bp, blen)) > 0) if (write(to_fd, bp, nread) != nread) { warn("%s", to); goto err; } if (nread < 0) { warn("%s", from); err: if (unlink(to)) warn("%s: remove", to); (void)close(from_fd); (void)close(to_fd); return (1); } if (fcpxattr(from_fd, to_fd) == -1) warn("%s: error copying extended attributes", to); (void)close(from_fd); #ifdef BSD4_4 #if defined(__NetBSD__) ts[0] = sbp->st_atimespec; ts[1] = sbp->st_mtimespec; #else TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); #endif #else tval[0].tv_sec = sbp->st_atime; tval[1].tv_sec = sbp->st_mtime; tval[0].tv_usec = 0; tval[1].tv_usec = 0; #endif #ifdef __SVR4 if (utimes(to, tval)) #else #if defined(__NetBSD__) if (futimens(to_fd, ts)) #else if (futimes(to_fd, tval)) #endif #endif warn("%s: set times", to); if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { if (errno != EPERM) warn("%s: set owner/group", to); sbp->st_mode &= ~(S_ISUID | S_ISGID); } if (fchmod(to_fd, sbp->st_mode)) warn("%s: set mode", to); if (fchflags(to_fd, sbp->st_flags) && (errno != EOPNOTSUPP)) warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); if (close(to_fd)) { warn("%s", to); return (1); } if (unlink(from)) { warn("%s: remove", from); return (1); } if (vflg) printf("%s -> %s\n", from, to); return (0); }
RTR3DECL(int) RTPathSetTimesEx(const char *pszPath, PCRTTIMESPEC pAccessTime, PCRTTIMESPEC pModificationTime, PCRTTIMESPEC pChangeTime, PCRTTIMESPEC pBirthTime, uint32_t fFlags) { /* * Validate input. */ AssertPtrReturn(pszPath, VERR_INVALID_POINTER); AssertReturn(*pszPath, VERR_INVALID_PARAMETER); AssertPtrNullReturn(pAccessTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pModificationTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pChangeTime, VERR_INVALID_POINTER); AssertPtrNullReturn(pBirthTime, VERR_INVALID_POINTER); AssertMsgReturn(RTPATH_F_IS_VALID(fFlags, 0), ("%#x\n", fFlags), VERR_INVALID_PARAMETER); /* * Convert the paths. */ char const *pszNativePath; int rc = rtPathToNative(&pszNativePath, pszPath, NULL); if (RT_SUCCESS(rc)) { RTFSOBJINFO ObjInfo; /* * If it's a no-op, we'll only verify the existance of the file. */ if (!pAccessTime && !pModificationTime) rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, fFlags); else { /* * Convert the input to timeval, getting the missing one if necessary, * and call the API which does the change. */ struct timeval aTimevals[2]; if (pAccessTime && pModificationTime) { RTTimeSpecGetTimeval(pAccessTime, &aTimevals[0]); RTTimeSpecGetTimeval(pModificationTime, &aTimevals[1]); } else { rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags); if (RT_SUCCESS(rc)) { RTTimeSpecGetTimeval(pAccessTime ? pAccessTime : &ObjInfo.AccessTime, &aTimevals[0]); RTTimeSpecGetTimeval(pModificationTime ? pModificationTime : &ObjInfo.ModificationTime, &aTimevals[1]); } else Log(("RTPathSetTimes('%s',%p,%p,,): RTPathQueryInfo failed with %Rrc\n", pszPath, pAccessTime, pModificationTime, rc)); } if (RT_SUCCESS(rc)) { if (fFlags & RTPATH_F_FOLLOW_LINK) { if (utimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } #if (defined(RT_OS_DARWIN) && MAC_OS_X_VERSION_MIN_REQUIRED >= 1050) \ || defined(RT_OS_FREEBSD) \ || defined(RT_OS_LINUX) \ || defined(RT_OS_OS2) /** @todo who really has lutimes? */ else { if (lutimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } #else else { if (pAccessTime && pModificationTime) rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_UNIX, fFlags); if (RT_SUCCESS(rc) && RTFS_IS_SYMLINK(ObjInfo.Attr.fMode)) rc = VERR_NS_SYMLINK_SET_TIME; else if (RT_SUCCESS(rc)) { if (utimes(pszNativePath, aTimevals)) rc = RTErrConvertFromErrno(errno); } } #endif if (RT_FAILURE(rc)) Log(("RTPathSetTimes('%s',%p,%p,,): failed with %Rrc and errno=%d\n", pszPath, pAccessTime, pModificationTime, rc, errno)); } } rtPathFreeNative(pszNativePath, pszPath); }
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 */ } }
void sink(int argc, char *argv[]) { static BUF buffer; struct stat stb; struct timeval tv[2]; enum { YES, NO, DISPLAYED } wrerr; BUF *bp; off_t i, j, size; int amt, exists, first, mask, mode, ofd, omode; size_t count; int setimes, targisdir, wrerrno = 0; char ch, *cp, *np, *targ, *vect[1], buf[BUFSIZ], path[PATH_MAX]; const char *why; #define atime tv[0] #define mtime tv[1] #define SCREWUP(str) { why = str; goto screwup; } setimes = targisdir = 0; mask = umask(0); if (!pflag) (void)umask(mask); if (argc != 1) { run_err("ambiguous target"); exit(1); } targ = *argv; if (targetshouldbedirectory) verifydir(targ); (void)write(rem, "", 1); if (stat(targ, &stb) == 0 && S_ISDIR(stb.st_mode)) targisdir = 1; for (first = 1;; first = 0) { cp = buf; if (read(rem, cp, 1) <= 0) return; if (*cp++ == '\n') SCREWUP("unexpected <newline>"); do { if (read(rem, &ch, sizeof(ch)) != sizeof(ch)) SCREWUP("lost connection"); *cp++ = ch; } while (cp < &buf[BUFSIZ - 1] && ch != '\n'); *cp = 0; if (buf[0] == '\01' || buf[0] == '\02') { if (iamremote == 0) (void)write(STDERR_FILENO, buf + 1, strlen(buf + 1)); if (buf[0] == '\02') exit(1); ++errs; continue; } if (buf[0] == 'E') { (void)write(rem, "", 1); return; } if (ch == '\n') *--cp = 0; cp = buf; if (*cp == 'T') { setimes++; cp++; mtime.tv_sec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.sec not delimited"); mtime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("mtime.usec not delimited"); atime.tv_sec = strtol(cp, &cp, 10); if (!cp || *cp++ != ' ') SCREWUP("atime.sec not delimited"); atime.tv_usec = strtol(cp, &cp, 10); if (!cp || *cp++ != '\0') SCREWUP("atime.usec not delimited"); (void)write(rem, "", 1); continue; } if (*cp != 'C' && *cp != 'D') { /* * Check for the case "rcp remote:foo\* local:bar". * In this case, the line "No match." can be returned * by the shell before the rcp command on the remote is * executed so the ^Aerror_message convention isn't * followed. */ if (first) { run_err("%s", cp); exit(1); } SCREWUP("expected control record"); } mode = 0; for (++cp; cp < buf + 5; cp++) { if (*cp < '0' || *cp > '7') SCREWUP("bad mode"); mode = (mode << 3) | (*cp - '0'); } if (*cp++ != ' ') SCREWUP("mode not delimited"); for (size = 0; isdigit(*cp);) size = size * 10 + (*cp++ - '0'); if (*cp++ != ' ') SCREWUP("size not delimited"); if (targisdir) { if (strlen(targ) + (*targ ? 1 : 0) + strlen(cp) >= sizeof(path)) { run_err("%s%s%s: name too long", targ, *targ ? "/" : "", cp); exit(1); } (void)snprintf(path, sizeof(path), "%s%s%s", targ, *targ ? "/" : "", cp); np = path; } else np = targ; exists = stat(np, &stb) == 0; if (buf[0] == 'D') { int mod_flag = pflag; if (exists) { if (!S_ISDIR(stb.st_mode)) { errno = ENOTDIR; goto bad; } if (pflag) (void)chmod(np, mode); } else { /* Handle copying from a read-only directory */ mod_flag = 1; if (mkdir(np, mode | S_IRWXU) < 0) goto bad; } vect[0] = np; sink(1, vect); if (setimes) { setimes = 0; if (utimes(np, tv) < 0) run_err("%s: set times: %s", np, strerror(errno)); } if (mod_flag) (void)chmod(np, mode); continue; } omode = mode; mode |= S_IWRITE; if ((ofd = open(np, O_WRONLY|O_CREAT, mode)) < 0) { bad: run_err("%s: %s", np, strerror(errno)); continue; } (void)write(rem, "", 1); if ((bp = allocbuf(&buffer, ofd, BUFSIZ)) == NULL) { (void)close(ofd); continue; } cp = bp->buf; wrerr = NO; for (count = i = 0; i < size; i += BUFSIZ) { amt = BUFSIZ; if (i + amt > size) amt = size - i; count += amt; do { j = read(rem, cp, amt); if (j <= 0) { run_err("%s", j ? strerror(errno) : "dropped connection"); exit(1); } amt -= j; cp += j; } while (amt > 0); if (count == bp->cnt) { /* Keep reading so we stay sync'd up. */ if (wrerr == NO) { j = write(ofd, bp->buf, count); if (j != (off_t)count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } } count = 0; cp = bp->buf; } } if (count != 0 && wrerr == NO && (j = write(ofd, bp->buf, count)) != (off_t)count) { wrerr = YES; wrerrno = j >= 0 ? EIO : errno; } if (ftruncate(ofd, size)) { run_err("%s: truncate: %s", np, strerror(errno)); wrerr = DISPLAYED; } if (pflag) { if (exists || omode != mode) if (fchmod(ofd, omode)) run_err("%s: set mode: %s", np, strerror(errno)); } else { if (!exists && omode != mode) if (fchmod(ofd, omode & ~mask)) run_err("%s: set mode: %s", np, strerror(errno)); } (void)close(ofd); (void)response(); if (setimes && wrerr == NO) { setimes = 0; if (utimes(np, tv) < 0) { run_err("%s: set times: %s", np, strerror(errno)); wrerr = DISPLAYED; } } switch(wrerr) { case YES: run_err("%s: %s", np, strerror(wrerrno)); break; case NO: (void)write(rem, "", 1); break; case DISPLAYED: break; } } screwup: run_err("protocol error: %s", why); exit(1); }
/* Return: * -1 error, copy not made * 0 copy is made or user answered "no" in interactive mode * (failures to preserve mode/owner/times are not reported in exit code) */ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) { /* This is a recursive function, try to minimize stack usage */ /* NB: each struct stat is ~100 bytes */ struct stat source_stat; struct stat dest_stat; smallint retval = 0; smallint dest_exists = 0; smallint ovr; /* Inverse of cp -d ("cp without -d") */ #define FLAGS_DEREF (flags & (FILEUTILS_DEREFERENCE + FILEUTILS_DEREFERENCE_L0)) if ((FLAGS_DEREF ? stat : lstat)(source, &source_stat) < 0) { /* This may be a dangling symlink. * Making [sym]links to dangling symlinks works, so... */ if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) goto make_links; bb_perror_msg("can't stat '%s'", source); return -1; } if (lstat(dest, &dest_stat) < 0) { if (errno != ENOENT) { bb_perror_msg("can't stat '%s'", dest); return -1; } } else { if (source_stat.st_dev == dest_stat.st_dev && source_stat.st_ino == dest_stat.st_ino ) { bb_error_msg("'%s' and '%s' are the same file", source, dest); return -1; } dest_exists = 1; } #if ENABLE_SELINUX if ((flags & FILEUTILS_PRESERVE_SECURITY_CONTEXT) && is_selinux_enabled() > 0) { security_context_t con; if (lgetfilecon(source, &con) >= 0) { if (setfscreatecon(con) < 0) { bb_perror_msg("can't set setfscreatecon %s", con); freecon(con); return -1; } } else if (errno == ENOTSUP || errno == ENODATA) { setfscreatecon_or_die(NULL); } else { bb_perror_msg("can't lgetfilecon %s", source); return -1; } } #endif if (S_ISDIR(source_stat.st_mode)) { DIR *dp; const char *tp; struct dirent *d; mode_t saved_umask = 0; if (!(flags & FILEUTILS_RECUR)) { bb_error_msg("omitting directory '%s'", source); return -1; } /* Did we ever create source ourself before? */ tp = is_in_ino_dev_hashtable(&source_stat); if (tp) { /* We did! it's a recursion! man the lifeboats... */ bb_error_msg("recursion detected, omitting directory '%s'", source); return -1; } if (dest_exists) { if (!S_ISDIR(dest_stat.st_mode)) { bb_error_msg("target '%s' is not a directory", dest); return -1; } /* race here: user can substitute a symlink between * this check and actual creation of files inside dest */ } else { /* Create DEST */ mode_t mode; saved_umask = umask(0); mode = source_stat.st_mode; if (!(flags & FILEUTILS_PRESERVE_STATUS)) mode = source_stat.st_mode & ~saved_umask; /* Allow owner to access new dir (at least for now) */ mode |= S_IRWXU; if (mkdir(dest, mode) < 0) { umask(saved_umask); bb_perror_msg("can't create directory '%s'", dest); return -1; } umask(saved_umask); /* need stat info for add_to_ino_dev_hashtable */ if (lstat(dest, &dest_stat) < 0) { bb_perror_msg("can't stat '%s'", dest); return -1; } } /* remember (dev,inode) of each created dir. * NULL: name is not remembered */ add_to_ino_dev_hashtable(&dest_stat, NULL); /* Recursively copy files in SOURCE */ dp = opendir(source); if (dp == NULL) { retval = -1; goto preserve_mode_ugid_time; } while ((d = readdir(dp)) != NULL) { char *new_source, *new_dest; new_source = concat_subpath_file(source, d->d_name); if (new_source == NULL) continue; new_dest = concat_path_file(dest, d->d_name); if (copy_file(new_source, new_dest, flags & ~FILEUTILS_DEREFERENCE_L0) < 0) retval = -1; free(new_source); free(new_dest); } closedir(dp); if (!dest_exists && chmod(dest, source_stat.st_mode & ~saved_umask) < 0 ) { bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); /* retval = -1; - WRONG! copy *WAS* made */ } goto preserve_mode_ugid_time; } if (flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) { int (*lf)(const char *oldpath, const char *newpath); make_links: /* Hmm... maybe * if (DEREF && MAKE_SOFTLINK) source = realpath(source) ? * (but realpath returns NULL on dangling symlinks...) */ lf = (flags & FILEUTILS_MAKE_SOFTLINK) ? symlink : link; if (lf(source, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (lf(source, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } /* _Not_ jumping to preserve_mode_ugid_time: * (sym)links don't have those */ return 0; } if (/* "cp thing1 thing2" without -R: just open and read() from thing1 */ !(flags & FILEUTILS_RECUR) /* "cp [-opts] regular_file thing2" */ || S_ISREG(source_stat.st_mode) /* DEREF uses stat, which never returns S_ISLNK() == true. * So the below is never true: */ /* || (FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) */ ) { int src_fd; int dst_fd; mode_t new_mode; if (!FLAGS_DEREF && S_ISLNK(source_stat.st_mode)) { /* "cp -d symlink dst": create a link */ goto dont_cat; } if (ENABLE_FEATURE_PRESERVE_HARDLINKS && !FLAGS_DEREF) { const char *link_target; link_target = is_in_ino_dev_hashtable(&source_stat); if (link_target) { if (link(link_target, dest) < 0) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; if (link(link_target, dest) < 0) { bb_perror_msg("can't create link '%s'", dest); return -1; } } return 0; } add_to_ino_dev_hashtable(&source_stat, dest); } src_fd = open_or_warn(source, O_RDONLY); if (src_fd < 0) return -1; /* Do not try to open with weird mode fields */ new_mode = source_stat.st_mode; if (!S_ISREG(source_stat.st_mode)) new_mode = 0666; // POSIX way is a security problem versus (sym)link attacks if (!ENABLE_FEATURE_NON_POSIX_CP) { dst_fd = open(dest, O_WRONLY|O_CREAT|O_TRUNC, new_mode); } else { /* safe way: */ dst_fd = open(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); } if (dst_fd == -1) { ovr = ask_and_unlink(dest, flags); if (ovr <= 0) { close(src_fd); return ovr; } /* It shouldn't exist. If it exists, do not open (symlink attack?) */ dst_fd = open3_or_warn(dest, O_WRONLY|O_CREAT|O_EXCL, new_mode); if (dst_fd < 0) { close(src_fd); return -1; } } #if ENABLE_SELINUX if ((flags & (FILEUTILS_PRESERVE_SECURITY_CONTEXT|FILEUTILS_SET_SECURITY_CONTEXT)) && is_selinux_enabled() > 0 ) { security_context_t con; if (getfscreatecon(&con) == -1) { bb_perror_msg("getfscreatecon"); return -1; } if (con) { if (setfilecon(dest, con) == -1) { bb_perror_msg("setfilecon:%s,%s", dest, con); freecon(con); return -1; } freecon(con); } } #endif if (bb_copyfd_eof(src_fd, dst_fd) == -1) retval = -1; /* Careful with writing... */ if (close(dst_fd) < 0) { bb_perror_msg("error writing to '%s'", dest); retval = -1; } /* ...but read size is already checked by bb_copyfd_eof */ close(src_fd); /* "cp /dev/something new_file" should not * copy mode of /dev/something */ if (!S_ISREG(source_stat.st_mode)) return retval; goto preserve_mode_ugid_time; } dont_cat: /* Source is a symlink or a special file */ /* We are lazy here, a bit lax with races... */ if (dest_exists) { errno = EEXIST; ovr = ask_and_unlink(dest, flags); if (ovr <= 0) return ovr; } if (S_ISLNK(source_stat.st_mode)) { char *lpath = xmalloc_readlink_or_warn(source); if (lpath) { int r = symlink(lpath, dest); free(lpath); if (r < 0) { bb_perror_msg("can't create symlink '%s'", dest); return -1; } if (flags & FILEUTILS_PRESERVE_STATUS) if (lchown(dest, source_stat.st_uid, source_stat.st_gid) < 0) bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } /* _Not_ jumping to preserve_mode_ugid_time: * symlinks don't have those */ return 0; } if (S_ISBLK(source_stat.st_mode) || S_ISCHR(source_stat.st_mode) || S_ISSOCK(source_stat.st_mode) || S_ISFIFO(source_stat.st_mode) ) { if (mknod(dest, source_stat.st_mode, source_stat.st_rdev) < 0) { bb_perror_msg("can't create '%s'", dest); return -1; } } else { bb_error_msg("unrecognized file '%s' with mode %x", source, source_stat.st_mode); return -1; } preserve_mode_ugid_time: if (flags & FILEUTILS_PRESERVE_STATUS /* Cannot happen: */ /* && !(flags & (FILEUTILS_MAKE_SOFTLINK|FILEUTILS_MAKE_HARDLINK)) */ ) { struct timeval times[2]; times[1].tv_sec = times[0].tv_sec = source_stat.st_mtime; times[1].tv_usec = times[0].tv_usec = 0; /* BTW, utimes sets usec-precision time - just FYI */ if (utimes(dest, times) < 0) bb_perror_msg("can't preserve %s of '%s'", "times", dest); if (chown(dest, source_stat.st_uid, source_stat.st_gid) < 0) { source_stat.st_mode &= ~(S_ISUID | S_ISGID); bb_perror_msg("can't preserve %s of '%s'", "ownership", dest); } #if ENABLE_XATTR /* Preserve extended attributes. We must copy it after chown() * because it resets capabilities. */ if (copy_file_attr(source, dest) == -1) bb_perror_msg("can't preserve %s of '%s'", "extended attributes", dest); #endif if (chmod(dest, source_stat.st_mode) < 0) bb_perror_msg("can't preserve %s of '%s'", "permissions", dest); } return retval; }
static void docopy (const char *src, const char *dst) { str tdst = strbuf ("%s#%d~", dst, int (getpid ())); tmppath = tdst.cstr (); tmofile = src; alarm (timeout); int sfd = open (src, O_RDONLY); struct stat sb; if (fstat (sfd, &sb) < 0) { close (sfd); sfd = -1; } alarm (0); if (sfd < 0) fatal ("%s: %m\n", src); tmofile = tmppath; alarm (timeout); unlink (tdst); int dfd = open (tdst, O_WRONLY|O_CREAT|O_TRUNC, 0444); alarm (0); if (dfd < 0) fatal ("%s: %m\n", tmppath); for (;;) { int n; char buf[65536]; tmofile = src; alarm (timeout); n = read (sfd, buf, sizeof (buf)); alarm (0); if (n == 0) break; else if (n < 0) { warn ("fatal: %s: %m\n", src); cleanup (); } tmofile = tdst; alarm (timeout); int nw = write (dfd, buf, n); alarm (0); if (nw != n) { warn ("fatal: %s: %m\n", tmppath); cleanup (); } } close (sfd); int r; tmofile = tdst; alarm (timeout); r = fsync (dfd); if (r >= 0) r = close (dfd); if (r >= 0) { struct timeval tvs[2]; tvs[0].tv_sec = sb.st_atime; tvs[1].tv_sec = sb.st_mtime; #ifdef SFS_HAVE_STAT_ST_MTIMESPEC tvs[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; tvs[1].tv_usec = sb.st_mtimespec.tv_nsec / 1000; #else /* !SFS_HAVE_STAT_ST_MTIMESPEC */ tvs[0].tv_usec = tvs[1].tv_usec = 0; #endif /* !SFS_HAVE_STAT_ST_MTIMESPEC */ r = utimes (tmppath, tvs); } alarm (0); if (r < 0) { warn ("fatal: %s: %m\n", tmppath); cleanup (); } tmofile = dst; alarm (timeout); r = rename (tmppath, dst); alarm (0); if (r < 0) { warn ("fatal: %s: %m\n", tmppath); cleanup (); } tmofile = tmppath = NULL; }