From nobody@FreeBSD.ORG Tue Nov 16 04:12:21 1999
Return-Path: <nobody@FreeBSD.ORG>
Received: by hub.freebsd.org (Postfix, from userid 32767)
	id F2C7B14A2F; Tue, 16 Nov 1999 04:12:20 -0800 (PST)
Message-Id: <19991116121220.F2C7B14A2F@hub.freebsd.org>
Date: Tue, 16 Nov 1999 04:12:20 -0800 (PST)
From: okimoto@mrit.mei.co.jp
Sender: nobody@FreeBSD.ORG
To: freebsd-gnats-submit@freebsd.org
Subject: install(1) hangs when intalling files same directory.
X-Send-Pr-Version: www-1.0

>Number:         14920
>Category:       bin
>Synopsis:       install(1) hangs when intalling files same directory.
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Tue Nov 16 04:20:00 PST 1999
>Closed-Date:    Fri Jul 20 20:06:27 PDT 2001
>Last-Modified:  Fri Jul 20 20:09:30 PDT 2001
>Originator:     Yoshiyuki OKIMOTO
>Release:        3.3-RELEASE
>Organization:
Matsushita Research Institute Tokyo, Inc.
>Environment:
FreeBSD sango 3.3-RELEASE FreeBSD 3.3-RELEASE #1: Tue Nov  2 12:20:21 JST 1999     root@sango:/usr/src/sys/compile/SANGO  i386
>Description:
Unable kill install(1), when installing files 
to same directory.
>How-To-Repeat:
1) touch ./foo
2) install ./foo .
>Fix:
install(1) should detect original source directory and
target directory is same or not, I think.

>Release-Note:
>Audit-Trail:

From: Alexander Langer <alex@cichlids.com>
To: okimoto@mrit.mei.co.jp
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: bin/14920: install(1) hangs when intalling files same directory.
Date: Wed, 17 Nov 1999 21:51:32 +0100

 I've done a patch to xinstall, that detects if the source-file is the
 same as the destination file, also if they are in the same directory:
 
 alex:~ $ touch foo bar
 alex:~ $ install foo bar .
 install: foo and ./foo are the same file
 alex:~ $ install foo bar ../../home/alex
 install: foo and ../../home/alex/foo are the same file
 alex:~ $ install foo bar /usr/home/alex 
 install: foo and /usr/home/alex/foo are the same file
 alex:~ $ install foo bar test/..       
 install: foo and test/../foo are the same file
 alex:~ $ 
 
 
 Here is the patch:
 
 --- xinstall.c.old	Wed Nov 17 20:32:40 1999
 +++ xinstall.c	Wed Nov 17 21:48:05 1999
 @@ -90,7 +90,7 @@
  int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose;
  int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
  char *group, *owner, pathbuf[MAXPATHLEN];
 -char pathbuf2[MAXPATHLEN];
 +char pathbuf2[MAXPATHLEN], pathbuf3[MAXPATHLEN];
  
  #define	DIRECTORY	0x01		/* Tell install it's a directory. */
  #define	SETFLAGS	0x02		/* Tell install to set flags. */
 @@ -105,6 +105,7 @@
  void	strip __P((char *));
  void	usage __P((void));
  int	trymmap __P((int));
 +char *	basename __P((char *));
  
  #define ALLOW_NUMERIC_IDS 1
  #ifdef ALLOW_NUMERIC_IDS
 @@ -128,7 +129,7 @@
  	int argc;
  	char *argv[];
  {
 -	struct stat from_sb, to_sb;
 +	struct stat from_sb, to_sb, fileto_sb;
  	mode_t *set;
  	u_long fset;
  	u_int iflags;
 @@ -222,8 +223,28 @@
  
  	no_target = stat(to_name = argv[argc - 1], &to_sb);
  	if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
 -		for (; *argv != to_name; ++argv)
 -			install(*argv, to_name, fset, iflags | DIRECTORY);
 +		for (; *argv != to_name; ++argv) {
 +			if (stat(*argv, &from_sb))
 +				err(EX_OSERR, "%s", *argv);
 +			snprintf(pathbuf3, MAXPATHLEN,
 +			    "%s%s%s", to_name,
 +			    to_name[strlen(to_name) - 1] == '/' ? "" : "/",
 +			    basename(*argv));
 +			if (stat(pathbuf3, &fileto_sb) && errno != ENOENT)
 +				err(EX_OSERR, "%s", pathbuf3);
 +			else if (errno == ENOENT)
 +				goto target_ok;
 +			if (!S_ISREG(fileto_sb.st_mode)) {
 +				errno = EFTYPE;
 +				err(EX_OSERR, "%s", pathbuf3);
 +			}
 +			if (fileto_sb.st_dev == from_sb.st_dev &&
 +			    fileto_sb.st_ino == from_sb.st_ino)
 +				errx(EX_USAGE,
 +				    "%s and %s are the same file", *argv, pathbuf3);
 +		target_ok:
 +			install(*argv, pathbuf3, fset, iflags);
 +	    }
  		exit(EX_OK);
  		/* NOTREACHED */
  	}
 @@ -736,4 +757,19 @@
  		return (1);
  #endif
  	return (0);
 +}
 +
 +/*
 + * basename --
 + *	return the filename of a file whose absolut path is given
 + *	as pointer to char.
 + */
 +char *
 +basename(name)
 +	char *name;
 +{
 +	char *slash;
 +
 +	slash = strrchr(name, '/');
 +	return (slash == NULL ? name : slash + 1);
  }
 

From: Sheldon Hearn <sheldonh@uunet.co.za>
To: okimoto@mrit.mei.co.jp
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: bin/14920: install(1) hangs when intalling files same directory. 
Date: Thu, 18 Nov 1999 12:27:28 +0200

 On Tue, 16 Nov 1999 04:12:20 PST, okimoto@mrit.mei.co.jp wrote:
 
 > Unable kill install(1), when installing files 
 > to same directory.
 > >How-To-Repeat:
 > 1) touch ./foo
 > 2) install ./foo .
 
 This doesn't cause a problem in CURRENT.  Could you please test this for
 3.3-STABLE.  If it's still a problem there, then we'll need to
 investigate the difference between RELENG_3 and HEAD and figure out what
 fix needs to be backported.
 
 Ciao,
 Sheldon.
 

From: okimoto@mrit.mei.co.jp
To: sheldonh@uunet.co.za
Cc: freebsd-gnats-submit@FreeBSD.ORG, okimoto@mrit.mei.co.jp
Subject: Re: bin/14920: install(1) hangs when intalling files same directory. 
Date: Thu, 18 Nov 1999 20:32:59 +0900

 Thank you for repling my report.
 
 But, I have received some mails from "alex@cichlids.com",
 maybe a menbar of freebsd-gnats-submit. And this problem
 has solved.
 
 He said this problem was  3.3-RELEASE case. But,
 install(1) should detect if the source-file is the
 same as the destination file, he also thought. 
 So, he made a patch for "xinstall.c", and it's good.
 
 Thank you, Mr.Alexander. 
 And thank you, Mr.Sheldon. 
 
 # ..though, I should report this to somewhere?
 
 ---
 Yoshiyuki OKIMOTO
 

From: Alexander Langer <alex@cichlids.com>
To: okimoto@mrit.mei.co.jp
Cc: freebsd-gnats-submit@FreeBSD.ORG
Subject: Re: bin/14920: install(1) hangs when intalling files same directory.
Date: Thu, 18 Nov 1999 15:49:55 +0100

 ok. This patch seems nice.
 Please discard the old one.
 
 I also fixed a /dev/null error. Please see the thread on freebsd-bugs
 about this.
 
 Sheldon suggested to use realpath(), so I did.
 
 Usage-log:
 
 alex:~ $ mkdir test
 mkdir: test: File exists
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo
 test
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo
 test/foo
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo
 test/.. 
 xinstall: foo and /usr/home/alex/foo are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo .      
 xinstall: foo and /usr/home/alex/foo are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo ./
 xinstall: foo and /usr/home/alex/foo are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall foo
 ./../alex
 xinstall: foo and /usr/home/alex/foo are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall ./foo
 ./../alex
 xinstall: ./foo and /usr/home/alex/foo are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall test           
 test          test.scm      testdatei     testwort.bak  
 test.c        testbla       testwort      
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall
 test/bar test
 xinstall: test/bar and /usr/home/alex/test/bar are the same file
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall
 test/bar test/..
 alex:~ $ touch foo; /usr/obj/usr/src/usr.bin/xinstall/xinstall
 /dev/null test  
 xinstall: Cannot install /dev/null to a directory
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 YES, I did it. I changed the error-msg. But I think, it's ok.
 
 alex:~ $ rm -rf bar ; /usr/obj/usr/src/usr.bin/xinstall/xinstall
 /dev/null bar ; ls -l bar
 -rwxr-xr-x  1 alex  alex  0 18 Nov 15:45 bar*
 
 Patch:
 
 --- xinstall.c.old	Wed Nov 17 20:32:40 1999
 +++ xinstall.c	Thu Nov 18 15:48:12 1999
 @@ -90,7 +90,7 @@
  int debug, docompare, docopy, dodir, dopreserve, dostrip, nommap, verbose;
  int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
  char *group, *owner, pathbuf[MAXPATHLEN];
 -char pathbuf2[MAXPATHLEN];
 +char pathbuf2[MAXPATHLEN], pathbuf3[MAXPATHLEN];
  
  #define	DIRECTORY	0x01		/* Tell install it's a directory. */
  #define	SETFLAGS	0x02		/* Tell install to set flags. */
 @@ -319,7 +319,7 @@
  	u_long fset;
  	u_int flags;
  {
 -	struct stat from_sb, to_sb;
 +	struct stat from_sb, to_sb, fileto_sb;
  	int devnull, from_fd, to_fd, serrno;
  	char *p, *old_to_name = 0;
  
 @@ -327,25 +327,35 @@
  		fprintf(stderr, "install: invoked without -C for %s to %s\n",
  			from_name, to_name);
  
 +	if (strcmp(from_name, _PATH_DEVNULL) == 0) {
 +		from_sb.st_flags = 0;	/* XXX */
 +		devnull = 1;
 +	} else 
 +		devnull = 0;
  	/* If try to install NULL file to a directory, fails. */
 -	if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
 +	if (flags & DIRECTORY && devnull)
 +		errx(EX_USAGE, "Cannot install %s to a directory",
 +		    _PATH_DEVNULL);
 +
 +	if (flags & DIRECTORY) {
 +		if (realpath(to_name, pathbuf2) == NULL)
 +			errx(EX_OSERR, "%s", pathbuf2);
 +		(void)snprintf(pathbuf3, sizeof(pathbuf3),
 +		   "%s/%s", pathbuf2,
 +		    (p = strrchr(from_name, '/')) ? ++p : from_name);
 +		to_name = pathbuf3; /* target path is in pathbuf3 */
  		if (stat(from_name, &from_sb))
  			err(EX_OSERR, "%s", from_name);
 +		if (stat(to_name, &fileto_sb) && errno != ENOENT)
 +			err(EX_OSERR, "%s", to_name);
  		if (!S_ISREG(from_sb.st_mode)) {
  			errno = EFTYPE;
  			err(EX_OSERR, "%s", from_name);
  		}
 -		/* Build the target path. */
 -		if (flags & DIRECTORY) {
 -			(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
 -			    to_name,
 -			    (p = strrchr(from_name, '/')) ? ++p : from_name);
 -			to_name = pathbuf;
 -		}
 -		devnull = 0;
 -	} else {
 -		from_sb.st_flags = 0;	/* XXX */
 -		devnull = 1;
 +		if (fileto_sb.st_dev == from_sb.st_dev &&
 +		    fileto_sb.st_ino == from_sb.st_ino)
 +			errx(EX_USAGE,
 +			     "%s and %s are the same file", from_name, pathbuf3);
  	}
  
  	if (docompare) {
 #### diff ends here
 
 So long,
 
 Alex
 -- 
 I doubt, therefore I might be. 
 
State-Changed-From-To: open->closed 
State-Changed-By: mike 
State-Changed-When: Fri Jul 20 20:06:27 PDT 2001 
State-Changed-Why:  

This problem was apparently fixed in 4.0-RELEASE.  See sheldonh's 
comments for details. 

http://www.FreeBSD.org/cgi/query-pr.cgi?pr=14920 
>Unformatted:
