From 8957cdb51ae75457ab52b39e0b6b8779092093fa Mon Sep 17 00:00:00 2001 From: Sascha Wildner Date: Mon, 23 Mar 2015 21:09:09 +0100 Subject: [PATCH] ln(1): Sync up with FreeBSD. * 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 | 3 +- bin/ln/ln.1 | 153 ++++++++++++++++++++++++++---- bin/ln/ln.c | 237 ++++++++++++++++++++++++++++++++--------------- bin/ln/symlink.7 | 98 +++++++++++++------- 4 files changed, 364 insertions(+), 127 deletions(-) diff --git a/bin/ln/Makefile b/bin/ln/Makefile index 5e783677b9..65bfb9508d 100644 --- a/bin/ln/Makefile +++ b/bin/ln/Makefile @@ -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 diff --git a/bin/ln/ln.1 b/bin/ln/ln.1 index f7190aca32..df22dbfae5 100644 --- a/bin/ln/ln.1 +++ b/bin/ln/ln.1 @@ -1,3 +1,4 @@ +.\"- .\" Copyright (c) 1980, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -29,23 +30,26 @@ .\" 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 @@ -53,8 +57,13 @@ .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 , diff --git a/bin/ln/ln.c b/bin/ln/ln.c index d2d9a88640..ffa2cff337 100644 --- a/bin/ln/ln.c +++ b/bin/ln/ln.c @@ -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 @@ -37,18 +36,22 @@ #include #include +#include +#include #include #include #include #include -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); } diff --git a/bin/ln/symlink.7 b/bin/ln/symlink.7 index b9d269b0f2..a0edf17f96 100644 --- a/bin/ln/symlink.7 +++ b/bin/ln/symlink.7 @@ -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 , -- 2.41.0