FileUtils.touch not consistent with system('touch')

Hi,

I have a world-writable file, not owned by me, that I try to touch:

-rwxrwxrwx 1 root root 0 Oct 11 10:29 /tmp/file

ruby -rfileutils -e ‘FileUtils.touch("/tmp/file")’
/usr/lib/ruby/1.8/fileutils.rb:1014:in utime': Operation not permitted - /tmp/file (Errno::EPERM) from /usr/lib/ruby/1.8/fileutils.rb:1014:intouch’
from /usr/lib/ruby/1.8/fileutils.rb:1012:in `touch’
from -e:1

but:
ruby -e ‘system(“touch /tmp/file”)’

succeeds.

The reason is that File.utime cannot be called with a NULL argument.
Linux (and I assume this is Posix) allows a utime call with NULL if
the file is writeable,
but to change the time a process must have special privileges.
With the current File.utime FileUtils cannot implement this.

Cheers,

Han H.

Hi,

At Wed, 11 Oct 2006 19:08:36 +0900,
Nobuyoshi N. wrote in [ruby-talk:219042]:

Index: lib/fileutils.rb

RCS file: /pub/cvs/ruby/lib/fileutils.rb,v
retrieving revision 1.73
diff -U 2 -p -r1.73 fileutils.rb

  •    raise unless created
    

Sorry, should be if' but notunless’.

Hi,

At Wed, 11 Oct 2006 17:37:05 +0900,
Han H. wrote in [ruby-talk:219037]:

The reason is that File.utime cannot be called with a NULL argument.
Linux (and I assume this is Posix) allows a utime call with NULL if
the file is writeable,
but to change the time a process must have special privileges.
With the current File.utime FileUtils cannot implement this.

What about this?

Index: file.c

RCS file: /pub/cvs/ruby/file.c,v
retrieving revision 1.253
diff -U 2 -p -r1.253 file.c
— file.c 25 Sep 2006 12:25:03 -0000 1.253
+++ file.c 11 Oct 2006 08:53:57 -0000
@@ -1966,11 +1966,14 @@ rb_file_s_utime(int argc, VALUE *argv)
{
VALUE atime, mtime, rest;

  • struct timeval tvp[2];
  • struct timeval tvs[2], *tvp = NULL;
    long n;

    rb_scan_args(argc, argv, “2*”, &atime, &mtime, &rest);

  • tvp[0] = rb_time_timeval(atime);
  • tvp[1] = rb_time_timeval(mtime);
  • if (!NIL_P(atime) || !NIL_P(mtime)) {

  • tvp = tvs;

  • tvp[0] = rb_time_timeval(atime);

  • tvp[1] = rb_time_timeval(mtime);

  • }

    n = apply2files(utime_internal, rest, tvp);
    @@ -2001,14 +2004,17 @@ rb_file_s_utime(int argc, VALUE *argv)
    long n;
    struct timeval tv;

  • struct utimbuf utbuf;
  • struct utimbuf utbuf, *utp = NULL;

    rb_scan_args(argc, argv, “2*”, &atime, &mtime, &rest);

  • tv = rb_time_timeval(atime);
  • utbuf.actime = tv.tv_sec;
  • tv = rb_time_timeval(mtime);
  • utbuf.modtime = tv.tv_sec;
  • if (!NIL_P(atime) || !NIL_P(mtime)) {
  • utp = &utbuf;
  • tv = rb_time_timeval(atime);
  • utp->actime = tv.tv_sec;
  • tv = rb_time_timeval(mtime);
  • utp->modtime = tv.tv_sec;
  • }
  • n = apply2files(utime_internal, rest, &utbuf);
  • n = apply2files(utime_internal, rest, utp);
    return LONG2FIX(n);
    }
    @@ -2924,4 +2930,9 @@ rb_file_join(VALUE ary, VALUE sep)
    }
    else if (!*tail) {
    +#ifdef DOSISH_DRIVE_LETTER
  •   if ((tail - name == 2) && has_drive_letter(name)) {
    
  •       rb_str_buf_cat(result, ".", 1);
    
  •   }
    

+#endif
rb_str_buf_append(result, sep);
}
Index: lib/fileutils.rb

RCS file: /pub/cvs/ruby/lib/fileutils.rb,v
retrieving revision 1.73
diff -U 2 -p -r1.73 fileutils.rb
— lib/fileutils.rb 9 Oct 2006 14:41:24 -0000 1.73
+++ lib/fileutils.rb 11 Oct 2006 09:02:41 -0000
@@ -1008,14 +1008,21 @@ module FileUtils
fu_check_options options, OPT_TABLE[‘touch’]
list = fu_list(list)

  • fu_output_message “touch #{list.join ’ '}” if options[:verbose]
  • if options[:verbose]
  •  fu_output_message "touch #{options[:nocreate] ? ' -c' :
    

‘’}#{options[:mtime] ? options[:mtime].strftime(’ -t %Y%m%d%H%M.%S’) :
‘’}#{list.join ’ '}"

  • end
    return if options[:noop]
  • t = Time.now
  • created = nocreate = options[:nocreate]
  • t = options[:mtime]
    list.each do |path|
  •  created = nocreate
     begin
       File.utime(t, t, path)
     rescue Errno::ENOENT
    
  •    raise unless created
       File.open(path, 'a') {
         ;
       }
    
  •    created = true
    
  •    retry if t
     end
    
    end
    @@ -1023,5 +1030,5 @@ module FileUtils
    module_function :touch
  • OPT_TABLE[‘touch’] = [:noop, :verbose]
  • OPT_TABLE[‘touch’] = [:noop, :verbose, :mtime, :nocreate]

    private

Hi,

In mail “Re: FileUtils.touch not consistent with system(‘touch’)”
“Nobuyoshi N.” [email protected] wrote:

The reason is that File.utime cannot be called with a NULL argument.
Linux (and I assume this is Posix) allows a utime call with NULL if
the file is writeable,
but to change the time a process must have special privileges.
With the current File.utime FileUtils cannot implement this.

What about this?

Index: lib/fileutils.rb

This part seems nice, please commit it after File.utime
patch is committed.

Best Regards,
Minero A.

Hi,

At Wed, 11 Oct 2006 19:08:36 +0900,
Nobuyoshi N. wrote in [ruby-talk:219042]:

Linux (and I assume this is Posix) allows a utime call with NULL if
the file is writeable,

rb_w32_utime() didn’t support it.

I’m not sure it should be LocalFileTime or not.

Index: win32/win32.c

RCS file: /pub/cvs/ruby/win32/win32.c,v
retrieving revision 1.195
diff -U 2 -p -r1.195 win32.c
— win32/win32.c 12 Aug 2006 06:56:09 -0000 1.195
+++ win32/win32.c 12 Oct 2006 04:50:36 -0000
@@ -4118,9 +4118,15 @@ rb_w32_utime(const char *path, const str
}

  • if (unixtime_to_filetime(times->actime, &atime)) {
  • return -1;
  • if (times) {
  • if (unixtime_to_filetime(times->actime, &atime)) {
  •   return -1;
    
  • }
  • if (unixtime_to_filetime(times->modtime, &mtime)) {
  •   return -1;
    
  • }
    }
  • if (unixtime_to_filetime(times->modtime, &mtime)) {
  • return -1;
  • else {
  • GetSystemTimeAsFileTime(&atime);
  • mtime = atime;
    }