ln(1): Sync up with FreeBSD.
authorSascha Wildner <saw@online.de>
Mon, 23 Mar 2015 20:09:09 +0000 (21:09 +0100)
committerSascha Wildner <saw@online.de>
Mon, 23 Mar 2015 20:09:24 +0000 (21:09 +0100)
* Adds POSIX.1-2008's -L (hardlinks to symbolic links link to the
  symlink target, which stays our default) and -P (hardlinks to
  symbolic links link to the symlink itself) options.

* Adds -F (if the target is a directory, remove it to make the link
  happen) and -w (warns if the source of a symlink does not exist)
  options.

* Some bug fixes I guess. :)

* It also improves the checks for identical files. Our former checks
  were producing kind of confusing results in conjunction with
  'install -C' (i.e., for libraries and includes, if LINKS is used
  from the Makefile) upon repeated installation.
Reported-by: marino
bin/ln/Makefile
bin/ln/ln.1
bin/ln/ln.c
bin/ln/symlink.7

index 5e78367..65bfb95 100644 (file)
@@ -1,6 +1,5 @@
 #      @(#)Makefile    8.2 (Berkeley) 5/31/93
-# $FreeBSD: src/bin/ln/Makefile,v 1.7.2.2 2001/08/01 04:38:44 obrien Exp $
-# $DragonFly: src/bin/ln/Makefile,v 1.5 2005/02/06 06:16:40 okumoto Exp $
+# $FreeBSD: head/bin/ln/Makefile 87323 2001-12-04 01:57:47Z obrien $
 
 PROG=  ln
 MAN=   ln.1 symlink.7
index f7190ac..df22dbf 100644 (file)
@@ -1,3 +1,4 @@
+.\"-
 .\" Copyright (c) 1980, 1990, 1993
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)ln.1        8.2 (Berkeley) 12/30/93
-.\" $FreeBSD: src/bin/ln/ln.1,v 1.11.2.8 2003/02/04 07:31:59 keramida Exp $
-.\" $DragonFly: src/bin/ln/ln.1,v 1.8 2008/09/07 07:54:48 swildner Exp $
+.\" $FreeBSD: head/bin/ln/ln.1 244791 2012-12-28 22:06:33Z gjb $
 .\"
-.Dd September 27, 2009
+.Dd March 23, 2015
 .Dt LN 1
 .Os
 .Sh NAME
 .Nm ln ,
 .Nm link
-.Nd make links
+.Nd link files
 .Sh SYNOPSIS
 .Nm
-.Op Fl fhinsv
+.Op Fl L | Fl P | Fl s Op Fl F
+.Op Fl f | iw
+.Op Fl hnv
 .Ar source_file
 .Op Ar target_file
 .Nm
-.Op Fl fhinsv
+.Op Fl L | Fl P | Fl s Op Fl F
+.Op Fl f | iw
+.Op Fl hnv
 .Ar source_file ...
 .Ar target_dir
 .Nm link
 .Sh DESCRIPTION
 The
 .Nm
-utility creates a new directory entry (linked file) which has the
-same modes as the original file.
+utility creates a new directory entry (linked file) for the file name
+specified by
+.Ar target_file .
+The
+.Ar target_file
+will be created with the same file modes as the
+.Ar source_file .
 It is useful for maintaining multiple copies of a file in many places
 at once without using up storage for the
 .Dq copies ;
@@ -68,6 +77,37 @@ to a file is one of the differences between a hard and symbolic link.
 .Pp
 The options are as follows:
 .Bl -tag -width flag
+.It Fl F
+If the target file already exists and is a directory, then remove it
+so that the link may occur.
+The
+.Fl F
+option should be used with either
+.Fl f
+or
+.Fl i
+options.
+If none is specified,
+.Fl f
+is implied.
+The
+.Fl F
+option is a no-op unless
+.Fl s
+option is specified.
+.It Fl L
+When creating a hard link to a symbolic link,
+create a hard link to the target of the symbolic link.
+This is the default.
+This option cancels the
+.Fl P
+option.
+.It Fl P
+When creating a hard link to a symbolic link,
+create a hard link to the symbolic link itself.
+This option cancels the
+.Fl L
+option.
 .It Fl f
 If the target file already exists,
 then unlink it so that the link may occur.
@@ -75,6 +115,8 @@ then unlink it so that the link may occur.
 .Fl f
 option overrides any previous
 .Fl i
+and
+.Fl w
 options.)
 .It Fl h
 If the
@@ -112,6 +154,8 @@ Create a symbolic link.
 Cause
 .Nm
 to be verbose, showing files as they are processed.
+.It Fl w
+Warn if the source of a symbolic link does not currently exist.
 .El
 .Pp
 By default,
@@ -122,26 +166,27 @@ links.
 A hard link to a file is indistinguishable from the original directory entry;
 any changes to a file are effectively independent of the name used to reference
 the file.
-Hard links may not normally refer to directories and may not span file systems.
+Directories may not be hardlinked, and hard links may not span file systems.
 .Pp
-A symbolic link contains the name of the file to which it is linked.
+A symbolic link contains the name of the file to
+which it is linked.
 The referenced file is used when an
-.Xr open  2
+.Xr open 2
 operation is performed on the link.
 A
-.Xr stat  2
+.Xr stat 2
 on a symbolic link will return the linked-to file; an
-.Xr lstat  2
+.Xr lstat 2
 must be done to obtain information about the link.
 The
-.Xr readlink  2
+.Xr readlink 2
 call may be used to read the contents of a symbolic link.
 Symbolic links may span file systems and may refer to directories.
 .Pp
 Given one or two arguments,
 .Nm
 creates a link to an existing file
-.Ar source_file  .
+.Ar source_file .
 If
 .Ar target_file
 is given, the link has that name;
@@ -150,7 +195,7 @@ may also be a directory in which to place the link;
 otherwise it is placed in the current directory.
 If only the directory is specified, the link will be made
 to the last component of
-.Ar source_file  .
+.Ar source_file .
 .Pp
 Given more than two arguments,
 .Nm
@@ -195,6 +240,71 @@ Regular
 .Xr environ 7
 environment variables are not used to resolve variant symlinks.
 .Sh EXAMPLES
+Create a symbolic link named
+.Pa /home/src
+and point it to
+.Pa /usr/src :
+.Pp
+.Dl # ln -s /usr/src /home/src
+.Pp
+Hard link
+.Pa /usr/local/bin/fooprog
+to file
+.Pa /usr/local/bin/fooprog-1.0 :
+.Pp
+.Dl # ln /usr/local/bin/fooprog-1.0 /usr/local/bin/fooprog
+.Pp
+As an exercise, try the following commands:
+.Bd -literal -offset indent
+# ls -i /bin/[
+11553 /bin/[
+# ls -i /bin/test
+11553 /bin/test
+.Ed
+.Pp
+Note that both files have the same inode; that is,
+.Pa /bin/[
+is essentially an alias for the
+.Xr test 1
+command.
+This hard link exists so
+.Xr test 1
+may be invoked from shell scripts, for example, using the
+.Li "if [ ]"
+construct.
+.Pp
+In the next example, the second call to
+.Nm
+removes the original
+.Pa foo
+and creates a replacement pointing to
+.Pa baz :
+.Bd -literal -offset indent
+# mkdir bar baz
+# ln -s bar foo
+# ln -shf baz foo
+.Ed
+.Pp
+Without the
+.Fl h
+option, this would instead leave
+.Pa foo
+pointing to
+.Pa bar
+and inside
+.Pa foo
+create a new symlink
+.Pa baz
+pointing to itself.
+This results from directory-walking.
+.Pp
+An easy rule to remember is that the argument order for
+.Nm
+is the same as for
+.Xr cp 1 :
+The first argument needs to exist, the second one is created.
+.Pp
+A simple variable symlink example:
 .Bd -literal -offset indent
 sysctl vfs.varsym_enable=1
 
@@ -210,9 +320,10 @@ varsym fubar=yy; cat test
 The
 .Fl h ,
 .Fl i ,
-.Fl n
-and
+.Fl n ,
 .Fl v
+and
+.Fl w
 options are non-standard and their use in scripts is not recommended.
 They are provided solely for compatibility with other
 .Nm
@@ -220,6 +331,12 @@ implementations.
 .Pp
 Variant symlinks are unique (among BSDs) to
 .Dx .
+.Pp
+The
+.Fl F
+option is a
+.Fx
+extension and should not be used in portable scripts.
 .Sh SEE ALSO
 .Xr varsym 1 ,
 .Xr link 2 ,
index d2d9a88..ffa2cff 100644 (file)
@@ -1,4 +1,4 @@
-/*
+/*-
  * Copyright (c) 1987, 1993, 1994
  *     The Regents of the University of California.  All rights reserved.
  *
@@ -28,8 +28,7 @@
  *
  * @(#) Copyright (c) 1987, 1993, 1994 The Regents of the University of California.  All rights reserved.
  * @(#)ln.c    8.2 (Berkeley) 3/31/94
- * $FreeBSD: src/bin/ln/ln.c,v 1.15.2.4 2002/07/12 07:34:38 tjr Exp $
- * $DragonFly: src/bin/ln/ln.c,v 1.11 2006/09/25 09:27:21 corecode Exp $
+ * $FreeBSD: head/bin/ln/ln.c 251261 2013-06-02 17:55:00Z eadler $
  */
 
 #include <sys/param.h>
 
 #include <err.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
-static int     fflag;                          /* Unlink existing files. */
-static int     hflag;                          /* Check new name for symlink first. */
-static int     iflag;                          /* Interactive mode. */
-static int     sflag;                          /* Symbolic, not hard, link. */
-static int     vflag;                          /* Verbose output. */
-                                       /* System link call. */
-static int     (*linkf)(const char *, const char *);
+static int     fflag;                  /* Unlink existing files. */
+static int     Fflag;                  /* Remove empty directories also. */
+static int     hflag;                  /* Check new name for symlink first. */
+static int     iflag;                  /* Interactive mode. */
+static int     Pflag;                  /* Create hard links to symlinks. */
+static int     sflag;                  /* Symbolic, not hard, link. */
+static int     vflag;                  /* Verbose output. */
+static int     wflag;                  /* Warn if symlink target does not
+                                        * exist, and -f is not enabled. */
 static char    linkch;
 
 static int     linkit(const char *, const char *, int);
@@ -58,7 +61,7 @@ int
 main(int argc, char *argv[])
 {
        struct stat sb;
-       char *p, *sourcedir;
+       char *p, *targetdir;
        int ch, exitval;
 
        /*
@@ -77,15 +80,24 @@ main(int argc, char *argv[])
                argv += optind;
                if (argc != 2)
                        usage();
-               linkf = link;
                exit(linkit(argv[0], argv[1], 0));
        }
 
-       while ((ch = getopt(argc, argv, "fhinsv")) != -1) {
+       while ((ch = getopt(argc, argv, "FLPfhinsvw")) != -1)
                switch (ch) {
+               case 'F':
+                       Fflag = 1;
+                       break;
+               case 'L':
+                       Pflag = 0;
+                       break;
+               case 'P':
+                       Pflag = 1;
+                       break;
                case 'f':
                        fflag = 1;
                        iflag = 0;
+                       wflag = 0;
                        break;
                case 'h':
                case 'n':
@@ -101,131 +113,197 @@ main(int argc, char *argv[])
                case 'v':
                        vflag = 1;
                        break;
+               case 'w':
+                       wflag = 1;
+                       break;
                case '?':
                default:
                        usage();
                }
-       }
 
        argv += optind;
        argc -= optind;
 
-       linkf = sflag ? symlink : link;
        linkch = sflag ? '-' : '=';
+       if (sflag == 0)
+               Fflag = 0;
+       if (Fflag == 1 && iflag == 0) {
+               fflag = 1;
+               wflag = 0;              /* Implied when fflag != 0 */
+       }
 
-       switch (argc) {
+       switch(argc) {
        case 0:
                usage();
                /* NOTREACHED */
-       case 1:                         /* ln target */
+       case 1:                         /* ln source */
                exit(linkit(argv[0], ".", 1));
-       case 2:                         /* ln target source */
+       case 2:                         /* ln source target */
                exit(linkit(argv[0], argv[1], 0));
        default:
                ;
        }
-                                       /* ln target1 target2 directory */
-       sourcedir = argv[argc - 1];
-       if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
+                                       /* ln source1 source2 directory */
+       targetdir = argv[argc - 1];
+       if (hflag && lstat(targetdir, &sb) == 0 && S_ISLNK(sb.st_mode)) {
                /*
                 * We were asked not to follow symlinks, but found one at
                 * the target--simulate "not a directory" error
                 */
-               errc(1, ENOTDIR, "%s", sourcedir);
+               errno = ENOTDIR;
+               err(1, "%s", targetdir);
        }
-       if (stat(sourcedir, &sb) != 0)
-               err(1, "%s", sourcedir);
+       if (stat(targetdir, &sb))
+               err(1, "%s", targetdir);
        if (!S_ISDIR(sb.st_mode))
                usage();
-       for (exitval = 0; *argv != sourcedir; ++argv)
-               exitval |= linkit(*argv, sourcedir, 1);
+       for (exitval = 0; *argv != targetdir; ++argv)
+               exitval |= linkit(*argv, targetdir, 1);
        exit(exitval);
 }
 
 /*
- * Nomenclature warning!
- *
- * In this source "target" and "source" are used the opposite way they
- * are used in the ln(1) manual.  Here "target" is the existing file and
- * "source" specifies the to-be-created link to "target".
+ * Two pathnames refer to the same directory entry if the directories match
+ * and the final components' names match.
  */
 static int
-linkit(const char *target, const char *source, int isdir)
+samedirent(const char *path1, const char *path2)
+{
+       const char *file1, *file2;
+       char pathbuf[PATH_MAX];
+       struct stat sb1, sb2;
+
+       if (strcmp(path1, path2) == 0)
+               return 1;
+       file1 = strrchr(path1, '/');
+       if (file1 != NULL)
+               file1++;
+       else
+               file1 = path1;
+       file2 = strrchr(path2, '/');
+       if (file2 != NULL)
+               file2++;
+       else
+               file2 = path2;
+       if (strcmp(file1, file2) != 0)
+               return 0;
+       if (file1 - path1 >= PATH_MAX || file2 - path2 >= PATH_MAX)
+               return 0;
+       if (file1 == path1)
+               memcpy(pathbuf, ".", 2);
+       else {
+               memcpy(pathbuf, path1, file1 - path1);
+               pathbuf[file1 - path1] = '\0';
+       }
+       if (stat(pathbuf, &sb1) != 0)
+               return 0;
+       if (file2 == path2)
+               memcpy(pathbuf, ".", 2);
+       else {
+               memcpy(pathbuf, path2, file2 - path2);
+               pathbuf[file2 - path2] = '\0';
+       }
+       if (stat(pathbuf, &sb2) != 0)
+               return 0;
+       return sb1.st_dev == sb2.st_dev && sb1.st_ino == sb2.st_ino;
+}
+
+static int
+linkit(const char *source, const char *target, int isdir)
 {
        struct stat sb;
        const char *p;
        int ch, exists, first;
        char path[PATH_MAX];
+       char wbuf[PATH_MAX];
+       char bbuf[PATH_MAX];
 
        if (!sflag) {
-               /* If target doesn't exist, quit now. */
-               if (stat(target, &sb) != 0) {
-                       warn("%s", target);
+               /* If source doesn't exist, quit now. */
+               if ((Pflag ? lstat : stat)(source, &sb)) {
+                       warn("%s", source);
                        return (1);
                }
                /* Only symbolic links to directories. */
                if (S_ISDIR(sb.st_mode)) {
                        errno = EISDIR;
-                       warn("%s", target);
+                       warn("%s", source);
                        return (1);
                }
        }
 
        /*
-        * If the source is a directory (and not a symlink if hflag),
-        * append the target's name.
+        * If the target is a directory (and not a symlink if hflag),
+        * append the source's name.
         */
        if (isdir ||
-           (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
-           (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) {
-               if ((p = strrchr(target, '/')) == NULL)
-                       p = target;
-               else
-                       ++p;
-               if (snprintf(path, sizeof(path), "%s/%s", source, p) >=
-                   (int)sizeof(path)) {
+           (lstat(target, &sb) == 0 && S_ISDIR(sb.st_mode)) ||
+           (!hflag && stat(target, &sb) == 0 && S_ISDIR(sb.st_mode))) {
+               if (strlcpy(bbuf, source, sizeof(bbuf)) >= sizeof(bbuf) ||
+                   (p = basename(bbuf)) == NULL ||
+                   snprintf(path, sizeof(path), "%s/%s", target, p) >=
+                   (ssize_t)sizeof(path)) {
                        errno = ENAMETOOLONG;
-                       warn("%s", target);
+                       warn("%s", source);
                        return (1);
                }
-               source = path;
+               target = path;
        }
 
-       exists = (lstat(source, &sb) == 0);
        /*
-        * If doing hard links and the source (destination) exists and it
-        * actually is the same file like the target (existing file), we
-        * complain that the files are identical.  If -f is specified, we
-        * accept the job as already done and return with success.
+        * If the link source doesn't exist, and a symbolic link was
+        * requested, and -w was specified, give a warning.
         */
-       if (exists && !sflag) {
-               struct stat tsb;
-
-               if (stat(target, &tsb) != 0) {
-                       warn("%s: disappeared", target);
-                       return (1);
+       if (sflag && wflag) {
+               if (*source == '/') {
+                       /* Absolute link source. */
+                       if (stat(source, &sb) != 0)
+                                warn("warning: %s inaccessible", source);
+               } else {
+                       /*
+                        * Relative symlink source.  Try to construct the
+                        * absolute path of the source, by appending `source'
+                        * to the parent directory of the target.
+                        */
+                       strlcpy(bbuf, target, sizeof(bbuf));
+                       p = dirname(bbuf);
+                       if (p != NULL) {
+                               snprintf(wbuf, sizeof(wbuf), "%s/%s",
+                                   p, source);
+                               if (stat(wbuf, &sb) != 0)
+                                       warn("warning: %s", source);
+                       }
                }
+       }
 
-               if (tsb.st_dev == sb.st_dev && tsb.st_ino == sb.st_ino) {
-                       warnx("%s and %s are identical (not linked).", target, source);
-                       if (fflag)
-                               return (0);
-                       else
-                               return (1);
+       /*
+        * If the file exists, first check it is not the same directory entry.
+        */
+       exists = !lstat(target, &sb);
+       if (exists) {
+               if (!sflag && samedirent(source, target)) {
+                       warnx("%s and %s are the same directory entry",
+                           source, target);
+                       return (1);
                }
        }
        /*
-        * If the file exists, then unlink it forcibly if -f was specified
+        * Then unlink it forcibly if -f was specified
         * and interactively if -i was specified.
         */
        if (fflag && exists) {
-               if (unlink(source) != 0) {
-                       warn("%s", source);
+               if (Fflag && S_ISDIR(sb.st_mode)) {
+                       if (rmdir(target)) {
+                               warn("%s", target);
+                               return (1);
+                       }
+               } else if (unlink(target)) {
+                       warn("%s", target);
                        return (1);
                }
        } else if (iflag && exists) {
                fflush(stdout);
-               fprintf(stderr, "replace %s? ", source);
+               fprintf(stderr, "replace %s? ", target);
 
                first = ch = getchar();
                while(ch != '\n' && ch != EOF)
@@ -235,19 +313,26 @@ linkit(const char *target, const char *source, int isdir)
                        return (1);
                }
 
-               if (unlink(source) != 0) {
-                       warn("%s", source);
+               if (Fflag && S_ISDIR(sb.st_mode)) {
+                       if (rmdir(target)) {
+                               warn("%s", target);
+                               return (1);
+                       }
+               } else if (unlink(target)) {
+                       warn("%s", target);
                        return (1);
                }
        }
 
        /* Attempt the link. */
-       if ((*linkf)(target, source) != 0) {
-               warn("%s", source);
+       if (sflag ? symlink(source, target) :
+           linkat(AT_FDCWD, source, AT_FDCWD, target,
+           Pflag ? 0 : AT_SYMLINK_FOLLOW)) {
+               warn("%s", target);
                return (1);
        }
        if (vflag)
-               printf("%s %c> %s\n", source, linkch, target);
+               printf("%s %c> %s\n", target, linkch, source);
        return (0);
 }
 
@@ -255,8 +340,8 @@ static void
 usage(void)
 {
        fprintf(stderr, "%s\n%s\n%s\n",
-           "usage: ln [-fhinsv] source_file [target_file]",
-           "       ln [-fhinsv] source_file ... target_dir",
+           "usage: ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file [target_file]",
+           "       ln [-s [-F] | -L | -P] [-f | -i] [-hnv] source_file ... target_dir",
            "       link source_file target_file");
        exit(1);
 }
index b9d269b..a0edf17 100644 (file)
@@ -1,3 +1,4 @@
+.\"-
 .\" Copyright (c) 1992, 1993, 1994
 .\"    The Regents of the University of California.  All rights reserved.
 .\"
@@ -26,9 +27,9 @@
 .\" SUCH DAMAGE.
 .\"
 .\"    @(#)symlink.7   8.3 (Berkeley) 3/31/94
-.\" $FreeBSD: src/bin/ln/symlink.7,v 1.13.2.7 2003/03/03 19:04:46 trhodes Exp $
+.\" $FreeBSD: head/bin/ln/symlink.7 278848 2015-02-16 12:56:55Z jilles $
 .\"
-.Dd July 31, 2010
+.Dd March 23, 2015
 .Dt SYMLINK 7
 .Os
 .Sh NAME
@@ -45,7 +46,7 @@ file.
 Hard links may not refer to directories and may not reference files
 on different file systems.
 A symbolic link contains the name of the file to which it is linked,
-i.e. it is a pointer to another name, and not to an underlying object.
+i.e., it is a pointer to another name, and not to an underlying object.
 For this reason, symbolic links may reference directories and may span
 file systems.
 .Pp
@@ -68,7 +69,7 @@ the link.
 Symbolic links may reference other symbolic links,
 in which case the links are dereferenced until an object that is
 not a symbolic link is found,
-a symbolic link which references a file which doesn't exist is found,
+a symbolic link which references a file which does not exist is found,
 or a loop is detected.
 (Loop detection is done by placing an upper limit on the number of
 links that may be followed, and an error results if this limit is
@@ -102,16 +103,23 @@ the system call
 would return a file descriptor to the file
 .Dq afile .
 .Pp
-There are six system calls that do not follow links, and which operate
+There are thirteen system calls that do not follow links, and which operate
 on the symbolic link itself.
 They are:
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
 .Xr lchown 2 ,
+.Xr lpathconf 2 ,
 .Xr lstat 2 ,
+.Xr lutimes 2 ,
 .Xr readlink 2 ,
+.Xr readlinkat 2 ,
 .Xr rename 2 ,
+.Xr renameat 2 ,
 .Xr rmdir 2 ,
+.Xr unlink 2 ,
 and
-.Xr unlink 2 .
+.Xr unlinkat 2 .
 Because
 .Xr remove 3
 is an alias for
@@ -119,15 +127,47 @@ is an alias for
 it also does not follow symbolic links.
 When
 .Xr rmdir 2
+or
+.Xr unlinkat 2
+with the
+.Dv AT_REMOVEDIR
+flag
 is applied to a symbolic link, it fails with the error
 .Er ENOTDIR .
 .Pp
+The
+.Xr linkat 2
+system call does not follow symbolic links
+unless given the
+.Dv AT_SYMLINK_FOLLOW
+flag.
+.Pp
+The following system calls follow symbolic links
+unless given the
+.Dv AT_SYMLINK_NOFOLLOW
+flag:
+.Xr chflagsat 2 ,
+.Xr fchmodat 2 ,
+.Xr fchownat 2 ,
+and
+.Xr fstatat 2 .
+.\"and
+.\".Xr utimensat 2 .
+.Pp
 The owner and group of an existing symbolic link can be changed by
 means of the
 .Xr lchown 2
 system call.
-The other file attributes, such as the modification time and access
-permissions, are not used by the system and cannot be changed.
+The flags, access permissions, owner/group and modification time of
+an existing symbolic link can be changed by means of the
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
+.Xr lchown 2 ,
+and
+.Xr lutimes 2
+system calls, respectively.
+Of these, only the flags and ownership are used by the system;
+the access permissions are ignored.
 .Pp
 The
 .Bx 4.4
@@ -157,7 +197,7 @@ would display the contents of the file
 .Dq Li afile .
 .Pp
 It is important to realize that this rule includes commands which may
-optionally traverse file trees, e.g. the command
+optionally traverse file trees, e.g.\& the command
 .Dq Li "chown file"
 is included in this rule, while the command
 .Dq Li "chown -R file"
@@ -182,7 +222,7 @@ would change the ownership of
 .Dq Li slink
 itself.
 .Pp
-There are four exceptions to this rule.
+There are five exceptions to this rule.
 The
 .Xr mv 1
 and
@@ -198,7 +238,7 @@ The
 command is also an exception to this rule.
 For compatibility with historic systems (when
 .Nm ls
-is not doing a tree walk, i.e. the
+is not doing a tree walk, i.e., the
 .Fl R
 option is not specified),
 the
@@ -213,7 +253,8 @@ or if the
 .Fl d
 or
 .Fl l
-options are not specified.  (The
+options are not specified.
+(The
 .Nm ls
 command is the only command where the
 .Fl H
@@ -224,13 +265,12 @@ a file tree.)
 .Pp
 The
 .Xr file 1
-command is also an exception to this rule.
-The
-.Xr file 1
-command does not follow symbolic links named as argument by default.
-The
-.Xr file 1
-command does follow symbolic links named as argument if
+and
+.Xr stat 1
+commands are also exceptions to this rule.
+These
+commands do not follow symbolic links named as argument by default,
+but do follow symbolic links named as argument if the
 .Fl L
 option is specified.
 .Pp
@@ -267,13 +307,6 @@ not of type directory.
 Operations that apply to symbolic links are performed on the links
 themselves, but otherwise the links are ignored.
 .Pp
-For example, the command
-.Dq Li "chown -R user slink directory"
-will ignore
-.Dq Li slink ,
-because symbolic links in this system do not have owners.
-Any symbolic links encountered during the tree traversal will also be
-ignored.
 The command
 .Dq Li "rm -r slink directory"
 will remove
@@ -281,9 +314,7 @@ will remove
 as well as any symbolic links encountered in the tree traversal of
 .Dq Li directory ,
 because symbolic links may be removed.
-In no case will either
-.Nm chown
-or
+In no case will
 .Nm rm
 affect the file which
 .Dq Li slink
@@ -410,14 +441,16 @@ options.
 To maintain compatibility with historic systems,
 the
 .Nm ls
-command acts a little differently.  If you do not specify the
+command acts a little differently.
+If you do not specify the
 .Fl F ,
 .Fl d
 or
 .Fl l
 options,
 .Nm ls
-will follow symbolic links specified on the command line.  If the
+will follow symbolic links specified on the command line.
+If the
 .Fl L
 flag is specified,
 .Nm ls
@@ -437,8 +470,11 @@ whether specified on the command line or encountered in the tree walk.
 .Xr pax 1 ,
 .Xr rm 1 ,
 .Xr tar 1 ,
+.Xr lchflags 2 ,
+.Xr lchmod 2 ,
 .Xr lchown 2 ,
 .Xr lstat 2 ,
+.Xr lutimes 2 ,
 .Xr readlink 2 ,
 .Xr rename 2 ,
 .Xr renameat 2 ,