Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 15 Nov 2005 08:30:54 -0800 (PST)
From:      Virgil Champlin <champlin@stupidog.org>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   ports/89077: The port lang/ruby18 upgrade to 1.8.3 has a flaw in the file rename function.
Message-ID:  <20051115163054.25D91238B@whisper.stupidog.org>
Resent-Message-ID: <200511151640.jAFGeOc9064119@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         89077
>Category:       ports
>Synopsis:       The port lang/ruby18 upgrade to 1.8.3 has a flaw in the file rename function.
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-ports-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Nov 15 16:40:24 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Virgil Champlin
>Release:        FreeBSD 5.4-RELEASE-p8 i386
>Organization:
Stupidog.org
>Environment:
System: FreeBSD eeyore.stupidog.org 5.4-RELEASE-p8 FreeBSD 5.4-RELEASE-p8 #21: Wed Oct 12 11:02:47 PDT 2005 root@whisper.stupidog.org:/usr/obj/usr/src/sys/STUPIDOG i386

	/usr/local/lib/ruby/1.8/fileutils.rb

>Description:

The Ruby function to rename files, FileUtils.mv in lib/fileutils.rb,
does not remove (unlink) the source file when the source and destination
are on different file systems.  This is new behavior.

>How-To-Repeat:

This is noticeable in the behavior of pkg_fetch, a function in the
dependent port, sysutils/portupgrade.  When upgrading from remote
repository packages, pkg_fetch retrieves the prospective package to a
temporary directory created in PKG_TMPDIR.  It then applies some sanity and moves
successful packages to the PACKAGES directory.  The default for
PKG_TMPDIR is /var/tmp which in many cases will not be the same file
system as PACKAGES.  The lingering original file will be noticed when a
subsequent removal of the temporary directory fails because it is not
empty.  e.g.

** Could not clean up temporary directory: Directory not empty - /var/tmp/portupgradeaOSUjspd

This does not prevent the upgrade but will leave the temporary directory
and its lone file cluttering PKG_TMPDIR with each successful pkg_fetch.

You can easily demonstrate this with the assistance of Ruby's
interactive interpreter, irb.  Create a small file on one file system; use
Ruby to rename it as another file on the same file system; then use Ruby
to rename it across a file system boundary.  The second operation is
effectively a copy.

Bash> echo "Hello World" >foo.txt
Bash> irb -r fileutils
irb(main):001:0> FileUtils.mv "foo.txt", "foo1.txt"
=> 0
irb(main):002:0> FileUtils.mv "foo1.txt", "/tmp/foo2.txt"
=> nil
Bash> ls -l foo* /tmp/foo*
-rw-r--r--  1 champlin  champlin  12 Nov 12 22:15 /tmp/foo2.txt
-rw-r--r--  1 champlin  champlin  12 Nov 12 22:15 foo1.txt


>Fix:

This is noticeable in the behavior of pkg_fetch, a function in the
dependent port, sysutils/portupgrade.  When upgrading from remote
repository packages, pkg_fetch retrieves the prospective package to a
temporary directory created in PKG_TMPDIR.  It then applies some sanity and moves
successful packages to the PACKAGES directory.  The default for
PKG_TMPDIR is /var/tmp which in many cases will not be the same file
system as PACKAGES.  The lingering original file will be noticed when a
subsequent removal of the temporary directory fails because it is not
empty.  e.g.

** Could not clean up temporary directory: Directory not empty - /var/tmp/portupgradeaOSUjspd

This does not prevent the upgrade but will leave the temporary directory
and its lone file cluttering PKG_TMPDIR with each successful pkg_fetch.

You can easily demonstrate this with the assistance of Ruby's
interactive interpreter, irb.  Create a small file on one file system; use
Ruby to rename it as another file on the same file system; then use Ruby
to rename it across a file system boundary.  The second operation is
effectively a copy.

Bash> echo "Hello World" >foo.txt
Bash> irb -r fileutils
irb(main):001:0> FileUtils.mv "foo.txt", "foo1.txt"
=> 0
irb(main):002:0> FileUtils.mv "foo1.txt", "/tmp/foo2.txt"
=> nil
Bash> ls -l foo* /tmp/foo*
-rw-r--r--  1 champlin  champlin  12 Nov 12 22:15 /tmp/foo2.txt
-rw-r--r--  1 champlin  champlin  12 Nov 12 22:15 foo1.txt

FIX

A commit for version 1.36.2.6 of fileutils.rb (ruby_1_8 branch) on Dec
27, 2004 removed the File.unlink that accompanied the special case of
crossing file system boundaries.  It was actually propagating the change
from the MAIN branch and I could not determine the reason for its
removal.  Restoring the File.unlink to FileUtils.mv restored the
expected behavior.  You can view the cvs log, focused on 1.36.2.6 at:

http://www.ruby-lang.org/cgi-bin/cvsweb.cgi/ruby/lib/fileutils.rb#rev1.36.2.6

The following patch restores the unlink but should be reviewed by
someone that actually knows ruby.

--- /usr/local/lib/ruby/1.8/fileutils.rb.orig   Tue Nov 15 08:08:48 2005
+++ /usr/local/lib/ruby/1.8/fileutils.rb        Tue Nov 15 08:10:21 2005
@@ -500,7 +500,10 @@
         begin
           File.rename s, d
         rescue Errno::EXDEV
-          copy_entry s, d, true
+          begin
+            copy_entry s, d, true
+            File.unlink s
+          end
         end
       rescue SystemCallError
         raise unless options[:force]


APOLOGY

This is way too verbose but I don't know how to tell a short story the
right way.  It always comes out long so please forgive.  I also wished
to submit this to the Ruby folks but couldn't quite figure out the
acceptable way.  I am unfamiliar with Ruby and its culture so hoped that
the port maintainer would be kind enough to correct either myself or the
Ruby project.  Thank you very much for your patience and wonderful
efforts.  -virgil

>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20051115163054.25D91238B>