0001-Add-support-for-lutimes.patch

Krzysztof Wilczynski, 10/24/2012 10:39 AM

Download (10.5 KB)

View differences:

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;