Subject: pax problem+fix
To: None <tech-userlevel@netbsd.org>
From: Hans Bulfone <jsb@nil.at>
List: tech-userlevel
Date: 06/21/2003 13:24:32
--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

hi!

I'm using pkgsrc (and therefore pax) on Linux and have found the following
problem:

When installing the libxml2 package, the following command is executed:

cd tutorial && $(PAX) -rwpppm . $(DESTDIR)$(TARGET_DIR)/tutorial

As pax does not treat '.' specially, it checks if
$(DESTDIR)$(TARGET_DIR)/tutorial/. exists, which is the case, and
then tries to remove it.

On BSD, rmdir("directory/.") gives an Invalid Argument error.
(I've actually only tried this on FreeBSD as my NetBSD machine
is currently broken :( )

On Linux, when the ext2 filesysteme is used (but not with xfs),
the rmdir command succeeds and causes pax to enter an infinite
loop where it repeatedly creates and removes the target directory:

lstat(".", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/root/test2/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
mkdir("/root/test2/.", 0755)            = -1 EEXIST (File exists)
lstat("/root/test2/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
rmdir("/root/test2/.")                  = 0
mkdir("/root/test2/.", 0755)            = -1 ENOENT (No such file or directory)
lstat("/root/test2/.", 0xbfffda5c)      = -1 ENOENT (No such file or directory)
lstat("/root", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/root/test2", 0xbfffda4c)        = -1 ENOENT (No such file or directory)
mkdir("/root/test2", 0777)              = 0
access("/root/test2", R_OK|W_OK|X_OK)   = 0
mkdir("/root/test2/.", 0755)            = -1 EEXIST (File exists)
lstat("/root/test2/.", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
rmdir("/root/test2/.")                  = 0
lstat("/root", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/root/test2", 0xbfffda4c)        = -1 ENOENT (No such file or directory)
mkdir("/root/test2", 0777)              = 0
access("/root/test2", R_OK|W_OK|X_OK)   = 0

The attached patch (against the version of the pkgsrc-bootstrap-kit,
but imho it should apply to the /src/bin/pax version as well)
fixes the problem by checking against 'dir/.' in unlnk_exist() (file_subs.c).

hans.

--YZ5djTAD1cGYuMQK
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="bootstrap-pkgsrc-pax-linux.patch"

diff -Nrc bootstrap-pkgsrc.ORIG/pax/file_subs.c bootstrap-pkgsrc/pax/file_subs.c
*** bootstrap-pkgsrc.ORIG/pax/file_subs.c	Mon Mar  3 12:50:51 2003
--- bootstrap-pkgsrc/pax/file_subs.c	Wed Jun 18 13:05:54 2003
***************
*** 556,561 ****
--- 556,562 ----
  unlnk_exist(char *name, int type)
  {
  	struct stat sb;
+ 	size_t namelen;
  
  	/*
  	 * the file does not exist, or -k we are done
***************
*** 569,575 ****
--- 570,586 ----
  		/*
  		 * try to remove a directory, if it fails and we were going to
  		 * create a directory anyway, tell the caller (return a 1)
+ 		 * if name ends with "/.", don't try to remove it (as it might
+ 		 * succeed on Linux/ext2 and cause an endless loop).
  		 */
+ 		namelen = strlen(name);
+ 		if (namelen >= 2 && name[namelen-1] == '.' && name[namelen-2] == '/')
+ 		{
+ 			if (type == PAX_DIR)
+ 				return(1);
+ 			syswarn(1, EINVAL, "Cannot remove directory %s", name);
+ 			return(-1);
+ 		}
  		if (rmdir(name) < 0) {
  			if (type == PAX_DIR)
  				return(1);

--YZ5djTAD1cGYuMQK--