From eugen@ws092.svzserv.kemerovo.su  Sun Jan 22 13:43:52 2006
Return-Path: <eugen@ws092.svzserv.kemerovo.su>
Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125])
	by hub.freebsd.org (Postfix) with ESMTP id D7D0516A420
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 22 Jan 2006 13:43:52 +0000 (GMT)
	(envelope-from eugen@ws092.svzserv.kemerovo.su)
Received: from ws092.svzserv.kemerovo.su (kost.svzserv.kemerovo.su [213.184.65.82])
	by mx1.FreeBSD.org (Postfix) with ESMTP id EC00F43D45
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 22 Jan 2006 13:43:51 +0000 (GMT)
	(envelope-from eugen@ws092.svzserv.kemerovo.su)
Received: from ws092.svzserv.kemerovo.su (localhost [127.0.0.1])
	by ws092.svzserv.kemerovo.su (8.13.4/8.13.4) with ESMTP id k0MDhU4F048464
	for <FreeBSD-gnats-submit@freebsd.org>; Sun, 22 Jan 2006 20:43:30 +0700 (KRAT)
	(envelope-from eugen@ws092.svzserv.kemerovo.su)
Received: (from root@localhost)
	by ws092.svzserv.kemerovo.su (8.13.4/8.13.3/Submit) id k0MDh9xq048442;
	Sun, 22 Jan 2006 20:43:09 +0700 (KRAT)
	(envelope-from eugen)
Message-Id: <200601221343.k0MDh9xq048442@ws092.svzserv.kemerovo.su>
Date: Sun, 22 Jan 2006 20:43:09 +0700 (KRAT)
From: Eugene Grosbein <eugen@grosbein.pp.ru>
Reply-To: Eugene Grosbein <eugen@ws092.svzserv.kemerovo.su>
To: FreeBSD-gnats-submit@freebsd.org
Cc:
Subject: [patch] ln -f -s does not remove existing directory
X-Send-Pr-Version: 3.113
X-GNATS-Notify:

>Number:         92149
>Category:       bin
>Synopsis:       [patch] ln(1): ln -f -s does not remove existing directory
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    glebius
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Sun Jan 22 13:50:02 GMT 2006
>Closed-Date:    Sat Mar 18 21:50:00 GMT 2006
>Last-Modified:  Sat Mar 18 21:50:00 GMT 2006
>Originator:     Eugene Grosbein
>Release:        FreeBSD 6.0-STABLE i386
>Organization:
Svyaz-Service JSC
>Environment:
System: FreeBSD ws092.svzserv.kemerovo.su 6.0-STABLE FreeBSD 6.0-STABLE #6: Tue Dec 27 22:06:20 KRAT 2005 eugen@ws092.svzserv.kemerovo.su:/usr/obj/usr/src/sys/TEST i386

>Description:

	"ln -f -s" may be used to create a symlink to the file and
	the target file will be unlinked if it exists.

	However, ln will fail with 'Operation not permitted' message
	when target is a directory because unlink(2) does not remove
	empty directories.

>How-To-Repeat:

	mkdir -p dir/etc
	ln -f -s /etc dir

>Fix:

	The following patch allows ln(1) to remove existing target
	if the -s option is supplied and the target is the (empty) directory.

	The patch is for src/bin/ln:

--- ln.c.orig	Sun Jan 22 20:05:44 2006
+++ ln.c	Sun Jan 22 20:28:10 2006
@@ -198,8 +198,16 @@
 	/*
 	 * If the file exists, then unlink it forcibly if -f was specified
 	 * and interactively if -i was specified.
+	 *
+	 * For the directory, remove it when dealing with symbolic link only.
 	 */
 	if (fflag && exists) {
+		if (sflag && rmdir(source)) {
+			if (errno != ENOTDIR) {
+			    warn("%s", source);
+			    return (1);
+			}
+		}
 		if (unlink(source)) {
 			warn("%s", source);
 			return (1);
@@ -216,6 +224,12 @@
 			return (1);
 		}
 
+		if (sflag && rmdir(source)) {
+			if (errno != ENOTDIR) {
+			    warn("%s", source);
+			    return (1);
+			}
+		}
 		if (unlink(source)) {
 			warn("%s", source);
 			return (1);
--- ln.1.orig	Sun Jan 22 20:18:12 2006
+++ ln.1	Sun Jan 22 20:36:06 2006
@@ -69,8 +69,12 @@
 The options are as follows:
 .Bl -tag -width flag
 .It Fl f
-If the target file already exists,
-then unlink it so that the link may occur.
+If the target file or directory already exists,
+then remove it so that the link may occur.
+Note that no attempt to remove the directory is performed
+when running without the
+.Fl s
+option.
 (The
 .Fl f
 option overrides any previous
>Release-Note:
>Audit-Trail:

From: Eugene Grosbein <eugen@kuzbass.ru>
To: bug-followup@freebsd.org
Cc:  
Subject: Re: bin/92149
Date: Mon, 23 Jan 2006 13:08:07 +0700

 Hi!
 
 Sorry, I've sent wrong (buggy) version of patch, here is right one.
 
 --- ln.c.orig	Sun Jan 22 20:05:44 2006
 +++ ln.c	Mon Jan 23 12:47:00 2006
 @@ -198,9 +198,17 @@
  	/*
  	 * If the file exists, then unlink it forcibly if -f was specified
  	 * and interactively if -i was specified.
 +	 *
 +	 * For the directory, remove it when dealing with symbolic link only.
  	 */
  	if (fflag && exists) {
 -		if (unlink(source)) {
 +		if (S_ISDIR(sb.st_mode)) {
 +			if (sflag && rmdir(source)) {
 +				warn("%s", source);
 +				return (1);
 +			}
 +		}
 +		else if (unlink(source)) {
  			warn("%s", source);
  			return (1);
  		}
 @@ -216,7 +224,13 @@
  			return (1);
  		}
  
 -		if (unlink(source)) {
 +		if (S_ISDIR(sb.st_mode)) {
 +			if (sflag && rmdir(source)) {
 +				warn("%s", source);
 +				return (1);
 +			}
 +		}
 +		else if (unlink(source)) {
  			warn("%s", source);
  			return (1);
  		}
 --- ln.1.orig	Sun Jan 22 20:18:12 2006
 +++ ln.1	Mon Jan 23 12:44:28 2006
 @@ -69,8 +69,12 @@
  The options are as follows:
  .Bl -tag -width flag
  .It Fl f
 -If the target file already exists,
 -then unlink it so that the link may occur.
 +If the target file or (empty) directory already exists,
 +then remove it so that the link may occur.
 +Note that no attempt to remove the directory is performed
 +when running without the
 +.Fl s
 +option.
  (The
  .Fl f
  option overrides any previous

From: Gleb Smirnoff <glebius@FreeBSD.org>
To: Eugene Grosbein <eugen@grosbein.pp.ru>
Cc: FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/92149: [patch] ln -f -s does not remove existing directory
Date: Mon, 23 Jan 2006 11:53:36 +0300

 E> >Description:
 E> 
 E> 	"ln -f -s" may be used to create a symlink to the file and
 E> 	the target file will be unlinked if it exists.
 E> 
 E> 	However, ln will fail with 'Operation not permitted' message
 E> 	when target is a directory because unlink(2) does not remove
 E> 	empty directories.
 
 I think that the current behavior is standard, while suggested behavior
 is going to violate SUSv3. At least I understand the standard this way:
 
   If the destination path exists:
 
    1.	If the -f option is not specified, ln shall write a diagnostic message
 	to standard error, do nothing more with the current source_file, and go
 	on to any remaining source_files.
    2.	Actions shall be performed equivalent to the unlink() function defined in
 	the System Interfaces volume of IEEE Std 1003.1-2001, called using
 	destination as the path argument. If this fails for any reason, ln shall
 	write a diagnostic message to standard error, do nothing more with the
 	current source_file, and go on to any remaining source_files.
 
  http://www.opengroup.org/onlinepubs/000095399/utilities/ln.html
 
 -- 
 Totus tuus, Glebius.
 GLEBIUS-RIPN GLEB-RIPE

From: Eugene Grosbein <eugen@grosbein.pp.ru>
To: Gleb Smirnoff <glebius@FreeBSD.org>
Cc: FreeBSD-gnats-submit@FreeBSD.org
Subject: Re: bin/92149: [patch] ln -f -s does not remove existing directory
Date: Mon, 23 Jan 2006 18:32:45 +0700

 On Mon, Jan 23, 2006 at 11:53:36AM +0300, Gleb Smirnoff wrote:
 
 > E> 	"ln -f -s" may be used to create a symlink to the file and
 > E> 	the target file will be unlinked if it exists.
 > E> 
 > E> 	However, ln will fail with 'Operation not permitted' message
 > E> 	when target is a directory because unlink(2) does not remove
 > E> 	empty directories.
 > 
 > I think that the current behavior is standard, while suggested behavior
 > is going to violate SUSv3. At least I understand the standard this way:
 > 
 >   If the destination path exists:
 > 
 >    1.	If the -f option is not specified, ln shall write a diagnostic message
 > 	to standard error, do nothing more with the current source_file, and go
 > 	on to any remaining source_files.
 >    2.	Actions shall be performed equivalent to the unlink() function defined in
 > 	the System Interfaces volume of IEEE Std 1003.1-2001, called using
 > 	destination as the path argument. If this fails for any reason, ln shall
 > 	write a diagnostic message to standard error, do nothing more with the
 > 	current source_file, and go on to any remaining source_files.
 > 
 >  http://www.opengroup.org/onlinepubs/000095399/utilities/ln.html
 
 Then I'd like to introduce new command line option enabling desired
 behavour. Should I correct the patch?
 
 Eugene Grosbein

From: Gleb Smirnoff <glebius@FreeBSD.org>
To: Eugene Grosbein <eugen@grosbein.pp.ru>
Cc: freebsd-gnats-submit@FreeBSD.org
Subject: Re: bin/92149: [patch] ln -f -s does not remove existing directory
Date: Wed, 25 Jan 2006 15:06:49 +0300

 On Mon, Jan 23, 2006 at 11:40:06AM +0000, Eugene Grosbein wrote:
 E>  On Mon, Jan 23, 2006 at 11:53:36AM +0300, Gleb Smirnoff wrote:
 E>  
 E>  > E> 	"ln -f -s" may be used to create a symlink to the file and
 E>  > E> 	the target file will be unlinked if it exists.
 E>  > E> 
 E>  > E> 	However, ln will fail with 'Operation not permitted' message
 E>  > E> 	when target is a directory because unlink(2) does not remove
 E>  > E> 	empty directories.
 E>  > 
 E>  > I think that the current behavior is standard, while suggested behavior
 E>  > is going to violate SUSv3. At least I understand the standard this way:
 E>  > 
 E>  >   If the destination path exists:
 E>  > 
 E>  >    1.	If the -f option is not specified, ln shall write a diagnostic message
 E>  > 	to standard error, do nothing more with the current source_file, and go
 E>  > 	on to any remaining source_files.
 E>  >    2.	Actions shall be performed equivalent to the unlink() function defined in
 E>  > 	the System Interfaces volume of IEEE Std 1003.1-2001, called using
 E>  > 	destination as the path argument. If this fails for any reason, ln shall
 E>  > 	write a diagnostic message to standard error, do nothing more with the
 E>  > 	current source_file, and go on to any remaining source_files.
 E>  > 
 E>  >  http://www.opengroup.org/onlinepubs/000095399/utilities/ln.html
 E>  
 E>  Then I'd like to introduce new command line option enabling desired
 E>  behavour. Should I correct the patch?
 
 Yes, I think this will be acceptable.
 
 -- 
 Totus tuus, Glebius.
 GLEBIUS-RIPN GLEB-RIPE
State-Changed-From-To: open->feedback 
State-Changed-By: glebius 
State-Changed-When: Tue Jan 31 08:34:40 UTC 2006 
State-Changed-Why:  
Awaiting updated patch. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=92149 

From: Eugene Grosbein <eugen@grosbein.pp.ru>
To: bug-followup@freebsd.org
Cc: Gleb Smirnoff <glebius@freebsd.org>
Subject: Re: bin/92149 : [patch] ln(1): ln -f -s does not remove existing directory
Date: Tue, 14 Feb 2006 01:23:32 +0700

 E>>  Then I'd like to introduce new command line option enabling desired
 E>>  behavour. Should I correct the patch?
  
 > Yes, I think this will be acceptable.
 
 Here it comes. It introduces new option -F that implies -f
 and removes empty target directory when dealing with symlinks.
 
 Index: ln.1
 ===================================================================
 RCS file: /home/ncvs/src/bin/ln/ln.1,v
 retrieving revision 1.30
 diff -u -r1.30 ln.1
 --- ln.1	16 Jan 2005 16:41:57 -0000	1.30
 +++ ln.1	13 Feb 2006 18:15:21 -0000
 @@ -41,11 +41,11 @@
  .Nd make links
  .Sh SYNOPSIS
  .Nm
 -.Op Fl fhinsv
 +.Op Fl fFhinsv
  .Ar source_file
  .Op Ar target_file
  .Nm
 -.Op Fl fhinsv
 +.Op Fl fFhinsv
  .Ar source_file ...
  .Ar target_dir
  .Nm link
 @@ -76,6 +76,16 @@
  option overrides any previous
  .Fl i
  options.)
 +.It Fl F
 +This implies
 +.Fl f
 +option and will remove the target even if it is empty directory and
 +.Fl s
 +option is supplied also. No attempt to remove the target directory
 +is performed when
 +.Fl s
 +option is omitted. This is most useful with two non-option arguments
 +to create a symbolic link to the source directory.
  .It Fl h
  If the
  .Ar target_file
 @@ -99,6 +109,8 @@
  .Fl i
  option overrides any previous
  .Fl f
 +and
 +.Fl F
  options.)
  .It Fl n
  Same as
 @@ -179,6 +191,10 @@
  They are provided solely for compatibility with other
  .Nm
  implementations.
 +.Pp
 +The
 +.Fl F
 +option is FreeBSD extention and should not be used in portable scripts.
  .Sh SEE ALSO
  .Xr link 2 ,
  .Xr lstat 2 ,
 Index: ln.c
 ===================================================================
 RCS file: /home/ncvs/src/bin/ln/ln.c,v
 retrieving revision 1.33
 diff -u -r1.33 ln.c
 --- ln.c	9 Feb 2005 17:37:37 -0000	1.33
 +++ ln.c	13 Feb 2006 18:17:56 -0000
 @@ -53,6 +53,7 @@
  #include <unistd.h>
  
  int	fflag;				/* Unlink existing files. */
 +int	Fflag;				/* Remove existing empty directories also. */
  int	hflag;				/* Check new name for symlink first. */
  int	iflag;				/* Interactive mode. */
  int	sflag;				/* Symbolic, not hard, link. */
 @@ -91,8 +92,11 @@
  		exit(linkit(argv[0], argv[1], 0));
  	}
  
 -	while ((ch = getopt(argc, argv, "fhinsv")) != -1)
 +	while ((ch = getopt(argc, argv, "fFhinsv")) != -1)
  		switch (ch) {
 +		case 'F':
 +			Fflag = 1;
 +			/* FALLTHROUGH */
  		case 'f':
  			fflag = 1;
  			iflag = 0;
 @@ -104,6 +108,7 @@
  		case 'i':
  			iflag = 1;
  			fflag = 0;
 +			Fflag = 0;
  			break;
  		case 's':
  			sflag = 1;
 @@ -121,6 +126,8 @@
  
  	linkf = sflag ? symlink : link;
  	linkch = sflag ? '-' : '=';
 +	if (!sflag)
 +		Fflag = 0;
  
  	switch(argc) {
  	case 0:
 @@ -198,9 +205,17 @@
  	/*
  	 * If the file exists, then unlink it forcibly if -f was specified
  	 * and interactively if -i was specified.
 +	 *
 +	 * For the directory, remove it only when -F and -s were specified.
  	 */
  	if (fflag && exists) {
 -		if (unlink(source)) {
 +		if (Fflag && S_ISDIR(sb.st_mode)) {
 +			if (rmdir(source)) {
 +				warn("%s", source);
 +				return (1);
 +			}
 +		}
 +		else if (unlink(source)) {
  			warn("%s", source);
  			return (1);
  		}
 @@ -236,8 +251,8 @@
  usage(void)
  {
  	(void)fprintf(stderr, "%s\n%s\n%s\n",
 -	    "usage: ln [-fhinsv] source_file [target_file]",
 -	    "       ln [-fhinsv] source_file ... target_dir",
 +	    "usage: ln [-fFhinsv] source_file [target_file]",
 +	    "       ln [-fFhinsv] source_file ... target_dir",
  	    "       link source_file target_file");
  	exit(1);
  }
State-Changed-From-To: feedback->patched 
State-Changed-By: glebius 
State-Changed-When: Tue Feb 14 11:08:19 UTC 2006 
State-Changed-Why:  
Committed to HEAD, thanks! 


Responsible-Changed-From-To: freebsd-bugs->glebius 
Responsible-Changed-By: glebius 
Responsible-Changed-When: Tue Feb 14 11:08:19 UTC 2006 
Responsible-Changed-Why:  
I'm handling this one. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=92149 
State-Changed-From-To: patched->closed 
State-Changed-By: glebius 
State-Changed-When: Sat Mar 18 21:49:48 UTC 2006 
State-Changed-Why:  
Merged to RELENG_6. 

http://www.freebsd.org/cgi/query-pr.cgi?pr=92149 
>Unformatted:
