Feature #7106 ยป 0001-Add-support-for-lutimes.patch
configure.in | ||
---|---|---|
1496 | 1496 |
AC_LIBOBJ([signbit]) |
1497 | 1497 |
fi |
1498 | 1498 |
AC_CHECK_FUNCS(fmod killpg wait4 waitpid fork spawnv syscall __syscall chroot getcwd eaccess\ |
1499 |
truncate ftruncate ftello chsize times utimes utimensat fcntl lockf lstat\ |
|
1499 |
truncate ftruncate ftello chsize times utimes lutimes utimensat fcntl lockf lstat\
|
|
1500 | 1500 |
truncate64 ftruncate64 ftello64 fseeko fseeko64 \ |
1501 | 1501 |
link symlink readlink readdir_r fsync fdatasync fchown posix_fadvise\ |
1502 | 1502 |
setitimer setruid seteuid setreuid setresuid socketpair\ |
file.c | ||
---|---|---|
92 | 92 |
#define chown(p, o, g) rb_w32_uchown((p), (o), (g)) |
93 | 93 |
#undef utime |
94 | 94 |
#define utime(p, t) rb_w32_uutime((p), (t)) |
95 |
#undef lutime |
|
96 |
#define lutime(p, t) rb_w32_luutime((p), (t)) |
|
95 | 97 |
#undef link |
96 | 98 |
#define link(f, t) rb_w32_ulink((f), (t)) |
97 | 99 |
#undef unlink |
... | ... | |
2206 | 2208 |
#define rb_file_s_lchown rb_f_notimplement |
2207 | 2209 |
#endif |
2208 | 2210 | |
2211 |
typedef enum utime_mode utime_mode_t; |
|
2212 | ||
2213 |
enum utime_mode { |
|
2214 |
M_SYMLINK_FOLLOW = 0, |
|
2215 |
M_SYMLINK_NOFOLLOW |
|
2216 |
}; |
|
2217 | ||
2209 | 2218 |
struct utime_args { |
2210 | 2219 |
const struct timespec* tsp; |
2211 | 2220 |
VALUE atime, mtime; |
2221 |
utime_mode_t mode; |
|
2212 | 2222 |
}; |
2213 | 2223 | |
2224 |
#if !defined(FOLLOW_SYMLINK_P) |
|
2225 |
# define FOLLOW_SYMLINK_P(x) ((x)->mode == M_SYMLINK_FOLLOW) |
|
2226 |
#endif |
|
2227 | ||
2214 | 2228 |
#if defined DOSISH || defined __CYGWIN__ |
2215 | 2229 |
NORETURN(static void utime_failed(VALUE, const struct timespec *, VALUE, VALUE)); |
2216 | 2230 | |
... | ... | |
2252 | 2266 |
#if defined(HAVE_UTIMES) |
2253 | 2267 | |
2254 | 2268 |
static void |
2255 |
utime_internal(const char *path, VALUE pathv, void *arg)
|
|
2269 |
utime_helper(const char *path, VALUE pathv, void *arg)
|
|
2256 | 2270 |
{ |
2257 | 2271 |
struct utime_args *v = arg; |
2258 | 2272 |
const struct timespec *tsp = v->tsp; |
... | ... | |
2260 | 2274 | |
2261 | 2275 |
#ifdef HAVE_UTIMENSAT |
2262 | 2276 |
static int try_utimensat = 1; |
2277 |
const int flags = FOLLOW_SYMLINK_P(v) ? 0 : AT_SYMLINK_NOFOLLOW; |
|
2263 | 2278 | |
2264 | 2279 |
if (try_utimensat) { |
2265 |
if (utimensat(AT_FDCWD, path, tsp, 0) < 0) {
|
|
2280 |
if (utimensat(AT_FDCWD, path, tsp, flags) < 0) {
|
|
2266 | 2281 |
if (errno == ENOSYS) { |
2267 | 2282 |
try_utimensat = 0; |
2268 | 2283 |
goto no_utimensat; |
... | ... | |
2281 | 2296 |
tvbuf[1].tv_usec = (int)(tsp[1].tv_nsec / 1000); |
2282 | 2297 |
tvp = tvbuf; |
2283 | 2298 |
} |
2284 |
if (utimes(path, tvp) < 0)
|
|
2299 |
if ((FOLLOW_SYMLINK_P(v) ? utimes(path, tvp) : lutimes(path, tvp)) < 0)
|
|
2285 | 2300 |
utime_failed(pathv, tsp, v->atime, v->mtime); |
2286 | 2301 |
} |
2287 | 2302 | |
... | ... | |
2295 | 2310 |
#endif |
2296 | 2311 | |
2297 | 2312 |
static void |
2298 |
utime_internal(const char *path, VALUE pathv, void *arg)
|
|
2313 |
utime_helper(const char *path, VALUE pathv, void *arg)
|
|
2299 | 2314 |
{ |
2300 | 2315 |
struct utime_args *v = arg; |
2301 | 2316 |
const struct timespec *tsp = v->tsp; |
... | ... | |
2305 | 2320 |
utbuf.modtime = tsp[1].tv_sec; |
2306 | 2321 |
utp = &utbuf; |
2307 | 2322 |
} |
2308 |
if (utime(path, utp) < 0)
|
|
2323 |
if ((FOLLOW_SYMLINK_P(v) ? utimes(path, utp) : lutimes(path, utp)) < 0)
|
|
2309 | 2324 |
utime_failed(pathv, tsp, v->atime, v->mtime); |
2310 | 2325 |
} |
2311 | 2326 | |
2312 | 2327 |
#endif |
2313 | 2328 | |
2314 |
/* |
|
2315 |
* call-seq: |
|
2316 |
* File.utime(atime, mtime, file_name,...) -> integer |
|
2317 |
* |
|
2318 |
* Sets the access and modification times of each |
|
2319 |
* named file to the first two arguments. Returns |
|
2320 |
* the number of file names in the argument list. |
|
2321 |
*/ |
|
2322 | ||
2323 | 2329 |
static VALUE |
2324 |
rb_file_s_utime(int argc, VALUE *argv)
|
|
2330 |
utime_internal(int argc, VALUE *argv, utime_mode_t mode)
|
|
2325 | 2331 |
{ |
2326 | 2332 |
VALUE rest; |
2327 | 2333 |
struct utime_args args; |
... | ... | |
2337 | 2343 |
tsp[1] = rb_time_timespec(args.mtime); |
2338 | 2344 |
} |
2339 | 2345 |
args.tsp = tsp; |
2346 |
#ifdef HAVE_LUTIMES |
|
2347 |
args.mode = mode; |
|
2348 |
#else |
|
2349 |
args.mode = M_SYMLINK_FOLLOW; |
|
2350 |
#endif |
|
2340 | 2351 | |
2341 |
n = apply2files(utime_internal, rest, &args);
|
|
2352 |
n = apply2files(utime_helper, rest, &args);
|
|
2342 | 2353 |
return LONG2FIX(n); |
2343 | 2354 |
} |
2344 | 2355 | |
2356 |
/* |
|
2357 |
* call-seq: |
|
2358 |
* File.utime(atime, mtime, file_name,...) -> integer |
|
2359 |
* |
|
2360 |
* Sets the access and modification times of each |
|
2361 |
* named file to the first two arguments. Returns |
|
2362 |
* the number of file names in the argument list. |
|
2363 |
* See also File::lutime. |
|
2364 |
*/ |
|
2365 | ||
2366 |
static inline VALUE |
|
2367 |
rb_file_s_utime(int argc, VALUE *argv) |
|
2368 |
{ |
|
2369 |
return utime_internal(argc, argv, M_SYMLINK_FOLLOW); |
|
2370 |
} |
|
2371 | ||
2372 |
#ifdef HAVE_LUTIMES |
|
2373 |
/* |
|
2374 |
* call-seq: |
|
2375 |
* File.lutime(atime, mtime, file_name,...) -> integer |
|
2376 |
* |
|
2377 |
* Equivalent to File::utime, but does not follow |
|
2378 |
* symbolic links (so it will change the +atime+ |
|
2379 |
* and +mtime+ associated with the link, not the |
|
2380 |
* file referenced by the link). Often not available. |
|
2381 |
* |
|
2382 |
* See also File::utime. |
|
2383 |
*/ |
|
2384 | ||
2385 |
static VALUE |
|
2386 |
rb_file_s_lutime(int argc, VALUE *argv) |
|
2387 |
{ |
|
2388 |
return utime_internal(argc, argv, M_SYMLINK_NOFOLLOW); |
|
2389 |
} |
|
2390 |
#else |
|
2391 |
#define rb_file_s_lutime rb_f_notimplement |
|
2392 |
#endif |
|
2393 | ||
2394 |
#if defined(FOLLOW_SYMLINK_P) |
|
2395 |
# undef FOLLOW_SYMLINK_P |
|
2396 |
#endif |
|
2397 | ||
2345 | 2398 |
NORETURN(static void sys_fail2(VALUE,VALUE)); |
2346 | 2399 |
static void |
2347 | 2400 |
sys_fail2(VALUE s1, VALUE s2) |
... | ... | |
5439 | 5492 |
rb_define_singleton_method(rb_cFile, "ctime", rb_file_s_ctime, 1); |
5440 | 5493 | |
5441 | 5494 |
rb_define_singleton_method(rb_cFile, "utime", rb_file_s_utime, -1); |
5495 |
rb_define_singleton_method(rb_cFile, "lutime", rb_file_s_lutime, -1); |
|
5442 | 5496 |
rb_define_singleton_method(rb_cFile, "chmod", rb_file_s_chmod, -1); |
5443 | 5497 |
rb_define_singleton_method(rb_cFile, "chown", rb_file_s_chown, -1); |
5444 | 5498 |
rb_define_singleton_method(rb_cFile, "lchmod", rb_file_s_lchmod, -1); |
lib/fileutils.rb | ||
---|---|---|
1181 | 1181 |
public |
1182 | 1182 | |
1183 | 1183 |
# |
1184 |
# Options: noop verbose |
|
1184 |
# Options: noop verbose mtime nocreate nofollow
|
|
1185 | 1185 |
# |
1186 | 1186 |
# Updates modification time (mtime) and access time (atime) of file(s) in |
1187 | 1187 |
# +list+. Files are created if they don't exist. |
... | ... | |
1201 | 1201 |
list.each do |path| |
1202 | 1202 |
created = nocreate |
1203 | 1203 |
begin |
1204 |
File.utime(t, t, path) |
|
1204 |
if File.symlink?(path) and options[:nofollow] |
|
1205 |
File.lutime(t, t, path) |
|
1206 |
else |
|
1207 |
File.utime(t, t, path) |
|
1208 |
end |
|
1205 | 1209 |
rescue Errno::ENOENT |
1206 | 1210 |
raise if created |
1207 | 1211 |
File.open(path, 'a') { |
... | ... | |
1213 | 1217 |
end |
1214 | 1218 |
end |
1215 | 1219 | |
1216 |
define_command('touch', :noop, :verbose, :mtime, :nocreate) |
|
1220 |
define_command('touch', :noop, :verbose, :mtime, :nocreate, :nofollow)
|
|
1217 | 1221 | |
1218 | 1222 |
private |
1219 | 1223 | |
... | ... | |
1442 | 1446 | |
1443 | 1447 |
def copy_metadata(path) |
1444 | 1448 |
st = lstat() |
1445 |
if !st.symlink? |
|
1449 |
if st.symlink? |
|
1450 |
begin |
|
1451 |
File.lutime st.atime, st.mtime, path |
|
1452 |
rescue NotImplementedError |
|
1453 |
end |
|
1454 |
else |
|
1446 | 1455 |
File.utime st.atime, st.mtime, path |
1447 | 1456 |
end |
1448 | 1457 |
begin |
test/ruby/test_file_exhaustive.rb | ||
---|---|---|
374 | 374 |
assert_equal(t + 2, File.mtime(@zerofile)) |
375 | 375 |
end |
376 | 376 | |
377 |
def test_lutime |
|
378 |
return unless @symlinkfile |
|
379 |
t = Time.local(2000) |
|
380 |
assert_equal(1, File.lutime(t + 1, t + 2, @symlinkfile)) |
|
381 |
assert_equal(2, File.lutime(t + 1, t + 2, @symlinkfile, @symlinkfile)) |
|
382 |
assert_equal(t + 1, File.lstat(@symlinkfile).atime) |
|
383 |
assert_equal(t + 2, File.lstat(@symlinkfile).mtime) |
|
384 |
assert_not_equal(t + 1, File.atime(@symlinkfile)) |
|
385 |
assert_not_equal(t + 2, File.mtime(@symlinkfile)) |
|
386 |
assert_raise(Errno::ENOENT) { File.lutime(t + 1, t + 2, @nofile) } |
|
387 |
rescue NotImplementedError |
|
388 |
end |
|
389 | ||
377 | 390 |
def test_hardlink |
378 | 391 |
return unless @hardlinkfile |
379 | 392 |
assert_equal("file", File.ftype(@hardlinkfile)) |
win32/win32.c | ||
---|---|---|
6195 | 6195 |
} |
6196 | 6196 | |
6197 | 6197 |
/* License: Ruby's */ |
6198 |
typedef enum utime_mode utime_mode_t; |
|
6199 | ||
6200 |
enum utime_mode { |
|
6201 |
M_SYMLINK_FOLLOW = 0, |
|
6202 |
M_SYMLINK_NOFOLLOW |
|
6203 |
}; |
|
6204 | ||
6205 |
/* License: Ruby's */ |
|
6198 | 6206 |
static int |
6199 | 6207 |
unixtime_to_filetime(time_t time, FILETIME *ft) |
6200 | 6208 |
{ |
... | ... | |
6208 | 6216 | |
6209 | 6217 |
/* License: Ruby's */ |
6210 | 6218 |
static int |
6211 |
wutime(const WCHAR *path, const struct utimbuf *times) |
|
6219 |
wutime(const WCHAR *path, const struct utimbuf *times, utime_mode_t mode)
|
|
6212 | 6220 |
{ |
6213 | 6221 |
HANDLE hFile; |
6214 | 6222 |
FILETIME atime, mtime; |
... | ... | |
6232 | 6240 |
mtime = atime; |
6233 | 6241 |
} |
6234 | 6242 | |
6243 |
int flags = FILE_FLAG_BACKUP_SEMANTICS; |
|
6244 | ||
6245 |
if (mode == M_SYMLINK_NOFOLLOW) |
|
6246 |
flags |= FILE_FLAG_OPEN_REPARSE_POINT; |
|
6247 | ||
6235 | 6248 |
RUBY_CRITICAL({ |
6236 | 6249 |
const DWORD attr = GetFileAttributesW(path); |
6237 | 6250 |
if (attr != (DWORD)-1 && (attr & FILE_ATTRIBUTE_READONLY)) |
6238 | 6251 |
SetFileAttributesW(path, attr & ~FILE_ATTRIBUTE_READONLY); |
6239 | 6252 |
hFile = CreateFileW(path, GENERIC_WRITE, 0, 0, OPEN_EXISTING, |
6240 |
FILE_FLAG_BACKUP_SEMANTICS, 0);
|
|
6253 |
flags, 0);
|
|
6241 | 6254 |
if (hFile == INVALID_HANDLE_VALUE) { |
6242 | 6255 |
errno = map_errno(GetLastError()); |
6243 | 6256 |
ret = -1; |
... | ... | |
6258 | 6271 | |
6259 | 6272 |
/* License: Ruby's */ |
6260 | 6273 |
int |
6261 |
rb_w32_uutime(const char *path, const struct utimbuf *times)
|
|
6274 |
uutime_internal(const char *path, const struct utimbuf *time, utime_mode_t mode)
|
|
6262 | 6275 |
{ |
6263 | 6276 |
WCHAR *wpath; |
6264 | 6277 |
int ret; |
6265 | 6278 | |
6266 | 6279 |
if (!(wpath = utf8_to_wstr(path, NULL))) |
6267 | 6280 |
return -1; |
6268 |
ret = wutime(wpath, times); |
|
6281 |
ret = wutime(wpath, times, mode);
|
|
6269 | 6282 |
free(wpath); |
6270 | 6283 |
return ret; |
6271 | 6284 |
} |
6272 | 6285 | |
6273 | 6286 |
/* License: Ruby's */ |
6274 | 6287 |
int |
6275 |
rb_w32_utime(const char *path, const struct utimbuf *times)
|
|
6288 |
utime_internal(const char *path, const struct utimbuf *times, utime_mode_t mode)
|
|
6276 | 6289 |
{ |
6277 | 6290 |
WCHAR *wpath; |
6278 | 6291 |
int ret; |
6279 | 6292 | |
6280 | 6293 |
if (!(wpath = filecp_to_wstr(path, NULL))) |
6281 | 6294 |
return -1; |
6282 |
ret = wutime(wpath, times); |
|
6295 |
ret = wutime(wpath, times, mode);
|
|
6283 | 6296 |
free(wpath); |
6284 | 6297 |
return ret; |
6285 | 6298 |
} |
6286 | 6299 | |
6287 | 6300 |
/* License: Ruby's */ |
6288 | 6301 |
int |
6302 |
rb_w32_uutime(const char *path, const struct utimbuf *times) |
|
6303 |
{ |
|
6304 |
return uutime_internal(path, utimbuf, M_SYMLINK_FOLLOW); |
|
6305 |
} |
|
6306 | ||
6307 |
/* License: Ruby's */ |
|
6308 |
int |
|
6309 |
rb_w32_utime(const char *path, const struct utimbuf *times) |
|
6310 |
{ |
|
6311 |
return utime_internal(path, utimbuf, M_SYMLINK_FOLLOW); |
|
6312 |
} |
|
6313 | ||
6314 |
/* License: Ruby's */ |
|
6315 |
int |
|
6316 |
rb_w32_luutime(const char *path, const struct utimbuf *times) |
|
6317 |
{ |
|
6318 |
return uutime_internal(path, utimbuf, M_SYMLINK_NOFOLLOW); |
|
6319 |
} |
|
6320 | ||
6321 |
/* License: Ruby's */ |
|
6322 |
int |
|
6323 |
rb_w32_lutime(const char *path, const struct utimbuf *times) |
|
6324 |
{ |
|
6325 |
return utime_internal(path, utimbuf, M_SYMLINK_NOFOLLOW); |
|
6326 |
} |
|
6327 | ||
6328 |
/* License: Ruby's */ |
|
6329 |
int |
|
6289 | 6330 |
rb_w32_uchdir(const char *path) |
6290 | 6331 |
{ |
6291 | 6332 |
WCHAR *wpath; |