"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. 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: How-To-Repeat: mkdir -p dir/etc ln -f -s /etc dir
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
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
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
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 Awaiting updated patch.
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 Committed to HEAD, thanks!
Responsible Changed From-To: freebsd-bugs->glebius I'm handling this one.
State Changed From-To: patched->closed Merged to RELENG_6.