Feature #7106 » 0001-Add-support-for-lutimes.patch
| configure.in | ||
|---|---|---|
|
AC_LIBOBJ([signbit])
|
||
|
fi
|
||
|
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall __syscall chroot getcwd eaccess\
|
||
|
truncate ftruncate ftello chsize times utimes utimensat fcntl lockf lstat\
|
||
|
truncate ftruncate ftello chsize times utimes lutimes utimensat fcntl lockf lstat\
|
||
|
truncate64 ftruncate64 ftello64 fseeko fseeko64 \
|
||
|
link symlink readlink readdir_r fsync fdatasync fchown posix_fadvise\
|
||
|
setitimer setruid seteuid setreuid setresuid socketpair\
|
||
| file.c | ||
|---|---|---|
|
#define chown(p, o, g) rb_w32_uchown((p), (o), (g))
|
||
|
#undef utime
|
||
|
#define utime(p, t) rb_w32_uutime((p), (t))
|
||
|
#undef lutime
|
||
|
#define lutime(p, t) rb_w32_luutime((p), (t))
|
||
|
#undef link
|
||
|
#define link(f, t) rb_w32_ulink((f), (t))
|
||
|
#undef unlink
|
||
| ... | ... | |
|
#define rb_file_s_lchown rb_f_notimplement
|
||
|
#endif
|
||
|
typedef enum utime_mode utime_mode_t;
|
||
|
enum utime_mode {
|
||
|
M_SYMLINK_FOLLOW = 0,
|
||
|
M_SYMLINK_NOFOLLOW
|
||
|
};
|
||
|
struct utime_args {
|
||
|
const struct timespec* tsp;
|
||
|
VALUE atime, mtime;
|
||
|
utime_mode_t mode;
|
||
|
};
|
||
|
#if !defined(FOLLOW_SYMLINK_P)
|
||
|
# define FOLLOW_SYMLINK_P(x) ((x)->mode == M_SYMLINK_FOLLOW)
|
||
|
#endif
|
||
|
#if defined DOSISH || defined __CYGWIN__
|
||
|
NORETURN(static void utime_failed(VALUE, const struct timespec *, VALUE, VALUE));
|
||
| ... | ... | |
|
#if defined(HAVE_UTIMES)
|
||
|
static void
|
||
|
utime_internal(const char *path, VALUE pathv, void *arg)
|
||
|
utime_helper(const char *path, VALUE pathv, void *arg)
|
||
|
{
|
||
|
struct utime_args *v = arg;
|
||
|
const struct timespec *tsp = v->tsp;
|
||
| ... | ... | |
|
#ifdef HAVE_UTIMENSAT
|
||
|
static int try_utimensat = 1;
|
||
|
const int flags = FOLLOW_SYMLINK_P(v) ? 0 : AT_SYMLINK_NOFOLLOW;
|
||
|
if (try_utimensat) {
|
||
|
if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
|
||
|
if (utimensat(AT_FDCWD, path, tsp, flags) < 0) {
|
||
|
if (errno == ENOSYS) {
|
||
|
try_utimensat = 0;
|
||
|
goto no_utimensat;
|
||
| ... | ... | |
|
tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000);
|
||
|
tvp = tvbuf;
|
||
|
}
|
||
|
if (utimes(path, tvp) < 0)
|
||
|
if ((FOLLOW_SYMLINK_P(v) ? utimes(path, tvp) : lutimes(path, tvp)) < 0)
|
||
|
utime_failed(pathv, tsp, v->atime, v->mtime);
|
||
|
}
|
||
| ... | ... | |
|
#endif
|
||
|
static void
|
||
|
utime_internal(const char *path, VALUE pathv, void *arg)
|
||
|
utime_helper(const char *path, VALUE pathv, void *arg)
|
||
|
{
|
||
|
struct utime_args *v = arg;
|
||
|
const struct timespec *tsp = v->tsp;
|
||
| ... | ... | |
|
utbuf.modtime = tsp[1].tv_sec;
|
||
|
utp = &utbuf;
|
||
|
}
|
||
|
if (utime(path, utp) < 0)
|
||
|
if ((FOLLOW_SYMLINK_P(v) ? utimes(path, utp) : lutimes(path, utp)) < 0)
|
||
|
utime_failed(pathv, tsp, v->atime, v->mtime);
|
||
|
}
|
||
|
#endif
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* File.utime(atime, mtime, file_name,...) -> integer
|
||
|
*
|
||
|
* Sets the access and modification times of each
|
||
|
* named file to the first two arguments. Returns
|
||
|
* the number of file names in the argument list.
|
||
|
*/
|
||
|
static VALUE
|
||
|
rb_file_s_utime(int argc, VALUE *argv)
|
||
|
utime_internal(int argc, VALUE *argv, utime_mode_t mode)
|
||
|
{
|
||
|
VALUE rest;
|
||
|
struct utime_args args;
|
||
| ... | ... | |
|
tsp[1] = rb_time_timespec(args.mtime);
|
||
|
}
|
||
|
args.tsp = tsp;
|
||
|
#ifdef HAVE_LUTIMES
|
||
|
args.mode = mode;
|
||
|
#else
|
||
|
args.mode = M_SYMLINK_FOLLOW;
|
||
|
#endif
|
||
|
n = apply2files(utime_internal, rest, &args);
|
||
|
n = apply2files(utime_helper, rest, &args);
|
||
|
return LONG2FIX(n);
|
||
|
}
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* File.utime(atime, mtime, file_name,...) -> integer
|
||
|
*
|
||
|
* Sets the access and modification times of each
|
||
|
* named file to the first two arguments. Returns
|
||
|
* the number of file names in the argument list.
|
||
|
* See also File::lutime.
|
||
|
*/
|
||
|
static inline VALUE
|
||
|
rb_file_s_utime(int argc, VALUE *argv)
|
||
|
{
|
||
|
return utime_internal(argc, argv, M_SYMLINK_FOLLOW);
|
||
|
}
|
||
|
#ifdef HAVE_LUTIMES
|
||
|
/*
|
||
|
* call-seq:
|
||
|
* File.lutime(atime, mtime, file_name,...) -> integer
|
||
|
*
|
||
|
* Equivalent to File::utime, but does not follow
|
||
|
* symbolic links (so it will change the +atime+
|
||
|
* and +mtime+ associated with the link, not the
|
||
|
* file referenced by the link). Often not available.
|
||
|
*
|
||
|
* See also File::utime.
|
||
|
*/
|
||
|
static VALUE
|
||
|
rb_file_s_lutime(int argc, VALUE *argv)
|
||
|
{
|
||
|
return utime_internal(argc, argv, M_SYMLINK_NOFOLLOW);
|
||
|
}
|
||
|
#else
|
||
|
#define rb_file_s_lutime rb_f_notimplement
|
||
|
#endif
|
||
|
#if defined(FOLLOW_SYMLINK_P)
|
||
|
# undef FOLLOW_SYMLINK_P
|
||
|
#endif
|
||
|
NORETURN(static void sys_fail2(VALUE,VALUE));
|
||
|
static void
|
||
|
sys_fail2(VALUE s1, VALUE s2)
|
||
| ... | ... | |
|
rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1);
|
||
|
rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1);
|
||
|
rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1);
|
||
|
rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1);
|
||
|
rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1);
|
||
|
rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1);
|
||
| lib/fileutils.rb | ||
|---|---|---|
|
public
|
||
|
#
|
||
|
# Options: noop verbose
|
||
|
# Options: noop verbose mtime nocreate nofollow
|
||
|
#
|
||
|
# Updates modification time (mtime) and access time (atime) of file(s) in
|
||
|
# +list+. Files are created if they don't exist.
|
||
| ... | ... | |
|
list.each do |path|
|
||
|
created = nocreate
|
||
|
begin
|
||
|
File.utime(t, t, path)
|
||
|
if File.symlink?(path) and options[:nofollow]
|
||
|
File.lutime(t, t, path)
|
||
|
else
|
||
|
File.utime(t, t, path)
|
||
|
end
|
||
|
rescue Errno::ENOENT
|
||
|
raise if created
|
||
|
File.open(path, 'a') {
|
||
| ... | ... | |
|
end
|
||
|
end
|
||
|
define_command('touch', :noop, :verbose, :mtime, :nocreate)
|
||
|
define_command('touch', :noop, :verbose, :mtime, :nocreate, :nofollow)
|
||
|
private
|
||
| ... | ... | |
|
def copy_metadata(path)
|
||
|
st = lstat()
|
||
|
if !st.symlink?
|
||
|
if st.symlink?
|
||
|
begin
|
||
|
File.lutime st.atime, st.mtime, path
|
||
|
rescue NotImplementedError
|
||
|
end
|
||
|
else
|
||
|
File.utime st.atime, st.mtime, path
|
||
|
end
|
||
|
begin
|
||
| test/ruby/test_file_exhaustive.rb | ||
|---|---|---|
|
assert_equal(t + 2, File.mtime(@zerofile))
|
||
|
end
|
||
|
def test_lutime
|
||
|
return unless @symlinkfile
|
||
|
t = Time.local(2000)
|
||
|
assert_equal(1, File.lutime(t + 1, t + 2, @symlinkfile))
|
||
|
assert_equal(2, File.lutime(t + 1, t + 2, @symlinkfile, @symlinkfile))
|
||
|
assert_equal(t + 1, File.lstat(@symlinkfile).atime)
|
||
|
assert_equal(t + 2, File.lstat(@symlinkfile).mtime)
|
||
|
assert_not_equal(t + 1, File.atime(@symlinkfile))
|
||
|
assert_not_equal(t + 2, File.mtime(@symlinkfile))
|
||
|
assert_raise(Errno::ENOENT) { File.lutime(t + 1, t + 2, @nofile) }
|
||
|
rescue NotImplementedError
|
||
|
end
|
||
|
def test_hardlink
|
||
|
return unless @hardlinkfile
|
||
|
assert_equal("file", File.ftype(@hardlinkfile))
|
||
| win32/win32.c | ||
|---|---|---|
|
}
|
||
|
/* License: Ruby's */
|
||
|
typedef enum utime_mode utime_mode_t;
|
||
|
enum utime_mode {
|
||
|
M_SYMLINK_FOLLOW = 0,
|
||
|
M_SYMLINK_NOFOLLOW
|
||
|
};
|
||
|
/* License: Ruby's */
|
||
|
static int
|
||
|
unixtime_to_filetime(time_t time, FILETIME *ft)
|
||
|
{
|
||
| ... | ... | |
|
/* License: Ruby's */
|
||
|
static int
|
||
|
wutime(const WCHAR *path, const struct utimbuf *times)
|
||
|
wutime(const WCHAR *path, const struct utimbuf *times, utime_mode_t mode)
|
||
|
{
|
||
|
HANDLE hFile;
|
||
|
FILETIME atime, mtime;
|
||
| ... | ... | |
|
mtime = atime;
|
||
|
}
|
||
|
int flags = FILE_FLAG_BACKUP_SEMANTICS;
|
||
|
if (mode == M_SYMLINK_NOFOLLOW)
|
||
|
flags |= FILE_FLAG_OPEN_REPARSE_POINT;
|
||
|
RUBY_CRITICAL({
|
||
|
const DWORD attr = GetFileAttributesW(path);
|
||
|
if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY))
|
||
|
SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY);
|
||
|
hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING,
|
||
|
FILE_FLAG_BACKUP_SEMANTICS, 0);
|
||
|
flags, 0);
|
||
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||
|
errno = map_errno(GetLastError());
|
||
|
ret = -1;
|
||
| ... | ... | |
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_uutime(const char *path, const struct utimbuf *times)
|
||
|
uutime_internal(const char *path, const struct utimbuf *time, utime_mode_t mode)
|
||
|
{
|
||
|
WCHAR *wpath;
|
||
|
int ret;
|
||
|
if (!(wpath = utf8_to_wstr(path, NULL)))
|
||
|
return -1;
|
||
|
ret = wutime(wpath, times);
|
||
|
ret = wutime(wpath, times, mode);
|
||
|
free(wpath);
|
||
|
return ret;
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_utime(const char *path, const struct utimbuf *times)
|
||
|
utime_internal(const char *path, const struct utimbuf *times, utime_mode_t mode)
|
||
|
{
|
||
|
WCHAR *wpath;
|
||
|
int ret;
|
||
|
if (!(wpath = filecp_to_wstr(path, NULL)))
|
||
|
return -1;
|
||
|
ret = wutime(wpath, times);
|
||
|
ret = wutime(wpath, times, mode);
|
||
|
free(wpath);
|
||
|
return ret;
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_uutime(const char *path, const struct utimbuf *times)
|
||
|
{
|
||
|
return uutime_internal(path, utimbuf, M_SYMLINK_FOLLOW);
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_utime(const char *path, const struct utimbuf *times)
|
||
|
{
|
||
|
return utime_internal(path, utimbuf, M_SYMLINK_FOLLOW);
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_luutime(const char *path, const struct utimbuf *times)
|
||
|
{
|
||
|
return uutime_internal(path, utimbuf, M_SYMLINK_NOFOLLOW);
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_lutime(const char *path, const struct utimbuf *times)
|
||
|
{
|
||
|
return utime_internal(path, utimbuf, M_SYMLINK_NOFOLLOW);
|
||
|
}
|
||
|
/* License: Ruby's */
|
||
|
int
|
||
|
rb_w32_uchdir(const char *path)
|
||
|
{
|
||
|
WCHAR *wpath;
|
||