Subject: Re: pax problem+fix
To: None <tech-userlevel@netbsd.org, tech-pkg@netbsd.org>
From: Hans Bulfone <jsb@atnet.at>
List: tech-pkg
Date: 06/21/2003 21:50:18
hi!

On Sat, Jun 21, 2003 at 01:29:48PM -0400, Jan Schaumann wrote:
> > 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.
> 
> I'm not sure if I follow you here.  How does pax not treat "." in a
> special way?  Or rather, how should it?  When I install libxml2, it
> performs the step you mentioned without any problems.  It cd's into the
> 'tutorial' directory, then paxes everything from that directory ('.')
> into the other directory ('-rw'), preserving appropriate permissions and
> timestamps ('-pppm').
> 
> Why should it try to remove a directory?
> 
> Or am I missing something?

Maybe I was not clear enough:

The endless loop only occurs on Linux when the ext2 filesystem is used
(as far as I have tested).  It is actually not a problem on *BSD.

For example, when pax is invoked as follows:

  mkdir /root/test1 ; mkdir /root/test2 ; (create some files in test1)
  cd /root/test1 ; pax -rwpppm . /root/test2

the following happens:

- in ar_subs.c:copy(), the first destination path is '/root/test2/.',
  it is created using node_creat():

/*
 * create a link or special file
 */
if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG))
        res = lnk_creat(arcn);
else
        res = node_creat(arcn);

- node_creat() tries to create the directory '/root/test2/.', but
  it already exists:

mkdir("/root/test2/.", 0755)            = -1 EEXIST (File exists)

- node_creat() calls unlnk_exist() to move the existing file
  out of the way, which happens to be the destination
  directory itself :(

rmdir("/root/test2/.")                  = 0

  on *BSD, this call fails with an EINVAL error, but as
  unlnk_exist checks if it had to remove a directory because
  a directory has to be created, the error is not shown to
  the user and the process continues normally.

  on linux, the directory is removed.

- after this node_creat() tries again to create the directory:

mkdir("/root/test2/.", 0755)            = -1 ENOENT (No such file or directory)

  but it cannot be created using this path because test2
  does not exist anymore.

- after failing for the 2nd time, node_creat() calls chk_path() to
  create any missing parent directories:

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

- node_creat() tries again:

mkdir("/root/test2/.", 0755)            = -1 EEXIST (File exists)

- from this point on, the process hangs in an endless loop.
  in each iteration, the directory is first unlinked by calling
  unlnk_exist() and then recreated using chk_path().

> P.S.: Should we move this thread to tech-pkg?

I've also wondered which of the two lists is more appropriate, as it's
only a problem when using pkgsrc on Linux, but I don't know since I'm
new to both :(

hans.