diff --git a/.gitignore b/.gitignore index 909a9f33..89ae9129 100644 --- a/.gitignore +++ b/.gitignore @@ -16,7 +16,6 @@ .*.sw? # Downloaded files: -/file_cmds/ /libarchive/ /libinfo/ /libutil/ diff --git a/file_cmds/.DS_Store b/file_cmds/.DS_Store new file mode 100644 index 00000000..56304f91 Binary files /dev/null and b/file_cmds/.DS_Store differ diff --git a/file_cmds/chflags/chflags.1 b/file_cmds/chflags/chflags.1 new file mode 100644 index 00000000..5eb3dfc8 --- /dev/null +++ b/file_cmds/chflags/chflags.1 @@ -0,0 +1,193 @@ +.\"- +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 +.\" $FreeBSD: src/bin/chflags/chflags.1,v 1.30.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $ +.\" +.Dd March 3, 2006 +.Dt CHFLAGS 1 +.Os +.Sh NAME +.Nm chflags +.Nd change file flags +.Sh SYNOPSIS +.Nm +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar flags +.Ar +.Sh DESCRIPTION +The +.Nm +utility modifies the file flags of the listed files +as specified by the +.Ar flags +operand. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl f +Do not display a diagnostic message if +.Nm +could not modify the flags for +.Va file , +nor modify the exit status to reflect such failures. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl h +If the +.Ar file +is a symbolic link, +change the file flags of the link itself rather than the file to which it points. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Change the file flags for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm +to be verbose, showing filenames as the flags are modified. +If the +.Fl v +option is specified more than once, the old and new flags of the file +will also be printed, in octal notation. +.El +.Pp +The flags are specified as an octal number or a comma separated list +of keywords. +The following keywords are currently defined: +.Pp +.Bl -tag -offset indent -width ".Cm opaque" +.It Cm arch , archived +set the archived flag (super-user only) +.It Cm opaque +set the opaque flag (owner or super-user only). +[Directory is opaque when viewed through a union mount] +.It Cm nodump +set the nodump flag (owner or super-user only) +.It Cm sappnd , sappend +set the system append-only flag (super-user only) +.It Cm schg , schange , simmutable +set the system immutable flag (super-user only) +.It Cm uappnd , uappend +set the user append-only flag (owner or super-user only) +.It Cm uchg , uchange , uimmutable +set the user immutable flag (owner or super-user only) +.It Cm hidden +set the hidden flag +[Hide item from GUI] +.El +.Pp +As discussed in +.Xr chflags 2 , +the +.Ar sappnd +and +.Ar schg +flags may only be unset when the system is in single-user mode. +.Pp +Putting the letters +.Dq Ar no +before or removing the letters +.Dq Ar no +from a keyword causes the flag to be cleared. +For example: +.Pp +.Bl -tag -offset indent -width "nouchg" -compact +.It Ar nouchg +clear the user immutable flag (owner or super-user only) +.It Ar dump +clear the nodump flag (owner or super-user only) +.El +.Pp +Unless the +.Fl H +or +.Fl L +options are given, +.Nm +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +You can use "ls -lO" to see the flags of existing files. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr ls 1 , +.Xr chflags 2 , +.Xr stat 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh HISTORY +The +.Nm +command first appeared in +.Bx 4.4 . +.Sh BUGS +Only a limited number of utilities are +.Nm +aware. +Some of these tools include +.Xr ls 1 , +.Xr cp 1 , +.Xr find 1 , +.Xr install 1 , +.Xr dump 8 , +and +.Xr restore 8 . +In particular a tool which is not currently +.Nm +aware is the +.Xr pax 1 +utility. diff --git a/file_cmds/chflags/chflags.c b/file_cmds/chflags/chflags.c new file mode 100644 index 00000000..ebd64d51 --- /dev/null +++ b/file_cmds/chflags/chflags.c @@ -0,0 +1,214 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +static char sccsid[] = "@(#)chflags.c 8.5 (Berkeley) 4/1/94"; +#endif +#endif + +#include +__FBSDID("$FreeBSD: src/bin/chflags/chflags.c,v 1.26.2.1.2.1 2009/10/25 01:10:29 kensmith Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + + +static void usage(void); + +int +chflags_main(int argc, char *argv[]) +{ + FTS *ftsp; + FTSENT *p; + u_long clear = 0, newflags, set; + long val; + int Hflag, Lflag, Rflag, fflag, hflag, vflag; + int ch, fts_options, oct, rval; + char *flags, *ep; + int (*change_flags)(const char *, u_int); + + Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'v': + vflag++; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag) { + errx(1, "the -R and -h options " + "may not be specified together"); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + if (hflag) + change_flags = lchflags; + else + change_flags = chflags; + + flags = *argv; + if (*flags >= '0' && *flags <= '7') { + errno = 0; + val = strtol(flags, &ep, 8); + if (val < 0) + errno = ERANGE; + if (errno) { + err(1, "invalid flags: %s", flags); + } + if (*ep) { + errx(1, "invalid flags: %s", flags); + } + set = val; + oct = 1; + } else { + if (strtofflags(&flags, &set, &clear)) { + errx(1, "invalid flag: %s", flags); + } + clear = ~clear; + oct = 0; + } + + if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) { + err(1, NULL); + } + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: /* Change it at FTS_DP if we're recursive. */ + if (!Rflag) + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chflag, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (!hflag) + continue; + /* FALLTHROUGH */ + default: + break; + } + if (oct) + newflags = set; + else + newflags = (p->fts_statp->st_flags | set) & clear; + if (newflags == p->fts_statp->st_flags) + continue; + if ((*change_flags)(p->fts_accpath, (u_int)newflags) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } else if (vflag) { + (void)fprintf(thread_stdout, "%s", p->fts_path); + if (vflag > 1) + (void)fprintf(thread_stdout, ": 0%lo -> 0%lo", + (u_long)p->fts_statp->st_flags, + newflags); + (void)fprintf(thread_stdout, "\n"); + } + } + if (errno) { + err(1, "fts_read"); + } + exit(rval); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: chflags [-fhv] [-R [-H | -L | -P]] flags file ...\n"); + exit(1); +} diff --git a/file_cmds/chmod/chmod.1 b/file_cmds/chmod/chmod.1 new file mode 100644 index 00000000..dccb9539 --- /dev/null +++ b/file_cmds/chmod/chmod.1 @@ -0,0 +1,583 @@ +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94 +.\" $FreeBSD: src/bin/chmod/chmod.1,v 1.33 2002/10/01 20:32:59 trhodes Exp $ +.\" +.Dd July 08, 2004 +.Dt CHMOD 1 +.Os +.Sh NAME +.Nm chmod +.Nd change file modes or Access Control Lists +.Sh SYNOPSIS +.Nm chmod +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Ar mode +.Ar +.Nm chmod +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Op -a | +a | =a +.Ar ACE +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl E +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl C +.Ar +.Nm chmod +.Op Fl fhv +.Op Fl R Op Fl H | L | P +.Op Fl N +.Ar +.Sh DESCRIPTION +The +.Nm chmod +utility modifies the file mode bits of the listed files +as specified by the +.Ar mode +operand. It may also be used to modify the Access Control +Lists (ACLs) associated with the listed files. +.Pp +The generic options are as follows: +.Bl -tag -width Ds +.It Fl f +Do not display a diagnostic message if +.Nm chmod +could not modify the mode for +.Va file . +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed by +default.) +.It Fl h +If the file is a symbolic link, change the mode of the link itself +rather than the file that the link points to. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl R +Change the modes of the file hierarchies rooted in the files +instead of just the files themselves. +.It Fl v +Cause +.Nm chmod +to be verbose, showing filenames as the mode is modified. +If the +.Fl v +flag is specified more than once, the old and new modes of the file +will also be printed, in both octal and symbolic notation. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +Only the owner of a file or the super-user is permitted to change +the mode of a file. +.Sh DIAGNOSTICS +.Ex -std +.Sh MODES +Modes may be absolute or symbolic. +An absolute mode is an octal number constructed from the sum of +one or more of the following values: +.Pp +.Bl -tag -width 6n -compact -offset indent +.It Li 4000 +(the set-user-ID-on-execution bit) Executable files with this bit set +will run with effective uid set to the uid of the file owner. +Directories with the set-user-id bit set will force all files and +sub-directories created in them to be owned by the directory owner +and not by the uid of the creating process, if the underlying file +system supports this feature: see +.Xr chmod 2 +and the +.Ar suiddir +option to +.Xr mount 8 . +.It Li 2000 +(the set-group-ID-on-execution bit) Executable files with this bit set +will run with effective gid set to the gid of the file owner. +.It Li 1000 +(the sticky bit) +See +.Xr chmod 2 +and +.Xr sticky 8 . +.It Li 0400 +Allow read by owner. +.It Li 0200 +Allow write by owner. +.It Li 0100 +For files, allow execution by owner. +For directories, allow the owner to +search in the directory. +.It Li 0040 +Allow read by group members. +.It Li 0020 +Allow write by group members. +.It Li 0010 +For files, allow execution by group members. +For directories, allow +group members to search in the directory. +.It Li 0004 +Allow read by others. +.It Li 0002 +Allow write by others. +.It Li 0001 +For files, allow execution by others. +For directories allow others to +search in the directory. +.El +.Pp +For example, the absolute mode that permits read, write and execute by +the owner, read and execute by group members, read and execute by +others, and no set-uid or set-gid behaviour is 755 +(400+200+100+040+010+004+001). +.Pp +The symbolic mode is described by the following grammar: +.Bd -literal -offset indent +mode ::= clause [, clause ...] +clause ::= [who ...] [action ...] action +action ::= op [perm ...] +who ::= a | u | g | o +op ::= + | \- | = +perm ::= r | s | t | w | x | X | u | g | o +.Ed +.Pp +The +.Ar who +symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts +of the mode bits, respectively. +The +.Ar who +symbol ``a'' is equivalent to ``ugo''. +.Pp +The +.Ar perm +symbols represent the portions of the mode bits as follows: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It r +The read bits. +.It s +The set-user-ID-on-execution and set-group-ID-on-execution bits. +.It t +The sticky bit. +.It w +The write bits. +.It x +The execute/search bits. +.It X +The execute/search bits if the file is a directory or any of the +execute/search bits are set in the original (unmodified) mode. +Operations with the +.Ar perm +symbol ``X'' are only meaningful in conjunction with the +.Ar op +symbol ``+'', and are ignored in all other cases. +.It u +The user permission bits in the original mode of the file. +.It g +The group permission bits in the original mode of the file. +.It o +The other permission bits in the original mode of the file. +.El +.Pp +The +.Ar op +symbols represent the operation performed, as follows: +.Bl -tag -width 4n +.It + +If no value is supplied for +.Ar perm , +the ``+'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.It \&\- +If no value is supplied for +.Ar perm , +the ``\-'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is cleared. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are cleared. +.It = +The mode bits specified by the +.Ar who +value are cleared, or, if no who value is specified, the owner, group +and other mode bits are cleared. +Then, if no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.El +.Pp +Each +.Ar clause +specifies one or more operations to be performed on the mode +bits, and each operation is applied to the mode bits in the +order specified. +.Pp +Operations upon the other permissions only (specified by the symbol +``o'' by itself), in combination with the +.Ar perm +symbols ``s'' or ``t'', are ignored. +.Sh EXAMPLES OF VALID MODES +.Bl -tag -width "u=rwx,go=u-w" -compact +.It Li 644 +make a file readable by anyone and writable by the owner only. +.Pp +.It Li go-w +deny write permission to group and others. +.Pp +.It Li =rw,+X +set the read and write permissions to the usual defaults, but +retain any execute permissions that are currently set. +.Pp +.It Li +X +make a directory or file searchable/executable by everyone if it is +already searchable/executable by anyone. +.Pp +.It Li 755 +.It Li u=rwx,go=rx +.It Li u=rwx,go=u-w +make a file readable/executable by everyone and writable by the owner only. +.Pp +.It Li go= +clear all mode bits for group and others. +.Pp +.It Li g=u-w +set the group bits equal to the user bits, but clear the group write bit. +.El +.Sh ACL MANIPULATION OPTIONS +ACLs are manipulated using extensions to the symbolic mode +grammar. Each file has one ACL, containing an ordered list of entries. +Each entry refers to a user or group, and grants or denies a set of +permissions. +In cases where a user and a group exist with the same name, the +user/group name can be prefixed with "user:" or "group:" in order to +specify the type of name. +.Pp +If the user or group name contains spaces you can use ':' as the delimiter +between name and permission. +.Pp +The following permissions are applicable to all filesystem objects: +.Bl -tag -width 6n -compact -offset indent +.It delete +Delete the item. Deletion may be granted by either this permission +on an object or the delete_child right on the containing directory. +.It readattr +Read an objects basic attributes. This is implicitly granted if +the object can be looked up and not explicitly denied. +.It writeattr +Write an object's basic attributes. +.It readextattr +Read extended attributes. +.It writeextattr +Write extended attributes. +.It readsecurity +Read an object's extended security information (ACL). +.It writesecurity +Write an object's security information (ownership, mode, ACL). +.It chown +Change an object's ownership. +.El +.Pp +The following permissions are applicable to directories: +.Bl -tag -width 6n -compact -offset indent +.It list +List entries. +.It search +Look up files by name. +.It add_file +Add a file. +.It add_subdirectory +Add a subdirectory. +.It delete_child +Delete a contained object. See the file delete permission above. +.El +.Pp +The following permissions are applicable to non-directory filesystem objects: +.Bl -tag -width 6n -compact -offset indent +.It read +Open for reading. +.It write +Open for writing. +.It append +Open for writing, but in a fashion that only allows writes into areas of +the file not previously written. +.It execute +Execute the file as a script or program. +.El +.Pp +ACL inheritance is controlled with the following permissions words, which +may only be applied to directories: +.Bl -tag -width 6n -compact -offset indent +.It file_inherit +Inherit to files. +.It directory_inherit +Inherit to directories. +.It limit_inherit +This flag is only relevant to entries inherited by subdirectories; it +causes the directory_inherit flag to be cleared in the entry that is +inherited, preventing further nested subdirectories from also +inheriting the entry. +.It only_inherit +The entry is inherited by created items but not considered when processing +the ACL. +.El +.Pp +The ACL manipulation options are as follows: +.Bl -tag -width Ds +.It \fB+a\fR +The +a mode parses a new ACL entry from the next argument on +the commandline and inserts it into the canonical location in the +ACL. If the supplied entry refers to an identity already listed, the +two entries are combined. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + # chmod +a "admin allow write" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write + # chmod +a "guest deny read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write + # chmod +a "admin allow delete" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + # chmod +a "User 1:allow:read" file + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: User 1 allow read + 3: admin allow write,delete +.Pp +The +a mode strives to maintain correct canonical form for the ACL. + local deny + local allow + inherited deny + inherited allow +.Pp +By default, chmod adds entries to the top of the local deny and local +allow lists. Inherited entries are added by using the +ai mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + 3: juser inherited deny delete + 4: admin inherited allow delete + 5: backup inherited deny read + 6: admin inherited allow write-security + # chmod +ai "others allow read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + 3: juser inherited deny delete + 4: others inherited allow read + 5: admin inherited allow delete + 6: backup inherited deny read + 7: admin inherited allow write-security +.It \fB+a#\fR +When a specific ordering is required, the exact location at which an +entry will be inserted is specified with the +a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write + # chmod +a# 2 "others deny read" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: others deny read + 3: admin allow write +.Pp +The +ai# mode may be used to insert inherited entries at a specific +location. Note that these modes allow non-canonical ACL ordering to +be constructed. +.It Fl a +The -a mode is used to delete ACL entries. All entries exactly +matching the supplied entry will be deleted. If the entry lists a +subset of rights granted by an entry, only the rights listed are +removed. Entries may also be deleted by index using the -a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: guest deny read + 2: admin allow write,delete + # chmod -a# 1 file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write,delete + # chmod -a "admin allow write" file1 + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow delete +.Pp +Inheritance is not considered when processing the -a mode; rights and +entries will be removed regardless of their inherited state. +.Pp +If the user or group name contains spaces you can use ':' as the delimiter +.Pp +\fBExample\fR + # chmod +a "User 1:allow:read" file +.It \fB=a#\fR +Individual entries are rewritten using the =a# mode. +.Pp +\fBExamples\fR + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow delete + # chmod =a# 1 "admin allow write,chown" + # ls -le + -rw-r--r--+ 1 juser wheel 0 Apr 28 14:06 file1 + owner: juser + 1: admin allow write,chown +.Pp +This mode may not be used to add new entries. +.It Fl E +Reads the ACL information from stdin, as a sequential list +of ACEs, separated by newlines. If the information parses correctly, +the existing information is replaced. +.It Fl C +Returns false if any of the named files have ACLs in non-canonical order. +.It Fl i +Removes the 'inherited' bit from all entries in the named file(s) ACLs. +.It Fl I +Removes all inherited entries from the named file(s) ACL(s). +.It Fl N +Removes the ACL from the named file(s). +.El +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr fsaclctl 1 , +.Xr install 1 , +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 , +.Xr fts 3 , +.Xr setmode 3 , +.Xr symlink 7 , +.Xr chown 8 , +.Xr mount 8 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm chmod +utility is expected to be +.St -p1003.2 +compatible with the exception of the +.Ar perm +symbol +.Dq t +which is not included in that standard. +.Sh HISTORY +A +.Nm chmod +command appeared in +.At v1 . diff --git a/file_cmds/chmod/chmod.c b/file_cmds/chmod/chmod.c new file mode 100644 index 00000000..92a2ed9c --- /dev/null +++ b/file_cmds/chmod/chmod.c @@ -0,0 +1,469 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include "chmod_acl.h" +#include "ios_error.h" // remap exit to pthread_exit + +#endif /*__APPLE__*/ + +__thread int chmod_fflag = 0; + +int chmod_main(int, char *[]); +void chmod_usage(void); + +int +chmod_main(int argc, char *argv[]) +{ + chmod_fflag = 0; + FTS *ftsp = NULL; + FTSENT *p = NULL; + mode_t *set = NULL; + long val = 0; + int oct = 0; + int Hflag, Lflag, Pflag, Rflag, ch, fts_options, hflag, rval; + int vflag; + char *ep, *mode; + mode_t newmode, omode; +#ifdef __APPLE__ + unsigned int acloptflags = 0; + long aclpos = -1; + int inheritance_level = 0; + int index = 0; + size_t acloptlen = 0; + int ace_arg_not_required = 0; + acl_t acl_input = NULL; + optind = 1; opterr = 1; optreset = 1; +#endif /* __APPLE__*/ + int (*change_mode)(const char *, mode_t); + + set = NULL; + omode = 0; + Hflag = Lflag = Pflag = Rflag = chmod_fflag = hflag = vflag = 0; +#ifndef __APPLE__ + while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) +#else + while ((ch = getopt(argc, argv, "ACEHILNPRVXafghinorstuvwx")) != -1) +#endif + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + Pflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + Pflag = 1; + break; + case 'R': + Rflag = 1; + break; + case 'f': + chmod_fflag = 1; + break; + case 'h': + /* + * In System V (and probably POSIX.2) the -h option + * causes chmod to change the mode of the symbolic + * link. 4.4BSD's symbolic links didn't have modes, + * so it was an undocumented noop. In FreeBSD 3.0, + * lchmod(2) is introduced and this option does real + * work. + */ + hflag = 1; + break; +#ifdef __APPLE__ + case 'a': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch) + --optind; + goto done; + case 'A': +// acloptflags |= ACL_FLAG | ACL_TO_STDOUT; +// ace_arg_not_required = 1; + errx(1, "-A not implemented"); + goto done; + case 'E': + acloptflags |= ACL_FLAG | ACL_FROM_STDIN; + goto done; + case 'C': + acloptflags |= ACL_FLAG | ACL_CHECK_CANONICITY; + ace_arg_not_required = 1; + goto done; + case 'i': + acloptflags |= ACL_FLAG | ACL_REMOVE_INHERIT_FLAG; + ace_arg_not_required = 1; + goto done; + case 'I': + acloptflags |= ACL_FLAG | ACL_REMOVE_INHERITED_ENTRIES; + ace_arg_not_required = 1; + goto done; + case 'n': + acloptflags |= ACL_FLAG | ACL_NO_TRANSLATE; + break; + case 'N': + acloptflags |= ACL_FLAG | ACL_CLEAR_FLAG; + ace_arg_not_required = 1; + goto done; + case 'V': +// acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR; +// ace_arg_not_required = 1; + errx(1, "-V not implemented"); + goto done; +#endif /* __APPLE__ */ + /* + * XXX + * "-[rwx]" are valid mode commands. If they are the entire + * argument, getopt has moved past them, so decrement optind. + * Regardless, we're done argument processing. + */ + case 'g': case 'o': case 'r': case 's': + case 't': case 'u': case 'w': case 'X': case 'x': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch && + argv[optind - 1][2] == '\0') + --optind; + goto done; + case 'v': + vflag++; + break; + case '?': + default: + chmod_usage(); + } +done: argv += optind; + argc -= optind; + +#ifdef __APPLE__ + if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2)) + chmod_usage(); + if (!Rflag && (Hflag || Lflag || Pflag)) + warnx("options -H, -L, -P only useful with -R"); +#else /* !__APPLE__ */ + if (argc < 2) + chmod_usage(); +#endif /* __APPLE__ */ + +#ifdef __APPLE__ + if (!(acloptflags & ACL_FLAG) && ((acloptlen = strlen(argv[0])) > 1) && (argv[0][1] == 'a')) { + acloptflags |= ACL_FLAG; + switch (argv[0][0]) { + case '+': + acloptflags |= ACL_SET_FLAG; + break; + case '-': + acloptflags |= ACL_DELETE_FLAG; + break; + case '=': + acloptflags |= ACL_REWRITE_FLAG; + break; + default: + acloptflags &= ~ACL_FLAG; + goto apnoacl; + } + + if (argc < 3) + chmod_usage(); + + if (acloptlen > 2) { + for (index = 2; index < acloptlen; index++) { + switch (argv[0][index]) { + case '#': + acloptflags |= ACL_ORDER_FLAG; + + if (argc < ((acloptflags & ACL_DELETE_FLAG) + ? 3 : 4)) + chmod_usage(); + argv++; + argc--; + errno = 0; + aclpos = strtol(argv[0], &ep, 0); + + if (aclpos > ACL_MAX_ENTRIES + || aclpos < 0) + errno = ERANGE; + if (errno || *ep) { + errx(1, "Invalid ACL entry number: %ld", aclpos); + } + if (acloptflags & ACL_DELETE_FLAG) + ace_arg_not_required = 1; + + goto apdone; + case 'i': + acloptflags |= ACL_INHERIT_FLAG; + /* The +aii.. syntax to specify + * inheritance level is rather unwieldy, + * find an alternative. + */ + inheritance_level++; + if (inheritance_level > 1) + warnx("Inheritance across more than one generation is not currently supported"); + if (inheritance_level >= MAX_INHERITANCE_LEVEL) + goto apdone; + break; + default: + errno = EINVAL; + chmod_usage(); + } + } + } +apdone: + argv++; + argc--; + } +apnoacl: +#endif /*__APPLE__*/ + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag) { + errx(1, "the -R and -h options may not be specified together."); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + if (hflag) + change_mode = lchmod; + else + change_mode = chmod; +#ifdef __APPLE__ + if (acloptflags & ACL_FROM_STDIN) { + ssize_t readval = 0; + size_t readtotal = 0; + + mode = (char *) malloc(MAX_ACL_TEXT_SIZE); + + if (mode == NULL) { + err(1, "Unable to allocate mode string"); + } + /* Read the ACEs from STDIN */ + do { + readtotal += readval; + readval = read(fileno(thread_stdin), mode + readtotal, + MAX_ACL_TEXT_SIZE); + } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE)); + + if (0 == readtotal) { + errx(1, "-E specified, but read from STDIN failed"); + } + else + mode[readtotal - 1] = '\0'; + --argv; + } + else +#endif /* __APPLE */ + mode = *argv; + +#ifdef __APPLE__ + if ((acloptflags & ACL_FLAG)) { + + /* Are we deleting by entry number, verifying + * canonicity or performing some other operation that + * does not require an input entry? If so, there's no + * entry to convert. + */ + if (ace_arg_not_required) { + --argv; + } + else { + /* Parse the text into an ACL*/ + acl_input = parse_acl_entries(mode); + if (acl_input == NULL) { + errx(1, "Invalid ACL specification: %s", mode); + } + } + } + else { +#endif /* __APPLE__*/ + if (*mode >= '0' && *mode <= '7') { + errno = 0; + val = strtol(mode, &ep, 8); + if (val > USHRT_MAX || val < 0) + errno = ERANGE; + if (errno) { + err(1, "Invalid file mode: %s", mode); + } + if (*ep) { + errx(1, "Invalid file mode: %s", mode); + } + omode = (mode_t)val; + oct = 1; + } else { + if ((set = setmode(mode)) == NULL) { + errx(1, "Invalid file mode: %s", mode); + } + oct = 0; + } +#ifdef __APPLE__ + } +#endif /* __APPLE__*/ + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) { + err(1, "fts_open"); + } + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (!Rflag) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_DNR: /* Warn, chmod, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_DP: /* Already changed at FTS_D. */ + continue; + case FTS_NS: + if (acloptflags & ACL_FLAG) /* don't need stat for -N */ + break; + case FTS_ERR: /* Warn, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (!hflag) + continue; + /* else */ + /* FALLTHROUGH */ + default: + break; + } +#ifdef __APPLE__ +/* If an ACL manipulation option was specified, manipulate */ + if (acloptflags & ACL_FLAG) { + if (0 != modify_file_acl(acloptflags, p->fts_accpath, acl_input, (int)aclpos, inheritance_level, !hflag)) + rval = 1; + } + else { +#endif /* __APPLE__ */ + newmode = oct ? omode : getmode(set, p->fts_statp->st_mode); + if ((newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) + continue; + if ((*change_mode)(p->fts_accpath, newmode) && !chmod_fflag) { + warn("Unable to change file mode on %s", p->fts_path); + rval = 1; + } else { + if (vflag) { + (void)fprintf(thread_stdout, "%s", p->fts_accpath); + + if (vflag > 1) { + char m1[12], m2[12]; + + strmode(p->fts_statp->st_mode, m1); + strmode((p->fts_statp->st_mode & + S_IFMT) | newmode, m2); + + (void)fprintf(thread_stdout, ": 0%o [%s] -> 0%o [%s]", + p->fts_statp->st_mode, m1, + (p->fts_statp->st_mode & S_IFMT) | + newmode, m2); + } + (void)fprintf(thread_stdout, "\n"); + } + + } +#ifdef __APPLE__ + } +#endif /* __APPLE__*/ + } + if (errno) { + err(1, "fts_read"); + } +#ifdef __APPLE__ + if (acl_input) + acl_free(acl_input); + if (mode && (acloptflags & ACL_FROM_STDIN)) + free(mode); + +#endif /* __APPLE__ */ + if (set) + free(set); + exit(rval); +} + +void +chmod_usage(void) +{ +#ifdef __APPLE__ + (void)fprintf(stderr, + "usage:\tchmod [-fhv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n" + "\tchmod [-fhv] [-R [-H | -L | -P]] [-E | -C | -N | -i | -I] file ...\n"); /* add -A and -V when implemented */ +#else + (void)fprintf(stderr, + "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); +#endif /* __APPLE__ */ + exit(1); +} diff --git a/file_cmds/chmod/chmod_acl.c b/file_cmds/chmod/chmod_acl.c new file mode 100644 index 00000000..a2b77a9d --- /dev/null +++ b/file_cmds/chmod/chmod_acl.c @@ -0,0 +1,881 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "chmod_acl.h" +#include "ios_error.h" + +extern void chmod_usage(void); + +#ifdef __APPLE__ +static struct { + acl_perm_t perm; + char *name; + int flags; +#define ACL_PERM_DIR (1<<0) +#define ACL_PERM_FILE (1<<1) +} acl_perms[] = { + {ACL_READ_DATA, "read", ACL_PERM_FILE}, + {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, + {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, + {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, + {ACL_EXECUTE, "execute", ACL_PERM_FILE}, + {ACL_SEARCH, "search", ACL_PERM_DIR}, + {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, + {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, + {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, + {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, + {0, NULL, 0} +}; + +static struct { + acl_flag_t flag; + char *name; + int flags; +} acl_flags[] = { + {ACL_ENTRY_INHERITED, "inherited", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, + {0, NULL, 0} +}; + +/* TBD - Many of these routines could potentially be considered for + * inclusion in a library. If that is done, either avoid use of "err" + * and implement a better fall-through strategy in case of errors, + * or use err_set_exit() and make various structures globals. + */ + +#define NAME_USER (1) +#define NAME_GROUP (2) +#define NAME_EITHER (NAME_USER | NAME_GROUP) + +/* Perform a name to uuid mapping - calls through to memberd */ + +uuid_t * +name_to_uuid(char *tok, int nametype) { + uuid_t *entryg = NULL; + size_t len = strlen(tok); + + if ((entryg = (uuid_t *) calloc(1, sizeof(uuid_t))) == NULL) { + errx(1, "Unable to allocate a uuid"); + } + + if ((nametype & NAME_USER) && mbr_identifier_to_uuid(ID_TYPE_USERNAME, tok, len, *entryg) == 0) { + return entryg; + } + + if ((nametype & NAME_GROUP) && mbr_identifier_to_uuid(ID_TYPE_GROUPNAME, tok, len, *entryg) == 0) { + return entryg; + } + + free(entryg); + errx(1, "Unable to translate '%s' to a UUID", tok); + return NULL; // never reached +} + +/* Convert an acl entry in string form to an acl_entry_t */ +int +parse_entry(char *entrybuf, acl_entry_t newent) { + char *tok; + char *pebuf; + uuid_t *entryg; + + acl_tag_t tag; + acl_permset_t perms; + acl_flagset_t flags; + unsigned permcount = 0; + unsigned pindex = 0; + char *delimiter = " "; + int nametype = NAME_EITHER; + + acl_get_permset(newent, &perms); + acl_get_flagset_np(newent, &flags); + + pebuf = entrybuf; + + if (0 == strncmp(entrybuf, "user:", 5)) { + nametype = NAME_USER; + pebuf += 5; + } else if (0 == strncmp(entrybuf, "group:", 6)) { + nametype = NAME_GROUP; + pebuf += 6; + } + + if (strchr(pebuf, ':')) /* User/Group names can have spaces */ + delimiter = ":"; + tok = strsep(&pebuf, delimiter); + + if ((tok == NULL) || *tok == '\0') { + errx(1, "Invalid entry format -- expected user or group name"); + } + + /* parse the name into a qualifier */ + entryg = name_to_uuid(tok, nametype); + + tok = strsep(&pebuf, ": "); /* Stick with delimiter? */ + if ((tok == NULL) || *tok == '\0') { + errx(1, "Invalid entry format -- expected allow or deny"); + } + + /* is the verb 'allow' or 'deny'? */ + if (!strcmp(tok, "allow")) { + tag = ACL_EXTENDED_ALLOW; + } else if (!strcmp(tok, "deny")) { + tag = ACL_EXTENDED_DENY; + } else { + errx(1, "Unknown tag type '%s'", tok); + } + + /* parse permissions */ + for (; (tok = strsep(&pebuf, ",")) != NULL;) { + if (*tok != '\0') { + /* is it a permission? */ + for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) { + if (!strcmp(acl_perms[pindex].name, tok)) { + /* got one */ + acl_add_perm(perms, acl_perms[pindex].perm); + permcount++; + goto found; + } + } + /* is it a flag? */ + for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) { + if (!strcmp(acl_flags[pindex].name, tok)) { + /* got one */ + acl_add_flag_np(flags, acl_flags[pindex].flag); + permcount++; + goto found; + } + } + errx(1,"Invalid permission type '%s'", tok); + found: + continue; + } + } + if (0 == permcount) { + errx(1, "No permissions specified"); + } + acl_set_tag_type(newent, tag); + acl_set_qualifier(newent, entryg); + acl_set_permset(newent, perms); + acl_set_flagset_np(newent, flags); + free(entryg); + entryg = NULL; + + return(0); +} + +/* Convert one or more acl entries in string form to an acl_t */ +acl_t +parse_acl_entries(const char *input) { + acl_t acl_input; + acl_entry_t newent; + char *inbuf; + char *oinbuf; + + char **bufp, *entryv[ACL_MAX_ENTRIES]; +#if 0 +/* XXX acl_from_text(), when implemented, will presumably use the canonical + * text representation format, which is what chmod should be using + * We may need to add an entry number to the input + */ + /* Translate the user supplied ACL entry */ + /* acl_input = acl_from_text(input); */ +#else + inbuf = malloc(MAX_ACL_TEXT_SIZE); + + if (inbuf == NULL) { + err(1, "malloc() failed"); + } + strncpy(inbuf, input, MAX_ACL_TEXT_SIZE); + inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0'; + + if ((acl_input = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + + oinbuf = inbuf; + + for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;) + if (**bufp != '\0') { + if (0 != acl_create_entry(&acl_input, &newent)) { + err(1, "acl_create_entry() failed"); + } + if (0 != parse_entry(*bufp, newent)) { + errx(1, "Failed parsing entry '%s'", *bufp); + } + if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) { + errx(1, "Too many entries"); + } + } + + free(inbuf); + return acl_input; +#endif /* #if 0 */ +} + +/* XXX No Libc support for inherited entries and generation determination yet */ +unsigned +get_inheritance_level(acl_entry_t entry) { +/* XXX to be implemented */ + return 1; +} + +/* Determine a "score" for an acl entry. The entry scores higher if it's + * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher + * than inherited entries. + */ + +int +score_acl_entry(acl_entry_t entry) { + + acl_tag_t tag; + acl_flagset_t flags; + acl_permset_t perms; + + int score = 0; + + if (entry == NULL) + return (MINIMUM_TIER); + + if (acl_get_tag_type(entry, &tag) != 0) { + err(1, "Malformed ACL entry, no tag present"); + } + if (acl_get_flagset_np(entry, &flags) != 0){ + err(1, "Unable to obtain flagset"); + } + if (acl_get_permset(entry, &perms) != 0) { + err(1, "Malformed ACL entry, no permt present"); + } + + switch(tag) { + case ACL_EXTENDED_ALLOW: + break; + case ACL_EXTENDED_DENY: + score++; + break; + default: + errx(1, "Unknown tag type %d present in ACL entry", tag); + /* NOTREACHED */ + } + + if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED)) + score += get_inheritance_level(entry) * INHERITANCE_TIER; + + return score; +} + +int +compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) { + return bcmp(qa, qb, sizeof(uuid_t)); +} + +/* Compare two ACL permsets. + * Returns : + * MATCH_SUBSET if bperms is a subset of aperms + * MATCH_SUPERSET if bperms is a superset of aperms + * MATCH_PARTIAL if the two permsets have a common subset + * MATCH_EXACT if the two permsets are identical + * MATCH_NONE if they are disjoint + */ + +int +compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms) +{ + int i; +/* TBD Implement other match levels as needed */ + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(aperms, acl_perms[i].perm) != + acl_get_perm_np(bperms, acl_perms[i].perm)) + return MATCH_NONE; + } + return MATCH_EXACT; +} + +static int +compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags) +{ + int i; +/* TBD Implement other match levels as needed */ + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(aflags, acl_flags[i].flag) != + acl_get_flag_np(bflags, acl_flags[i].flag)) + return MATCH_NONE; + } + return MATCH_EXACT; +} + +/* Compares two ACL entries for equality */ +int +compare_acl_entries(acl_entry_t a, acl_entry_t b) +{ + acl_tag_t atag, btag; + acl_permset_t aperms, bperms; + acl_flagset_t aflags, bflags; + int pcmp = 0, fcmp = 0; + void *aqual, *bqual; + + aqual = acl_get_qualifier(a); + bqual = acl_get_qualifier(b); + + int compare = compare_acl_qualifiers(aqual, bqual); + acl_free(aqual); + acl_free(bqual); + + if (compare != 0) + return MATCH_NONE; + + if (0 != acl_get_tag_type(a, &atag)) { + err(1, "No tag type present in entry"); + } + if (0!= acl_get_tag_type(b, &btag)) { + err(1, "No tag type present in entry"); + } + + if (atag != btag) + return MATCH_NONE; + + if ((acl_get_permset(a, &aperms) != 0) || + (acl_get_flagset_np(a, &aflags) != 0) || + (acl_get_permset(b, &bperms) != 0) || + (acl_get_flagset_np(b, &bflags) != 0)) { + err(1, "error fetching permissions"); + } + + pcmp = compare_acl_permsets(aperms, bperms); + fcmp = compare_acl_flagsets(aflags, bflags); + + if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE)) + return(MATCH_PARTIAL); + else + return(MATCH_EXACT); +} + +/* Verify that an ACL is in canonical order. Currently, the canonical + * form is: + * local deny + * local allow + * inherited deny (parent) + * inherited allow (parent) + * inherited deny (grandparent) + * inherited allow (grandparent) + * ... + */ +unsigned int +is_canonical(acl_t acl) { + + unsigned aindex; + acl_entry_t entry; + int score = 0, next_score = 0; + +/* XXX - is a zero entry ACL in canonical form? */ + if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) + return 1; + + score = score_acl_entry(entry); + + for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0; + aindex++) { + if (score < (next_score = score_acl_entry(entry))) + return 0; + score = next_score; + } + return 1; +} + + +/* Iterate through an ACL, and find the canonical position for the + * specified entry + */ +unsigned int +find_canonical_position(acl_t acl, acl_entry_t modifier) { + + acl_entry_t entry; + int mscore = 0; + unsigned mpos = 0; + + /* Check if there's an entry with the same qualifier + * and tag type; if not, find the appropriate slot + * for the score. + */ + + if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry)) + return 0; + + mscore = score_acl_entry(modifier); + + while (mscore < score_acl_entry(entry)) { + + mpos++; + + if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry)) + break; + + } + return mpos; +} + +int canonicalize_acl_entries(acl_t acl); + +/* For a given acl_entry_t "modifier", find the first exact or + * partially matching entry from the specified acl_t acl + */ + +int +find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp, + unsigned match_inherited) { + + acl_entry_t entry = NULL; + + unsigned aindex; + int cmp, fcmp = MATCH_NONE; + + for (aindex = 0; + acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY, &entry) == 0; + aindex++) { + cmp = compare_acl_entries(entry, modifier); + if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) { + if (match_inherited) { + acl_flagset_t eflags, mflags; + + if (0 != acl_get_flagset_np(modifier, &mflags)) { + err(1, "Unable to get flagset"); + } + + if (0 != acl_get_flagset_np(entry, &eflags)) { + err(1, "Unable to get flagset"); + } + + if (compare_acl_flagsets(mflags, eflags) == MATCH_EXACT) { + *rentryp = entry; + fcmp = cmp; + } + } + else { + *rentryp = entry; + fcmp = cmp; + } + } + if (fcmp == MATCH_EXACT) + break; + } + return fcmp; +} + +/* Remove all perms specified in modifier from rentry*/ +int +subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int* valid_perms) +{ + acl_permset_t rperms, mperms; + acl_flagset_t rflags, mflags; + if (valid_perms) + *valid_perms = 0; + int i; + + if ((acl_get_permset(rentry, &rperms) != 0) || + (acl_get_flagset_np(rentry, &rflags) != 0) || + (acl_get_permset(modifier, &mperms) != 0) || + (acl_get_flagset_np(modifier, &mflags) != 0)) { + err(1, "error computing ACL modification"); + } + + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(mperms, acl_perms[i].perm)) + acl_delete_perm(rperms, acl_perms[i].perm); + else if (valid_perms && acl_get_perm_np(rperms, acl_perms[i].perm)) + (*valid_perms)++; + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(mflags, acl_flags[i].flag)) + acl_delete_flag_np(rflags, acl_flags[i].flag); + } + acl_set_permset(rentry, rperms); + acl_set_flagset_np(rentry, rflags); + return 0; +} +/* Add the perms specified in modifier to rentry */ +static int +merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier) +{ + acl_permset_t rperms, mperms; + acl_flagset_t rflags, mflags; + int i; + + if ((acl_get_permset(rentry, &rperms) != 0) || + (acl_get_flagset_np(rentry, &rflags) != 0) || + (acl_get_permset(modifier, &mperms) != 0) || + (acl_get_flagset_np(modifier, &mflags) != 0)) { + err(1, "error computing ACL modification"); + } + + for (i = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(mperms, acl_perms[i].perm)) + acl_add_perm(rperms, acl_perms[i].perm); + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(mflags, acl_flags[i].flag)) + acl_add_flag_np(rflags, acl_flags[i].flag); + } + acl_set_permset(rentry, rperms); + acl_set_flagset_np(rentry, rflags); + return 0; +} + +int +modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, + int position, int inheritance_level, + unsigned flag_new_acl, const char* path) { + + unsigned cpos = 0; + acl_entry_t newent = NULL; + int dmatch = 0; + acl_entry_t rentry = NULL; + unsigned retval = 0; + acl_t oacl = *oaclp; + +/* Add the inherited flag if requested by the user*/ + if (modifier && (optflags & ACL_INHERIT_FLAG)) { + acl_flagset_t mflags; + + acl_get_flagset_np(modifier, &mflags); + acl_add_flag_np(mflags, ACL_ENTRY_INHERITED); + acl_set_flagset_np(modifier, mflags); + } + + if (optflags & ACL_SET_FLAG) { + if (position != -1) { + if (0 != acl_create_entry_np(&oacl, &newent, position)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } else { +/* If an entry exists, add the new permissions to it, else add an + * entry in the canonical position. + */ + +/* First, check for a matching entry - if one exists, merge flags */ + dmatch = find_matching_entry(oacl, modifier, &rentry, 1); + + if (dmatch != MATCH_NONE) { + if (dmatch == MATCH_EXACT) +/* Nothing to be done */ + goto ma_exit; + + if (dmatch == MATCH_PARTIAL) { + merge_entry_perms(rentry, modifier); + goto ma_exit; + } + } +/* Insert the entry in canonical order */ + cpos = find_canonical_position(oacl, modifier); + if (0!= acl_create_entry_np(&oacl, &newent, cpos)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } + } else if (optflags & ACL_DELETE_FLAG) { + if (flag_new_acl) { + warnx("No ACL present '%s'", path); + retval = 1; + } else if (position != -1 ) { + if (0 != acl_get_entry(oacl, position, &rentry)) { + warnx("Invalid entry number '%s'", path); + retval = 1; + } else { + acl_delete_entry(oacl, rentry); + } + } else { + unsigned match_found = 0, aindex; + for (aindex = 0; + acl_get_entry(oacl, rentry == NULL ? + ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY, &rentry) == 0; + aindex++) { + unsigned cmp; + cmp = compare_acl_entries(rentry, modifier); + if ((cmp == MATCH_EXACT) || + (cmp == MATCH_PARTIAL)) { + match_found++; + if (cmp == MATCH_EXACT) + acl_delete_entry(oacl, rentry); + else { + int valid_perms; +/* In the event of a partial match, remove the specified perms from the + * entry */ + subtract_from_entry(rentry, modifier, &valid_perms); + /* if no perms survived then delete the entry */ + if (valid_perms == 0) + acl_delete_entry(oacl, rentry); + } + } + } + if (0 == match_found) { + warnx("Entry not found when attempting delete '%s'",path); + retval = 1; + } + } + } else if (optflags & ACL_REWRITE_FLAG) { + acl_entry_t rentry; + + if (-1 == position) { + chmod_usage(); + } + if (0 == flag_new_acl) { + if (0 != acl_get_entry(oacl, position, + &rentry)) { + err(1, "Invalid entry number '%s'", path); + } + + if (0 != acl_delete_entry(oacl, rentry)) { + err(1, "Unable to delete entry '%s'", path); + } + } + if (0!= acl_create_entry_np(&oacl, &newent, position)) { + err(1, "acl_create_entry() failed"); + } + acl_copy_entry(newent, modifier); + } +ma_exit: + *oaclp = oacl; + return retval; +} + +int +modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow) { + + acl_t oacl = NULL; + unsigned aindex = 0, flag_new_acl = 0; + acl_entry_t newent = NULL; + acl_entry_t entry = NULL; + unsigned retval = 0; + + extern __thread int chmod_fflag; + +/* XXX acl_get_file() returns a zero entry ACL if an ACL was previously + * associated with the file, and has had its entries removed. + * However, POSIX 1003.1e states that a zero entry ACL should be + * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is + * associated with the path; it + * does not specifically state that a request for ACL_TYPE_EXTENDED + * should not return a zero entry ACL, however. + */ + +/* Determine if we've been given a zero entry ACL, or create an ACL if + * none exists. There are some issues to consider here: Should we create + * a zero-entry ACL for a delete or check canonicity operation? + */ + + if (path == NULL) + chmod_usage(); + + if (optflags & ACL_CLEAR_FLAG) { + filesec_t fsec = filesec_init(); + if (fsec == NULL) { + err(1, "filesec_init() failed"); + } + if (filesec_set_property(fsec, FILESEC_ACL, _FILESEC_REMOVE_ACL) != 0) { + err(1, "filesec_set_property() failed"); + } + if (follow) { + if (chmodx_np(path, fsec) != 0) { + if (!chmod_fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + if (fchmodx_np(fd, fsec) != 0) { + if (!chmod_fflag) { + warn("Failed to clear ACL on file %s", path); + } + retval = 1; + } + close(fd); + } else { + if (!chmod_fflag) { + warn("Failed to open file %s", path); + } + retval = 1; + } + } + filesec_free(fsec); + return (retval); + } + + if (optflags & ACL_FROM_STDIN) { + oacl = acl_dup(modifier); + } else { + if (follow) { + oacl = acl_get_file(path, ACL_TYPE_EXTENDED); + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + oacl = acl_get_fd_np(fd, ACL_TYPE_EXTENDED); + close(fd); + } + } + if ((oacl == NULL) || + (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) { + if ((oacl = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + flag_new_acl = 1; + position = 0; + } + + if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG | + ACL_REMOVE_INHERITED_ENTRIES))) { + acl_t facl = NULL; + if ((facl = acl_init(1)) == NULL) { + err(1, "acl_init() failed"); + } + for (aindex = 0; + acl_get_entry(oacl, + (entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY), &entry) == 0; + aindex++) { + acl_flagset_t eflags; + acl_entry_t fent = NULL; + if (acl_get_flagset_np(entry, &eflags) != 0) { + err(1, "Unable to obtain flagset"); + } + + if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) { + if (optflags & ACL_REMOVE_INHERIT_FLAG) { + acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED); + acl_set_flagset_np(entry, eflags); + acl_create_entry(&facl, &fent); + acl_copy_entry(fent, entry); + } + } + else { + acl_create_entry(&facl, &fent); + acl_copy_entry(fent, entry); + } + } + if (oacl) + acl_free(oacl); + oacl = facl; + } else if (optflags & ACL_TO_STDOUT) { + ssize_t len; /* need to get printacl() from ls(1) */ + char *text = acl_to_text(oacl, &len); + puts(text); + acl_free(text); + } else if (optflags & ACL_CHECK_CANONICITY) { + if (flag_new_acl) { + warnx("No ACL currently associated with file '%s'", path); + } + retval = is_canonical(oacl); + } else if ((optflags & ACL_SET_FLAG) && (position == -1) && + (!is_canonical(oacl))) { + warnx("The specified file '%s' does not have an ACL in canonical order, please specify a position with +a# ", path); + retval = 1; + } else if (((optflags & ACL_DELETE_FLAG) && (position != -1)) + || (optflags & ACL_CHECK_CANONICITY)) { + retval = modify_acl(&oacl, NULL, optflags, position, + inheritance_level, flag_new_acl, path); + } else if ((optflags & (ACL_REMOVE_INHERIT_FLAG|ACL_REMOVE_INHERITED_ENTRIES)) && flag_new_acl) { + warnx("No ACL currently associated with file '%s'", path); + retval = 1; + } else { + if (!modifier) { /* avoid bus error in acl_get_entry */ + errx(1, "Internal error: modifier should not be NULL"); + } + for (aindex = 0; + acl_get_entry(modifier, + (entry == NULL ? ACL_FIRST_ENTRY : + ACL_NEXT_ENTRY), &entry) == 0; + aindex++) { + + retval += modify_acl(&oacl, entry, optflags, + position, inheritance_level, + flag_new_acl, path); + } + } + } + +/* XXX Potential race here, since someone else could've modified or + * read the ACL on this file (with the intention of modifying it) in + * the interval from acl_get_file() to acl_set_file(); we can + * minimize one aspect of this window by comparing the original acl + * to a fresh one from acl_get_file() but we could consider a + * "changeset" mechanism, common locking strategy, or kernel + * supplied reservation mechanism to prevent this race. + */ + if (!(optflags & (ACL_TO_STDOUT|ACL_CHECK_CANONICITY))) { + int status = -1; + if (follow) { + status = acl_set_file(path, ACL_TYPE_EXTENDED, oacl); + } else { + int fd = open(path, O_SYMLINK); + if (fd != -1) { + status = acl_set_fd_np(fd, oacl, + ACL_TYPE_EXTENDED); + close(fd); + } + } + if (status != 0) { + if (!chmod_fflag) + warn("Failed to set ACL on file '%s'", path); + retval = 1; + } + } + + if (oacl) + acl_free(oacl); + + return retval; +} + +#endif /*__APPLE__*/ diff --git a/file_cmds/chmod/chmod_acl.h b/file_cmds/chmod/chmod_acl.h new file mode 100644 index 00000000..c76d076d --- /dev/null +++ b/file_cmds/chmod/chmod_acl.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include + +#define ACL_FLAG (1<<0) +#define ACL_SET_FLAG (1<<1) +#define ACL_DELETE_FLAG (1<<2) +#define ACL_REWRITE_FLAG (1<<3) +#define ACL_ORDER_FLAG (1<<4) +#define ACL_INHERIT_FLAG (1<<5) +#define ACL_FOLLOW_LINK (1<<6) +#define ACL_FROM_STDIN (1<<7) +#define ACL_CHECK_CANONICITY (1<<8) +#define ACL_REMOVE_INHERIT_FLAG (1<<9) +#define ACL_REMOVE_INHERITED_ENTRIES (1<<10) +#define ACL_NO_TRANSLATE (1<<11) +#define ACL_INVOKE_EDITOR (1<<12) +#define ACL_TO_STDOUT (1<<13) +#define ACL_CLEAR_FLAG (1<<14) + +#define INHERITANCE_TIER (-5) +#define MINIMUM_TIER (-1000) + +#define MATCH_EXACT (2) +#define MATCH_PARTIAL (1) +#define MATCH_NONE (-1) +#define MATCH_SUBSET (-2) +#define MATCH_SUPERSET (-3) + +#define MAX_ACL_TEXT_SIZE 4096 +#define MAX_INHERITANCE_LEVEL 1024 + +extern int search_acl_block(char *tok); +extern int parse_entry(char *entrybuf, acl_entry_t newent); +extern acl_t parse_acl_entries(const char *input); +extern int score_acl_entry(acl_entry_t entry); +extern unsigned get_inheritance_level(acl_entry_t entry); +extern int compare_acl_qualifiers(uuid_t *qa, uuid_t *qb); +extern int compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms); +extern int compare_acl_entries(acl_entry_t a, acl_entry_t b); +extern unsigned is_canonical(acl_t acl); +extern int find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentry, unsigned match_inherited); +extern unsigned find_canonical_position(acl_t acl, acl_entry_t modifier); +extern int subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier, int *valid_perms); +extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl, const char* path); +extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level, int follow); +extern uuid_t *name_to_uuid(char *tok, int nametype); +#endif /* __APPLE__*/ diff --git a/file_cmds/chown/chgrp.1 b/file_cmds/chown/chgrp.1 new file mode 100644 index 00000000..769c3d55 --- /dev/null +++ b/file_cmds/chown/chgrp.1 @@ -0,0 +1,142 @@ +.\" Copyright (c) 1983, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/usr.sbin/chown/chgrp.1,v 1.13 2001/08/15 09:09:46 ru Exp $ +.\" +.Dd March 31, 1994 +.Dt CHGRP 1 +.Os +.Sh NAME +.Nm chgrp +.Nd change group +.Sh SYNOPSIS +.Nm chgrp +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar group +.Ar +.Sh DESCRIPTION +The +.Nm chgrp +utility sets the group ID of the file named by each +.Ar file +operand to the +.Ar group +ID specified by the group operand. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +The force option ignores errors, except for usage errors and doesn't +query about strange modes (unless the user does not have proper permissions). +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed). +.It Fl h +If the file is a symbolic link, the group ID of the link itself is changed +rather than the file that is pointed to. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. Use +.Fl h +to change the group ID of a symbolic link. +.It Fl R +Change the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm chgrp +to be verbose, showing files as the group is modified. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Ar group +operand can be either a group name from the group database, +or a numeric group ID. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +The user invoking +.Nm chgrp +must belong to the specified group and be the owner of the file, +or be the super-user. +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +In previous versions of this system, symbolic links did not have groups. +.Pp +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh FILES +.Bl -tag -width /etc/group -compact +.It Pa /etc/group +group ID file +.El +.Sh SEE ALSO +.Xr chown 2 , +.Xr fts 3 , +.Xr group 5 , +.Xr passwd 5 , +.Xr symlink 7 , +.Xr chown 8 +.Sh STANDARDS +The +.Nm chgrp +utility is expected to be +.St -p1003.2 +compatible. diff --git a/file_cmds/chown/chown.8 b/file_cmds/chown/chown.8 new file mode 100644 index 00000000..a3ffa093 --- /dev/null +++ b/file_cmds/chown/chown.8 @@ -0,0 +1,175 @@ +.\" Copyright (c) 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chown.8 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/usr.sbin/chown/chown.8,v 1.20 2002/07/14 14:42:43 charnier Exp $ +.\" +.Dd March 31, 1994 +.Dt CHOWN 8 +.Os +.Sh NAME +.Nm chown +.Nd change file owner and group +.Sh SYNOPSIS +.Nm chown +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar owner Ns Op : Ns Ar group +.Ar +.Nm chown +.Op Fl fhv +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.No : Ns Ar group +.Ar +.Sh DESCRIPTION +The +.Nm chown +utility changes the user ID and/or the group ID of the specified files. +Symbolic links named by arguments are silently left unchanged unless +.Fl h +is used. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +Don't report any failure to change file owner or group, nor modify +the exit status to reflect such failures. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl h +If the file is a symbolic link, change the user ID and/or the +group ID of the link itself. +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +Instead, the user and/or group ID of the link itself are modified. +This is the default. Use +.Fl h +to change the user ID and/or the group of symbolic links. +.It Fl R +Change the user ID and/or the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl v +Cause +.Nm chown +to be verbose, showing files as the owner is modified. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Ar owner +and +.Ar group +operands are both optional; +however, at least one must be specified. +If the +.Ar group +operand is specified, it must be preceded by a colon (``:'') character. +.Pp +The +.Ar owner +may be either a numeric user ID or a user name. +If a user name is also a numeric user ID, the operand is used as a +user name. +The +.Ar group +may be either a numeric group ID or a group name. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +For obvious security reasons, +the ownership of a file may only be altered by a super-user. +Similarly, only a member of a group can change a file's group ID +to that group. +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +Previous versions of the +.Nm chown +utility used the dot (``.'') character to distinguish the group name. +This has been changed to be a colon (``:'') character, +so that user and group names may contain the dot character. +.Pp +On previous versions of this system, +symbolic links did not have owners. +.Pp +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh LEGACY DESCRIPTION +In legacy mode, the +.Fl R +and +.Fl RP +options do not change the user ID +or the group ID of symbolic links. +.Sh SEE ALSO +.Xr chgrp 1 , +.Xr find 1 , +.Xr chown 2 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm chown +utility is expected to be +.St -p1003.2 +compliant. +.Sh HISTORY +A +.Nm chown +utility appeared in +.At v1 . diff --git a/file_cmds/chown/chown.c b/file_cmds/chown/chown.c new file mode 100644 index 00000000..9b7db8cf --- /dev/null +++ b/file_cmds/chown/chown.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; +#endif +#endif /* not lint */ + +#include +__RCSID("$FreeBSD: src/usr.sbin/chown/chown.c,v 1.24 2002/07/17 16:22:24 dwmalone Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +static void a_gid(const char *); +static void a_uid(const char *); +static void chownerr(const char *); +static uid_t id(const char *, const char *); +static void usage(void); + +static uid_t uid; +static gid_t gid; +static int ischown; +static const char *gname; + +int +chown_main(int argc, char **argv) +{ + FTS *ftsp; + FTSENT *p; + int Hflag, Lflag, Pflag, Rflag, fflag, hflag, vflag; + int ch, fts_options, rval; + char *cp; + int unix2003_compat = 0; + int symlink_found = 0; + + if (argc < 1) + usage(); + cp = strrchr(argv[0], '/'); + cp = (cp != NULL) ? cp + 1 : argv[0]; + ischown = (strcmp(cp, "chown") == 0); + + Hflag = Lflag = Pflag = Rflag = fflag = hflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + hflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + if (!Rflag && (Hflag || Lflag || Pflag)) + warnx("options -H, -L, -P only useful with -R"); + + if (Rflag) { + fts_options = FTS_PHYSICAL; + if (hflag && (Hflag || Lflag)) { + errx(1, "the -R%c and -h options may not be " + "specified together\n", Hflag ? 'H' : 'L'); + } + if (Hflag) + fts_options |= FTS_COMFOLLOW; + else if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else + fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; + + uid = (uid_t)-1; + gid = (gid_t)-1; + if (ischown) { + unix2003_compat = COMPAT_MODE("bin/chown", "Unix2003"); + if ((cp = strchr(*argv, ':')) != NULL) { + *cp++ = '\0'; + a_gid(cp); + } +#ifdef SUPPORT_DOT + else if ((cp = strchr(*argv, '.')) != NULL) { + warnx("separation of user and group with a period is deprecated"); + *cp++ = '\0'; + a_gid(cp); + } +#endif + a_uid(*argv); + } else { + unix2003_compat = COMPAT_MODE("bin/chgrp", "Unix2003"); + a_gid(*argv); + } + + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) { + err(1, NULL); + } + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + symlink_found = 0; + switch (p->fts_info) { + case FTS_D: /* Change it at FTS_DP. */ + if (!Rflag) + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chown. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + if (hflag) + break; + else { + symlink_found = 1; + if (unix2003_compat) { + if (Hflag || Lflag) { /* -H or -L was specified */ + if (p->fts_errno) { + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + rval = 1; + continue; + } + } + break; /* Otherwise symlinks keep going */ + } + continue; + } + default: + break; + } + if (unix2003_compat) { + /* Can only avoid updating times if both uid and gid are -1 */ + if ((uid == (uid_t)-1) && (gid == (gid_t)-1)) + continue; + } else { + if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && + (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) + continue; + } + if (((hflag || symlink_found) ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { + if (!fflag) { + chownerr(p->fts_path); + rval = 1; + } + } else { + if (vflag) + fprintf(thread_stdout, "%s\n", p->fts_path); + } + } + if (errno) { + err(1, "fts_read"); + } + exit(rval); +} + +void +a_gid(const char *s) +{ + struct group *gr; + + if (*s == '\0') /* Argument was "uid[:.]". */ + return; + gname = s; + gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); +} + +void +a_uid(const char *s) +{ + struct passwd *pw; + + if (*s == '\0') /* Argument was "[:.]gid". */ + return; + uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); +} + +static uid_t +id(const char *name, const char *type) +{ + unsigned long val; + char *ep; + + errno = 0; + val = strtoul(name, &ep, 10); + if (errno || *ep != '\0' || val > UID_MAX) { + errx(1, "%s: illegal %s name", name, type); + } + return (uid_t)val; +} + +void +chownerr(const char *file) +{ + static uid_t euid = -1; + static int ngroups = -1; + gid_t groups[NGROUPS_MAX]; + + /* Check for chown without being root. */ + if (errno != EPERM || (uid != (uid_t)-1 && + euid == (uid_t)-1 && (euid = geteuid()) != 0)) { + warn("%s", file); + return; + } + + /* Check group membership; kernel just returns EPERM. */ + if (gid != (gid_t)-1 && ngroups == -1 && + euid == (uid_t)-1 && (euid = geteuid()) != 0) { + ngroups = getgroups(NGROUPS_MAX, groups); + while (--ngroups >= 0 && gid != groups[ngroups]); + if (ngroups < 0) { + warnx("you are not a member of group %s", gname); + return; + } + } + warn("%s", file); +} + +void +usage(void) +{ + + if (ischown) + (void)fprintf(stderr, "%s\n%s\n", + "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]" + " file ...", + " chown [-fhv] [-R [-H | -L | -P]] :group file ..."); + else + (void)fprintf(stderr, "%s\n", + "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ..."); + exit(1); +} diff --git a/file_cmds/cksum/cksum.1 b/file_cmds/cksum/cksum.1 new file mode 100644 index 00000000..b4161fee --- /dev/null +++ b/file_cmds/cksum/cksum.1 @@ -0,0 +1,182 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cksum.1 8.2 (Berkeley) 4/28/95 +.\" $FreeBSD: src/usr.bin/cksum/cksum.1,v 1.19 2005/01/17 07:44:13 ru Exp $ +.\" +.Dd April 28, 1995 +.Dt CKSUM 1 +.Os +.Sh NAME +.Nm cksum , +.Nm sum +.Nd display file checksums and block counts +.Sh SYNOPSIS +.Nm +.Op Fl o Ar 1 | 2 | 3 +.Op Ar +.Nm sum +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility writes to the standard output three whitespace separated +fields for each input file. +These fields are a checksum +.Tn CRC , +the total number of octets in the file and the file name. +If no file name is specified, the standard input is used and no file name +is written. +.Pp +The +.Nm sum +utility is identical to the +.Nm +utility, except that it defaults to using historic algorithm 1, as +described below. +It is provided for compatibility only. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl o +Use historic algorithms instead of the (superior) default one. +.Pp +Algorithm 1 is the algorithm used by historic +.Bx +systems as the +.Xr sum 1 +algorithm and by historic +.At V +systems as the +.Xr sum 1 +algorithm when using the +.Fl r +option. +This is a 16-bit checksum, with a right rotation before each addition; +overflow is discarded. +.Pp +Algorithm 2 is the algorithm used by historic +.At V +systems as the +default +.Xr sum 1 +algorithm. +This is a 32-bit checksum, and is defined as follows: +.Bd -unfilled -offset indent +s = sum of all bytes; +r = s % 2^16 + (s % 2^32) / 2^16; +cksum = (r % 2^16) + r / 2^16; +.Ed +.Pp +Algorithm 3 is what is commonly called the +.Ql 32bit CRC +algorithm. +This is a 32-bit checksum. +.Pp +Both algorithm 1 and 2 write to the standard output the same fields as +the default algorithm except that the size of the file in bytes is +replaced with the size of the file in blocks. +For historic reasons, the block size is 1024 for algorithm 1 and 512 +for algorithm 2. +Partial blocks are rounded up. +.El +.Pp +The default +.Tn CRC +used is based on the polynomial used for +.Tn CRC +error checking +in the networking standard +.St -iso8802-3 . +The +.Tn CRC +checksum encoding is defined by the generating polynomial: +.Pp +.Bd -unfilled -offset indent +G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 +.Ed +.Pp +Mathematically, the +.Tn CRC +value corresponding to a given file is defined by +the following procedure: +.Bd -ragged -offset indent +The +.Ar n +bits to be evaluated are considered to be the coefficients of a mod 2 +polynomial M(x) of degree +.Ar n Ns \-1 . +These +.Ar n +bits are the bits from the file, with the most significant bit being the most +significant bit of the first octet of the file and the last bit being the least +significant bit of the last octet, padded with zero bits (if necessary) to +achieve an integral number of octets, followed by one or more octets +representing the length of the file as a binary value, least significant octet +first. +The smallest number of octets capable of representing this integer are used. +.Pp +M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by +G(x) using mod 2 division, producing a remainder R(x) of degree <= 31. +.Pp +The coefficients of R(x) are considered to be a 32-bit sequence. +.Pp +The bit sequence is complemented and the result is the CRC. +.Ed +.Sh EXIT STATUS +.Ex -std cksum sum +.Sh SEE ALSO +.Xr md5 1 +.Pp +The default calculation is identical to that given in pseudo-code +in the following +.Tn ACM +article. +.Rs +.%T "Computation of Cyclic Redundancy Checks Via Table Lookup" +.%A Dilip V. Sarwate +.%J "Communications of the" Tn ACM +.%D "August 1988" +.Re +.Sh STANDARDS +The +.Nm +utility is expected to conform to +.St -p1003.2-92 . +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.4 . diff --git a/file_cmds/cksum/cksum.c b/file_cmds/cksum/cksum.c new file mode 100644 index 00000000..82015913 --- /dev/null +++ b/file_cmds/cksum/cksum.c @@ -0,0 +1,151 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/cksum.c,v 1.17 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" +#include +#include "ios_error.h" // remap exit to pthread_exit + +static void usage(void); + +int +chksum_main(int argc, char **argv) +{ + uint32_t val; + int ch, fd, rval; + off_t len; + char *fn, *p; + int (*cfncn)(int, uint32_t *, off_t *); + void (*pfncn)(char *, u_int32_t, off_t); + + cfncn=NULL; + optind = 1; opterr = 1; optreset = 1; + + if(*argv) { + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (!strcmp(p, "sum")) { + cfncn = csum1; + pfncn = psum1; + ++argv; + } + } + + if(!cfncn) { + cfncn = crc; + pfncn = pcrc; + + while ((ch = getopt(argc, argv, "o:")) != -1) + switch (ch) { + case 'o': + if (!strcmp(optarg, "1")) { + cfncn = csum1; + pfncn = psum1; + } else if (!strcmp(optarg, "2")) { + cfncn = csum2; + pfncn = psum2; + } else if (!strcmp(optarg, "3")) { + cfncn = chksum_crc32; + pfncn = pcrc; + } else { + warnx("illegal argument to -o option"); + usage(); + } + break; + case '?': + default: + usage(); + } +// argc -= optind; + argv += optind; + } + + fd = fileno(thread_stdin); + fn = NULL; + rval = 0; + do { + if (*argv) { + fn = *argv++; + if ((fd = open(fn, O_RDONLY, 0)) < 0) { + warn("%s", fn); + rval = 1; + continue; + } + } + if (cfncn(fd, &val, &len)) { + warn("%s", fn ? fn : "stdin"); + rval = 1; + } else + pfncn(fn, val, len); + (void)close(fd); + } while (*argv); + exit(rval); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: cksum [-o 1 | 2 | 3] [file ...]\n"); + (void)fprintf(stderr, " sum [file ...]\n"); + exit(1); +} diff --git a/file_cmds/cksum/crc.c b/file_cmds/cksum/crc.c new file mode 100644 index 00000000..57179d5d --- /dev/null +++ b/file_cmds/cksum/crc.c @@ -0,0 +1,148 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/crc.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +static const uint32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. + */ +uint32_t crc_total = ~0; /* The crc over a number of files. */ + +int +crc(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc; + ssize_t nr; + off_t len; + u_char *p; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + lcrc = 0; + len = 0; + crc_total = ~crc_total; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(lcrc, *p); + COMPUTE(crc_total, *p); + } + if (nr < 0) + return (1); + + *clen = len; + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(lcrc, len & 0xff); + COMPUTE(crc_total, len & 0xff); + } + + *cval = ~lcrc; + crc_total = ~crc_total; + return (0); +} diff --git a/file_cmds/cksum/crc32.c b/file_cmds/cksum/crc32.c new file mode 100644 index 00000000..0dfc89f5 --- /dev/null +++ b/file_cmds/cksum/crc32.c @@ -0,0 +1,122 @@ +/* + * This code implements the AUTODIN II polynomial used by Ethernet, + * and can be used to calculate multicast address hash indices. + * It assumes that the low order bits will be transmitted first, + * and consequently the low byte should be sent first when + * the crc computation is finished. The crc should be complemented + * before transmission. + * The variable corresponding to the macro argument "crc" should + * be an unsigned long and should be preset to all ones for Ethernet + * use. An error-free packet will leave 0xDEBB20E3 in the crc. + * Spencer Garrett + */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/crc32.c,v 1.9 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include +#include + +#include "extern.h" + +#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff]) + +/* generated using the AUTODIN II polynomial + * x^32 + x^26 + x^23 + x^22 + x^16 + + * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 + */ +static const uint32_t crctab[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, + 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, + 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, + 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, + 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, + 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, + 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, + 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, + 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, + 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, + 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, + 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, + 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, + 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, + 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, + 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, + 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, + 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, + 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, + 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, + 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, + 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, + 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, + 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, + 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, + 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, + 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, + 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, + 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, + 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, + 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, + 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, + 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, + 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, + 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, + 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, + 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, + 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, + 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, + 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, + 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, + 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, + 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, + 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, + 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, + 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, + 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, +}; + +uint32_t crc32_total = 0; + +int +chksum_crc32(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc = ~0; + ssize_t nr; + off_t len ; + char buf[BUFSIZ], *p ; + + len = 0 ; + crc32_total = ~crc32_total ; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + CRC(lcrc, *p) ; + CRC(crc32_total, *p) ; + } + if (nr < 0) + return 1 ; + + *clen = len ; + *cval = ~lcrc ; + crc32_total = ~crc32_total ; + return 0 ; +} diff --git a/file_cmds/cksum/extern.h b/file_cmds/cksum/extern.h new file mode 100644 index 00000000..addf8b48 --- /dev/null +++ b/file_cmds/cksum/extern.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/cksum/extern.h,v 1.6 2003/03/13 23:32:28 robert Exp $ + */ + +#include + +__BEGIN_DECLS +int crc(int, uint32_t *, off_t *); +void pcrc(char *, uint32_t, off_t); +void psum1(char *, uint32_t, off_t); +void psum2(char *, uint32_t, off_t); +int csum1(int, uint32_t *, off_t *); +int csum2(int, uint32_t *, off_t *); +int chksum_crc32(int, uint32_t *, off_t *); +__END_DECLS diff --git a/file_cmds/cksum/print.c b/file_cmds/cksum/print.c new file mode 100644 index 00000000..8b9c99c5 --- /dev/null +++ b/file_cmds/cksum/print.c @@ -0,0 +1,76 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/print.c,v 1.7 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" +#include "ios_error.h" + +void +pcrc(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)len); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} + +void +psum1(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)(len + 1023) / 1024); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} + +void +psum2(char *fn, uint32_t val, off_t len) +{ + (void)fprintf(thread_stdout, "%lu %jd", (u_long)val, (intmax_t)(len + 511) / 512); + if (fn != NULL) + (void)fprintf(thread_stdout, " %s", fn); + (void)fprintf(thread_stdout, "\n"); +} diff --git a/file_cmds/cksum/sum.1 b/file_cmds/cksum/sum.1 new file mode 100644 index 00000000..db048008 --- /dev/null +++ b/file_cmds/cksum/sum.1 @@ -0,0 +1 @@ +.so man1/cksum.1 diff --git a/file_cmds/cksum/sum1.c b/file_cmds/cksum/sum1.c new file mode 100644 index 00000000..fa016ce2 --- /dev/null +++ b/file_cmds/cksum/sum1.c @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/sum1.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +int +csum1(int fd, uint32_t *cval, off_t *clen) +{ + ssize_t nr; + u_int lcrc; + off_t total; + u_char *p; + u_char buf[8192]; + + /* + * 16-bit checksum, rotating right before each addition; + * overflow is discarded. + */ + lcrc = 0; + total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) { + if (lcrc & 1) + lcrc |= 0x10000; + lcrc = ((lcrc >> 1) + *p) & 0xffff; + } + if (nr < 0) + return (1); + + *cval = lcrc; + *clen = total; + return (0); +} diff --git a/file_cmds/cksum/sum2.c b/file_cmds/cksum/sum2.c new file mode 100644 index 00000000..126b4280 --- /dev/null +++ b/file_cmds/cksum/sum2.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/cksum/sum2.c,v 1.8 2003/03/13 23:32:28 robert Exp $"); + +#include + +#include +#include + +#include "extern.h" + +int +csum2(int fd, uint32_t *cval, off_t *clen) +{ + uint32_t lcrc; + ssize_t nr; + off_t total; + u_char *p; + u_char buf[8192]; + + /* + * Draft 8 POSIX 1003.2: + * + * s = sum of all bytes + * r = s % 2^16 + (s % 2^32) / 2^16 + * lcrc = (r % 2^16) + r / 2^16 + */ + lcrc = 0; + total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) + lcrc += *p; + if (nr < 0) + return (1); + + lcrc = (lcrc & 0xffff) + (lcrc >> 16); + lcrc = (lcrc & 0xffff) + (lcrc >> 16); + + *cval = lcrc; + *clen = total; + return (0); +} diff --git a/file_cmds/compress/compress.1 b/file_cmds/compress/compress.1 new file mode 100644 index 00000000..a353c19a --- /dev/null +++ b/file_cmds/compress/compress.1 @@ -0,0 +1,253 @@ +.\" Copyright (c) 1986, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" James A. Woods, derived from original work by Spencer Thomas +.\" and Joseph Orost. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)compress.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/compress/compress.1,v 1.23 2010/12/11 08:32:16 joel Exp $ +.\" +.Dd May 17, 2002 +.Dt COMPRESS 1 +.Os +.Sh NAME +.Nm compress , +.Nm uncompress +.Nd compress and expand data +.Sh SYNOPSIS +.Nm +.Op Fl fv +.Op Fl b Ar bits +.Op Ar +.Nm +.Fl c +.Op Fl b Ar bits +.Op Ar file +.Nm uncompress +.Op Fl fv +.Op Ar +.Nm uncompress +.Fl c +.Op Ar file +.Sh DESCRIPTION +The +.Nm +utility reduces the size of files using adaptive Lempel-Ziv coding. +Each +.Ar file +is renamed to the same name plus the extension +.Pa .Z . +A +.Ar file +argument with a +.Pa .Z +extension will be ignored except it will cause an +error exit after other arguments are processed. +If compression would not reduce the size of a +.Ar file , +the file is ignored. +.Pp +The +.Nm uncompress +utility restores compressed files to their original form, renaming the +files by deleting the +.Pa .Z +extensions. +A file specification need not include the file's +.Pa .Z +extension. +If a file's name in its file system does not have a +.Pa .Z +extension, it will not be uncompressed and it will cause +an error exit after other arguments are processed. +.Pp +If renaming the files would cause files to be overwritten and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +If prompting is not possible or confirmation is not received, the files +are not overwritten. +.Pp +As many of the modification time, access time, file flags, file mode, +user ID, and group ID as allowed by permissions are retained in the +new file. +.Pp +If no files are specified or a +.Ar file +argument is a single dash +.Pq Sq Fl , +the standard input is compressed or uncompressed to the standard output. +If either the input and output files are not regular files, the checks for +reduction in size and file overwriting are not performed, the input file is +not removed, and the attributes of the input file are not retained +in the output file. +.Pp +The options are as follows: +.Bl -tag -width ".Fl b Ar bits" +.It Fl b Ar bits +The code size (see below) is limited to +.Ar bits , +which must be in the range 9..16. +The default is 16. +.It Fl c +Compressed or uncompressed output is written to the standard output. +No files are modified. +The +.Fl v +option is ignored. +Compression is attempted even if the results will be larger than the +original. +.It Fl f +Files are overwritten without prompting for confirmation. +Also, for +.Nm compress , +files are compressed even if they are not actually reduced in size. +.It Fl v +Print the percentage reduction of each file. +Ignored by +.Nm uncompress +or if the +.Fl c +option is also used. +.El +.Pp +The +.Nm +utility uses a modified Lempel-Ziv algorithm. +Common substrings in the file are first replaced by 9-bit codes 257 and up. +When code 512 is reached, the algorithm switches to 10-bit codes and +continues to use more bits until the +limit specified by the +.Fl b +option or its default is reached. +.Pp +After the limit is reached, +.Nm +periodically checks the compression ratio. +If it is increasing, +.Nm +continues to use the existing code dictionary. +However, if the compression ratio decreases, +.Nm +discards the table of substrings and rebuilds it from scratch. +This allows +the algorithm to adapt to the next "block" of the file. +.Pp +The +.Fl b +option is unavailable for +.Nm uncompress +since the +.Ar bits +parameter specified during compression +is encoded within the output, along with +a magic number to ensure that neither decompression of random data nor +recompression of compressed data is attempted. +.Pp +The amount of compression obtained depends on the size of the +input, the number of +.Ar bits +per code, and the distribution of common substrings. +Typically, text such as source code or English is reduced by 50\-60%. +Compression is generally much better than that achieved by Huffman +coding (as used in the historical command pack), or adaptive Huffman +coding (as used in the historical command compact), and takes less +time to compute. +.Sh EXIT STATUS +.Ex -std compress uncompress +.Pp +The +.Nm compress +utility exits 2 if attempting to compress a file would not reduce its size +and the +.Fl f +option was not specified and if no other error occurs. +.Sh SEE ALSO +.Xr gunzip 1 , +.Xr gzexe 1 , +.Xr gzip 1 , +.Xr zcat 1 , +.Xr zmore 1 , +.Xr znew 1 +.Rs +.%A Welch, Terry A. +.%D June, 1984 +.%T "A Technique for High Performance Data Compression" +.%J "IEEE Computer" +.%V 17:6 +.%P pp. 8-19 +.Re +.Sh STANDARDS +The +.Nm compress +and +.Nm uncompress +utilities conform to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . +.Sh BUGS +Some of these might be considered otherwise-undocumented features. +.Pp +.Nm compress : +If the utility does not compress a file because doing so would not +reduce its size, and a file of the same name except with an +.Pa .Z +extension exists, the named file is not really ignored as stated above; +it causes a prompt to confirm the overwriting of the file with the extension. +If the operation is confirmed, that file is deleted. +.Pp +.Nm uncompress : +If an empty file is compressed (using +.Fl f ) , +the resulting +.Pa .Z +file is also empty. +That seems right, but if +.Nm uncompress +is then used on that file, an error will occur. +.Pp +Both utilities: If a +.Sq Fl +argument is used and the utility prompts the user, the standard input +is taken as the user's reply to the prompt. +.Pp +Both utilities: +If the specified file does not exist, but a similarly-named one with (for +.Nm compress ) +or without (for +.Nm uncompress ) +a +.Pa .Z +extension does exist, the utility will waste the user's time by not +immediately emitting an error message about the missing file and +continuing. +Instead, it first asks for confirmation to overwrite +the existing file and then does not overwrite it. diff --git a/file_cmds/compress/compress.c b/file_cmds/compress/compress.c new file mode 100644 index 00000000..e83cd1a9 --- /dev/null +++ b/file_cmds/compress/compress.c @@ -0,0 +1,465 @@ +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; +#endif +#endif + +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.23 2010/12/11 08:32:16 joel Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "zopen.h" +#include "ios_error.h" + +static void compress(const char *, const char *, int); +static void cwarn(const char *, ...) __printflike(1, 2); +static void cwarnx(const char *, ...) __printflike(1, 2); +static void decompress(const char *, const char *, int); +static int permission(const char *); +static void setfile(const char *, struct stat *); +static void usage(int); + +static int eval, force, verbose, cat; + +int +compress_main(int argc, char *argv[]) +{ + enum {COMPRESS, DECOMPRESS} style = 0; + size_t len; + int bits, ch; + char *p, newname[MAXPATHLEN]; + + if (argc < 1) + usage(1); + // init all flags + eval = force = verbose = cat = 0; + optind = 1; opterr = 1; optreset = 1; + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (!strcmp(p, "uncompress")) + style = DECOMPRESS; + else if (!strcmp(p, "compress")) + style = COMPRESS; + else if (!strcmp(p, "zcat")) { + cat = 1; + style = DECOMPRESS; + } else { + // errx(1, "unknown program name"); + fprintf(thread_stderr, "unknown program name: %s\n", p); + ios_exit(1); + } + + bits = 0; + while ((ch = getopt(argc, argv, "b:cdfv")) != -1) + switch(ch) { + case 'b': + bits = strtol(optarg, &p, 10); + if (*p) { + errx(1, "illegal bit count -- %s", optarg); + } + break; + case 'c': + cat = 1; + break; + case 'd': /* Backward compatible. */ + style = DECOMPRESS; + break; + case 'f': + force = 1; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(style == COMPRESS); + } + argc -= optind; + argv += optind; + + if (argc == 0) { + cat = 1; + switch(style) { + case COMPRESS: + (void)compress("/dev/stdin", "/dev/stdout", bits); + break; + case DECOMPRESS: + (void)decompress("/dev/stdin", "/dev/stdout", bits); + break; + } + exit (eval); + } + + if (cat == 1 && argc > 1) { + errx(1, "the -c option permits only a single file argument"); + } + + for (; *argv; ++argv) + switch(style) { + case COMPRESS: + if (strcmp(*argv, "-") == 0) { + cat = 1; + compress("/dev/stdin", "/dev/stdout", bits); + break; + } else if (cat) { + compress(*argv, "/dev/stdout", bits); + break; + } + if ((p = rindex(*argv, '.')) != NULL && + !strcmp(p, ".Z")) { + cwarnx("%s: name already has trailing .Z", + *argv); + break; + } + len = strlen(*argv); + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + compress(*argv, newname, bits); + break; + case DECOMPRESS: + if (strcmp(*argv, "-") == 0) { + cat = 1; + decompress("/dev/stdin", "/dev/stdout", bits); + break; + } + len = strlen(*argv); + if ((p = rindex(*argv, '.')) == NULL || + strcmp(p, ".Z")) { + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + decompress(newname, + cat ? "/dev/stdout" : *argv, bits); + } else { + if (len - 2 > sizeof(newname) - 1) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len - 2); + newname[len - 2] = '\0'; + decompress(*argv, + cat ? "/dev/stdout" : newname, bits); + } + break; + } + exit (eval); +} + +void +compress(const char *in, const char *out, int bits) +{ + size_t nr; + struct stat isb, sb; + FILE *ifp = NULL, *ofp = NULL; + int exists, isreg, oreg; + u_char buf[1024]; + + exists = !stat(out, &sb); + if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) { + cwarnx("%s already exists", out); + return; + } + isreg = oreg = !exists || S_ISREG(sb.st_mode); + + if ((ifp = fopen(in, "r")) == NULL) { + cwarn("%s", in); + return; + } + if (stat(in, &isb)) { /* DON'T FSTAT! */ + cwarn("%s", in); + goto err; + } + if (!S_ISREG(isb.st_mode)) + isreg = 0; + + if ((ofp = zopen(out, "w", bits)) == NULL) { + cwarn("%s", out); + goto err; + } + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + ofp = NULL; + + if (!cat && isreg) { + if (stat(out, &sb)) { + cwarn("%s", out); + goto err; + } + + if (!force && sb.st_size >= isb.st_size) { + if (verbose) + (void)fprintf(stderr, "compress: %s: file would grow; left unmodified\n", + in); + eval = 2; + if (unlink(out)) + cwarn("%s", out); + goto err; + } + + setfile(out, &isb); + + if (unlink(in)) + cwarn("%s", in); + + if (verbose) { + (void)fprintf(stderr, "%s: ", out); + if (isb.st_size > sb.st_size) + (void)fprintf(stderr, "%.0f%% compression\n", + ((float)sb.st_size / isb.st_size) * 100.0); + else + (void)fprintf(stderr, "%.0f%% expansion\n", + ((float)isb.st_size / sb.st_size) * 100.0); + } + } + return; + +err: if (ofp) { + if (!cat && oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +decompress(const char *in, const char *out, int bits) +{ + size_t nr; + struct stat sb; + FILE *ifp, *ofp; + int exists, isreg, oreg; + u_char buf[1024]; + + exists = !stat(out, &sb); + if (!force && exists && S_ISREG(sb.st_mode) && !cat && !permission(out)) { + cwarnx("%s already exists", out); + return; + } + isreg = oreg = !exists || S_ISREG(sb.st_mode); + + ofp = NULL; + if ((ifp = zopen(in, "r", bits)) == NULL) { + cwarn("%s", in); + return; + } + if (stat(in, &sb)) { + cwarn("%s", in); + goto err; + } + if (!S_ISREG(sb.st_mode)) + isreg = 0; + + /* + * Try to read the first few uncompressed bytes from the input file + * before blindly truncating the output file. + */ + if ((nr = fread(buf, 1, sizeof(buf), ifp)) == 0) { + cwarn("%s", in); + (void)fclose(ifp); + return; + } + if ((ofp = fopen(out, "w")) == NULL || + (nr != 0 && fwrite(buf, 1, nr, ofp) != nr)) { + cwarn("%s", out); + (void)fclose(ifp); + return; + } + + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + + if (!cat && isreg) { + setfile(out, &sb); + + if (unlink(in)) + cwarn("%s", in); + if (verbose) { + struct stat isb = sb; + stat(out, &sb); + (void)fprintf(stderr, "%s: ", out); + if (isb.st_size > sb.st_size) + (void)fprintf(stderr, "%.0f%% compression\n", + ((float)sb.st_size / isb.st_size) * 100.0); + else + (void)fprintf(stderr, "%.0f%% expansion\n", + ((float)isb.st_size / sb.st_size) * 100.0); + } + } + return; + +err: if (ofp) { + if (!cat && oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +setfile(const char *name, struct stat *fs) +{ + static struct timeval tv[2]; + + fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (utimes(name, tv)) + cwarn("utimes: %s", name); + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (chown(name, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) + cwarn("chown: %s", name); + fs->st_mode &= ~(S_ISUID|S_ISGID); + } + if (chmod(name, fs->st_mode) && errno != ENOTSUP) + cwarn("chmod: %s", name); + + if (chflags(name, fs->st_flags) && errno != ENOTSUP) + cwarn("chflags: %s", name); +} + +int +permission(const char *fname) +{ + int ch, first; + + if (!ios_isatty(STDERR_FILENO)) + return (0); + (void)fprintf(stderr, "overwrite %s? ", fname); + fflush(stderr); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y'); +} + +void +usage(int iscompress) +{ + if (iscompress) + (void)fprintf(stderr, + "usage: compress [-cfv] [-b bits] [file ...]\n"); + else + (void)fprintf(stderr, + "usage: uncompress [-cfv] [-b bits] [file ...]\n"); + exit(1); +} + +void +cwarnx(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + eval = 1; +} + +void +cwarn(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + eval = 1; +} diff --git a/file_cmds/compress/doc/NOTES b/file_cmds/compress/doc/NOTES new file mode 100644 index 00000000..4ced6896 --- /dev/null +++ b/file_cmds/compress/doc/NOTES @@ -0,0 +1,142 @@ + + $FreeBSD: src/usr.bin/compress/doc/NOTES,v 1.3 2011/10/16 14:30:28 eadler Exp $ + +From: James A. Woods + +>From vn Fri Dec 2 18:05:27 1988 +Subject: Re: Looking for C source for RSA +Newsgroups: sci.crypt + +# Illegitimi noncarborundum + +Patents are a tar pit. + +A good case can be made that most are just a license to sue, and nothing +is illegal until a patent is upheld in court. + +For example, if you receive netnews by means other than 'nntp', +these very words are being modulated by 'compress', +a variation on the patented Lempel-Ziv-Welch algorithm. + +Original Ziv-Lempel is patent number 4,464,650, and the more powerful +LZW method is #4,558,302. Yet despite any similarities between 'compress' +and LZW (the public-domain 'compress' code was designed and given to the +world before the ink on the Welch patent was dry), no attorneys from Sperry +(the assignee) have asked you to unplug your Usenet connection. + +Why? I can't speak for them, but it is possible the claims are too broad, +or, just as bad, not broad enough. ('compress' does things not mentioned +in the Welch patent.) Maybe they realize that they can commercialize +LZW better by selling hardware implementations rather than by licensing +software. Again, the LZW software delineated in the patent is *not* +the same as that of 'compress'. + +At any rate, court-tested software patents are a different animal; +corporate patents in a portfolio are usually traded like baseball cards +to shut out small fry rather than actually be defended before +non-technical juries. Perhaps RSA will undergo this test successfully, +although the grant to "exclude others from making, using, or selling" +the invention would then only apply to the U.S. (witness the +Genentech patent of the TPA molecule in the U.S. but struck down +in Great Britain as too broad.) + +The concept is still exotic for those who learned in school the rule of thumb +that one may patent "apparatus" but not an "idea". +Apparently this all changed in Diamond v. Diehr (1981) when the U. S. Supreme +Court reversed itself. + +Scholars should consult the excellent article in the Washington and Lee +Law Review (fall 1984, vol. 41, no. 4) by Anthony and Colwell for a +comprehensive survey of an area which will remain murky for some time. + +Until the dust clears, how you approach ideas which are patented depends +on how paranoid you are of a legal onslaught. Arbitrary? Yes. But +the patent bar the CCPA (Court of Customs and Patent Appeals) +thanks you for any uncertainty as they, at least, stand to gain +from any trouble. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +From: James A. Woods +Subject: Re: Looking for C source for RSA (actually 'compress' patents) + + In article <2042@eos.UUCP> you write: + >The concept is still exotic for those who learned in school the rule of thumb + >that one may patent "apparatus" but not an "idea". + +A rule of thumb that has never been completely valid, as any chemical +engineer can tell you. (Chemical processes were among the earliest patents, +as I recall.) + + ah yes -- i date myself when relaying out-of-date advice from elderly + attorneys who don't even specialize in patents. one other interesting + class of patents include the output of optical lens design programs, + which yield formulae which can then fairly directly can be molded + into glass. although there are restrictions on patenting equations, + the "embedded systems" seem to fly past the legal gauntlets. + + anyway, I'm still learning about intellectual property law after + several conversations from a Unisys (nee sperry) lawyer re 'compress'. + + it's more complicated than this, but they're letting (oral + communication only) software versions of 'compress' slide + as far as licensing fees go. this includes 'arc', 'stuffit', + and other commercial wrappers for 'compress'. yet they are + signing up licensees for hardware chips. Hewlett-Packard + supposedly has an active vlsi project, and Unisys has + board-level LZW-based tape controllers. (to build LZW into + a disk controller would be strange, as you'd have to build + in a filesystem too!) + + it's byzantine + that Unisys is in a tiff with HP regarding the patents, + after discovering some sort of "compress" button on some + HP terminal product. why? well, professor Abraham Lempel jumped + from being department chairman of computer science at technion in + Israel to sperry (where he got the first patent), but then to work + at Hewlett-Packard on sabbatical. the second Welch patent + is only weakly derivative of the first, so they want chip + licenses and HP relented. however, everyone agrees something + like the current Unix implementation is the way to go with + software, so HP (and UCB) long ago asked spencer Thomas and i to sign + off on copyright permission (although they didn't need to, it being pd). + Lempel, HP, and Unisys grumbles they can't make money off the + software since a good free implementation (not the best -- + i have more ideas!) escaped via Usenet. (Lempel's own pascal + code was apparently horribly slow.) + i don't follow the IBM 'arc' legal bickering; my impression + is that the pc folks are making money off the archiver/wrapper + look/feel of the thing [if ms-dos can be said to have a look and feel]. + + now where is telebit with the compress firmware? in a limbo + netherworld, probably, with sperry still welcoming outfits + to sign patent licenses, a common tactic to bring other small fry + into the fold. the guy who crammed 12-bit compress into the modem + there left. also what is transpiring with 'compress' and sys 5 rel 4? + beats me, but if sperry got a hold of them on these issues, + at&t would likely re-implement another algorithm if they + thought 'compress' infringes. needful to say, i don't think + it does after the above mentioned legal conversation. + my own beliefs on whether algorithms should be patentable at all + change with the weather. if the courts finally nail down + patent protection for algorithms, academic publication in + textbooks will be somewhat at odds with the engineering world, + where the textbook codes will simply be a big tease to get + money into the patent holder coffers... + + oh, if you implement LZW from the patent, you won't get + good rates because it doesn't mention adaptive table reset, + lack thereof being *the* serious deficiency of Thomas' first version. + + now i know that patent law generally protects against independent + re-invention (like the 'xor' hash function pleasantly mentioned + in the patent [but not the paper]). + but the upshot is that if anyone ever wanted to sue us, + we're partially covered with + independently-developed twists, plus the fact that some of us work + in a bureaucratic morass (as contractor to a public agency in my case). + + quite a mess, huh? I've wanted to tell someone this stuff + for a long time, for posterity if nothing else. + +james + diff --git a/file_cmds/compress/doc/README b/file_cmds/compress/doc/README new file mode 100644 index 00000000..0828cdfb --- /dev/null +++ b/file_cmds/compress/doc/README @@ -0,0 +1,284 @@ + + @(#)README 8.1 (Berkeley) 6/9/93 + $FreeBSD: src/usr.bin/compress/doc/README,v 1.3 2002/12/30 21:18:11 schweikh Exp $ + +Compress version 4.0 improvements over 3.0: + o compress() speedup (10-50%) by changing division hash to xor + o decompress() speedup (5-10%) + o Memory requirements reduced (3-30%) + o Stack requirements reduced to less than 4kb + o Removed 'Big+Fast' compress code (FBITS) because of compress speedup + o Portability mods for Z8000 and PC/XT (but not zeus 3.2) + o Default to 'quiet' mode + o Unification of 'force' flags + o Manual page overhaul + o Portability enhancement for M_XENIX + o Removed text on #else and #endif + o Added "-V" switch to print version and options + o Added #defines for SIGNED_COMPARE_SLOW + o Added Makefile and "usermem" program + o Removed all floating point computations + o New programs: [deleted] + +The "usermem" script attempts to determine the maximum process size. Some +editing of the script may be necessary (see the comments). [It should work +fine on 4.3 BSD.] If you can't get it to work at all, just create file +"USERMEM" containing the maximum process size in decimal. + +The following preprocessor symbols control the compilation of "compress.c": + + o USERMEM Maximum process memory on the system + o SACREDMEM Amount to reserve for other processes + o SIGNED_COMPARE_SLOW Unsigned compare instructions are faster + o NO_UCHAR Don't use "unsigned char" types + o BITS Overrules default set by USERMEM-SACREDMEM + o vax Generate inline assembler + o interdata Defines SIGNED_COMPARE_SLOW + o M_XENIX Makes arrays < 65536 bytes each + o pdp11 BITS=12, NO_UCHAR + o z8000 BITS=12 + o pcxt BITS=12 + o BSD4_2 Allow long filenames ( > 14 characters) & + Call setlinebuf(stderr) + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified with the "-b" flag. + +memory: at least BITS +------ -- ----- ---- + 433,484 16 + 229,600 15 + 127,536 14 + 73,464 13 + 0 12 + +The default is BITS=16. + +The maximum bits can be overruled by specifying "-DBITS=bits" at +compilation time. + +WARNING: files compressed on a large machine with more bits than allowed by +a version of compress on a smaller machine cannot be decompressed! Use the +"-b12" flag to generate a file on a large machine that can be uncompressed +on a 16-bit machine. + +The output of compress 4.0 is fully compatible with that of compress 3.0. +In other words, the output of compress 4.0 may be fed into uncompress 3.0 or +the output of compress 3.0 may be fed into uncompress 4.0. + +The output of compress 4.0 not compatible with that of +compress 2.0. However, compress 4.0 still accepts the output of +compress 2.0. To generate output that is compatible with compress +2.0, use the undocumented "-C" flag. + + -from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85 +-------------------------------- + +Enclosed is compress version 3.0 with the following changes: + +1. "Block" compression is performed. After the BITS run out, the + compression ratio is checked every so often. If it is decreasing, + the table is cleared and a new set of substrings are generated. + + This makes the output of compress 3.0 not compatible with that of + compress 2.0. However, compress 3.0 still accepts the output of + compress 2.0. To generate output that is compatible with compress + 2.0, use the undocumented "-C" flag. + +2. A quiet "-q" flag has been added for use by the news system. + +3. The character chaining has been deleted and the program now uses + hashing. This improves the speed of the program, especially + during decompression. Other speed improvements have been made, + such as using putc() instead of fwrite(). + +4. A large table is used on large machines when a relatively small + number of bits is specified. This saves much time when compressing + for a 16-bit machine on a 32-bit virtual machine. Note that the + speed improvement only occurs when the input file is > 30000 + characters, and the -b BITS is less than or equal to the cutoff + described below. + +Most of these changes were made by James A. Woods (ames!jaw). Thank you +James! + +To compile compress: + + cc -O -DUSERMEM=usermem -o compress compress.c + +Where "usermem" is the amount of physical user memory available (in bytes). +If any physical memory is to be reserved for other processes, put in +"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved. + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified, and the cutoff bits where the large+fast table is used. + +memory: at least BITS cutoff +------ -- ----- ---- ------ + 4,718,592 16 13 + 2,621,440 16 12 + 1,572,864 16 11 + 1,048,576 16 10 + 631,808 16 -- + 329,728 15 -- + 178,176 14 -- + 99,328 13 -- + 0 12 -- + +The default memory size is 750,000 which gives a maximum BITS=16 and no +large+fast table. + +The maximum bits can be overruled by specifying "-DBITS=bits" at +compilation time. + +If your machine doesn't support unsigned characters, define "NO_UCHAR" +when compiling. + +If your machine has "int" as 16-bits, define "SHORT_INT" when compiling. + +After compilation, move "compress" to a standard executable location, such +as /usr/local. Then: + cd /usr/local + ln compress uncompress + ln compress zcat + +On machines that have a fixed stack size (such as Perkin-Elmer), set the +stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). + +Next, install the manual (compress.l). + cp compress.l /usr/man/manl + cd /usr/man/manl + ln compress.l uncompress.l + ln compress.l zcat.l + + - or - + + cp compress.l /usr/man/man1/compress.1 + cd /usr/man/man1 + ln compress.1 uncompress.1 + ln compress.1 zcat.1 + + regards, + petsd!joe + +Here is a note from the net: + +>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985 +Path: ames!hplabs!pesnta!amd!turtlevax!ken +From: ken@turtlevax.UUCP (Ken Turkowski) +Newsgroups: net.sources +Subject: Re: Compress release 3.0 : sample Makefile +Organization: CADLINC, Inc. @ Menlo Park, CA + +In the compress 3.0 source recently posted to mod.sources, there is a +#define variable which can be set for optimum performance on a machine +with a large amount of memory. A program (usermem) to calculate the +usable amount of physical user memory is enclosed, as well as a sample +4.2BSD Vax Makefile for compress. + +Here is the README file from the previous version of compress (2.0): + +>Enclosed is compress.c version 2.0 with the following bugs fixed: +> +>1. The packed files produced by compress are different on different +> machines and dependent on the vax sysgen option. +> The bug was in the different byte/bit ordering on the +> various machines. This has been fixed. +> +> This version is NOT compatible with the original vax posting +> unless the '-DCOMPATIBLE' option is specified to the C +> compiler. The original posting has a bug which I fixed, +> causing incompatible files. I recommend you NOT to use this +> option unless you already have a lot of packed files from +> the original posting by Thomas. +>2. The exit status is not well defined (on some machines) causing the +> scripts to fail. +> The exit status is now 0,1 or 2 and is documented in +> compress.l. +>3. The function getopt() is not available in all C libraries. +> The function getopt() is no longer referenced by the +> program. +>4. Error status is not being checked on the fwrite() and fflush() calls. +> Fixed. +> +>The following enhancements have been made: +> +>1. Added facilities of "compact" into the compress program. "Pack", +> "Unpack", and "Pcat" are no longer required (no longer supplied). +>2. Installed work around for C compiler bug with "-O". +>3. Added a magic number header (\037\235). Put the bits specified +> in the file. +>4. Added "-f" flag to force overwrite of output file. +>5. Added "-c" flag and "zcat" program. 'ln compress zcat' after you +> compile. +>6. The 'uncompress' script has been deleted; simply +> 'ln compress uncompress' after you compile and it will work. +>7. Removed extra bit masking for machines that support unsigned +> characters. If your machine doesn't support unsigned characters, +> define "NO_UCHAR" when compiling. +> +>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a +>standard executable location, such as /usr/local. Then: +> cd /usr/local +> ln compress uncompress +> ln compress zcat +> +>On machines that have a fixed stack size (such as Perkin-Elmer), set the +>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). +> +>Next, install the manual (compress.l). +> cp compress.l /usr/man/manl - or - +> cp compress.l /usr/man/man1/compress.1 +> +>Here is the README that I sent with my first posting: +> +>>Enclosed is a modified version of compress.c, along with scripts to make it +>>run identically to pack(1), unpack(1), and pcat(1). Here is what I +>>(petsd!joe) and a colleague (petsd!peora!srd) did: +>> +>>1. Removed VAX dependencies. +>>2. Changed the struct to separate arrays; saves mucho memory. +>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.) +>>4. Sorted the character next chain and changed the search to stop +>>prematurely. This saves a lot on the execution time when compressing. +>> +>>This version is totally compatible with the original version. Even though +>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit +>>machine, due to the size of the arrays. +>> +>>Here is the README file from the original author: +>> +>>>Well, with all this discussion about file compression (for news batching +>>>in particular) going around, I decided to implement the text compression +>>>algorithm described in the June Computer magazine. The author claimed +>>>blinding speed and good compression ratios. It's certainly faster than +>>>compact (but, then, what wouldn't be), but it's also the same speed as +>>>pack, and gets better compression than both of them. On 350K bytes of +>>>Unix-wizards, compact took about 8 minutes of CPU, pack took about 80 +>>>seconds, and compress (herein) also took 80 seconds. But, compact and +>>>pack got about 30% compression, whereas compress got over 50%. So, I +>>>decided I had something, and that others might be interested, too. +>>> +>>>As is probably true of compact and pack (although I haven't checked), +>>>the byte order within a word is probably relevant here, but as long as +>>>you stay on a single machine type, you should be ok. (Can anybody +>>>elucidate on this?) There are a couple of asm's in the code (extv and +>>>insv instructions), so anyone porting it to another machine will have to +>>>deal with this anyway (and could probably make it compatible with Vax +>>>byte order at the same time). Anyway, I've linted the code (both with +>>>and without -p), so it should run elsewhere. Note the longs in the +>>>code, you can take these out if you reduce BITS to <= 15. +>>> +>>>Have fun, and as always, if you make good enhancements, or bug fixes, +>>>I'd like to see them. +>>> +>>>=Spencer (thomas@utah-20, {harpo,hplabs,arizona}!utah-cs!thomas) +>> +>> regards, +>> joe +>> +>>-- +>>Full-Name: Joseph M. Orost +>>UUCP: ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe +>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724 +>>Phone: (201) 870-5844 diff --git a/file_cmds/compress/doc/revision.log b/file_cmds/compress/doc/revision.log new file mode 100644 index 00000000..04c96e68 --- /dev/null +++ b/file_cmds/compress/doc/revision.log @@ -0,0 +1,118 @@ +/* $FreeBSD: src/usr.bin/compress/doc/revision.log,v 1.5 2011/03/31 14:35:33 emaste Exp $ */ + +/* + * $Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $ + * + * Revision 4.0 85/07/30 12:50:00 joe + * Removed ferror() calls in output routine on every output except first. + * Prepared for release to the world. + * + * Revision 3.6 85/07/04 01:22:21 joe + * Remove much wasted storage by overlaying hash table with the tables + * used by decompress: tab_suffix[1<putc] and + * added signal catcher [plus beef in writeerr()] to delete effluvia. + * + * Revision 2.0 84/08/28 22:00:00 petsd!joe + * Add check for foreground before prompting user. Insert maxbits into + * compressed file. Force file being uncompressed to end with ".Z". + * Added "-c" flag and "zcat". Prepared for release. + * + * Revision 1.10 84/08/24 18:28:00 turtlevax!ken + * Will only compress regular files (no directories), added a magic number + * header (plus an undocumented -n flag to handle old files without headers), + * added -f flag to force overwriting of possibly existing destination file, + * otherwise the user is prompted for a response. Will tack on a .Z to a + * filename if it doesn't have one when decompressing. Will only replace + * file if it was compressed. + * + * Revision 1.9 84/08/16 17:28:00 turtlevax!ken + * Removed scanargs(), getopt(), added .Z extension and unlimited number of + * filenames to compress. Flags may be clustered (-Ddvb12) or separated + * (-D -d -v -b 12), or combination thereof. Modes and other status is + * copied with copystat(). -O bug for 4.2 seems to have disappeared with + * 1.8. + * + * Revision 1.8 84/08/09 23:15:00 joe + * Made it compatible with vax version, installed jim's fixes/enhancements + * + * Revision 1.6 84/08/01 22:08:00 joe + * Sped up algorithm significantly by sorting the compress chain. + * + * Revision 1.5 84/07/13 13:11:00 srd + * Added C version of vax asm routines. Changed structure to arrays to + * save much memory. Do unsigned compares where possible (faster on + * Perkin-Elmer) + * + * Revision 1.4 84/07/05 03:11:11 thomas + * Clean up the code a little and lint it. (Lint complains about all + * the regs used in the asm, but I'm not going to "fix" this.) + * + * Revision 1.3 84/07/05 02:06:54 thomas + * Minor fixes. + * + * Revision 1.2 84/07/05 00:27:27 thomas + * Add variable bit length output. + * + */ + +static char rcs_ident[] = + "$Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release $"; diff --git a/file_cmds/compress/uncompress.1 b/file_cmds/compress/uncompress.1 new file mode 100644 index 00000000..208c3ce8 --- /dev/null +++ b/file_cmds/compress/uncompress.1 @@ -0,0 +1 @@ +.so man1/compress.1 \ No newline at end of file diff --git a/file_cmds/compress/zcat.sh b/file_cmds/compress/zcat.sh new file mode 100644 index 00000000..6799f461 --- /dev/null +++ b/file_cmds/compress/zcat.sh @@ -0,0 +1,38 @@ +#!/bin/sh - +# $NetBSD: zcat.sh,v 1.4 1995/03/26 19:54:37 glass Exp $ +# +# Copyright (c) 1992, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)zcat.sh 8.1 (Berkeley) 6/6/93 +# + +uncompress -c $* diff --git a/file_cmds/compress/zopen.3 b/file_cmds/compress/zopen.3 new file mode 100644 index 00000000..f6c7f344 --- /dev/null +++ b/file_cmds/compress/zopen.3 @@ -0,0 +1,137 @@ +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)zopen.3 8.1 (Berkeley) 6/9/93 +.\" $FreeBSD: src/usr.bin/compress/zopen.3,v 1.9 2010/12/11 08:32:16 joel Exp $ +.\" +.Dd June 9, 1993 +.Dt ZOPEN 3 +.Os +.Sh NAME +.Nm zopen +.Nd compressed stream open function +.Sh SYNOPSIS +.Fd #include \&"zopen.h\&" +.Ft FILE * +.Fn zopen "const char *path" "const char *mode" "int bits" +.Sh DESCRIPTION +The +.Fn zopen +function +opens the compressed file whose name is the string pointed to by +.Fa path +and associates a stream with it. +.Pp +The argument +.Fa mode +points to one of the following one-character strings: +.Bl -tag -width indent +.It Dq Li r +Open compressed file for reading. +The stream is positioned at the beginning of the file. +.It Dq Li w +Truncate file to zero length or create compressed file for writing. +The stream is positioned at the beginning of the file. +.El +.Pp +Any created files will have mode +.Pf \\*q Dv S_IRUSR +\&| +.Dv S_IWUSR +\&| +.Dv S_IRGRP +\&| +.Dv S_IWGRP +\&| +.Dv S_IROTH +\&| +.Dv S_IWOTH Ns \\*q +.Pq Li 0666 , +as modified by the process' +umask value (see +.Xr umask 2 ) . +.Pp +Files may only be read or written. +Seek operations are not allowed. +.Pp +The +.Fa bits +argument, if non-zero, is set to the bits code limit. +If zero, the default is 16. +See +.Xr compress 1 +for more information. +.Sh RETURN VALUES +Upon successful completion +.Fn zopen +returns a +.Tn FILE +pointer. +Otherwise, +.Dv NULL +is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Bl -tag -width [EINVAL] +.It Bq Er EINVAL +The +.Fa mode +or +.Fa bits +arguments specified to +.Fn zopen +were invalid. +.It Bq Er EFTYPE +The compressed file starts with an invalid header, or the compressed +file is compressed with more bits than can be handled. +.El +.Pp +The +.Fn zopen +function may also fail and set +.Va errno +for any of the errors specified for the routines +.Xr fopen 3 +or +.Xr funopen 3 . +.Sh SEE ALSO +.Xr compress 1 , +.Xr fopen 3 , +.Xr funopen 3 +.Sh HISTORY +The +.Nm +function +first appeared in +.Bx 4.4 . +.Sh BUGS +The +.Fn zopen +function +may not be portable to systems other than +.Bx . diff --git a/file_cmds/compress/zopen.c b/file_cmds/compress/zopen.c new file mode 100644 index 00000000..589ce525 --- /dev/null +++ b/file_cmds/compress/zopen.c @@ -0,0 +1,738 @@ +/*- + * Copyright (c) 1985, 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93"; +#endif /* LIBC_SCCS and not lint */ + +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/zopen.c,v 1.17 2011/09/28 08:47:17 bz Exp $"); + +/*- + * fcompress.c - File compression ala IEEE Computer, June 1984. + * + * Compress authors: + * Spencer W. Thomas (decvax!utah-cs!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + * Cleaned up and converted to library returning I/O streams by + * Diomidis Spinellis . + * + * zopen(filename, mode, bits) + * Returns a FILE * that can be used for read or write. The modes + * supported are only "r" and "w". Seeking is not allowed. On + * reading the file is decompressed, on writing it is compressed. + * The output is compatible with compress(1) with 16 bit tables. + * Any file produced by compress(1) can be read. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "zopen.h" + +#define BITS 16 /* Default bits. */ +#define HSIZE 69001 /* 95% occupancy */ + +/* A code_int must be able to hold 2**BITS values of type int, and also -1. */ +typedef long code_int; +typedef long count_int; + +typedef u_char char_type; +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ + +#define BIT_MASK 0x1f /* Defines for third byte of header. */ +#define BLOCK_MASK 0x80 + +/* + * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + * a fourth header byte (for expansion). + */ +#define INIT_BITS 9 /* Initial number of bits/code. */ + +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + +struct s_zstate { + FILE *zs_fp; /* File stream for I/O */ + char zs_mode; /* r or w */ + enum { + S_START, S_MIDDLE, S_EOF + } zs_state; /* State of computation */ + u_int zs_n_bits; /* Number of bits/code. */ + u_int zs_maxbits; /* User settable max # bits/code. */ + code_int zs_maxcode; /* Maximum code, given n_bits. */ + code_int zs_maxmaxcode; /* Should NEVER generate this code. */ + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; + code_int zs_hsize; /* For dynamic table sizing. */ + code_int zs_free_ent; /* First unused entry. */ + /* + * Block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ + int zs_block_compress; + int zs_clear_flg; + long zs_ratio; + count_int zs_checkpoint; + u_int zs_offset; + long zs_in_count; /* Length of input. */ + long zs_bytes_out; /* Length of compressed output. */ + long zs_out_count; /* # of codes output (for debugging). */ + char_type zs_buf[BITS]; + union { + struct { + long zs_fcode; + code_int zs_ent; + code_int zs_hsize_reg; + int zs_hshift; + } w; /* Write parameters */ + struct { + char_type *zs_stackp; + int zs_finchar; + code_int zs_code, zs_oldcode, zs_incode; + int zs_roffset, zs_size; + char_type zs_gbuf[BITS]; + } r; /* Read parameters */ + } u; +}; + +/* Definitions to retain old variable names */ +#define fp zs->zs_fp +#define zmode zs->zs_mode +#define state zs->zs_state +#define n_bits zs->zs_n_bits +#define maxbits zs->zs_maxbits +#define maxcode zs->zs_maxcode +#define maxmaxcode zs->zs_maxmaxcode +#define htab zs->zs_htab +#define codetab zs->zs_codetab +#define hsize zs->zs_hsize +#define free_ent zs->zs_free_ent +#define block_compress zs->zs_block_compress +#define clear_flg zs->zs_clear_flg +#define ratio zs->zs_ratio +#define checkpoint zs->zs_checkpoint +#define offset zs->zs_offset +#define in_count zs->zs_in_count +#define bytes_out zs->zs_bytes_out +#define out_count zs->zs_out_count +#define buf zs->zs_buf +#define fcode zs->u.w.zs_fcode +#define hsize_reg zs->u.w.zs_hsize_reg +#define ent zs->u.w.zs_ent +#define hshift zs->u.w.zs_hshift +#define stackp zs->u.r.zs_stackp +#define finchar zs->u.r.zs_finchar +#define code zs->u.r.zs_code +#define oldcode zs->u.r.zs_oldcode +#define incode zs->u.r.zs_incode +#define roffset zs->u.r.zs_roffset +#define size zs->u.r.zs_size +#define gbuf zs->u.r.zs_gbuf + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type as + * the codetab. The tab_suffix table needs 2**BITS characters. We get this + * from the beginning of htab. The output stack uses the rest of htab, and + * contains characters. There is plenty of room for any possible stack + * (stack used to be 8000 characters). + */ + +#define htabof(i) htab[i] +#define codetabof(i) codetab[i] + +#define tab_prefixof(i) codetabof(i) +#define tab_suffixof(i) ((char_type *)(htab))[i] +#define de_stack ((char_type *)&tab_suffixof(1 << BITS)) + +#define CHECK_GAP 10000 /* Ratio check interval. */ + +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* First free entry. */ +#define CLEAR 256 /* Table clear output code. */ + +static int cl_block(struct s_zstate *); +static void cl_hash(struct s_zstate *, count_int); +static code_int getcode(struct s_zstate *); +static int output(struct s_zstate *, code_int); +static int zclose(void *); +static int zread(void *, char *, int); +static int zwrite(void *, const char *, int); + +/*- + * Algorithm from "A Technique for High Performance Data Compression", + * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. + * + * Algorithm: + * Modified Lempel-Ziv method (LZW). Basically finds common + * substrings and replaces them with a variable size code. This is + * deterministic, and can be done on the fly. Thus, the decompression + * procedure needs no input table, but tracks the way the table was built. + */ + +/*- + * compress write + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ +static int +zwrite(void *cookie, const char *wbp, int num) +{ + code_int i; + int c, disp; + struct s_zstate *zs; + const u_char *bp; + u_char tmp; + int count; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (const u_char *)wbp; + if (state == S_MIDDLE) + goto middle; + state = S_MIDDLE; + + maxmaxcode = 1L << maxbits; + if (fwrite(magic_header, + sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) + return (-1); + tmp = (u_char)((maxbits) | block_compress); + if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) + return (-1); + + offset = 0; + bytes_out = 3; /* Includes 3-byte header mojo. */ + out_count = 0; + clear_flg = 0; + ratio = 0; + in_count = 1; + checkpoint = CHECK_GAP; + maxcode = MAXCODE(n_bits = INIT_BITS); + free_ent = ((block_compress) ? FIRST : 256); + + ent = *bp++; + --count; + + hshift = 0; + for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) + hshift++; + hshift = 8 - hshift; /* Set hash code range bound. */ + + hsize_reg = hsize; + cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */ + +middle: for (; count--;) { + c = *bp++; + in_count++; + fcode = (long)(((long)c << maxbits) + ent); + i = ((c << hshift) ^ ent); /* Xor hashing. */ + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } else if ((long)htabof(i) < 0) /* Empty slot. */ + goto nomatch; + disp = hsize_reg - i; /* Secondary hash (after G. Knott). */ + if (i == 0) + disp = 1; +probe: if ((i -= disp) < 0) + i += hsize_reg; + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } + if ((long)htabof(i) >= 0) + goto probe; +nomatch: if (output(zs, (code_int) ent) == -1) + return (-1); + out_count++; + ent = c; + if (free_ent < maxmaxcode) { + codetabof(i) = free_ent++; /* code -> hashtable */ + htabof(i) = fcode; + } else if ((count_int)in_count >= + checkpoint && block_compress) { + if (cl_block(zs) == -1) + return (-1); + } + } + return (num); +} + +static int +zclose(void *cookie) +{ + struct s_zstate *zs; + int rval; + + zs = cookie; + if (zmode == 'w') { /* Put out the final code. */ + if (output(zs, (code_int) ent) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + out_count++; + if (output(zs, (code_int) - 1) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + } + rval = fclose(fp) == EOF ? -1 : 0; + free(zs); + return (rval); +} + +/*- + * Output the given code. + * Inputs: + * code: A n_bits-bit integer. If == -1, then EOF. This assumes + * that n_bits =< (long)wordsize - 1. + * Outputs: + * Outputs code to the file. + * Assumptions: + * Chars are 8 bits long. + * Algorithm: + * Maintain a BITS character long buffer (so that 8 codes will + * fit in it exactly). Use the VAX insv instruction to insert each + * code in turn. When the buffer fills up empty it and start over. + */ + +static char_type lmask[9] = + {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; +static char_type rmask[9] = + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static int +output(struct s_zstate *zs, code_int ocode) +{ + int r_off; + u_int bits; + char_type *bp; + + r_off = offset; + bits = n_bits; + bp = buf; + if (ocode >= 0) { + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + /* + * Since ocode is always >= 8 bits, only need to mask the first + * hunk on the left. + */ + *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]); + bp++; + bits -= (8 - r_off); + ocode >>= 8 - r_off; + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + *bp++ = ocode; + ocode >>= 8; + bits -= 8; + } + /* Last bits. */ + if (bits) + *bp = ocode; + offset += n_bits; + if (offset == (n_bits << 3)) { + bp = buf; + bits = n_bits; + bytes_out += bits; + if (fwrite(bp, sizeof(char), bits, fp) != bits) + return (-1); +// bp += bits; +// bits = 0; + offset = 0; + } + /* + * If the next entry is going to be too big for the ocode size, + * then increase it, if possible. + */ + if (free_ent > maxcode || (clear_flg > 0)) { + /* + * Write the whole buffer, because the input side won't + * discover the size increase until after it has read it. + */ + if (offset > 0) { + if (fwrite(buf, 1, n_bits, fp) != n_bits) + return (-1); + bytes_out += n_bits; + } + offset = 0; + + if (clear_flg) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } else { + n_bits++; + if (n_bits == maxbits) + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + } + } else { + /* At EOF, write the rest of the buffer. */ + if (offset > 0) { + offset = (offset + 7) / 8; + if (fwrite(buf, 1, offset, fp) != offset) + return (-1); + bytes_out += offset; + } + offset = 0; + } + return (0); +} + +/* + * Decompress read. This routine adapts to the codes in the file building + * the "string" table on-the-fly; requiring no table to be stored in the + * compressed file. The tables used herein are shared with those of the + * compress() routine. See the definitions above. + */ +static int +zread(void *cookie, char *rbp, int num) +{ + u_int count; + struct s_zstate *zs; + u_char *bp, header[3]; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)rbp; + switch (state) { + case S_START: + state = S_MIDDLE; + break; + case S_MIDDLE: + goto middle; + case S_EOF: + goto eof; + } + + /* Check the magic number */ + if (fread(header, + sizeof(char), sizeof(header), fp) != sizeof(header) || + memcmp(header, magic_header, sizeof(magic_header)) != 0) { + errno = EFTYPE; + return (-1); + } + maxbits = header[2]; /* Set -b from file. */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1L << maxbits; + if (maxbits > BITS || maxbits < 12) { + errno = EFTYPE; + return (-1); + } + /* As above, initialize the first 256 entries in the table. */ + maxcode = MAXCODE(n_bits = INIT_BITS); + for (code = 255; code >= 0; code--) { + tab_prefixof(code) = 0; + tab_suffixof(code) = (char_type) code; + } + free_ent = block_compress ? FIRST : 256; + + finchar = oldcode = getcode(zs); + if (oldcode == -1) /* EOF already? */ + return (0); /* Get out of here */ + + /* First code must be 8 bits = char. */ + *bp++ = (u_char)finchar; + count--; + stackp = de_stack; + + while ((code = getcode(zs)) > -1) { + + if ((code == CLEAR) && block_compress) { + for (code = 255; code >= 0; code--) + tab_prefixof(code) = 0; + clear_flg = 1; + free_ent = FIRST; + oldcode = -1; + continue; + } + incode = code; + + /* Special case for kWkWk string. */ + if (code >= free_ent) { + if (code > free_ent || oldcode == -1) { + /* Bad stream. */ + errno = EINVAL; + return (-1); + } + *stackp++ = finchar; + code = oldcode; + } + /* + * The above condition ensures that code < free_ent. + * The construction of tab_prefixof in turn guarantees that + * each iteration decreases code and therefore stack usage is + * bound by 1 << BITS - 256. + */ + + /* Generate output characters in reverse order. */ + while (code >= 256) { + *stackp++ = tab_suffixof(code); + code = tab_prefixof(code); + } + *stackp++ = finchar = tab_suffixof(code); + + /* And put them out in forward order. */ +middle: do { + if (count-- == 0) + return (num); + *bp++ = *--stackp; + } while (stackp > de_stack); + + /* Generate the new entry. */ + if ((code = free_ent) < maxmaxcode && oldcode != -1) { + tab_prefixof(code) = (u_short) oldcode; + tab_suffixof(code) = finchar; + free_ent = code + 1; + } + + /* Remember previous code. */ + oldcode = incode; + } + state = S_EOF; +eof: return (num - count); +} + +/*- + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ +static code_int +getcode(struct s_zstate *zs) +{ + code_int gcode; + int r_off, bits; + char_type *bp; + + bp = gbuf; + if (clear_flg > 0 || roffset >= size || free_ent > maxcode) { + /* + * If the next entry will be too big for the current gcode + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if (free_ent > maxcode) { + n_bits++; + if (n_bits == maxbits) /* Won't get any bigger now. */ + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + if (clear_flg > 0) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } + size = fread(gbuf, 1, n_bits, fp); + if (size <= 0) /* End of file. */ + return (-1); + roffset = 0; + /* Round size down to integral number of codes. */ + size = (size << 3) - (n_bits - 1); + } + r_off = roffset; + bits = n_bits; + + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + + /* Get first part (low order bits). */ + gcode = (*bp++ >> r_off); + bits -= (8 - r_off); + r_off = 8 - r_off; /* Now, roffset into gcode word. */ + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + gcode |= *bp++ << r_off; + r_off += 8; + bits -= 8; + } + + /* High order bits. */ + gcode |= (*bp & rmask[bits]) << r_off; + roffset += n_bits; + + return (gcode); +} + +static int +cl_block(struct s_zstate *zs) /* Table clear for block compress. */ +{ + long rat; + + checkpoint = in_count + CHECK_GAP; + + if (in_count > 0x007fffff) { /* Shift will overflow. */ + rat = bytes_out >> 8; + if (rat == 0) /* Don't divide by zero. */ + rat = 0x7fffffff; + else + rat = in_count / rat; + } else + rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */ + if (rat > ratio) + ratio = rat; + else { + ratio = 0; + cl_hash(zs, (count_int) hsize); + free_ent = FIRST; + clear_flg = 1; + if (output(zs, (code_int) CLEAR) == -1) + return (-1); + } + return (0); +} + +static void +cl_hash(struct s_zstate *zs, count_int cl_hsize) /* Reset code table. */ +{ + count_int *htab_p; + long i, m1; + + m1 = -1; + htab_p = htab + cl_hsize; + i = cl_hsize - 16; + do { /* Might use Sys V memset(3) here. */ + *(htab_p - 16) = m1; + *(htab_p - 15) = m1; + *(htab_p - 14) = m1; + *(htab_p - 13) = m1; + *(htab_p - 12) = m1; + *(htab_p - 11) = m1; + *(htab_p - 10) = m1; + *(htab_p - 9) = m1; + *(htab_p - 8) = m1; + *(htab_p - 7) = m1; + *(htab_p - 6) = m1; + *(htab_p - 5) = m1; + *(htab_p - 4) = m1; + *(htab_p - 3) = m1; + *(htab_p - 2) = m1; + *(htab_p - 1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + for (i += 16; i > 0; i--) + *--htab_p = m1; +} + +FILE * +zopen(const char *fname, const char *mode, int bits) +{ + struct s_zstate *zs; + + if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0' || + bits < 0 || bits > BITS) { + errno = EINVAL; + return (NULL); + } + + if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL) + return (NULL); + + maxbits = bits ? bits : BITS; /* User settable max # bits/code. */ + maxmaxcode = 1L << maxbits; /* Should NEVER generate this code. */ + hsize = HSIZE; /* For dynamic table sizing. */ + free_ent = 0; /* First unused entry. */ + block_compress = BLOCK_MASK; + clear_flg = 0; + ratio = 0; + checkpoint = CHECK_GAP; + in_count = 1; /* Length of input. */ + out_count = 0; /* # of codes output (for debugging). */ + state = S_START; + roffset = 0; + size = 0; + + /* + * Layering compress on top of stdio in order to provide buffering, + * and ensure that reads and write work with the data specified. + */ + if ((fp = fopen(fname, mode)) == NULL) { + free(zs); + return (NULL); + } + switch (*mode) { + case 'r': + zmode = 'r'; + return (funopen(zs, zread, NULL, NULL, zclose)); + case 'w': + zmode = 'w'; + return (funopen(zs, NULL, zwrite, NULL, zclose)); + } + /* NOTREACHED */ + return (NULL); +} diff --git a/file_cmds/compress/zopen.h b/file_cmds/compress/zopen.h new file mode 100644 index 00000000..8ad5691f --- /dev/null +++ b/file_cmds/compress/zopen.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 1996 + * FreeBSD Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY FreeBSD Inc. AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL [your name] OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/compress/zopen.h,v 1.5 2002/03/22 01:19:31 imp Exp $ + */ + +#ifndef _ZOPEN_H_ +#define _ZOPEN_H_ + +FILE *zopen(const char *, const char *, int); + +#endif /* _ZOPEN_H_ */ diff --git a/file_cmds/cp/cp.1 b/file_cmds/cp/cp.1 new file mode 100644 index 00000000..8c346c7d --- /dev/null +++ b/file_cmds/cp/cp.1 @@ -0,0 +1,311 @@ +.\"- +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cp.1 8.3 (Berkeley) 4/18/94 +.\" $FreeBSD: src/bin/cp/cp.1,v 1.33 2005/02/25 00:40:46 trhodes Exp $ +.\" +.Dd February 23, 2005 +.Dt CP 1 +.Os +.Sh NAME +.Nm cp +.Nd copy files +.Sh SYNOPSIS +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fi | n +.Op Fl apvX +.Ar source_file target_file +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fi | n +.Op Fl apvX +.Ar source_file ... target_directory +.Sh DESCRIPTION +In the first synopsis form, the +.Nm cp +utility copies the contents of the +.Ar source_file +to the +.Ar target_file . +In the second synopsis form, +the contents of each named +.Ar source_file +is copied to the destination +.Ar target_directory . +The names of the files themselves are not changed. +If +.Nm cp +detects an attempt to copy a file to itself, the copy will fail. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl a +Same as +.Fl pPR +options. Preserves structure and attributes of files +but not directory structure. +.It Fl f +.\"For each existing destination pathname, remove it and +If the destination file cannot be opened, remove it and +create a new file, without prompting for confirmation +regardless of its permissions. +(The +.Fl f +option overrides any previous +.Fl n +option.) +.Pp +The target file is not unlinked before the copy. +Thus, any existing access rights will be retained. +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl i +Cause +.Nm cp +to write a prompt to the standard error output before copying a file +that would overwrite an existing file. +If the response from the standard input begins with the character +.Sq Li y +or +.Sq Li Y , +the file copy is attempted. +(The +.Fl i +option overrides any previous +.Fl n +option.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl n +Do not overwrite an existing file. +(The +.Fl n +option overrides any previous +.Fl f +or +.Fl i +options.) +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +This is the default. +.It Fl p +Cause +.Nm cp +to preserve the following attributes of each source +file in the copy: modification time, access time, +file flags, file mode, user ID, and group ID, as allowed by permissions. +Access Control Lists (ACLs) and Extended Attributes (EAs), +including resource forks, will also be preserved. +.Pp +If the user ID and group ID cannot be preserved, no error message +is displayed and the exit value is not altered. +.Pp +If the source file has its set-user-ID bit on and the user ID cannot +be preserved, the set-user-ID bit is not preserved +in the copy's permissions. +If the source file has its set-group-ID bit on and the group ID cannot +be preserved, the set-group-ID bit is not preserved +in the copy's permissions. +If the source file has both its set-user-ID and set-group-ID bits on, +and either the user ID or group ID cannot be preserved, neither +the set-user-ID nor set-group-ID bits are preserved in the copy's +permissions. +.It Fl R +If +.Ar source_file +designates a directory, +.Nm cp +copies the directory and the entire subtree connected at that point. +If the +.Ar source_file +ends in a +.Pa / , +the contents of the directory are copied rather than the +directory itself. +This option also causes symbolic links to be copied, rather than +indirected through, and for +.Nm cp +to create special files rather than copying them as normal files. +Created directories have the same mode as the corresponding source +directory, unmodified by the process' umask. +.Pp +In +.Fl R +mode, +.Nm cp +will continue copying even if errors are detected. +.Pp +Note that +.Nm cp +copies hard-linked files as separate files. +If you need to preserve hard links, consider using +.Xr tar 1 , +.Xr cpio 1 , +or +.Xr pax 1 +instead. +.It Fl v +Cause +.Nm cp +to be verbose, showing files as they are copied. +.It Fl X +Do not copy Extended Attributes (EAs) or resource forks. +.It Fl c +copy files using clonefile(2) +.El +.Pp +For each destination file that already exists, its contents are +overwritten if permissions allow. +Its mode, user ID, and group +ID are unchanged unless the +.Fl p +option was specified. +.Pp +In the second synopsis form, +.Ar target_directory +must exist unless there is only one named +.Ar source_file +which is a directory and the +.Fl R +flag is specified. +.Pp +If the destination file does not exist, the mode of the source file is +used as modified by the file mode creation mask +.Pf ( Ic umask , +see +.Xr csh 1 ) . +If the source file has its set-user-ID bit on, that bit is removed +unless both the source file and the destination file are owned by the +same user. +If the source file has its set-group-ID bit on, that bit is removed +unless both the source file and the destination file are in the same +group and the user is a member of that group. +If both the set-user-ID and set-group-ID bits are set, all of the above +conditions must be fulfilled or both bits are removed. +.Pp +Appropriate permissions are required for file creation or overwriting. +.Pp +Symbolic links are always followed unless the +.Fl R +flag is set, in which case symbolic links are not followed, by default. +The +.Fl H +or +.Fl L +flags (in conjunction with the +.Fl R +flag) cause symbolic links to be followed as described above. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +If +.Nm cp +receives a +.Dv SIGINFO +(see the +.Cm status +argument for +.Xr stty 1 ) +signal, the current input and output file and the percentage complete +will be written to the standard output. +.Sh EXIT STATUS +.Ex -std +.Sh COMPATIBILITY +Historic versions of the +.Nm cp +utility had a +.Fl r +option. +This implementation supports that option; +however, its use is strongly discouraged, +as it does not correctly copy special files, symbolic links, or fifo's. +.Pp +The +.Fl v +and +.Fl n +options are non-standard and their use in scripts is not recommended. +.Sh LEGACY DESCRIPTION +In legacy mode, +.Fl f +will override +.Fl i . +Also, under the +.Fl f +option, the target file is always unlinked before the copy. +Thus, new access rights will always be set. +.Pp +In +.Fl R +mode, copying will terminate if an error is encountered. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr mv 1 , +.Xr rcp 1 , +.Xr umask 2 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm cp +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm cp +command appeared in +.At v1 . diff --git a/file_cmds/cp/cp.c b/file_cmds/cp/cp.c new file mode 100644 index 00000000..3e9cce3f --- /dev/null +++ b/file_cmds/cp/cp.c @@ -0,0 +1,587 @@ +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)cp.c 8.2 (Berkeley) 4/1/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/cp/cp.c,v 1.52 2005/09/05 04:36:08 csjp Exp $"); + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +// #include +// #else /* !__APPLE__ */ +#define COMPAT_MODE(a,b) (1) +#include "ios_error.h" +#endif /* __APPLE__ */ + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +static char emptystring[] = ""; + +PATH_T to = { to.p_path, emptystring, "" }; + +__thread int cp_fflag, cp_iflag, cp_nflag, cp_pflag, cp_vflag; +#ifdef __APPLE__ +__thread int Xflag; +#endif /* __APPLE__ */ +static int Rflag, rflag; +__thread int cp_cflag = 0; +volatile __thread sig_atomic_t info; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +static int copy(char *[], enum op, int); +static void siginfo(int __unused); + +int +cp_main(int argc, char *argv[]) +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r, have_trailing_slash; + char *target; + + Hflag = Lflag = Pflag = 0; + cp_fflag = cp_iflag = cp_nflag = cp_pflag = cp_vflag = 0; +#ifdef __APPLE__ + Xflag = 0; +#endif /* __APPLE__ */ + Rflag = rflag = cp_cflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "cHLPRXafinprv")) != -1) + switch (ch) { + case 'c': + cp_cflag = 1; + break; + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'X': + Xflag = 1; + break; + case 'f': + cp_fflag = 1; + /* Determine if the STD is SUSv3 or Legacy */ + if (COMPAT_MODE("bin/cp", "unix2003")) + cp_nflag = 0; /* reset cp_nflag, but not cp_iflag */ + else + cp_iflag = cp_nflag = 0; /* reset both */ + break; + case 'i': + cp_iflag = 1; + if (COMPAT_MODE("bin/cp", "unix2003")) + cp_nflag = 0; /* reset cp_nflag, but not cp_fflag */ + else + cp_fflag = cp_nflag = 0; + break; + case 'n': + cp_nflag = 1; + cp_fflag = cp_iflag = 0; + break; + case 'p': + cp_pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'v': + cp_vflag = 1; + break; + case 'a': + cp_pflag = 1; + Pflag = 1; + Rflag = 1; + break; + default: + cp_usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc < 2) + cp_usage(); + + if (cp_cflag && Xflag) { + errx(1, "the -c and -X options may not be specified together"); + } + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) { + errx(1, + "cp: the -R and -r options may not be specified together.\n"); + } + if (Hflag || Lflag || Pflag) { + errx(1, + "cp: the -H, -L, and -P options may not be specified with the -r option.\n"); + } + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL | FTS_COMFOLLOW; + } + (void)signal(SIGINFO, siginfo); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlcpy(to.p_path, target, sizeof(to.p_path)) >= sizeof(to.p_path)) { + errx(1, "%s: name too long", target); + } + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + have_trailing_slash = (to.p_end[-1] == '/'); + if (have_trailing_slash) + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) { + err(1, "%s", to.p_path); + } + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) { + cp_usage(); + exit(1); + } + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + stat(*argv, &tmp_stat); + else + lstat(*argv, &tmp_stat); + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + + if (have_trailing_slash && type == FILE_TO_FILE) { + if (r == -1) { + errx(1, "directory %s does not exist", + to.p_path); + } + else { + errx(1, "%s is not a directory", to.p_path); + } + } + } else + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + + return (copy(argv, type, fts_options)); +} + +static int +copy(char *argv[], enum op type, int fts_options) +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base = 0, dne, badcp, rval; + size_t nlen; + char *p, *target_mid; + mode_t mask, mode; + + /* + * Keep an inverted copy of the umask, for use in correcting + * permissions on created directories when not using -p. + */ + mask = ~umask(0777); + umask(~mask); + + if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) { + err(1, "fts_open"); + } + for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; badcp = 0) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + rval = 1; + continue; + default: + ; + } +#ifdef __APPLE__ + +#ifdef __clang__ +#pragma clang diagnostic push +/* clang doesn't like fts_name[1], but we know better... */ +#pragma clang diagnostic ignored "-Warray-bounds" +#endif + /* Skip ._ when using copyfile and exists */ + if ((cp_pflag || !Xflag) && (curr->fts_level != FTS_ROOTLEVEL) && + (curr->fts_namelen > 2) && /* ._\0 is not AppleDouble */ + (curr->fts_name[0] == '.') && (curr->fts_name[1] == '_')) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + struct stat statbuf; + char path[PATH_MAX]; + char *p = strrchr(curr->fts_path, '/'); + if (p) { + size_t s = p + 2 - curr->fts_path; + if (s > sizeof(path)) s = sizeof(path); + strlcpy(path, curr->fts_path, s); + strlcat(path, curr->fts_name+2, sizeof(path)); + } else { + strlcpy(path, curr->fts_name+2, sizeof(path)); + } + if (!lstat(path, &statbuf)) { + continue; + } + } +#endif /* __APPLE__ */ + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatenation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + target_mid = to.target_end; + if (*p != '/' && target_mid[-1] != '/') + *target_mid++ = '/'; + *target_mid = 0; + if (target_mid - to.p_path + nlen >= PATH_MAX) { + warnx("%s%s: name too long (not copied)", + to.p_path, p); + rval = 1; + continue; + } + (void)strncat(target_mid, p, nlen); + to.p_end = target_mid + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + if (curr->fts_info == FTS_DP) { + /* + * We are nearly finished with this directory. If we + * didn't actually copy it, or otherwise don't need to + * change its attributes, then we are done. + */ + if (!curr->fts_number) + continue; + /* + * If -p is in effect, set all the attributes. + * Otherwise, set the correct permissions, limited + * by the umask. Optimise by avoiding a chmod() + * if possible (which is usually the case if we + * made the directory). Note that mkdir() does not + * honour setuid, setgid and sticky bits, but we + * normally want to preserve them on directories. + */ + if (cp_pflag) { + if (setfile(curr->fts_statp, -1)) + rval = 1; +#ifdef __APPLE__ + /* setfile will fail if writeattr is denied */ + if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_ACL)<0) + warn("%s: unable to copy ACL to %s", curr->fts_path, to.p_path); +#else /* !__APPLE__ */ + if (preserve_dir_acls(curr->fts_statp, + curr->fts_accpath, to.p_path) != 0) + rval = 1; +#endif /* __APPLE__ */ + } else { + mode = curr->fts_statp->st_mode; + if ((mode & (S_ISUID | S_ISGID | S_ISTXT)) || + ((mode | S_IRWXU) & mask) != (mode & mask)) + if (chmod(to.p_path, mode & mask) != 0){ + warn("chmod: %s", to.p_path); + rval = 1; + } + } + continue; + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with " + "non-directory %s\n", + to.p_path, curr->fts_path); + rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + /* Catch special case of a non-dangling symlink */ + if ((fts_options & FTS_LOGICAL) || + ((fts_options & FTS_COMFOLLOW) && + curr->fts_level == 0)) { + if (copy_file(curr, dne)) + badcp = rval = 1; + } else { + if (copy_link(curr, !dne)) + badcp = rval = 1; + } + break; + case S_IFDIR: + if (!Rflag && !rflag) { + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + badcp = rval = 1; + break; + } + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) { + if (COMPAT_MODE("bin/cp", "unix2003")) { + warn("%s", to.p_path); + } else { + err(1, "%s", to.p_path); + } + } + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + if (COMPAT_MODE("bin/cp", "unix2003")) { + warn("%s", to.p_path); + } else { + err(1, "%s", to.p_path); + } + } + /* + * Arrange to correct directory attributes later + * (in the post-order phase) if this is a new + * directory, or if the -p flag is in effect. + */ + curr->fts_number = cp_pflag || dne; +#ifdef __APPLE__ + if (!Xflag) { + if (copyfile(curr->fts_path, to.p_path, NULL, COPYFILE_XATTR) < 0) + warn("%s: unable to copy extended attributes to %s", curr->fts_path, to.p_path); + /* ACL and mtime set in postorder traversal */ + } +#endif /* __APPLE__ */ + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + badcp = rval = 1; + } else { + if (copy_file(curr, dne)) + badcp = rval = 1; + } + break; + default: + if (copy_file(curr, dne)) + badcp = rval = 1; + break; + } + if (cp_vflag && !badcp) + (void)fprintf(thread_stdout, "%s -> %s\n", curr->fts_path, to.p_path); + } + fts_close(ftsp); + if (errno) { + err(1, "fts_read"); + } + return (rval); +} + +static void +siginfo(int sig __unused) +{ + + info = 1; +} diff --git a/file_cmds/cp/extern.h b/file_cmds/cp/extern.h new file mode 100644 index 00000000..4e399de0 --- /dev/null +++ b/file_cmds/cp/extern.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + * $FreeBSD: src/bin/cp/extern.h,v 1.20 2005/09/05 04:36:08 csjp Exp $ + */ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[PATH_MAX]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern __thread int cp_fflag, cp_iflag, cp_nflag, cp_pflag, cp_vflag; +#ifdef __APPLE__ +extern __thread int Xflag; +#endif /* __APPLE__ */ +extern __thread int cp_cflag; +extern volatile __thread sig_atomic_t info; + +__BEGIN_DECLS +int copy_fifo(struct stat *, int); +int copy_file(const FTSENT *, int); +int copy_link(const FTSENT *, int); +int copy_special(struct stat *, int); +int setfile(struct stat *, int); +int preserve_dir_acls(struct stat *, char *, char *); +int preserve_fd_acls(int, int); +void cp_usage(void); +__END_DECLS diff --git a/file_cmds/cp/utils.c b/file_cmds/cp/utils.c new file mode 100644 index 00000000..090724aa --- /dev/null +++ b/file_cmds/cp/utils.c @@ -0,0 +1,534 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/bin/cp/utils.c,v 1.46 2005/09/05 04:36:08 csjp Exp $"); + +#include +#include +#include +#include +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +#include +// #include +#include +#include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "extern.h" +#include "ios_error.h" +#define cp_pct(x,y) (int)(100.0 * (double)(x) / (double)(y)) + +int +copy_file(const FTSENT *entp, int dne) +{ + static char buf[MAXBSIZE]; + struct stat *fs; + int ch, checkch, from_fd, rval, to_fd; + ssize_t rcount; + ssize_t wcount; + size_t wresid; + off_t wtotal; + char *bufp; +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + char *p; +#endif + mode_t mode = 0; + struct stat to_stat; + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + fs = entp->fts_statp; + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { +#define YESNO "(y/n [n]) " + if (cp_nflag) { + if (cp_vflag) + fprintf(thread_stdout, "%s not overwritten\n", to.p_path); + (void)close(from_fd); + return (1); + } else if (cp_iflag) { + (void)fprintf(thread_stderr, "overwrite %s? %s", + to.p_path, YESNO); + fflush(thread_stderr); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + (void)fprintf(thread_stderr, "not overwritten\n"); + fflush(thread_stderr); + return (1); + } + } + + if (cp_cflag) { + (void)unlink(to.p_path); + int error = clonefile(entp->fts_path, to.p_path, 0); + if (error) + warn("%s: clonefile failed", to.p_path); + (void)close(from_fd); + return error == 0 ? 0 : 1; + } + + if (COMPAT_MODE("bin/cp", "unix2003")) { + /* first try to overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + if (to_fd == -1) { + if (cp_fflag) { + /* Only if it fails remove file and create a new one */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + } + } else { + if (cp_fflag) { + /* remove existing destination file name, + * create a new file */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } else + /* overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } + } else { + + if (cp_cflag) { + int error = clonefile(entp->fts_path, to.p_path, 0); + if (error) + warn("%s: clonefile failed", to.p_path); + (void)close(from_fd); + return error == 0 ? 0 : 1; + } + + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1); + } + + rval = 0; + +#ifdef __APPLE__ + if (S_ISREG(fs->st_mode)) { + struct statfs sfs; + + /* + * Pre-allocate blocks for the destination file if it + * resides on Xsan. + */ + if (fstatfs(to_fd, &sfs) == 0 && + strcmp(sfs.f_fstypename, "acfs") == 0) { + fstore_t fst; + + fst.fst_flags = 0; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = fs->st_size; + + (void) fcntl(to_fd, F_PREALLOCATE, &fst); + } + } +#endif /* __APPLE__ */ + + if (fstat(to_fd, &to_stat) != -1) { + mode = to_stat.st_mode; + if ((mode & (S_IRWXG|S_IRWXO)) + && fchmod(to_fd, mode & ~(S_IRWXG|S_IRWXO))) { + if (errno != EPERM) /* we have write access but do not own the file */ + warn("%s: fchmod failed", to.p_path); + mode = 0; + } + } else { + warn("%s", to.p_path); + } + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + if (S_ISREG(fs->st_mode) && fs->st_size > 0 && + fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + MAP_SHARED, from_fd, (off_t)0)) == MAP_FAILED) { + warn("%s", entp->fts_path); + rval = 1; + } else { + wtotal = 0; + for (bufp = p, wresid = fs->st_size; ; + bufp += wcount, wresid -= (size_t)wcount) { + wcount = write(to_fd, bufp, wresid); + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(thread_stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + + } + if (wcount >= (ssize_t)wresid || wcount <= 0) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + } + /* Some systems don't unmap on close(2). */ + if (munmap(p, fs->st_size) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else +#endif + { + wtotal = 0; + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + for (bufp = buf, wresid = rcount; ; + bufp += wcount, wresid -= wcount) { + wcount = write(to_fd, bufp, wresid); + wtotal += wcount; + if (info) { + info = 0; + (void)fprintf(thread_stderr, + "%s -> %s %3d%%\n", + entp->fts_path, to.p_path, + cp_pct(wtotal, fs->st_size)); + + } + if (wcount >= (ssize_t)wresid || wcount <= 0) + break; + } + if (wcount != (ssize_t)wresid) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + + /* + * Don't remove the target even after an error. The target might + * not be a regular file, or its attributes might be important, + * or its contents might be irreplaceable. It would only be safe + * to remove it if we created it and its length is 0. + */ + if (mode != 0) + if (fchmod(to_fd, mode)) + warn("%s: fchmod failed", to.p_path); +#ifdef __APPLE__ + /* do these before setfile in case copyfile changes mtime */ + if (!Xflag && S_ISREG(fs->st_mode)) { /* skip devices, etc */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0) + warn("%s: could not copy extended attributes to %s", entp->fts_path, to.p_path); + } + if (cp_pflag && setfile(fs, to_fd)) + rval = 1; + if (cp_pflag) { + /* If this ACL denies writeattr then setfile will fail... */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0) + warn("%s: could not copy ACL to %s", entp->fts_path, to.p_path); + } +#else /* !__APPLE__ */ + if (cp_pflag && setfile(fs, to_fd)) + rval = 1; + if (cp_pflag && preserve_fd_acls(from_fd, to_fd) != 0) + rval = 1; +#endif /* __APPLE__ */ + (void)close(from_fd); + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + return (rval); +} + +int +copy_link(const FTSENT *p, int exists) +{ + ssize_t len; + char llink[PATH_MAX]; + + if ((len = readlink(p->fts_path, llink, sizeof(llink) - 1)) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + llink[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(llink, to.p_path)) { + warn("symlink: %s", llink); + return (1); + } +#ifdef __APPLE__ + if (!Xflag) + if (copyfile(p->fts_path, to.p_path, NULL, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC) <0) + warn("%s: could not copy extended attributes to %s", + p->fts_path, to.p_path, strerror(errno)); +#endif + return (cp_pflag ? setfile(p->fts_statp, -1) : 0); +} + +int +copy_fifo(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (cp_pflag ? setfile(from_stat, -1) : 0); +} + +int +copy_special(struct stat *from_stat, int exists) +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (cp_pflag ? setfile(from_stat, -1) : 0); +} + +int +setfile(struct stat *fs, int fd) +{ + static struct timeval tv[2]; + struct stat ts; + int rval, gotstat, islink, fdval; + + rval = 0; + fdval = fd != -1; + islink = !fdval && S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (fdval ? futimes(fd, tv) : (islink ? lutimes(to.p_path, tv) : utimes(to.p_path, tv))) { + warn("%sutimes: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + if (fdval ? fstat(fd, &ts) : (islink ? lstat(to.p_path, &ts) : + stat(to.p_path, &ts))) { + gotstat = 0; + } else { + gotstat = 1; + ts.st_mode &= S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; + } + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (!gotstat || fs->st_uid != ts.st_uid || fs->st_gid != ts.st_gid) { + if (fdval ? fchown(fd, fs->st_uid, fs->st_gid) : (islink ? + lchown(to.p_path, fs->st_uid, fs->st_gid) : + chown(to.p_path, fs->st_uid, fs->st_gid))) { + if (errno != EPERM) { + warn("%schown: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } + } + + if (!gotstat || fs->st_mode != ts.st_mode) { + if (fdval ? fchmod(fd, fs->st_mode) : (islink ? + lchmod(to.p_path, fs->st_mode) : + chmod(to.p_path, fs->st_mode))) { + warn("%schmod: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + } + + if (!gotstat || fs->st_flags != ts.st_flags) { + if (fdval ? fchflags(fd, fs->st_flags) : (islink ? + lchflags(to.p_path, fs->st_flags) : + chflags(to.p_path, fs->st_flags))) { + if (errno != EPERM) { + warn("%schflags: %s", fdval ? "f" : (islink ? "l" : ""), to.p_path); + rval = 1; + } + } + } + return (rval); +} + +#ifndef __APPLE__ +int +preserve_fd_acls(int source_fd, int dest_fd) +{ + struct acl *aclp; + acl_t acl; + + if (fpathconf(source_fd, _PC_ACL_EXTENDED) != 1 || + fpathconf(dest_fd, _PC_ACL_EXTENDED) != 1) + return (0); + acl = acl_get_fd(source_fd); + if (acl == NULL) { + warn("failed to get acl entries while setting %s", to.p_path); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt == 3) + return (0); + if (acl_set_fd(dest_fd, acl) < 0) { + warn("failed to set acl entries for %s", to.p_path); + return (1); + } + return (0); +} + +int +preserve_dir_acls(struct stat *fs, char *source_dir, char *dest_dir) +{ + acl_t (*aclgetf)(const char *, acl_type_t); + int (*aclsetf)(const char *, acl_type_t, acl_t); + struct acl *aclp; + acl_t acl; + + if (pathconf(source_dir, _PC_ACL_EXTENDED) != 1 || + pathconf(dest_dir, _PC_ACL_EXTENDED) != 1) + return (0); + /* + * If the file is a link we will not follow it + */ + if (S_ISLNK(fs->st_mode)) { + aclgetf = acl_get_link_np; + aclsetf = acl_set_link_np; + } else { + aclgetf = acl_get_file; + aclsetf = acl_set_file; + } + /* + * Even if there is no ACL_TYPE_DEFAULT entry here, a zero + * size ACL will be returned. So it is not safe to simply + * check the pointer to see if the default ACL is present. + */ + acl = aclgetf(source_dir, ACL_TYPE_DEFAULT); + if (acl == NULL) { + warn("failed to get default acl entries on %s", + source_dir, strerror(errno)); + return (1); + } + aclp = &acl->ats_acl; + if (aclp->acl_cnt != 0 && aclsetf(dest_dir, + ACL_TYPE_DEFAULT, acl) < 0) { + warn("failed to set default acl entries on %s", + dest_dir, strerror(errno)); + return (1); + } + acl = aclgetf(source_dir, ACL_TYPE_ACCESS); + if (acl == NULL) { + warn("failed to get acl entries on %s", source_dir); + return (1); + } + aclp = &acl->ats_acl; + if (aclsetf(dest_dir, ACL_TYPE_ACCESS, acl) < 0) { + warn("failed to set acl entries on %s", dest_dir); + return (1); + } + return (0); +} +#endif /* !__APPLE__ */ + +void +cp_usage(void) +{ + + if (COMPAT_MODE("bin/cp", "unix2003")) { + (void)fprintf(thread_stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file target_file", +" cp [-R [-H | -L | -P]] [-fi | -n] [-apvXc] source_file ... " +"target_directory"); + } else { + (void)fprintf(thread_stderr, "%s\n%s\n", +"usage: cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file target_file", +" cp [-R [-H | -L | -P]] [-f | -i | -n] [-apvXc] source_file ... " +"target_directory"); + } + exit(EX_USAGE); +} diff --git a/file_cmds/csh/strpct.c b/file_cmds/csh/strpct.c new file mode 100644 index 00000000..ac3b32d7 --- /dev/null +++ b/file_cmds/csh/strpct.c @@ -0,0 +1,99 @@ +/* $NetBSD: strpct.c,v 1.2 1998/05/08 18:43:54 fair Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Erik E. Fair + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Calculate a percentage without resorting to floating point + * and return a pointer to a string + * + * "digits" is the number of digits past the decimal place you want + * (zero being the straight percentage with no decimals) + * + * Erik E. Fair , May 8, 1997 + */ + +#include +#include + +#include + +char * strpct __P((u_long, u_long, u_int)); + +char * +strpct(numerator, denominator, digits) + u_long numerator, denominator; + u_int digits; +{ + int i; + u_long result, factor; + static char percent[32]; + + /* I should check for digit overflow here, too XXX */ + factor = 100L; + for(i = 0; i < digits; i++) { + factor *= 10; + } + + /* watch out for overflow! */ + if (numerator < (ULONG_MAX / factor)) { + numerator *= factor; + } else { + /* toss some of the bits of lesser significance */ + denominator /= factor; + } + + if (denominator == 0L) + denominator = 1L; + + result = numerator / denominator; + + if (digits == 0) { + (void) snprintf(percent, sizeof(percent), "%lu%%", result); + } else { + char fmt[32]; + + /* indirection to produce the right output format */ + (void) snprintf(fmt, sizeof(fmt), "%%lu.%%0%ulu%%%%", digits); + + factor /= 100L; /* undo initialization */ + + (void) snprintf(percent, sizeof(percent), + fmt, result / factor, result % factor); + } + + return(percent); +} diff --git a/file_cmds/dd/args.c b/file_cmds/dd/args.c new file mode 100644 index 00000000..463bd463 --- /dev/null +++ b/file_cmds/dd/args.c @@ -0,0 +1,421 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/args.c,v 1.31 2002/02/22 20:51:00 markm Exp $"; +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +static int c_arg(const void *, const void *); +static int c_conv(const void *, const void *); +static void f_bs(char *); +static void f_cbs(char *); +static void f_conv(char *); +static void f_count(char *); +static void f_files(char *); +static void f_ibs(char *); +static void f_if(char *); +static void f_obs(char *); +static void f_of(char *); +static void f_seek(char *); +static void f_skip(char *); +static quad_t get_num(char *); +static off_t get_offset(char *); + +static const struct arg { + const char *name; + void (*f)(char *); + u_int set, noset; +} args[] = { + { "bs", f_bs, C_BS, C_BS|C_OSYNC }, + { "cbs", f_cbs, C_CBS, C_CBS }, + { "conv", f_conv, 0, 0 }, + { "count", f_count, C_COUNT, C_COUNT }, + { "files", f_files, C_FILES, C_FILES }, + { "ibs", f_ibs, C_IBS, C_IBS }, + { "if", f_if, C_IF, C_IF }, + { "iseek", f_skip, C_SKIP, C_SKIP }, + { "obs", f_obs, C_OBS, C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "oseek", f_seek, C_SEEK, C_SEEK }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +static char *oper; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(char **argv) +{ + struct arg *ap, tmp; + char *arg; + + in.dbsz = out.dbsz = 512; + + if (argv[1] && !strcmp(argv[1], "--")) /* skip delimiter before operands */ + argv++; + while ((oper = *++argv) != NULL) { + if ((oper = strdup(oper)) == NULL) + errx(1, "unable to allocate space for the argument \"%s\"", *argv); + if ((arg = strchr(oper, '=')) == NULL) + errx(1, "unknown operand %s", oper); + *arg++ = '\0'; + if (!*arg) + errx(1, "no value specified for %s", oper); + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) + errx(1, "unknown operand %s", tmp.name); + if (ddflags & ap->noset) + errx(1, "%s: illegal argument combination or already set", + tmp.name); + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK | C_LCASE | C_SWAB | C_UCASE | + C_UNBLOCK)) + ddflags &= ~C_BS; + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS | C_OBS)) + warnx("bs supersedes ibs and obs"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK | C_UNBLOCK)) { + if (!(ddflags & C_CBS)) + errx(1, "record operations require cbs"); + if (cbsz == 0) + errx(1, "cbs cannot be zero"); + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII | C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else + errx(1, "cbs meaningless if not doing record operations"); + } else + cfunc = def; + + /* + * Bail out if the calculation of a file offset would overflow. + */ + if (in.offset > QUAD_MAX / in.dbsz || out.offset > QUAD_MAX / out.dbsz) + errx(1, "seek offsets cannot be larger than %qd", QUAD_MAX); +} + +static int +c_arg(const void *a, const void *b) +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static void +f_bs(char *arg) +{ + quad_t res; + + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "bs must be between 1 and %ld", SSIZE_MAX); + in.dbsz = out.dbsz = (size_t)res; +} + +static void +f_cbs(char *arg) +{ + quad_t res; + + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "cbs must be between 1 and %ld", SSIZE_MAX); + cbsz = (size_t)res; +} + +static void +f_count(char *arg) +{ + + cpy_cnt = get_num(arg); + if (cpy_cnt < 0) + errx(1, "count cannot be negative"); + if (cpy_cnt == 0) + cpy_cnt = -1; +} + +static void +f_files(char *arg) +{ + + files_cnt = get_num(arg); + if (files_cnt < 1) + errx(1, "files must be between 1 and %qd", QUAD_MAX); +} + +static void +f_ibs(char *arg) +{ + quad_t res; + + if (!(ddflags & C_BS)) { + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "ibs must be between 1 and %ld", SSIZE_MAX); + in.dbsz = (size_t)res; + } +} + +static void +f_if(char *arg) +{ + + in.name = arg; +} + +static void +f_obs(char *arg) +{ + quad_t res; + + if (!(ddflags & C_BS)) { + res = get_num(arg); + if (res < 1 || res > SSIZE_MAX) + errx(1, "obs must be between 1 and %ld", SSIZE_MAX); + out.dbsz = (size_t)res; + } +} + +static void +f_of(char *arg) +{ + + out.name = arg; +} + +static void +f_seek(char *arg) +{ + + out.offset = get_offset(arg); +} + +static void +f_skip(char *arg) +{ + + in.offset = get_offset(arg); +} + +static const struct conv { + const char *name; + u_int set, noset; + const u_char *ctab; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "sparse", C_SPARSE, 0, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, +}; + +static void +f_conv(char *arg) +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + cp = bsearch(&tmp, clist, sizeof(clist) / sizeof(struct conv), + sizeof(struct conv), c_conv); + if (cp == NULL) + errx(1, "unknown conversion %s", tmp.name); + if (ddflags & cp->noset) + errx(1, "%s: illegal conversion combination", tmp.name); + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(const void *a, const void *b) +{ + + return (strcmp(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +/* + * Convert an expression of the following forms to a quad_t. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1 << 10). + * 4) A positive decimal number followed by a m (mult by 1 << 20). + * 5) A positive decimal number followed by a g (mult by 1 << 30). + * 5) A positive decimal number followed by a w (mult by sizeof int). + * 6) Two or more positive decimal numbers (with/without [bkmgw]) + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static quad_t +get_num(char *val) +{ + quad_t num, t; + char *expr; + + errno = 0; + num = strtoq(val, &expr, 0); + if (errno != 0) /* Overflow or underflow. */ + err(1, "%s", oper); + + if (expr == val) /* No valid digits. */ + errx(1, "%s: illegal numeric value", oper); + + switch (*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1 << 10; + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1 << 20; + if (t > num) + goto erange; + ++expr; + break; + case 'g': + t = num; + num *= 1 << 30; + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + goto erange; + ++expr; + break; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'x': + t = num; + num *= get_num(expr + 1); + if (t <= num) + break; +erange: + errx(1, "%s: %s", oper, strerror(ERANGE)); + default: + errx(1, "%s: illegal numeric value", oper); + } + return (num); +} + +static off_t +get_offset(char *val) +{ + quad_t num; + + num = get_num(val); + if (num > QUAD_MAX) /* XXX can't happen && quad_t != off_t */ + errx(1, "%s: illegal offset", oper); /* Too big. */ + return ((off_t)num); +} diff --git a/file_cmds/dd/conv.c b/file_cmds/dd/conv.c new file mode 100644 index 00000000..59c7b015 --- /dev/null +++ b/file_cmds/dd/conv.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/conv.c,v 1.16 2002/02/02 06:24:12 imp Exp $"; +#endif /* not lint */ + +#include + +#include +#include + +#include "dd.h" +#include "extern.h" + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def(void) +{ + u_char *inp; + const u_char *t; + size_t cnt; + + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close(void) +{ + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block(void) +{ + u_char *inp, *outp; + const u_char *t; + size_t cnt, maxlen; + static int intrunc; + int ch; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; cnt && *inp++ != '\n'; --cnt) + ; + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + ch = 0; + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n'; + ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; cnt < maxlen && (ch = *inp++) != '\n'; + ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close(void) +{ + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, ctab ? ctab[' '] : ' ', + cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock(void) +{ + u_char *inp; + const u_char *t; + size_t cnt; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + for (cnt = in.dbrcnt, inp = in.dbp; cnt--;) { + *inp = t[*inp]; + --inp; + } + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t) + ; + if (t >= inp) { + cnt = t - inp + 1; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + *out.dbp++ = '\n'; + if (++out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close(void) +{ + u_char *t; + size_t cnt; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t) + ; + if (t >= in.db) { + cnt = t - in.db + 1; + (void)memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} diff --git a/file_cmds/dd/conv_tab.c b/file_cmds/dd/conv_tab.c new file mode 100644 index 00000000..a9f091ef --- /dev/null +++ b/file_cmds/dd/conv_tab.c @@ -0,0 +1,289 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/conv_tab.c,v 1.10 1999/09/12 16:51:53 green Exp $"; +#endif /* not lint */ + +#include + +/* + * There are currently six tables: + * + * ebcdic -> ascii 32V conv=oldascii + * ascii -> ebcdic 32V conv=oldebcdic + * ascii -> ibm ebcdic 32V conv=oldibm + * + * ebcdic -> ascii POSIX/S5 conv=ascii + * ascii -> ebcdic POSIX/S5 conv=ebcdic + * ascii -> ibm ebcdic POSIX/S5 conv=ibm + * + * Other tables are built from these if multiple conversions are being + * done. + * + * Tables used for conversions to/from IBM and EBCDIC to support an extension + * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted + * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were + * constructed by running against a file with all possible byte values. + * + * More information can be obtained in "Correspondences of 8-Bit and Hollerith + * Codes for Computer Environments-A USASI Tutorial", Communications of the + * ACM, Volume 11, Number 11, November 1968, pp. 783-789. + */ + +u_char casetab[256]; + +/* EBCDIC to ASCII -- 32V compatible. */ +const u_char e2a_32V[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- 32V compatible. */ +const u_char a2e_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- 32V compatible. */ +const u_char a2ibm_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- POSIX and System V compatible. */ +const u_char e2a_POSIX[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- POSIX and System V compatible. */ +const u_char a2e_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */ +const u_char a2ibm_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; diff --git a/file_cmds/dd/dd.1 b/file_cmds/dd/dd.1 new file mode 100644 index 00000000..8460813b --- /dev/null +++ b/file_cmds/dd/dd.1 @@ -0,0 +1,382 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dd.1 8.2 (Berkeley) 1/13/94 +.\" $FreeBSD: src/bin/dd/dd.1,v 1.19 2002/03/31 20:49:37 keramida Exp $ +.\" +.Dd January 13, 1994 +.Dt DD 1 +.Os +.Sh NAME +.Nm dd +.Nd convert and copy a file +.Sh SYNOPSIS +.Nm +.Op Ar operands ... +.Sh DESCRIPTION +The +.Nm +utility copies the standard input to the standard output. +Input data is read and written in 512-byte blocks. +If input reads are short, input from multiple reads are aggregated +to form the output block. +When finished, +.Nm +displays the number of complete and partial input and output blocks +and truncated input records to the standard error output. +.Pp +The following operands are available: +.\" XXX +.Bl -tag -width of=file +.It Cm bs Ns = Ns Ar n +Set both input and output block size to +.Ar n +bytes, superseding the +.Cm ibs +and +.Cm obs +operands. +If no conversion values other than +.Cm noerror , +.Cm notrunc +or +.Cm sync +are specified, then each input block is copied to the output as a +single block without any aggregation of short blocks. +.It Cm cbs Ns = Ns Ar n +Set the conversion record size to +.Ar n +bytes. +The conversion record size is required by the record oriented conversion +values. +.It Cm count Ns = Ns Ar n +Copy only +.Ar n +input blocks. +.It Cm files Ns = Ns Ar n +Copy +.Ar n +input files before terminating. +This operand is only applicable when the input device is a tape. +.It Cm ibs Ns = Ns Ar n +Set the input block size to +.Ar n +bytes instead of the default 512. +.It Cm if Ns = Ns Ar file +Read input from +.Ar file +instead of the standard input. +.It Cm iseek Ns = Ns Ar n +Seek on the input file +.Ar n +blocks. +This is synonymous with +.Cm skip Ns = Ns Ar n . +.It Cm obs Ns = Ns Ar n +Set the output block size to +.Ar n +bytes instead of the default 512. +.It Cm of Ns = Ns Ar file +Write output to +.Ar file +instead of the standard output. +Any regular output file is truncated unless the +.Cm notrunc +conversion value is specified. +If an initial portion of the output file is seeked past (see the +.Cm oseek +operand), +the output file is truncated at that point. +.It Cm oseek Ns = Ns Ar n +Seek on the output file +.Ar n +blocks. +This is synonymous with +.Cm seek Ns = Ns Ar n . +.It Cm seek Ns = Ns Ar n +Seek +.Ar n +blocks from the beginning of the output before copying. +On non-tape devices, an +.Xr lseek 2 +operation is used. +Otherwise, existing blocks are read and the data discarded. +If the user does not have read permission for the tape, it is positioned +using the tape +.Xr ioctl 2 +function calls. +If the seek operation is past the end of file, space from the current +end of file to the specified offset is filled with blocks of +.Dv NUL +bytes. +.It Cm skip Ns = Ns Ar n +Skip +.Ar n +blocks from the beginning of the input before copying. +On input which supports seeks, an +.Xr lseek 2 +operation is used. +Otherwise, input data is read and discarded. +For pipes, the correct number of bytes is read. +For all other devices, the correct number of blocks is read without +distinguishing between a partial or complete block being read. +.It Cm conv Ns = Ns Ar value Ns Op , Ns Ar value ... +Where +.Cm value +is one of the symbols from the following list. +.Bl -tag -width ".Cm unblock" +.It Cm ascii , oldascii +The same as the +.Cm unblock +value except that characters are translated from +.Tn EBCDIC +to +.Tn ASCII +before the +records are converted. +(These values imply +.Cm unblock +if the operand +.Cm cbs +is also specified.) +There are two conversion maps for +.Tn ASCII . +The value +.Cm ascii +specifies the recommended one which is compatible with +.At V . +The value +.Cm oldascii +specifies the one used in historic +.At +and +.No pre- Ns Bx 4.3 reno +systems. +.It Cm block +Treats the input as a sequence of newline or end-of-file terminated variable +length records independent of input and output block boundaries. +Any trailing newline character is discarded. +Each input record is converted to a fixed length output record where the +length is specified by the +.Cm cbs +operand. +Input records shorter than the conversion record size are padded with spaces. +Input records longer than the conversion record size are truncated. +The number of truncated input records, if any, are reported to the standard +error output at the completion of the copy. +.It Cm ebcdic , ibm , oldebcdic , oldibm +The same as the +.Cm block +value except that characters are translated from +.Tn ASCII +to +.Tn EBCDIC +after the +records are converted. +(These values imply +.Cm block +if the operand +.Cm cbs +is also specified.) +There are four conversion maps for +.Tn EBCDIC . +The value +.Cm ebcdic +specifies the recommended one which is compatible with +.At V . +The value +.Cm ibm +is a slightly different mapping, which is compatible with the +.At V +.Cm ibm +value. +The values +.Cm oldebcdic +and +.Cm oldibm +are maps used in historic +.At +and +.No pre- Ns Bx 4.3 reno +systems. +.It Cm lcase +Transform uppercase characters into lowercase characters. +.It Cm noerror +Do not stop processing on an input error. +When an input error occurs, a diagnostic message followed by the current +input and output block counts will be written to the standard error output +in the same format as the standard completion message. +If the +.Cm sync +conversion is also specified, any missing input data will be replaced +with +.Dv NUL +bytes (or with spaces if a block oriented conversion value was +specified) and processed as a normal input buffer. +If the +.Cm sync +conversion is not specified, the input block is omitted from the output. +On input files which are not tapes or pipes, the file offset +will be positioned past the block in which the error occurred using +.Xr lseek 2 . +.It Cm notrunc +Do not truncate the output file. +This will preserve any blocks in the output file not explicitly written +by +.Nm . +The +.Cm notrunc +value is not supported for tapes. +.It Cm osync +Pad the final output block to the full output block size. +If the input file is not a multiple of the output block size +after conversion, this conversion forces the final output block +to be the same size as preceding blocks for use on devices that require +regularly sized blocks to be written. +This option is incompatible with use of the +.Cm bs Ns = Ns Ar n +block size specification. +.It Cm sparse +If one or more output blocks would consist solely of +.Dv NUL +bytes, try to seek the output file by the required space instead of +filling them with +.Dv NUL Ns s , +resulting in a sparse file. +.It Cm swab +Swap every pair of input bytes. +If an input buffer has an odd number of bytes, the last byte will be +ignored during swapping. +.It Cm sync +Pad every input block to the input buffer size. +Spaces are used for pad bytes if a block oriented conversion value is +specified, otherwise +.Dv NUL +bytes are used. +.It Cm ucase +Transform lowercase characters into uppercase characters. +.It Cm unblock +Treats the input as a sequence of fixed length records independent of input +and output block boundaries. +The length of the input records is specified by the +.Cm cbs +operand. +Any trailing space characters are discarded and a newline character is +appended. +.El +.El +.Pp +Where sizes are specified, a decimal, octal, or hexadecimal number of +bytes is expected. +If the number ends with a +.Dq Li b , +.Dq Li k , +.Dq Li m , +.Dq Li g , +or +.Dq Li w , +the +number is multiplied by 512, 1024 (1K), 1048576 (1M), 1073741824 (1G) +or the number of bytes in an integer, respectively. +Two or more numbers may be separated by an +.Dq Li x +to indicate a product. +.Pp +When finished, +.Nm +displays the number of complete and partial input and output blocks, +truncated input records and odd-length byte-swapping blocks to the +standard error output. +A partial input block is one where less than the input block size +was read. +A partial output block is one where less than the output block size +was written. +Partial output blocks to tape devices are considered fatal errors. +Otherwise, the rest of the block will be written. +Partial output blocks to character devices will produce a warning message. +A truncated input block is one where a variable length record oriented +conversion value was specified and the input line was too long to +fit in the conversion record or was not newline terminated. +.Pp +Normally, data resulting from input or conversion or both are aggregated +into output blocks of the specified size. +After the end of input is reached, any remaining output is written as +a block. +This means that the final output block may be shorter than the output +block size. +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the +.Cm status +argument for +.Xr stty 1 ) +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message. +If +.Nm +receives a +.Dv SIGINT +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message and +.Nm +will exit. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr cp 1 , +.Xr tr 1 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +standard. +The +.Cm files +operand and the +.Cm ascii , +.Cm ebcdic , +.Cm ibm , +.Cm oldascii , +.Cm oldebcdic +and +.Cm oldibm +values are extensions to the +\*[Px] +standard. diff --git a/file_cmds/dd/dd.c b/file_cmds/dd/dd.c new file mode 100644 index 00000000..57311867 --- /dev/null +++ b/file_cmds/dd/dd.c @@ -0,0 +1,481 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/dd.c,v 1.36 2002/03/07 14:00:33 markm Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +static void dd_close(void); +static void dd_in(void); +static void getfdtype(IO *); +static void setup(void); + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc)(void); /* conversion function */ +quad_t cpy_cnt; /* # of blocks to copy */ +off_t pending = 0; /* pending seek if sparse */ +u_int ddflags; /* conversion options */ +size_t cbsz; /* conversion block size */ +quad_t files_cnt = 1; /* # of files to copy */ +const u_char *ctab; /* conversion table */ + +int +main(int argc, char *argv[]) +{ + (void)setlocale(LC_CTYPE, ""); + jcl(argv); + setup(); + + (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + + atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); +} + +static void +setup(void) +{ + u_int cnt; + struct timeval tv; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd == -1) + err(1, "%s", in.name); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + errx(1, "files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd == -1) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd == -1) + err(1, "%s", out.name); + } + + getfdtype(&out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK | C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err(1, "input buffer"); + out.db = in.db; + } else if ((in.db = malloc(MAX(in.dbsz, cbsz) + cbsz)) == NULL || + (out.db = malloc(out.dbsz + cbsz)) == NULL) + err(1, "output buffer"); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file. If it fails on a type of output file + * that it should _not_ fail on, error out. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK) && + out.flags & ISTRUNC) + if (ftruncate(out.fd, out.offset * out.dbsz) == -1) + err(1, "truncating %s", out.name); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE | C_UCASE)) { + if (ddflags & (C_ASCII | C_EBCDIC)) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = tolower((int)cnt); + } else { + for (cnt = 0; cnt <= 0377; ++cnt) + casetab[cnt] = toupper((int)cnt); + } + } + ctab = casetab; + } + + (void)gettimeofday(&tv, (struct timezone *)NULL); + st.start = tv.tv_sec + tv.tv_usec * 1e-6; +} + +static void +getfdtype(IO *io) +{ + struct stat sb; + int type; + + if (fstat(io->fd, &sb) == -1) + err(1, "%s", io->name); + if (S_ISREG(sb.st_mode)) + io->flags |= ISTRUNC; + if (S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode)) { + if (ioctl(io->fd, FIODTYPE, &type) == -1) { + err(1, "%s", io->name); + } else { +#ifdef __APPLE__ /* MacOSX uses enumeration for type not a bitmask */ + if (type == D_TAPE) + io->flags |= ISTAPE; + else if (type == D_DISK || type == D_TTY) { +#else /* !__APPLE__ */ + if (type & D_TAPE) + io->flags |= ISTAPE; + else if (type & (D_DISK | D_MEM)) { + if (type & D_DISK) { + const int one = 1; + + (void)ioctl(io->fd, DIOCWLABEL, &one); + } +#endif /* __APPLE__ */ + io->flags |= ISSEEK; + } +#ifdef __APPLE__ + if (S_ISCHR(sb.st_mode) && (type != D_TAPE)) +#else /* !__APPLE__ */ + if (S_ISCHR(sb.st_mode) && (type & D_TAPE) == 0) +#endif /* __APPLE__ */ + io->flags |= ISCHR; + } + return; + } + errno = 0; + if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; + else + io->flags |= ISSEEK; +} + +static void +dd_in(void) +{ + ssize_t n; + + for (;;) { + switch (cpy_cnt) { + case -1: /* count=0 was specified */ + return; + case 0: + break; + default: + if (st.in_full + st.in_part >= (u_quad_t)cpy_cnt) + return; + break; + } + + /* + * Zero the buffer first if sync; if doing block operations, + * use spaces. + */ + if (ddflags & C_SYNC) { + if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(in.dbp, ' ', in.dbsz); + else + memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n == -1) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(ddflags & C_NOERROR)) + err(1, "%s", in.name); + warn("%s", in.name); + summary(); + + /* + * If it's a seekable file descriptor, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (in.flags & ISSEEK && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s", in.name); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if ((size_t)n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbrcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, (size_t)n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Clean up any remaining I/O and flush output. If necessary, the output file + * is truncated. + */ +static void +dd_close(void) +{ + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt && out.dbcnt < out.dbsz) { + if (ddflags & (C_BLOCK | C_UNBLOCK)) + memset(out.dbp, ' ', out.dbsz - out.dbcnt); + else + memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt || pending) + dd_out(1); +} + +void +dd_out(int force) +{ + u_char *outp; + size_t cnt, i, n; + ssize_t nw; + static int warned; + int sparse; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + sparse = 0; + if (ddflags & C_SPARSE) { + sparse = 1; /* Is buffer sparse? */ + for (i = 0; i < cnt; i++) + if (outp[i] != 0) { + sparse = 0; + break; + } + } + if (sparse && !force) { + pending += cnt; + nw = cnt; + } else { + if (pending != 0) { + if (force) + pending--; + if (lseek(out.fd, pending, SEEK_CUR) == + -1) + err(2, "%s: seek error creating sparse file", + out.name); + if (force) + write(out.fd, outp, 1); + pending = 0; + } + if (cnt) + nw = write(out.fd, outp, cnt); + else + return; + } + + if (nw <= 0) { + if (nw == 0) + errx(1, "%s: end of device", out.name); + if (errno != EINTR) + err(1, "%s", out.name); + nw = 0; + } + outp += nw; + st.bytes += nw; + if ((size_t)nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if ((size_t)nw == cnt) + break; + if (out.flags & ISTAPE) + errx(1, "%s: short write on tape device", + out.name); + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; +} diff --git a/file_cmds/dd/dd.entitlements b/file_cmds/dd/dd.entitlements new file mode 100644 index 00000000..abb8bc5e --- /dev/null +++ b/file_cmds/dd/dd.entitlements @@ -0,0 +1,9 @@ + + + + + com.apple.private.security.disk-device-access + + + + diff --git a/file_cmds/dd/dd.h b/file_cmds/dd/dd.h new file mode 100644 index 00000000..d7e60a08 --- /dev/null +++ b/file_cmds/dd/dd.h @@ -0,0 +1,97 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + * $FreeBSD: src/bin/dd/dd.h,v 1.17 2002/02/22 20:51:00 markm Exp $ + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + /* XXX ssize_t? */ + size_t dbcnt; /* current buffer byte count */ + size_t dbrcnt; /* last read byte count */ + size_t dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe-like (see position.c) */ +#define ISTAPE 0x04 /* tape */ +#define ISSEEK 0x08 /* valid to seek on */ +#define NOREAD 0x10 /* not readable */ +#define ISTRUNC 0x20 /* valid to ftruncate() */ + u_int flags; + + const char *name; /* name */ + int fd; /* file descriptor */ + off_t offset; /* # of blocks to skip */ + +} IO; + +typedef struct { + u_quad_t in_full; /* # of full input blocks */ + u_quad_t in_part; /* # of partial input blocks */ + u_quad_t out_full; /* # of full output blocks */ + u_quad_t out_part; /* # of partial output blocks */ + u_quad_t trunc; /* # of truncated records */ + u_quad_t swab; /* # of odd-length swab blocks */ + u_quad_t bytes; /* # of bytes written */ + double start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 +#define C_OSYNC 0x100000 +#define C_SPARSE 0x200000 diff --git a/file_cmds/dd/extern.h b/file_cmds/dd/extern.h new file mode 100644 index 00000000..278c3688 --- /dev/null +++ b/file_cmds/dd/extern.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.3 (Berkeley) 4/2/94 + * $FreeBSD: src/bin/dd/extern.h,v 1.12 2002/02/02 06:24:12 imp Exp $ + */ + +#include + +void block(void); +void block_close(void); +void dd_out(int); +void def(void); +void def_close(void); +void jcl(char **); +void pos_in(void); +void pos_out(void); +void summary(void); +void summaryx(int); +void terminate(int); +void unblock(void); +void unblock_close(void); + +extern IO in, out; +extern STAT st; +extern void (*cfunc)(void); +extern quad_t cpy_cnt; +extern size_t cbsz; +extern u_int ddflags; +extern quad_t files_cnt; +extern const u_char *ctab; +extern const u_char a2e_32V[], a2e_POSIX[]; +extern const u_char e2a_32V[], e2a_POSIX[]; +extern const u_char a2ibm_32V[], a2ibm_POSIX[]; +extern u_char casetab[]; diff --git a/file_cmds/dd/install_symlink.sh b/file_cmds/dd/install_symlink.sh new file mode 100644 index 00000000..7e8665f6 --- /dev/null +++ b/file_cmds/dd/install_symlink.sh @@ -0,0 +1,16 @@ +#!/bin/sh +set -e +set -x + +case "$PLATFORM_NAME" in +iphoneos|appletvos|watchos|bridgeos) + ln -hfs /usr/local/bin/dd "$DSTROOT"/bin/dd + ;; +macosx) + ;; +*) + echo "Unsupported platform: $PLATFORM_NAME" + exit 1 + ;; +esac + diff --git a/file_cmds/dd/misc.c b/file_cmds/dd/misc.c new file mode 100644 index 00000000..b2203346 --- /dev/null +++ b/file_cmds/dd/misc.c @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/misc.c,v 1.23 2002/02/02 06:24:12 imp Exp $"; +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +void +summary(void) +{ + struct timeval tv; + double secs; + char buf[100]; + + (void)gettimeofday(&tv, (struct timezone *)NULL); + secs = tv.tv_sec + tv.tv_usec * 1e-6 - st.start; + if (secs < 1e-6) + secs = 1e-6; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%qu+%qu records in\n%qu+%qu records out\n", + st.in_full, st.in_part, st.out_full, st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%qu odd length swab %s\n", + st.swab, (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%qu truncated %s\n", + st.trunc, (st.trunc == 1) ? "record" : "records"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%qu bytes transferred in %.6f secs (%.0f bytes/sec)\n", + st.bytes, secs, st.bytes / secs); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +summaryx(int notused) +{ + int save_errno = errno; + + summary(); + errno = save_errno; +} + +/* ARGSUSED */ +void +terminate(int sig) +{ + + summary(); + _exit(sig == 0 ? 0 : 1); +} diff --git a/file_cmds/dd/position.c b/file_cmds/dd/position.c new file mode 100644 index 00000000..c110cd3f --- /dev/null +++ b/file_cmds/dd/position.c @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/bin/dd/position.c,v 1.20 2002/02/02 06:24:12 imp Exp $"; +#endif /* not lint */ + +#include + +#ifdef __APPLE__ +#include +#endif + +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in(void) +{ + off_t cnt; + int warned; + ssize_t nr; + size_t bcnt; + + /* If known to be seekable, try to seek on it. */ + if (in.flags & ISSEEK) { + errno = 0; + if (lseek(in.fd, in.offset * in.dbsz, SEEK_CUR) == -1 && + errno != 0) + err(1, "%s", in.name); + return; + } + + /* Don't try to read a really weird amount (like negative). */ + if (in.offset < 0) + errx(1, "%s: illegal offset", "iseek/skip"); + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + errx(1, "skip reached end of input"); + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + warn("%s", in.name); + warned = 1; + summary(); + } + continue; + } + err(1, "%s", in.name); + } +} + +void +pos_out(void) +{ + off_t cnt; + ssize_t n; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (out.flags & (ISSEEK | ISPIPE)) { + errno = 0; + if (lseek(out.fd, out.offset * out.dbsz, SEEK_CUR) == -1 && + errno != 0) + err(1, "%s", out.name); + return; + } + + /* Don't try to read a really weird amount (like negative). */ + if (out.offset < 0) + errx(1, "%s: illegal offset", "oseek/seek"); + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n == -1) + err(1, "%s", out.name); + + while (cnt++ < out.offset) { + n = write(out.fd, out.db, out.dbsz); + if (n == -1) + err(1, "%s", out.name); + if ((size_t)n != out.dbsz) + errx(1, "%s: write failure", out.name); + } + break; + } +} diff --git a/file_cmds/df/df.1 b/file_cmds/df/df.1 new file mode 100644 index 00000000..4560ab95 --- /dev/null +++ b/file_cmds/df/df.1 @@ -0,0 +1,217 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)df.1 8.3 (Berkeley) 5/8/95 +.\" $FreeBSD: src/bin/df/df.1,v 1.18.2.7 2002/04/22 22:17:36 keramida Exp $ +.\" +.Dd May 8, 1995 +.Dt DF 1 +.Os +.Sh NAME +.Nm df +.Nd display free disk space +.Sh SYNOPSIS +.Nm df +.Oo +.Fl b | h | H | k | +.Fl m | g | P +.Oc +.Op Fl ailn +.Op Fl t +.Op Fl T Ar type +.Op Ar file | filesystem ... +.Sh LEGACY SYNOPSIS +.Nm df +.Oo +.Fl b | h | H | k | +.Fl m | P +.Oc +.Op Fl ailn +.Op Fl t Ar type +.Op Fl T Ar type +.Op Ar file | filesystem ... +.Sh DESCRIPTION +The +.Nm df +utility +displays statistics about the amount of free disk space on the specified +.Ar filesystem +or on the filesystem of which +.Ar file +is a part. +Values are displayed in 512-byte per block counts. +If neither a file or a filesystem operand is specified, +statistics for all mounted filesystems are displayed +(subject to the +.Fl t +option below). +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Show all mount points, including those that were mounted with the MNT_IGNORE +flag. +.It Fl b +Use (the default) 512-byte blocks. +This is only useful as a way to override an +.Ev BLOCKSIZE +specification from the environment. +.It Fl g +Use 1073741824-byte (1-Gbyte) blocks rather than the default. +Note that this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl H +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 10 for sizes. +.It Fl h +"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte in order to reduce the number of +digits to three or less using base 2 for sizes. +.It Fl i +Include statistics on the number of free inodes. This option is now the default to conform to +.St -susv3 +Use +.Fl P +to suppress this output. +.It Fl k +Use 1024-byte (1-Kbyte) blocks, rather than the default. +Note that this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl l +Only display information about locally-mounted filesystems. +.It Fl m +Use 1048576-byte (1-Mbyte) blocks rather than the default. Note that +this overrides the +.Ev BLOCKSIZE +specification from the environment. +.It Fl n +Print out the previously obtained statistics from the filesystems. +This option should be used if it is possible that one or more +filesystems are in a state such that they will not be able to provide +statistics without a long delay. +When this option is specified, +.Nm df +will not request new statistics from the filesystems, but will respond +with the possibly stale statistics that were previously obtained. +.It Fl P +Use (the default) 512-byte blocks. +This is only useful as a way to override an +.Ev BLOCKSIZE +specification from the environment. +.It Fl T +Only print out statistics for filesystems of the specified types. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. +For example, the +.Nm df +command: +.Bd -literal -offset indent +df -T nonfs,mfs +.Ed +.Pp +lists all filesystems except those of type +.Tn NFS +and +.Tn MFS . +The +.Xr lsvfs 1 +command can be used to find out the types of filesystems +that are available on the system. +.It Fl t +If used with no arguments, +this option is a no-op +(Mac OS X already prints the total allocated-space figures). +If used with an argument, it acts like +.Fl T , +but this usage is deprecated and should not be relied upon. +.El +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, the block counts will be displayed in units of that size block. +.El +.Sh BUGS +The +.Fl n +and +.Fl t +flags are ignored if a file or filesystem is specified. +.Sh LEGACY DESCRIPTION +The "capacity" percentage is normally rounded up to the next higher integer. +In legacy mode, it is rounded down to the next lower integer. +.Pp +When the +.Fl P +option and the +.Fl k +option are used together, +sizes are reported in 1024-blocks. +In legacy mode, when the +.Fl P +option and +.Fl k +option are used together, +the last option specified dictates the reported block size. +.Pp +The +.Fl t +option is normally a no-op +(Mac OS X already prints the total allocated-space figures). +In legacy mode, it is equivalent to +.Fl T . +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr lsvfs 1 , +.Xr quota 1 , +.Xr fstatfs 2 , +.Xr getfsstat 2 , +.Xr statfs 2 , +.Xr getmntinfo 3 , +.Xr compat 5 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr quot 8 +.Sh HISTORY +A +.Nm df +command appeared in +.At v1 . diff --git a/file_cmds/df/df.c b/file_cmds/df/df.c new file mode 100644 index 00000000..649c1b73 --- /dev/null +++ b/file_cmds/df/df.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 1980, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)df.c 8.9 (Berkeley) 5/8/95"; +#else +__used static const char rcsid[] = + "$FreeBSD: src/bin/df/df.c,v 1.23.2.9 2002/07/01 00:14:24 iedowse Exp $"; +#endif +#endif /* not lint */ + +#ifdef __APPLE__ +#define MNT_IGNORE 0 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "libutil.h" +#include "ios_error.h" + +#ifdef __APPLE__ +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif + +#define UNITS_SI 1 +#define UNITS_2 2 + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +/* Maximum widths of various fields. */ +struct maxwidths { + int mntfrom; + int total; + int used; + int avail; + int iused; + int ifree; +}; + +static unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +static unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +static unit_t unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +static int bread(off_t, void *, int); +int checkvfsname(const char *, char **); +static char *getmntpt(char *); +static int int64width(int64_t); +static char *makenetvfslist(void); +char **makevfslist(const char *); +static void prthuman(struct statfs *, uint64_t); +static void prthumanval(int64_t); +static void prtstat(struct statfs *, struct maxwidths *); +static long regetmntinfo(struct statfs **, long, char **); +static unit_t unit_adjust(double *); +static void update_maxwidths(struct maxwidths *, struct statfs *); +static void usage(void); + +static int aflag = 0, hflag, iflag, nflag; +static int headerlen, timesthrough; + +static __inline int imax(int a, int b) +{ + return (a > b ? a : b); +} + +int +df_main(int argc, char *argv[]) +{ + struct stat stbuf; + struct statfs statfsbuf, *mntbuf; + struct maxwidths maxwidths; + char *mntpt, **vfslist; + long mntsize; + int ch, i, rv, tflag = 0, kludge_tflag = 0; + int kflag = 0; + // always init the flags: + aflag = hflag = iflag = nflag = 0; + headerlen = 0; timesthrough = 0; + optind = 1; opterr = 1; optreset = 1; + + const char *options = "abgHhiklmnPt:T:"; + if (COMPAT_MODE("bin/df", "unix2003")) { + /* Unix2003 requires -t be "include total capacity". which df + already does, but it conflicts with the old -t so we need to + *not* expect a string after -t (we provide -T in both cases + to cover the old use of -t) */ + options = "abgHhiklmnPtT:"; + // iflag = 1; + iflag = 0; + } + + vfslist = NULL; + while ((ch = getopt(argc, argv, options)) != -1) + switch (ch) { + case 'a': + aflag = 1; + break; + case 'b': + /* FALLTHROUGH */ + case 'P': + if (COMPAT_MODE("bin/df", "unix2003")) { + if (!kflag) { + /* -k overrides -P */ + putenv("BLOCKSIZE=512"); + } + iflag = 0; + } else { + putenv("BLOCKSIZE=512"); + } + hflag = 0; + break; + case 'g': + putenv("BLOCKSIZE=1g"); + hflag = 0; + break; + case 'H': + hflag = UNITS_SI; + valp = vals_si; + break; + case 'h': + hflag = UNITS_2; + valp = vals_base2; + break; + case 'i': + iflag = 1; + break; + case 'k': + if (COMPAT_MODE("bin/df", "unix2003")) { + putenv("BLOCKSIZE=1024"); + } else { + putenv("BLOCKSIZE=1k"); + } + kflag = 1; + hflag = 0; + break; + case 'l': + if (tflag) { + errx(1, "-l and -T are mutually exclusive."); + } + if (vfslist != NULL) + break; + vfslist = makevfslist(makenetvfslist()); + break; + case 'm': + putenv("BLOCKSIZE=1m"); + hflag = 0; + break; + case 'n': + nflag = 1; + break; + case 't': + /* Unix2003 uses -t for something we do by default */ + if (COMPAT_MODE("bin/df", "unix2003")) { + kludge_tflag = 1; + break; + } + case 'T': + if (vfslist != NULL) { + if (tflag) { + errx(1, "only one -%c option may be specified", ch); + } + else { + errx(1, "-l and -%c are mutually exclusive.", ch); + } + } + tflag++; + vfslist = makevfslist(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* If we are in unix2003 mode, have seen a -t but no -T and the first + non switch arg isn't a file, let's pretend they used -T on it. + This makes the Lexmark printer installer happy (PR-3918471) */ + if (tflag == 0 && kludge_tflag && *argv && stat(*argv, &stbuf) < 0 + && errno == ENOENT) { + vfslist = makevfslist(*argv++); + } + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + bzero(&maxwidths, sizeof(maxwidths)); + for (i = 0; i < mntsize; i++) + update_maxwidths(&maxwidths, &mntbuf[i]); + + rv = 0; + if (!*argv) { + mntsize = regetmntinfo(&mntbuf, mntsize, vfslist); + bzero(&maxwidths, sizeof(maxwidths)); + for (i = 0; i < mntsize; i++) + update_maxwidths(&maxwidths, &mntbuf[i]); + for (i = 0; i < mntsize; i++) { + if (aflag || (mntbuf[i].f_flags & MNT_IGNORE) == 0) + prtstat(&mntbuf[i], &maxwidths); + } + exit(rv); + } + + for (; *argv; argv++) { + if (stat(*argv, &stbuf) < 0) { + if ((mntpt = getmntpt(*argv)) == 0) { + warn("%s", *argv); + rv = 1; + continue; + } + } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { + warnx("%s: Raw devices not supported", *argv); + rv = 1; + continue; + } else + mntpt = *argv; + /* + * Statfs does not take a `wait' flag, so we cannot + * implement nflag here. + */ + if (statfs(mntpt, &statfsbuf) < 0) { + warn("%s", mntpt); + rv = 1; + continue; + } + /* Check to make sure the arguments we've been + * given are satisfied. Return an error if we + * have been asked to list a mount point that does + * not match the other args we've been given (-l, -t, etc.) + */ + if (checkvfsname(statfsbuf.f_fstypename, vfslist)) { + rv++; + continue; + } + + if (argc == 1) { + bzero(&maxwidths, sizeof(maxwidths)); + update_maxwidths(&maxwidths, &statfsbuf); + } + prtstat(&statfsbuf, &maxwidths); + } + return (rv); +} + +char * +getmntpt(char *name) +{ + long mntsize, i; + struct statfs *mntbuf; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (!strcmp(mntbuf[i].f_mntfromname, name)) + return (mntbuf[i].f_mntonname); + } + return (0); +} + +/* + * Make a pass over the filesystem info in ``mntbuf'' filtering out + * filesystem types not in vfslist and possibly re-stating to get + * current (not cached) info. Returns the new count of valid statfs bufs. + */ +long +regetmntinfo(struct statfs **mntbufp, long mntsize, char **vfslist) +{ + int i, j; + struct statfs *mntbuf; + + if (vfslist == NULL) + return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); + + mntbuf = *mntbufp; + for (j = 0, i = 0; i < mntsize; i++) { + if (checkvfsname(mntbuf[i].f_fstypename, vfslist)) + continue; + if (!nflag) + (void)statfs(mntbuf[i].f_mntonname,&mntbuf[j]); + else if (i != j) + mntbuf[j] = mntbuf[i]; + j++; + } + return (j); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(double *val) +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthuman(struct statfs *sfsp, uint64_t used) +{ + int64_t value; + + value = sfsp->f_blocks; + value *= sfsp->f_bsize; + prthumanval(value); + value = used; + value *= sfsp->f_bsize; + prthumanval(value); + value = sfsp->f_bavail; + value *= sfsp->f_bsize; + prthumanval(value); +} + +void +prthumanval(int64_t bytes) +{ + char buf[6]; + int flags; + + flags = HN_B | HN_NOSPACE | HN_DECIMAL; + if (hflag == UNITS_SI) + flags |= HN_DIVISOR_1000; + + humanize_number(buf, sizeof(buf) - (bytes < 0 ? 0 : 1), + bytes, "", HN_AUTOSCALE, flags); + + if (hflag == UNITS_SI) + (void)fprintf(thread_stdout, " %6s", buf); + else + (void)fprintf(thread_stdout, "%6si", buf); + +} + +/* + * Convert statfs returned filesystem size into BLOCKSIZE units. + * Attempts to avoid overflow for large filesystems. + */ +static intmax_t fsbtoblk(int64_t num, uint64_t fsbs, u_long bs, char *fs) +{ + if (num < 0) { + warnx("negative filesystem block count/size from fs %s", fs); + return 0; + } else if ((fsbs != 0) && (fsbs < bs)) { + return (num / (intmax_t) (bs / fsbs)); + } else { + return (num * (intmax_t) (fsbs / bs)); + } +} + +/* + * Print out status about a filesystem. + */ +void +prtstat(struct statfs *sfsp, struct maxwidths *mwp) +{ + static long blocksize; + // static int headerlen, timesthrough; + static const char *header; + uint64_t used, availblks, inodes; + char * avail_str; + + if (++timesthrough == 1) { + mwp->mntfrom = imax(mwp->mntfrom, (int)strlen("Filesystem")); + if (hflag) { + header = " Size"; + mwp->total = mwp->used = mwp->avail = (int)strlen(header); + } else { + header = getbsize(&headerlen, &blocksize); + mwp->total = imax(mwp->total, headerlen); + } + mwp->used = imax(mwp->used, (int)strlen("Used")); + if (COMPAT_MODE("bin/df", "unix2003") && !hflag) { + avail_str = "Available"; + } else { + avail_str = "Avail"; + } + mwp->avail = imax(mwp->avail, (int)strlen(avail_str)); + + (void)fprintf(thread_stdout, "%-*s %*s %*s %*s Capacity", mwp->mntfrom, + "Filesystem", mwp->total, header, mwp->used, "Used", + mwp->avail, avail_str); + if (iflag) { + mwp->iused = imax(mwp->iused, (int)strlen(" iused")); + mwp->ifree = imax(mwp->ifree, (int)strlen("ifree")); + (void)fprintf(thread_stdout, " %*s %*s %%iused", mwp->iused - 2, + "iused", mwp->ifree, "ifree"); + } + (void)fprintf(thread_stdout, " Mounted on\n"); + } + + (void)fprintf(thread_stdout, "%-*s", mwp->mntfrom, sfsp->f_mntfromname); + if (sfsp->f_blocks > sfsp->f_bfree) + used = sfsp->f_blocks - sfsp->f_bfree; + else + used = 0; + availblks = sfsp->f_bavail + used; + if (hflag) { + prthuman(sfsp, used); + } else { + (void)fprintf(thread_stdout, " %*jd %*jd %*jd", mwp->total, + fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize, sfsp->f_mntonname), + mwp->used, fsbtoblk(used, sfsp->f_bsize, blocksize, sfsp->f_mntonname), + mwp->avail, fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize, sfsp->f_mntonname)); + } + if (COMPAT_MODE("bin/df", "unix2003")) { + /* Standard says percentage must be rounded UP to next + integer value, not truncated */ + double value; + if (availblks == 0) + value = 100.0; + else { + value = (double)used / (double)availblks * 100.0; + if ((value-(int)value) > 0.0) value = value + 1.0; + } + (void)fprintf(thread_stdout, " %5.0f%%", trunc(value)); + } else { + (void)fprintf(thread_stdout, " %5.0f%%", + availblks == 0 ? 100.0 : (double)used / (double)availblks * 100.0); + } + if (iflag) { + inodes = sfsp->f_files; + used = inodes - sfsp->f_ffree; + (void)fprintf(thread_stdout, " %*llu %*llu %4.0f%% ", mwp->iused, used, + mwp->ifree, sfsp->f_ffree, inodes == 0 ? 100.0 : + (double)used / (double)inodes * 100.0); + } else + (void)fprintf(thread_stdout, " "); + (void)fprintf(thread_stdout, " %s\n", sfsp->f_mntonname); +} + +/* + * Update the maximum field-width information in `mwp' based on + * the filesystem specified by `sfsp'. + */ +void +update_maxwidths(struct maxwidths *mwp, struct statfs *sfsp) +{ + static long blocksize; + int dummy; + + if (blocksize == 0) + getbsize(&dummy, &blocksize); + + mwp->mntfrom = imax(mwp->mntfrom, (int)strlen(sfsp->f_mntfromname)); + mwp->total = imax(mwp->total, int64width(fsbtoblk(sfsp->f_blocks, + sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + if (sfsp->f_blocks >= sfsp->f_bfree) + mwp->used = imax(mwp->used, int64width(fsbtoblk(sfsp->f_blocks - + sfsp->f_bfree, sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + mwp->avail = imax(mwp->avail, int64width(fsbtoblk(sfsp->f_bavail, + sfsp->f_bsize, blocksize, sfsp->f_mntonname))); + mwp->iused = imax(mwp->iused, int64width(sfsp->f_files - sfsp->f_ffree)); + mwp->ifree = imax(mwp->ifree, int64width(sfsp->f_ffree)); +} + +/* Return the width in characters of the specified long. */ +int +int64width(int64_t val) +{ + int len; + + len = 0; + /* Negative or zero values require one extra digit. */ + if (val <= 0) { + val = -val; + len++; + } + while (val > 0) { + len++; + val /= 10; + } + + return (len); +} + +void +usage(void) +{ + + char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]"; + (void)fprintf(thread_stderr, + "usage: df [-b | -H | -h | -k | -m | -g | -P] [-ailn] [-T type] %s [filesystem ...]\n", t_flag); + exit(EX_USAGE); +} + +char * +makenetvfslist(void) +{ + char *str, *strptr, **listptr; +#ifndef __APPLE__ + int mib[3], maxvfsconf, cnt=0, i; + size_t miblen; + struct ovfsconf *ptr; +#else + int mib[4], maxvfsconf, cnt=0, i; + size_t miblen; + struct vfsconf vfc; +#endif + + mib[0] = CTL_VFS; mib[1] = VFS_GENERIC; mib[2] = VFS_MAXTYPENUM; + miblen=sizeof(maxvfsconf); + if (sysctl(mib, 3, + &maxvfsconf, &miblen, NULL, 0)) { + warn("sysctl failed"); + return (NULL); + } + + if ((listptr = malloc(sizeof(char*) * maxvfsconf)) == NULL) { + warnx("malloc failed"); + return (NULL); + } + +#ifndef __APPLE__ + for (ptr = getvfsent(); ptr; ptr = getvfsent()) + if (ptr->vfc_flags & VFCF_NETWORK) { + listptr[cnt++] = strdup(ptr->vfc_name); + if (listptr[cnt-1] == NULL) { + warnx("malloc failed"); + return (NULL); + } + } +#else + miblen = sizeof (struct vfsconf); + mib[2] = VFS_CONF; + for (i = 0; i < maxvfsconf; i++) { + mib[3] = i; + if (sysctl(mib, 4, &vfc, &miblen, NULL, 0) == 0) { + if (!(vfc.vfc_flags & MNT_LOCAL)) { + listptr[cnt++] = strdup(vfc.vfc_name); + if (listptr[cnt-1] == NULL) { + free(listptr); + warnx("malloc failed"); + return (NULL); + } + } + } + } +#endif + + if (cnt == 0 || + (str = malloc(sizeof(char) * (32 * cnt + cnt + 2))) == NULL) { + if (cnt > 0) + warnx("malloc failed"); + free(listptr); + return (NULL); + } + + *str = 'n'; *(str + 1) = 'o'; + for (i = 0, strptr = str + 2; i < cnt; i++, strptr++) { + strncpy(strptr, listptr[i], 32); + strptr += strlen(listptr[i]); + *strptr = ','; + free(listptr[i]); + } + *(--strptr) = '\0'; + + free(listptr); + return (str); +} diff --git a/file_cmds/df/vfslist.c b/file_cmds/df/vfslist.c new file mode 100644 index 00000000..e21eb32d --- /dev/null +++ b/file_cmds/df/vfslist.c @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)vfslist.c 8.1 (Berkeley) 5/8/95"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/sbin/mount/vfslist.c,v 1.4 1999/08/28 00:13:27 peter Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifndef __APPLE__ +#include "extern.h" +#endif + +static int skipvfs; + +int checkvfsname(const char *, const char **); +const char **makevfslist(const char *); + +int +checkvfsname(vfsname, vfslist) + const char *vfsname; + const char **vfslist; +{ + + if (vfslist == NULL) + return (0); + while (*vfslist != NULL) { + if (strcmp(vfsname, *vfslist) == 0) + return (skipvfs); + ++vfslist; + } + return (!skipvfs); +} + +const char ** +makevfslist(fslist) + const char *fslist; +{ + const char **av; + int i; + const char *cnextcp; + char *nextcp; + + if (fslist == NULL) + return (NULL); + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + skipvfs = 1; + } + for (i = 0, cnextcp = fslist; *cnextcp; cnextcp++) + if (*cnextcp == ',') + i++; + if ((av = malloc((size_t)(i + 2) * sizeof(char *))) == NULL) { + warnx("malloc failed"); + return (NULL); + } + nextcp = strdup(fslist); + i = 0; + av[i++] = nextcp; + while ((nextcp = strchr(nextcp, ',')) != NULL) { + *nextcp++ = '\0'; + av[i++] = nextcp; + } + av[i++] = NULL; + return (av); +} diff --git a/file_cmds/du/du.1 b/file_cmds/du/du.1 new file mode 100644 index 00000000..1bc6b2b6 --- /dev/null +++ b/file_cmds/du/du.1 @@ -0,0 +1,172 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)du.1 8.2 (Berkeley) 4/1/94 +.\" $FreeBSD: src/usr.bin/du/du.1,v 1.30 2005/05/21 09:55:05 ru Exp $ +.\" +.Dd June 2, 2004 +.Dt DU 1 +.Os +.Sh NAME +.Nm du +.Nd display disk usage statistics +.Sh SYNOPSIS +.Nm du +.Op Fl H | L | P +.Op Fl a | s | d Ar depth +.Op Fl c +.Op Fl h | k | m | g +.Op Fl x +.Op Fl I Ar mask +.Op Ar +.Sh DESCRIPTION +The +.Nm du +utility displays the file system block usage for each file argument +and for each directory in the file hierarchy rooted in each directory +argument. +If no file is specified, the block usage of the hierarchy rooted in +the current directory is displayed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a +Display an entry for each file in a file hierarchy. +.It Fl c +Display a grand total. +.It Fl d Ar depth +Display an entry for all files and directories +.Ar depth +directories deep. +.It Fl H +Symbolic links on the command line are followed, symbolic links in file +hierarchies are not followed. +.It Fl h +"Human-readable" output. +Use unit suffixes: Byte, Kilobyte, Megabyte, +Gigabyte, Terabyte and Petabyte. +.It Fl I Ar mask +Ignore files and directories matching the specified +.Ar mask . +.It Fl g +Display block counts in 1073741824-byte (1-Gbyte) blocks. +.It Fl k +Display block counts in 1024-byte (1-Kbyte) blocks. +.It Fl L +Symbolic links on the command line and in file hierarchies are followed. +.It Fl m +Display block counts in 1048576-byte (1-Mbyte) blocks. +.It Fl P +No symbolic links are followed. +This is the default. +.It Fl r +Generate messages about directories that cannot be read, files +that cannot be opened, and so on. +This is the default case. +This option exists solely for conformance with +.St -xpg4 . +.It Fl s +Display an entry for each specified file. +(Equivalent to +.Fl d Li 0 ) +.It Fl x +File system mount points are not traversed. +.El +.Pp +The +.Nm du +utility counts the storage used by symbolic links and not the files they +reference unless the +.Fl H +or +.Fl L +option is specified. +If either the +.Fl H +or +.Fl L +options are specified, storage used by any symbolic links which are +followed is not counted or displayed. +If more than one of the +.Fl H , +.Fl L , +and +.Fl P +options is specified, the last one given is used. +.Pp +Files having multiple hard links are counted (and displayed) a single +time per +.Nm du +execution. +Directories having multiple hard links (typically Time Machine backups) are +counted a single time per +.Nm du +execution. +.Sh ENVIRONMENT +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts will be displayed in units of that +size block. +If +.Ev BLOCKSIZE +is not set, and the +.Fl k +option is not specified, the block counts will be displayed in 512-byte blocks. +.El +.Sh LEGACY DESCRIPTION +In legacy mode, only one of the +.Fl H , +.Fl L , +or +.Fl P +options may be specified. +.Pp +The command will detect and report a SYMLOOP error +(loop involving symbolic links). +In legacy mode, this is not the case. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr df 1 , +.Xr fts 3 , +.Xr compat 5 , +.Xr symlink 7 , +.Xr quot 8 +.Sh HISTORY +A +.Nm du +command appeared in +.At v1 . diff --git a/file_cmds/du/du.c b/file_cmds/du/du.c new file mode 100644 index 00000000..8ecdd7a7 --- /dev/null +++ b/file_cmds/du/du.c @@ -0,0 +1,751 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/du/du.c,v 1.38 2005/04/09 14:31:40 stefanf Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(func, mode) (1) +#endif + +#define KILO_SZ(n) (n) +#define MEGA_SZ(n) ((n) * (n)) +#define GIGA_SZ(n) ((n) * (n) * (n)) +#define TERA_SZ(n) ((n) * (n) * (n) * (n)) +#define PETA_SZ(n) ((n) * (n) * (n) * (n) * (n)) + +#define KILO_2_SZ (KILO_SZ(1024ULL)) +#define MEGA_2_SZ (MEGA_SZ(1024ULL)) +#define GIGA_2_SZ (GIGA_SZ(1024ULL)) +#define TERA_2_SZ (TERA_SZ(1024ULL)) +#define PETA_2_SZ (PETA_SZ(1024ULL)) + +#define KILO_SI_SZ (KILO_SZ(1000ULL)) +#define MEGA_SI_SZ (MEGA_SZ(1000ULL)) +#define GIGA_SI_SZ (GIGA_SZ(1000ULL)) +#define TERA_SI_SZ (TERA_SZ(1000ULL)) +#define PETA_SI_SZ (PETA_SZ(1000ULL)) + +#define TWO_TB (2LL * 1024LL * 1024LL * 1024LL * 1024LL) + +static unsigned long long vals_si [] = {1, KILO_SI_SZ, MEGA_SI_SZ, GIGA_SI_SZ, TERA_SI_SZ, PETA_SI_SZ}; +static unsigned long long vals_base2[] = {1, KILO_2_SZ, MEGA_2_SZ, GIGA_2_SZ, TERA_2_SZ, PETA_2_SZ}; +static unsigned long long *valp; + +typedef enum { NONE, KILO, MEGA, GIGA, TERA, PETA, UNIT_MAX } unit_t; + +static int unitp [] = { NONE, KILO, MEGA, GIGA, TERA, PETA }; + +SLIST_HEAD(ignhead, ignentry) ignores; +struct ignentry { + char *mask; + SLIST_ENTRY(ignentry) next; +}; + +static int linkchk(FTSENT *); +static int dirlinkchk(FTSENT *); +static void usage(void); +static void prthumanval(double); +static unit_t unit_adjust(double *); +static void ignoreadd(const char *); +static void ignoreclean(void); +static int ignorep(FTSENT *); + +int +du_main(int argc, char *argv[]) +{ + FTS *fts; + FTSENT *p; + off_t savednumber = 0; + long blocksize; + int ftsoptions; + int listall; + int depth; + int Hflag, Lflag, Pflag, aflag, sflag, dflag, cflag, hflag, ch, notused, rval; + char **save; + static char dot[] = "."; + off_t *ftsnum, *ftsparnum; + + setlocale(LC_ALL, ""); + + Hflag = Lflag = Pflag = aflag = sflag = dflag = cflag = hflag = 0; + optind = 1; opterr = 1; optreset = 1; + + save = argv; + ftsoptions = FTS_NOCHDIR; + depth = INT_MAX; + SLIST_INIT(&ignores); + + while ((ch = getopt(argc, argv, "HI:LPasd:cghkmrx")) != -1) + switch (ch) { + case 'H': + Lflag = Pflag = 0; + Hflag = 1; + break; + case 'I': + ignoreadd(optarg); + break; + case 'L': + Hflag = Pflag = 0; + Lflag = 1; + break; + case 'P': + Hflag = Lflag = 0; + Pflag = 1; + break; + case 'a': + aflag = 1; + break; + case 's': + sflag = 1; + break; + case 'd': + dflag = 1; + errno = 0; + depth = atoi(optarg); + if (errno == ERANGE || depth < 0) { + warnx("invalid argument to option d: %s", optarg); + usage(); + } + break; + case 'c': + cflag = 1; + break; + case 'h': + putenv("BLOCKSIZE=512"); + hflag = 1; + valp = vals_base2; + break; + case 'k': + hflag = 0; + putenv("BLOCKSIZE=1024"); + break; + case 'm': + hflag = 0; + putenv("BLOCKSIZE=1048576"); + break; + case 'g': + hflag = 0; + putenv("BLOCKSIZE=1g"); + break; + case 'r': /* Compatibility. */ + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + +// argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + + if (Hflag + Lflag + Pflag > 1) + usage(); + + if (Hflag + Lflag + Pflag == 0) + Pflag = 1; /* -P (physical) is default */ + + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + + if (Lflag) + ftsoptions |= FTS_LOGICAL; + + if (Pflag) + ftsoptions |= FTS_PHYSICAL; + + listall = 0; + + if (aflag) { + if (sflag || dflag) + usage(); + listall = 1; + } else if (sflag) { + if (dflag) + usage(); + depth = 0; + } + + if (!*argv) { + argv = save; + argv[0] = dot; + argv[1] = NULL; + } + + (void) getbsize(¬used, &blocksize); + blocksize /= 512; + + rval = 0; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) { + err(1, "fts_open"); + } + + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_D: + if (ignorep(p) || dirlinkchk(p)) + fts_set(fts, p, FTS_SKIP); + break; + case FTS_DP: + if (ignorep(p)) + break; + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + ftsnum = (off_t *)&p->fts_number; + if (p->fts_statp->st_size < TWO_TB) { + ftsparnum[0] += ftsnum[0] += p->fts_statp->st_blocks; + } else { + ftsparnum[0] += ftsnum[0] += howmany(p->fts_statp->st_size, 512LL); + } + + if (p->fts_level <= depth) { + if (hflag) { + (void) prthumanval(howmany(*ftsnum, blocksize)); + (void) fprintf(thread_stdout, "\t%s\n", p->fts_path); + } else { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(*ftsnum, blocksize), + p->fts_path); + } + } + break; + case FTS_DC: /* Ignore. */ + if (COMPAT_MODE("bin/du", "unix2003")) { + errx(1, "Can't follow symlink cycle from %s to %s", p->fts_path, p->fts_cycle->fts_path); + } + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_SLNONE: + if (COMPAT_MODE("bin/du", "unix2003")) { + struct stat sb; + int rc = stat(p->fts_path, &sb); + if (rc < 0 && errno == ELOOP) { + errx(1, "Too many symlinks at %s", p->fts_path); + } + } + default: + if (ignorep(p)) + break; + + if (p->fts_statp->st_nlink > 1 && linkchk(p)) + break; + + if (listall || p->fts_level == 0) { + if (hflag) { + if (p->fts_statp->st_size < TWO_TB) { + (void) prthumanval(howmany(p->fts_statp->st_blocks, + blocksize)); + } else { + (void) prthumanval(howmany(howmany(p->fts_statp->st_size, 512LL), + blocksize)); + } + (void) fprintf(thread_stdout, "\t%s\n", p->fts_path); + } else { + if (p->fts_statp->st_size < TWO_TB) { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(p->fts_statp->st_blocks, blocksize), + p->fts_path); + } else { + (void) fprintf(thread_stdout, "%jd\t%s\n", + (intmax_t)howmany(howmany(p->fts_statp->st_size, 512LL), blocksize), + p->fts_path); + } + } + } + + ftsparnum = (off_t *)&p->fts_parent->fts_number; + if (p->fts_statp->st_size < TWO_TB) { + ftsparnum[0] += p->fts_statp->st_blocks; + } else { + ftsparnum[0] += p->fts_statp->st_size / 512LL; + } + } + savednumber = ((off_t *)&p->fts_parent->fts_number)[0]; + } + + if (errno) { + err(1, "fts_read"); + } + + if (cflag) { + if (hflag) { + (void) prthumanval(howmany(savednumber, blocksize)); + (void) fprintf(thread_stdout, "\ttotal\n"); + } else { + (void) fprintf(thread_stdout, "%jd\ttotal\n", (intmax_t)howmany(savednumber, blocksize)); + } + } + + ignoreclean(); + exit(rval); +} + +static int +linkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) { + errx(1, "No memory for hardlink detection"); + } + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = st->st_nlink - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +static int +dirlinkchk(FTSENT *p) +{ + struct links_entry { + struct links_entry *next; + struct links_entry *previous; + int links; + dev_t dev; + ino_t ino; + }; + static const size_t links_hash_initial_size = 8192; + static struct links_entry **buckets; + static struct links_entry *free_list; + static size_t number_buckets; + static unsigned long number_entries; + static char stop_allocating; + struct links_entry *le, **new_buckets; + struct stat *st; + size_t i, new_size; + int hash; + struct attrbuf { + int size; + int linkcount; + } buf; + struct attrlist attrList; + + memset(&attrList, 0, sizeof(attrList)); + attrList.bitmapcount = ATTR_BIT_MAP_COUNT; + attrList.dirattr = ATTR_DIR_LINKCOUNT; + if (-1 == getattrlist(p->fts_path, &attrList, &buf, sizeof(buf), 0)) + return 0; + if (buf.linkcount == 1) + return 0; + st = p->fts_statp; + + /* If necessary, initialize the hash table. */ + if (buckets == NULL) { + number_buckets = links_hash_initial_size; + buckets = malloc(number_buckets * sizeof(buckets[0])); + if (buckets == NULL) { + errx(1, "No memory for directory hardlink detection"); + } + for (i = 0; i < number_buckets; i++) + buckets[i] = NULL; + } + + /* If the hash table is getting too full, enlarge it. */ + if (number_entries > number_buckets * 10 && !stop_allocating) { + new_size = number_buckets * 2; + new_buckets = malloc(new_size * sizeof(struct links_entry *)); + + /* Try releasing the free list to see if that helps. */ + if (new_buckets == NULL && free_list != NULL) { + while (free_list != NULL) { + le = free_list; + free_list = le->next; + free(le); + } + new_buckets = malloc(new_size * sizeof(new_buckets[0])); + } + + if (new_buckets == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking directory hard links"); + } else { + memset(new_buckets, 0, + new_size * sizeof(struct links_entry *)); + for (i = 0; i < number_buckets; i++) { + while (buckets[i] != NULL) { + /* Remove entry from old bucket. */ + le = buckets[i]; + buckets[i] = le->next; + + /* Add entry to new bucket. */ + hash = (le->dev ^ le->ino) % new_size; + + if (new_buckets[hash] != NULL) + new_buckets[hash]->previous = + le; + le->next = new_buckets[hash]; + le->previous = NULL; + new_buckets[hash] = le; + } + } + free(buckets); + buckets = new_buckets; + number_buckets = new_size; + } + } + + /* Try to locate this entry in the hash table. */ + hash = ( st->st_dev ^ st->st_ino ) % number_buckets; + for (le = buckets[hash]; le != NULL; le = le->next) { + if (le->dev == st->st_dev && le->ino == st->st_ino) { + /* + * Save memory by releasing an entry when we've seen + * all of it's links. + */ + if (--le->links <= 0) { + if (le->previous != NULL) + le->previous->next = le->next; + if (le->next != NULL) + le->next->previous = le->previous; + if (buckets[hash] == le) + buckets[hash] = le->next; + number_entries--; + /* Recycle this node through the free list */ + if (stop_allocating) { + free(le); + } else { + le->next = free_list; + free_list = le; + } + } + return (1); + } + } + + if (stop_allocating) + return (0); + /* Add this entry to the links cache. */ + if (free_list != NULL) { + /* Pull a node from the free list if we can. */ + le = free_list; + free_list = le->next; + } else + /* Malloc one if we have to. */ + le = malloc(sizeof(struct links_entry)); + if (le == NULL) { + stop_allocating = 1; + warnx("No more memory for tracking hard links"); + return (0); + } + le->dev = st->st_dev; + le->ino = st->st_ino; + le->links = buf.linkcount - 1; + number_entries++; + le->next = buckets[hash]; + le->previous = NULL; + if (buckets[hash] != NULL) + buckets[hash]->previous = le; + buckets[hash] = le; + return (0); +} + +/* + * Output in "human-readable" format. Uses 3 digits max and puts + * unit suffixes at the end. Makes output compact and easy to read, + * especially on huge disks. + * + */ +unit_t +unit_adjust(double *val) +{ + double abval; + unit_t unit; + unsigned int unit_sz; + + abval = fabs(*val); + + unit_sz = abval ? ilogb(abval) / 10 : 0; + + if (unit_sz >= UNIT_MAX) { + unit = NONE; + } else { + unit = unitp[unit_sz]; + *val /= (double)valp[unit_sz]; + } + + return (unit); +} + +void +prthumanval(double bytes) +{ + unit_t unit; + + bytes *= 512; + unit = unit_adjust(&bytes); + + if (bytes == 0) + (void)fprintf(thread_stdout, " 0B"); + else if (bytes > 10) + (void)fprintf(thread_stdout, "%3.0f%c", bytes, "BKMGTPE"[unit]); + else + (void)fprintf(thread_stdout, "%3.1f%c", bytes, "BKMGTPE"[unit]); +} + +static void +usage(void) +{ + (void)fprintf(thread_stderr, + "usage: du [-H | -L | -P] [-a | -s | -d depth] [-c] [-h | -k | -m | -g] [-x] [-I mask] [file ...]\n"); + exit(EX_USAGE); +} + +void +ignoreadd(const char *mask) +{ + struct ignentry *ign; + + ign = calloc(1, sizeof(*ign)); + if (ign == NULL) { + errx(1, "cannot allocate memory"); + } + ign->mask = strdup(mask); + if (ign->mask == NULL) { + errx(1, "cannot allocate memory"); + } + SLIST_INSERT_HEAD(&ignores, ign, next); +} + +void +ignoreclean(void) +{ + struct ignentry *ign; + + while (!SLIST_EMPTY(&ignores)) { + ign = SLIST_FIRST(&ignores); + SLIST_REMOVE_HEAD(&ignores, next); + free(ign->mask); + free(ign); + } +} + +int +ignorep(FTSENT *ent) +{ + struct ignentry *ign; + +#ifdef __APPLE__ + if (S_ISDIR(ent->fts_statp->st_mode) && !strcmp("fd", ent->fts_name)) { + struct statfs sfsb; + int rc = statfs(ent->fts_accpath, &sfsb); + if (rc >= 0 && !strcmp("devfs", sfsb.f_fstypename)) { + /* Don't cd into /dev/fd/N since one of those is likely to be + the cwd as of the start of du which causes all manner of + unpleasant surprises */ + return 1; + } + } +#endif /* __APPLE__ */ + SLIST_FOREACH(ign, &ignores, next) + if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) + return 1; + return 0; +} diff --git a/file_cmds/file_cmds.xcodeproj/project.pbxproj b/file_cmds/file_cmds.xcodeproj/project.pbxproj new file mode 100644 index 00000000..be7b4511 --- /dev/null +++ b/file_cmds/file_cmds.xcodeproj/project.pbxproj @@ -0,0 +1,3857 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + FC8A8C3C14B64A9D001B97AD /* shar */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C3D14B64A9D001B97AD /* Build configuration list for PBXAggregateTarget "shar" */; + buildPhases = ( + FC8A8CC714B65CEB001B97AD /* ShellScript */, + FC8A8C3F14B64AA8001B97AD /* CopyFiles */, + ); + dependencies = ( + ); + name = shar; + productName = shar; + }; + FC8A8C4614B64DCD001B97AD /* readlink */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C4714B64DCE001B97AD /* Build configuration list for PBXAggregateTarget "readlink" */; + buildPhases = ( + FC8A8C4B14B64DEA001B97AD /* Copy Files */, + FC8A8C4C14B64DF9001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */, + ); + name = readlink; + productName = readlink; + }; + FC8A8C5014B650CF001B97AD /* unlink */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C5614B650CF001B97AD /* Build configuration list for PBXAggregateTarget "unlink" */; + buildPhases = ( + FC8A8C5314B650CF001B97AD /* CopyFiles */, + FC8A8C5514B650CF001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8C5914B65238001B97AD /* PBXTargetDependency */, + ); + name = unlink; + productName = readlink; + }; + FC8A8C5B14B652E1001B97AD /* chgrp */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C6114B652E1001B97AD /* Build configuration list for PBXAggregateTarget "chgrp" */; + buildPhases = ( + FC8A8C5E14B652E1001B97AD /* CopyFiles */, + FC8A8C6014B652E1001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8C6414B652FA001B97AD /* PBXTargetDependency */, + ); + name = chgrp; + productName = readlink; + }; + FC8A8C6714B6536D001B97AD /* sum */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C6D14B6536D001B97AD /* Build configuration list for PBXAggregateTarget "sum" */; + buildPhases = ( + FC8A8C6A14B6536D001B97AD /* CopyFiles */, + FC8A8C6C14B6536D001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8C7014B6537C001B97AD /* PBXTargetDependency */, + ); + name = sum; + productName = readlink; + }; + FC8A8C7314B6554E001B97AD /* link */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C7914B6554E001B97AD /* Build configuration list for PBXAggregateTarget "link" */; + buildPhases = ( + FC8A8C7614B6554E001B97AD /* CopyFiles */, + FC8A8C7814B6554E001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8C7C14B65562001B97AD /* PBXTargetDependency */, + ); + name = link; + productName = readlink; + }; + FC8A8C8014B655ED001B97AD /* executables */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8C8114B655ED001B97AD /* Build configuration list for PBXAggregateTarget "executables" */; + buildPhases = ( + ); + dependencies = ( + FC8A8C8414B655FD001B97AD /* PBXTargetDependency */, + FC8A8C8614B655FD001B97AD /* PBXTargetDependency */, + FC8A8C8814B655FD001B97AD /* PBXTargetDependency */, + FC8A8C8A14B655FD001B97AD /* PBXTargetDependency */, + FC8A8C8C14B655FD001B97AD /* PBXTargetDependency */, + FC8A8C8E14B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9014B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9214B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9414B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9614B655FD001B97AD /* PBXTargetDependency */, + FDAD94AA1808BDAA00B4D5A0 /* PBXTargetDependency */, + FC8A8C9814B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9A14B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9C14B655FD001B97AD /* PBXTargetDependency */, + FC8A8C9E14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CA014B655FD001B97AD /* PBXTargetDependency */, + FC8A8CA214B655FD001B97AD /* PBXTargetDependency */, + FC8A8CA414B655FD001B97AD /* PBXTargetDependency */, + FC8A8CA614B655FD001B97AD /* PBXTargetDependency */, + FC8A8CA814B655FD001B97AD /* PBXTargetDependency */, + FC8A8CAA14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CAC14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CAE14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CB014B655FD001B97AD /* PBXTargetDependency */, + FC8A8CB214B655FD001B97AD /* PBXTargetDependency */, + FC8A8CB414B655FD001B97AD /* PBXTargetDependency */, + FC8A8CB614B655FD001B97AD /* PBXTargetDependency */, + FC8A8CB814B655FD001B97AD /* PBXTargetDependency */, + FC8A8CBA14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CBC14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CBE14B655FD001B97AD /* PBXTargetDependency */, + FC8A8CD314B67BFD001B97AD /* PBXTargetDependency */, + FC8A8CC014B655FD001B97AD /* PBXTargetDependency */, + ); + name = executables; + productName = executables; + }; + FC8A8CC814B65F92001B97AD /* uncompress */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FC8A8CCE14B65F92001B97AD /* Build configuration list for PBXAggregateTarget "uncompress" */; + buildPhases = ( + FC8A8CCB14B65F92001B97AD /* CopyFiles */, + FC8A8CCD14B65F92001B97AD /* ShellScript */, + ); + dependencies = ( + FC8A8CC914B65F92001B97AD /* PBXTargetDependency */, + ); + name = uncompress; + productName = readlink; + }; + FDFF0C501811BA2F00BFC477 /* eOS */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FDFF0C931811BA2F00BFC477 /* Build configuration list for PBXAggregateTarget "eOS" */; + buildPhases = ( + ); + dependencies = ( + FDFF0C511811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C531811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C551811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C571811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C591811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C5B1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C5D1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C5F1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C611811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C631811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C671811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C691811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C6B1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C6D1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C6F1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C711811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C731811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C751811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C771811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C791811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C7B1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C7D1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C7F1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C811811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C831811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C851811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C871811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C891811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C8B1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C8D1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C8F1811BA2F00BFC477 /* PBXTargetDependency */, + FDFF0C911811BA2F00BFC477 /* PBXTargetDependency */, + ); + name = eOS; + productName = executables; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 3E59B9311D4A767600D3128C /* futimens.c in Sources */ = {isa = PBXBuildFile; fileRef = 3E59B9301D4A767600D3128C /* futimens.c */; }; + FC8A8A2814B6486E001B97AD /* chflags.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDCC14B6460C0070FACB /* chflags.c */; }; + FC8A8BE414B6494B001B97AD /* chflags.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDCB14B6460C0070FACB /* chflags.1 */; }; + FC8A8BE514B64958001B97AD /* chmod.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD014B6460C0070FACB /* chmod.c */; }; + FC8A8BE614B6495B001B97AD /* chmod_acl.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD114B6460C0070FACB /* chmod_acl.c */; }; + FC8A8BE714B6495D001B97AD /* chmod.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDCF14B6460C0070FACB /* chmod.1 */; }; + FC8A8BE814B64962001B97AD /* chown.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDD714B6460C0070FACB /* chown.c */; }; + FC8A8BEA14B6496E001B97AD /* cksum.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDDA14B6460C0070FACB /* cksum.1 */; }; + FC8A8BEB14B64970001B97AD /* cksum.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDB14B6460C0070FACB /* cksum.c */; }; + FC8A8BEC14B64972001B97AD /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDC14B6460C0070FACB /* crc.c */; }; + FC8A8BED14B64975001B97AD /* crc32.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDD14B6460C0070FACB /* crc32.c */; }; + FC8A8BEE14B64977001B97AD /* print.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE014B6460C0070FACB /* print.c */; }; + FC8A8BEF14B6497A001B97AD /* sum1.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE114B6460C0070FACB /* sum1.c */; }; + FC8A8BF014B6497D001B97AD /* sum2.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE214B6460C0070FACB /* sum2.c */; }; + FC8A8BF114B64982001B97AD /* compress.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDE414B6460C0070FACB /* compress.1 */; }; + FC8A8BF214B64984001B97AD /* uncompress.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDEB14B6460C0070FACB /* uncompress.1 */; }; + FC8A8BF314B64988001B97AD /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDE514B6460C0070FACB /* compress.c */; }; + FC8A8BF414B6498A001B97AD /* zopen.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDEE14B6460C0070FACB /* zopen.c */; }; + FC8A8BF514B64995001B97AD /* cp.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF214B6460C0070FACB /* cp.c */; }; + FC8A8BF614B64998001B97AD /* utils.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF514B6460C0070FACB /* utils.c */; }; + FC8A8BF714B6499A001B97AD /* cp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDF114B6460C0070FACB /* cp.1 */; }; + FC8A8BF814B649A4001B97AD /* args.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDF914B6460C0070FACB /* args.c */; }; + FC8A8BF914B649A5001B97AD /* conv.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFA14B6460C0070FACB /* conv.c */; }; + FC8A8BFA14B649A7001B97AD /* conv_tab.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFB14B6460C0070FACB /* conv_tab.c */; }; + FC8A8BFB14B649A9001B97AD /* dd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDFC14B6460C0070FACB /* dd.1 */; }; + FC8A8BFC14B649AC001B97AD /* dd.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDFD14B6460C0070FACB /* dd.c */; }; + FC8A8BFD14B649AE001B97AD /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0114B6460C0070FACB /* misc.c */; }; + FC8A8BFE14B649B1001B97AD /* position.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0214B6460C0070FACB /* position.c */; }; + FC8A8BFF14B649B4001B97AD /* df.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0414B6460C0070FACB /* df.1 */; }; + FC8A8C0114B649D1001B97AD /* df.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0514B6460C0070FACB /* df.c */; }; + FC8A8C0214B649D4001B97AD /* du.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0A14B6460C0070FACB /* du.c */; }; + FC8A8C0314B649D8001B97AD /* du.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0914B6460C0070FACB /* du.1 */; }; + FC8A8C0414B649DD001B97AD /* xinstall.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1014B6460C0070FACB /* xinstall.c */; }; + FC8A8C0514B649DF001B97AD /* install.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE0D14B6460C0070FACB /* install.1 */; }; + FC8A8C0614B649E2001B97AD /* ipcrm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1214B6460C0070FACB /* ipcrm.1 */; }; + FC8A8C0714B649E3001B97AD /* ipcrm.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1314B6460C0070FACB /* ipcrm.c */; }; + FC8A8C0814B649E8001B97AD /* ipcs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1714B6460C0070FACB /* ipcs.c */; }; + FC8A8C0914B649EA001B97AD /* ipcs.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1614B6460C0070FACB /* ipcs.1 */; }; + FC8A8C0A14B649ED001B97AD /* ln.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1A14B6460C0070FACB /* ln.1 */; }; + FC8A8C0B14B649EF001B97AD /* ln.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1B14B6460C0070FACB /* ln.c */; }; + FC8A8C0C14B649F5001B97AD /* ls.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2114B6460C0070FACB /* ls.1 */; }; + FC8A8C0D14B649F7001B97AD /* cmp.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE1F14B6460C0070FACB /* cmp.c */; }; + FC8A8C0E14B649F9001B97AD /* ls.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2214B6460C0070FACB /* ls.c */; }; + FC8A8C0F14B649FC001B97AD /* print.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2514B6460C0070FACB /* print.c */; }; + FC8A8C1014B649FE001B97AD /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2614B6460C0070FACB /* util.c */; }; + FC8A8C1114B64A04001B97AD /* mkdir.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2B14B6460C0070FACB /* mkdir.c */; }; + FC8A8C1214B64A06001B97AD /* mkdir.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2A14B6460C0070FACB /* mkdir.1 */; }; + FC8A8C1314B64A09001B97AD /* mkfifo.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE2E14B6460C0070FACB /* mkfifo.1 */; }; + FC8A8C1414B64A0A001B97AD /* mkfifo.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE2F14B6460C0070FACB /* mkfifo.c */; }; + FC8A8C1514B64A0D001B97AD /* mknod.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE3214B6460C0070FACB /* mknod.8 */; }; + FC8A8C1614B64A0F001B97AD /* mknod.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3314B6460C0070FACB /* mknod.c */; }; + FC8A8C1714B64A14001B97AD /* commoncrypto.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3514B6460C0070FACB /* commoncrypto.c */; }; + FC8A8C1814B64A17001B97AD /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3714B6460C0070FACB /* compare.c */; }; + FC8A8C1914B64A1A001B97AD /* create.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3814B6460C0070FACB /* create.c */; }; + FC8A8C1A14B64A22001B97AD /* excludes.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3914B6460C0070FACB /* excludes.c */; }; + FC8A8C1B14B64A27001B97AD /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3C14B6460C0070FACB /* misc.c */; }; + FC8A8C1C14B64A2D001B97AD /* mtree.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE3E14B6460C0070FACB /* mtree.c */; }; + FC8A8C1D14B64A31001B97AD /* spec.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4014B6460C0070FACB /* spec.c */; }; + FC8A8C1E14B64A34001B97AD /* specspec.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4114B6460C0070FACB /* specspec.c */; }; + FC8A8C1F14B64A38001B97AD /* verify.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4814B6460C0070FACB /* verify.c */; }; + FC8A8C2014B64A40001B97AD /* mtree.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE3D14B6460C0070FACB /* mtree.8 */; }; + FC8A8C2114B64A49001B97AD /* mv.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE4B14B6460C0070FACB /* mv.1 */; }; + FC8A8C2214B64A4B001B97AD /* mv.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE4C14B6460C0070FACB /* mv.c */; }; + FC8A8C2314B64A4F001B97AD /* pathchk.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE5014B6460C0070FACB /* pathchk.1 */; }; + FC8A8C2414B64A53001B97AD /* pathchk.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5114B6460C0070FACB /* pathchk.c */; }; + FC8A8C2514B64A59001B97AD /* pax.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE6614B6460C0070FACB /* pax.1 */; }; + FC8A8C2714B64A73001B97AD /* ar_io.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5314B6460C0070FACB /* ar_io.c */; }; + FC8A8C2814B64A73001B97AD /* ar_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5414B6460C0070FACB /* ar_subs.c */; }; + FC8A8C2914B64A73001B97AD /* buf_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5514B6460C0070FACB /* buf_subs.c */; }; + FC8A8C2A14B64A73001B97AD /* cache.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5614B6460C0070FACB /* cache.c */; }; + FC8A8C2B14B64A73001B97AD /* cpio.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5914B6460C0070FACB /* cpio.c */; }; + FC8A8C2C14B64A73001B97AD /* file_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5C14B6460C0070FACB /* file_subs.c */; }; + FC8A8C2D14B64A73001B97AD /* ftree.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5D14B6460C0070FACB /* ftree.c */; }; + FC8A8C2E14B64A73001B97AD /* gen_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE5F14B6460C0070FACB /* gen_subs.c */; }; + FC8A8C2F14B64A73001B97AD /* getoldopt.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6014B6460C0070FACB /* getoldopt.c */; }; + FC8A8C3014B64A73001B97AD /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6214B6460C0070FACB /* options.c */; }; + FC8A8C3114B64A73001B97AD /* pat_rep.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6414B6460C0070FACB /* pat_rep.c */; }; + FC8A8C3214B64A73001B97AD /* pax.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6714B6460C0070FACB /* pax.c */; }; + FC8A8C3314B64A73001B97AD /* pax_format.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6914B6460C0070FACB /* pax_format.c */; }; + FC8A8C3414B64A73001B97AD /* sel_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6B14B6460C0070FACB /* sel_subs.c */; }; + FC8A8C3514B64A73001B97AD /* tables.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6D14B6460C0070FACB /* tables.c */; }; + FC8A8C3614B64A73001B97AD /* tar.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE6F14B6460C0070FACB /* tar.c */; }; + FC8A8C3714B64A73001B97AD /* tty_subs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7114B6460C0070FACB /* tty_subs.c */; }; + FC8A8C3814B64A7C001B97AD /* rm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE7414B6460C0070FACB /* rm.1 */; }; + FC8A8C3914B64A7E001B97AD /* rm.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7514B6460C0070FACB /* rm.c */; }; + FC8A8C3A14B64A85001B97AD /* rmdir.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE7814B6460C0070FACB /* rmdir.1 */; }; + FC8A8C3B14B64A88001B97AD /* rmdir.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE7914B6460C0070FACB /* rmdir.c */; }; + FC8A8C4014B64AAB001B97AD /* shar.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8014B6460C0070FACB /* shar.1 */; }; + FC8A8C4114B64AC0001B97AD /* stat.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8414B6460C0070FACB /* stat.1 */; }; + FC8A8C4214B64AC3001B97AD /* stat.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE8514B6460C0070FACB /* stat.c */; }; + FC8A8C4314B64AC7001B97AD /* touch.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE8914B6460C0070FACB /* touch.c */; }; + FC8A8C4514B64AD7001B97AD /* touch.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE8814B6460C0070FACB /* touch.1 */; }; + FC8A8C4E14B64EAE001B97AD /* readlink.1 in Copy Files */ = {isa = PBXBuildFile; fileRef = FC8A8C4D14B64EA8001B97AD /* readlink.1 */; }; + FC8A8C5A14B6525A001B97AD /* unlink.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C4F14B650C3001B97AD /* unlink.1 */; }; + FC8A8C6514B65307001B97AD /* chgrp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDD514B6460C0070FACB /* chgrp.1 */; }; + FC8A8C7114B65389001B97AD /* sum.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C6614B6535B001B97AD /* sum.1 */; }; + FC8A8C7D14B65575001B97AD /* link.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC8A8C7214B65547001B97AD /* link.1 */; }; + FC8A8C7F14B65586001B97AD /* symlink.7 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BE1D14B6460C0070FACB /* symlink.7 */; }; + FC8A8CC214B658D7001B97AD /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC114B658D6001B97AD /* libutil.dylib */; }; + FC8A8CC314B6598F001B97AD /* vfslist.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BE0714B6460C0070FACB /* vfslist.c */; }; + FC8A8CC514B65C3D001B97AD /* libcurses.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC414B65C3D001B97AD /* libcurses.dylib */; }; + FC8A8CC614B65C46001B97AD /* libutil.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A8CC114B658D6001B97AD /* libutil.dylib */; }; + FC8A8CD014B65F9B001B97AD /* uncompress.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDEB14B6460C0070FACB /* uncompress.1 */; }; + FC8A8CD114B66E10001B97AD /* crc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCB1BDDC14B6460C0070FACB /* crc.c */; }; + FC8A8CD414B67D60001B97AD /* chown.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCB1BDD614B6460C0070FACB /* chown.8 */; }; + FDAD949F1808BBB900B4D5A0 /* gzip.c in Sources */ = {isa = PBXBuildFile; fileRef = FDAD94831808BB3A00B4D5A0 /* gzip.c */; }; + FDAD94A21808BC9100B4D5A0 /* libbz2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */; }; + FDAD94A41808BC9700B4D5A0 /* liblzma.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */; }; + FDAD94A61808BC9B00B4D5A0 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FDAD94A51808BC9B00B4D5A0 /* libz.dylib */; }; + FDAD94A81808BD0600B4D5A0 /* gzip.1 in Install Man Page */ = {isa = PBXBuildFile; fileRef = FDAD94821808BB3A00B4D5A0 /* gzip.1 */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + FC8A8C4914B64DE1001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BC414B648EF001B97AD; + remoteInfo = stat; + }; + FC8A8C5814B65238001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BAC14B648EF001B97AD; + remoteInfo = rm; + }; + FC8A8C6314B652FA001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1414B648E0001B97AD; + remoteInfo = chown; + }; + FC8A8C6F14B6537C001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1C14B648E3001B97AD; + remoteInfo = cksum; + }; + FC8A8C7B14B65562001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B6414B648EC001B97AD; + remoteInfo = ln; + }; + FC8A8C8314B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCB1BDB714B645D10070FACB; + remoteInfo = chflags; + }; + FC8A8C8514B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C5B14B652E1001B97AD; + remoteInfo = chgrp; + }; + FC8A8C8714B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B0B14B648D7001B97AD; + remoteInfo = chmod; + }; + FC8A8C8914B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1414B648E0001B97AD; + remoteInfo = chown; + }; + FC8A8C8B14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1C14B648E3001B97AD; + remoteInfo = cksum; + }; + FC8A8C8D14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B2414B648E5001B97AD; + remoteInfo = compress; + }; + FC8A8C8F14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B2C14B648E7001B97AD; + remoteInfo = cp; + }; + FC8A8C9114B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B3414B648EA001B97AD; + remoteInfo = dd; + }; + FC8A8C9314B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B3C14B648EA001B97AD; + remoteInfo = df; + }; + FC8A8C9514B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B4414B648EB001B97AD; + remoteInfo = du; + }; + FC8A8C9714B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B4C14B648EB001B97AD; + remoteInfo = install; + }; + FC8A8C9914B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B5414B648EB001B97AD; + remoteInfo = ipcrm; + }; + FC8A8C9B14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B5C14B648EC001B97AD; + remoteInfo = ipcs; + }; + FC8A8C9D14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C7314B6554E001B97AD; + remoteInfo = link; + }; + FC8A8C9F14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B6414B648EC001B97AD; + remoteInfo = ln; + }; + FC8A8CA114B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B6C14B648ED001B97AD; + remoteInfo = ls; + }; + FC8A8CA314B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B7414B648ED001B97AD; + remoteInfo = mkdir; + }; + FC8A8CA514B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B7C14B648ED001B97AD; + remoteInfo = mkfifo; + }; + FC8A8CA714B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B8414B648ED001B97AD; + remoteInfo = mknod; + }; + FC8A8CA914B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B8C14B648ED001B97AD; + remoteInfo = mtree; + }; + FC8A8CAB14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B9414B648EE001B97AD; + remoteInfo = mv; + }; + FC8A8CAD14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B9C14B648EE001B97AD; + remoteInfo = pathchk; + }; + FC8A8CAF14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BA414B648EE001B97AD; + remoteInfo = pax; + }; + FC8A8CB114B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C4614B64DCD001B97AD; + remoteInfo = readlink; + }; + FC8A8CB314B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BAC14B648EF001B97AD; + remoteInfo = rm; + }; + FC8A8CB514B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BB414B648EF001B97AD; + remoteInfo = rmdir; + }; + FC8A8CB714B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C3C14B64A9D001B97AD; + remoteInfo = shar; + }; + FC8A8CB914B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BC414B648EF001B97AD; + remoteInfo = stat; + }; + FC8A8CBB14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C6714B6536D001B97AD; + remoteInfo = sum; + }; + FC8A8CBD14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BCC14B648F0001B97AD; + remoteInfo = touch; + }; + FC8A8CBF14B655FD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C5014B650CF001B97AD; + remoteInfo = unlink; + }; + FC8A8CCA14B65F92001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BAC14B648EF001B97AD; + remoteInfo = rm; + }; + FC8A8CD214B67BFD001B97AD /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8CC814B65F92001B97AD; + remoteInfo = uncompress; + }; + FDAD94A91808BDAA00B4D5A0 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FDAD94961808BB6D00B4D5A0; + remoteInfo = gzip; + }; + FDFF0C521811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCB1BDB714B645D10070FACB; + remoteInfo = chflags; + }; + FDFF0C541811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C5B14B652E1001B97AD; + remoteInfo = chgrp; + }; + FDFF0C561811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B0B14B648D7001B97AD; + remoteInfo = chmod; + }; + FDFF0C581811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1414B648E0001B97AD; + remoteInfo = chown; + }; + FDFF0C5A1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B1C14B648E3001B97AD; + remoteInfo = cksum; + }; + FDFF0C5C1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B2414B648E5001B97AD; + remoteInfo = compress; + }; + FDFF0C5E1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B2C14B648E7001B97AD; + remoteInfo = cp; + }; + FDFF0C601811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B3414B648EA001B97AD; + remoteInfo = dd; + }; + FDFF0C621811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B3C14B648EA001B97AD; + remoteInfo = df; + }; + FDFF0C641811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B4414B648EB001B97AD; + remoteInfo = du; + }; + FDFF0C681811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B4C14B648EB001B97AD; + remoteInfo = install; + }; + FDFF0C6A1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B5414B648EB001B97AD; + remoteInfo = ipcrm; + }; + FDFF0C6C1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B5C14B648EC001B97AD; + remoteInfo = ipcs; + }; + FDFF0C6E1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C7314B6554E001B97AD; + remoteInfo = link; + }; + FDFF0C701811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B6414B648EC001B97AD; + remoteInfo = ln; + }; + FDFF0C721811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B6C14B648ED001B97AD; + remoteInfo = ls; + }; + FDFF0C741811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B7414B648ED001B97AD; + remoteInfo = mkdir; + }; + FDFF0C761811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B7C14B648ED001B97AD; + remoteInfo = mkfifo; + }; + FDFF0C781811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B8414B648ED001B97AD; + remoteInfo = mknod; + }; + FDFF0C7A1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B8C14B648ED001B97AD; + remoteInfo = mtree; + }; + FDFF0C7C1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B9414B648EE001B97AD; + remoteInfo = mv; + }; + FDFF0C7E1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8B9C14B648EE001B97AD; + remoteInfo = pathchk; + }; + FDFF0C801811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BA414B648EE001B97AD; + remoteInfo = pax; + }; + FDFF0C821811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C4614B64DCD001B97AD; + remoteInfo = readlink; + }; + FDFF0C841811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BAC14B648EF001B97AD; + remoteInfo = rm; + }; + FDFF0C861811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BB414B648EF001B97AD; + remoteInfo = rmdir; + }; + FDFF0C881811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C3C14B64A9D001B97AD; + remoteInfo = shar; + }; + FDFF0C8A1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BC414B648EF001B97AD; + remoteInfo = stat; + }; + FDFF0C8C1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C6714B6536D001B97AD; + remoteInfo = sum; + }; + FDFF0C8E1811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8BCC14B648F0001B97AD; + remoteInfo = touch; + }; + FDFF0C901811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8CC814B65F92001B97AD; + remoteInfo = uncompress; + }; + FDFF0C921811BA2F00BFC477 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FCB1BDAF14B645D00070FACB /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC8A8C5014B650CF001B97AD; + remoteInfo = unlink; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + FC8A8B0F14B648D7001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BE714B6495D001B97AD /* chmod.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B1714B648E0001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FC8A8CD414B67D60001B97AD /* chown.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B1F14B648E3001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BEA14B6496E001B97AD /* cksum.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B2714B648E5001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BF214B64984001B97AD /* uncompress.1 in CopyFiles */, + FC8A8BF114B64982001B97AD /* compress.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B2F14B648E7001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BF714B6499A001B97AD /* cp.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B3714B648EA001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BFB14B649A9001B97AD /* dd.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B3F14B648EA001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BFF14B649B4001B97AD /* df.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B4714B648EB001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0314B649D8001B97AD /* du.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B4F14B648EB001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0514B649DF001B97AD /* install.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B5714B648EB001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0614B649E2001B97AD /* ipcrm.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B5F14B648EC001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0914B649EA001B97AD /* ipcs.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B6714B648EC001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0A14B649ED001B97AD /* ln.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B6F14B648ED001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C0C14B649F5001B97AD /* ls.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B7714B648ED001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C1214B64A06001B97AD /* mkdir.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B7F14B648ED001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C1314B64A09001B97AD /* mkfifo.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B8714B648ED001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FC8A8C1514B64A0D001B97AD /* mknod.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B8F14B648ED001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FC8A8C2014B64A40001B97AD /* mtree.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B9714B648EE001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C2114B64A49001B97AD /* mv.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8B9F14B648EE001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C2314B64A4F001B97AD /* pathchk.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8BA714B648EE001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C2514B64A59001B97AD /* pax.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8BAF14B648EF001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C3814B64A7C001B97AD /* rm.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8BB714B648EF001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C3A14B64A85001B97AD /* rmdir.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8BC714B648EF001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C4114B64AC0001B97AD /* stat.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8BCF14B648F0001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C4514B64AD7001B97AD /* touch.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C3F14B64AA8001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C4014B64AAB001B97AD /* shar.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C4B14B64DEA001B97AD /* Copy Files */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C4E14B64EAE001B97AD /* readlink.1 in Copy Files */, + ); + name = "Copy Files"; + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C5314B650CF001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C5A14B6525A001B97AD /* unlink.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C5E14B652E1001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C6514B65307001B97AD /* chgrp.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C6A14B6536D001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C7114B65389001B97AD /* sum.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C7614B6554E001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8C7D14B65575001B97AD /* link.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8C7E14B6557E001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man7/; + dstSubfolderSpec = 0; + files = ( + FC8A8C7F14B65586001B97AD /* symlink.7 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC8A8CCB14B65F92001B97AD /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8CD014B65F9B001B97AD /* uncompress.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCB1BDB614B645D10070FACB /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC8A8BE414B6494B001B97AD /* chflags.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FDAD94951808BB6D00B4D5A0 /* Install Man Page */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = "$(GZIP_PREFIX)/share/man/man1"; + dstSubfolderSpec = 0; + files = ( + FDAD94A81808BD0600B4D5A0 /* gzip.1 in Install Man Page */, + ); + name = "Install Man Page"; + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0773099A1A3A4DFE00E9B4EA /* dd.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = dd.entitlements; sourceTree = ""; }; + 3E59B9301D4A767600D3128C /* futimens.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = futimens.c; sourceTree = ""; }; + FC8A8B1214B648D7001B97AD /* chmod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chmod; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B1A14B648E0001B97AD /* chown */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chown; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B2214B648E3001B97AD /* cksum */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cksum; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B2A14B648E5001B97AD /* compress */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = compress; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B3214B648E7001B97AD /* cp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = cp; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B3A14B648EA001B97AD /* dd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dd; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B4214B648EA001B97AD /* df */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = df; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B4A14B648EB001B97AD /* du */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = du; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B5214B648EB001B97AD /* install */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = install; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B5A14B648EB001B97AD /* ipcrm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ipcrm; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B6214B648EC001B97AD /* ipcs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ipcs; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B6A14B648EC001B97AD /* ln */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ln; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B7214B648ED001B97AD /* ls */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ls; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B7A14B648ED001B97AD /* mkdir */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mkdir; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B8214B648ED001B97AD /* mkfifo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mkfifo; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B8A14B648ED001B97AD /* mknod */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mknod; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B9214B648ED001B97AD /* mtree */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mtree; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8B9A14B648EE001B97AD /* mv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mv; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BA214B648EE001B97AD /* pathchk */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pathchk; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BAA14B648EE001B97AD /* pax */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pax; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BB214B648EF001B97AD /* rm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rm; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BBA14B648EF001B97AD /* rmdir */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = rmdir; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BCA14B648EF001B97AD /* stat */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = stat; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8BD214B648F0001B97AD /* touch */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = touch; sourceTree = BUILT_PRODUCTS_DIR; }; + FC8A8C4D14B64EA8001B97AD /* readlink.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = readlink.1; sourceTree = ""; }; + FC8A8C4F14B650C3001B97AD /* unlink.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = unlink.1; sourceTree = ""; }; + FC8A8C6614B6535B001B97AD /* sum.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = sum.1; sourceTree = ""; }; + FC8A8C7214B65547001B97AD /* link.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = link.1; sourceTree = ""; }; + FC8A8CC114B658D6001B97AD /* libutil.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libutil.dylib; path = /usr/lib/libutil.dylib; sourceTree = ""; }; + FC8A8CC414B65C3D001B97AD /* libcurses.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcurses.dylib; path = /usr/lib/libcurses.dylib; sourceTree = ""; }; + FCB1BDB814B645D10070FACB /* chflags */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chflags; sourceTree = BUILT_PRODUCTS_DIR; }; + FCB1BDCB14B6460C0070FACB /* chflags.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chflags.1; sourceTree = ""; }; + FCB1BDCC14B6460C0070FACB /* chflags.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chflags.c; sourceTree = ""; }; + FCB1BDCF14B6460C0070FACB /* chmod.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chmod.1; sourceTree = ""; }; + FCB1BDD014B6460C0070FACB /* chmod.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chmod.c; sourceTree = ""; }; + FCB1BDD114B6460C0070FACB /* chmod_acl.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chmod_acl.c; sourceTree = ""; }; + FCB1BDD214B6460C0070FACB /* chmod_acl.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = chmod_acl.h; sourceTree = ""; }; + FCB1BDD514B6460C0070FACB /* chgrp.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = chgrp.1; sourceTree = ""; }; + FCB1BDD614B6460C0070FACB /* chown.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = chown.8; sourceTree = ""; }; + FCB1BDD714B6460C0070FACB /* chown.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chown.c; sourceTree = ""; }; + FCB1BDDA14B6460C0070FACB /* cksum.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cksum.1; sourceTree = ""; }; + FCB1BDDB14B6460C0070FACB /* cksum.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cksum.c; sourceTree = ""; }; + FCB1BDDC14B6460C0070FACB /* crc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = crc.c; sourceTree = ""; }; + FCB1BDDD14B6460C0070FACB /* crc32.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = crc32.c; sourceTree = ""; }; + FCB1BDDE14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BDE014B6460C0070FACB /* print.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = ""; }; + FCB1BDE114B6460C0070FACB /* sum1.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sum1.c; sourceTree = ""; }; + FCB1BDE214B6460C0070FACB /* sum2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sum2.c; sourceTree = ""; usesTabs = 1; }; + FCB1BDE414B6460C0070FACB /* compress.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = compress.1; sourceTree = ""; }; + FCB1BDE514B6460C0070FACB /* compress.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = ""; }; + FCB1BDE714B6460C0070FACB /* NOTES */ = {isa = PBXFileReference; lastKnownFileType = text; path = NOTES; sourceTree = ""; }; + FCB1BDE814B6460C0070FACB /* README */ = {isa = PBXFileReference; lastKnownFileType = text; path = README; sourceTree = ""; }; + FCB1BDE914B6460C0070FACB /* revision.log */ = {isa = PBXFileReference; lastKnownFileType = text; path = revision.log; sourceTree = ""; }; + FCB1BDEB14B6460C0070FACB /* uncompress.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uncompress.1; sourceTree = ""; }; + FCB1BDEC14B6460C0070FACB /* zcat.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zcat.sh; sourceTree = ""; }; + FCB1BDED14B6460C0070FACB /* zopen.3 */ = {isa = PBXFileReference; lastKnownFileType = text; path = zopen.3; sourceTree = ""; }; + FCB1BDEE14B6460C0070FACB /* zopen.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zopen.c; sourceTree = ""; }; + FCB1BDEF14B6460C0070FACB /* zopen.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = zopen.h; sourceTree = ""; }; + FCB1BDF114B6460C0070FACB /* cp.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cp.1; sourceTree = ""; }; + FCB1BDF214B6460C0070FACB /* cp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cp.c; sourceTree = ""; }; + FCB1BDF314B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BDF514B6460C0070FACB /* utils.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utils.c; sourceTree = ""; }; + FCB1BDF714B6460C0070FACB /* strpct.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = strpct.c; sourceTree = ""; }; + FCB1BDF914B6460C0070FACB /* args.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = args.c; sourceTree = ""; }; + FCB1BDFA14B6460C0070FACB /* conv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv.c; sourceTree = ""; }; + FCB1BDFB14B6460C0070FACB /* conv_tab.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv_tab.c; sourceTree = ""; }; + FCB1BDFC14B6460C0070FACB /* dd.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = dd.1; sourceTree = ""; }; + FCB1BDFD14B6460C0070FACB /* dd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dd.c; sourceTree = ""; }; + FCB1BDFE14B6460C0070FACB /* dd.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = dd.h; sourceTree = ""; }; + FCB1BDFF14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BE0114B6460C0070FACB /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = ""; }; + FCB1BE0214B6460C0070FACB /* position.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = position.c; sourceTree = ""; }; + FCB1BE0414B6460C0070FACB /* df.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = df.1; sourceTree = ""; }; + FCB1BE0514B6460C0070FACB /* df.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = df.c; sourceTree = ""; }; + FCB1BE0714B6460C0070FACB /* vfslist.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vfslist.c; sourceTree = ""; }; + FCB1BE0914B6460C0070FACB /* du.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = du.1; sourceTree = ""; }; + FCB1BE0A14B6460C0070FACB /* du.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = du.c; sourceTree = ""; }; + FCB1BE0D14B6460C0070FACB /* install.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = install.1; sourceTree = ""; }; + FCB1BE0F14B6460C0070FACB /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = ""; }; + FCB1BE1014B6460C0070FACB /* xinstall.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = xinstall.c; sourceTree = ""; }; + FCB1BE1214B6460C0070FACB /* ipcrm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ipcrm.1; sourceTree = ""; }; + FCB1BE1314B6460C0070FACB /* ipcrm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ipcrm.c; sourceTree = ""; }; + FCB1BE1614B6460C0070FACB /* ipcs.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ipcs.1; sourceTree = ""; }; + FCB1BE1714B6460C0070FACB /* ipcs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ipcs.c; sourceTree = ""; }; + FCB1BE1A14B6460C0070FACB /* ln.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ln.1; sourceTree = ""; }; + FCB1BE1B14B6460C0070FACB /* ln.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ln.c; sourceTree = ""; }; + FCB1BE1D14B6460C0070FACB /* symlink.7 */ = {isa = PBXFileReference; lastKnownFileType = text; path = symlink.7; sourceTree = ""; }; + FCB1BE1F14B6460C0070FACB /* cmp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cmp.c; sourceTree = ""; }; + FCB1BE2014B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BE2114B6460C0070FACB /* ls.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = ls.1; sourceTree = ""; }; + FCB1BE2214B6460C0070FACB /* ls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ls.c; sourceTree = ""; }; + FCB1BE2314B6460C0070FACB /* ls.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ls.h; sourceTree = ""; }; + FCB1BE2514B6460C0070FACB /* print.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = print.c; sourceTree = ""; }; + FCB1BE2614B6460C0070FACB /* util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = ""; }; + FCB1BE2A14B6460C0070FACB /* mkdir.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mkdir.1; sourceTree = ""; }; + FCB1BE2B14B6460C0070FACB /* mkdir.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mkdir.c; sourceTree = ""; }; + FCB1BE2E14B6460C0070FACB /* mkfifo.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mkfifo.1; sourceTree = ""; }; + FCB1BE2F14B6460C0070FACB /* mkfifo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mkfifo.c; sourceTree = ""; }; + FCB1BE3214B6460C0070FACB /* mknod.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = mknod.8; sourceTree = ""; }; + FCB1BE3314B6460C0070FACB /* mknod.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mknod.c; sourceTree = ""; }; + FCB1BE3514B6460C0070FACB /* commoncrypto.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = commoncrypto.c; sourceTree = ""; }; + FCB1BE3614B6460C0070FACB /* commoncrypto.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = commoncrypto.h; sourceTree = ""; }; + FCB1BE3714B6460C0070FACB /* compare.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = compare.c; sourceTree = ""; }; + FCB1BE3814B6460C0070FACB /* create.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = create.c; sourceTree = ""; }; + FCB1BE3914B6460C0070FACB /* excludes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = excludes.c; sourceTree = ""; }; + FCB1BE3A14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BE3C14B6460C0070FACB /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = ""; }; + FCB1BE3D14B6460C0070FACB /* mtree.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = mtree.8; sourceTree = ""; }; + FCB1BE3E14B6460C0070FACB /* mtree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mtree.c; sourceTree = ""; }; + FCB1BE3F14B6460C0070FACB /* mtree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mtree.h; sourceTree = ""; }; + FCB1BE4014B6460C0070FACB /* spec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = spec.c; sourceTree = ""; }; + FCB1BE4114B6460C0070FACB /* specspec.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = specspec.c; sourceTree = ""; }; + FCB1BE4314B6460C0070FACB /* test00.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test00.sh; sourceTree = ""; }; + FCB1BE4414B6460C0070FACB /* test01.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test01.sh; sourceTree = ""; }; + FCB1BE4514B6460C0070FACB /* test02.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test02.sh; sourceTree = ""; }; + FCB1BE4614B6460C0070FACB /* test03.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test03.sh; sourceTree = ""; }; + FCB1BE4714B6460C0070FACB /* test04.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = test04.sh; sourceTree = ""; }; + FCB1BE4814B6460C0070FACB /* verify.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = verify.c; sourceTree = ""; }; + FCB1BE4B14B6460C0070FACB /* mv.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mv.1; sourceTree = ""; }; + FCB1BE4C14B6460C0070FACB /* mv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mv.c; sourceTree = ""; }; + FCB1BE4D14B6460C0070FACB /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = ""; }; + FCB1BE5014B6460C0070FACB /* pathchk.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pathchk.1; sourceTree = ""; }; + FCB1BE5114B6460C0070FACB /* pathchk.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pathchk.c; sourceTree = ""; }; + FCB1BE5314B6460C0070FACB /* ar_io.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ar_io.c; sourceTree = ""; }; + FCB1BE5414B6460C0070FACB /* ar_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ar_subs.c; sourceTree = ""; }; + FCB1BE5514B6460C0070FACB /* buf_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = buf_subs.c; sourceTree = ""; }; + FCB1BE5614B6460C0070FACB /* cache.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cache.c; sourceTree = ""; }; + FCB1BE5714B6460C0070FACB /* cache.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cache.h; sourceTree = ""; }; + FCB1BE5814B6460C0070FACB /* cpio.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = cpio.1; sourceTree = ""; }; + FCB1BE5914B6460C0070FACB /* cpio.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = cpio.c; sourceTree = ""; }; + FCB1BE5A14B6460C0070FACB /* cpio.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = cpio.h; sourceTree = ""; }; + FCB1BE5B14B6460C0070FACB /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = ""; }; + FCB1BE5C14B6460C0070FACB /* file_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = file_subs.c; sourceTree = ""; }; + FCB1BE5D14B6460C0070FACB /* ftree.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ftree.c; sourceTree = ""; }; + FCB1BE5E14B6460C0070FACB /* ftree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ftree.h; sourceTree = ""; }; + FCB1BE5F14B6460C0070FACB /* gen_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gen_subs.c; sourceTree = ""; }; + FCB1BE6014B6460C0070FACB /* getoldopt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = getoldopt.c; sourceTree = ""; }; + FCB1BE6214B6460C0070FACB /* options.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = options.c; sourceTree = ""; }; + FCB1BE6314B6460C0070FACB /* options.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = ""; }; + FCB1BE6414B6460C0070FACB /* pat_rep.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pat_rep.c; sourceTree = ""; }; + FCB1BE6514B6460C0070FACB /* pat_rep.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pat_rep.h; sourceTree = ""; }; + FCB1BE6614B6460C0070FACB /* pax.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pax.1; sourceTree = ""; }; + FCB1BE6714B6460C0070FACB /* pax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pax.c; sourceTree = ""; }; + FCB1BE6814B6460C0070FACB /* pax.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pax.h; sourceTree = ""; }; + FCB1BE6914B6460C0070FACB /* pax_format.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pax_format.c; sourceTree = ""; }; + FCB1BE6A14B6460C0070FACB /* pax_format.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pax_format.h; sourceTree = ""; }; + FCB1BE6B14B6460C0070FACB /* sel_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sel_subs.c; sourceTree = ""; }; + FCB1BE6C14B6460C0070FACB /* sel_subs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = sel_subs.h; sourceTree = ""; }; + FCB1BE6D14B6460C0070FACB /* tables.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tables.c; sourceTree = ""; }; + FCB1BE6E14B6460C0070FACB /* tables.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tables.h; sourceTree = ""; }; + FCB1BE6F14B6460C0070FACB /* tar.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tar.c; sourceTree = ""; }; + FCB1BE7014B6460C0070FACB /* tar.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = tar.h; sourceTree = ""; }; + FCB1BE7114B6460C0070FACB /* tty_subs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tty_subs.c; sourceTree = ""; }; + FCB1BE7414B6460C0070FACB /* rm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rm.1; sourceTree = ""; }; + FCB1BE7514B6460C0070FACB /* rm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rm.c; sourceTree = ""; }; + FCB1BE7814B6460C0070FACB /* rmdir.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = rmdir.1; sourceTree = ""; }; + FCB1BE7914B6460C0070FACB /* rmdir.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rmdir.c; sourceTree = ""; }; + FCB1BE7C14B6460C0070FACB /* rmt.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = rmt.8; sourceTree = ""; }; + FCB1BE7D14B6460C0070FACB /* rmt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = rmt.c; sourceTree = ""; }; + FCB1BE8014B6460C0070FACB /* shar.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = shar.1; sourceTree = ""; }; + FCB1BE8114B6460C0070FACB /* shar.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = shar.sh; sourceTree = ""; }; + FCB1BE8414B6460C0070FACB /* stat.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = stat.1; sourceTree = ""; }; + FCB1BE8514B6460C0070FACB /* stat.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = stat.c; sourceTree = ""; }; + FCB1BE8814B6460C0070FACB /* touch.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = touch.1; sourceTree = ""; }; + FCB1BE8914B6460C0070FACB /* touch.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = touch.c; sourceTree = ""; }; + FDAD94801808BB3A00B4D5A0 /* gzexe */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = gzexe; sourceTree = ""; }; + FDAD94811808BB3A00B4D5A0 /* gzexe.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = gzexe.1; sourceTree = ""; }; + FDAD94821808BB3A00B4D5A0 /* gzip.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = gzip.1; sourceTree = ""; }; + FDAD94831808BB3A00B4D5A0 /* gzip.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = gzip.c; sourceTree = ""; }; + FDAD94841808BB3A00B4D5A0 /* gzip.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = gzip.plist; sourceTree = ""; }; + FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = gzip.xcconfig; sourceTree = ""; }; + FDAD94861808BB3A00B4D5A0 /* install_scripts.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = install_scripts.sh; sourceTree = ""; }; + FDAD94871808BB3A00B4D5A0 /* unbzip2.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unbzip2.c; sourceTree = ""; }; + FDAD94881808BB3A00B4D5A0 /* unpack.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = ""; }; + FDAD94891808BB3A00B4D5A0 /* unxz.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = unxz.c; sourceTree = ""; }; + FDAD948A1808BB3A00B4D5A0 /* zdiff */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zdiff; sourceTree = ""; }; + FDAD948B1808BB3A00B4D5A0 /* zdiff.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zdiff.1; sourceTree = ""; }; + FDAD948C1808BB3A00B4D5A0 /* zforce */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zforce; sourceTree = ""; }; + FDAD948D1808BB3A00B4D5A0 /* zforce.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zforce.1; sourceTree = ""; }; + FDAD948E1808BB3A00B4D5A0 /* zmore */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = zmore; sourceTree = ""; }; + FDAD948F1808BB3A00B4D5A0 /* zmore.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = zmore.1; sourceTree = ""; }; + FDAD94901808BB3A00B4D5A0 /* znew */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = znew; sourceTree = ""; }; + FDAD94911808BB3A00B4D5A0 /* znew.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = znew.1; sourceTree = ""; }; + FDAD94921808BB3A00B4D5A0 /* zuncompress.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = zuncompress.c; sourceTree = ""; }; + FDAD94971808BB6D00B4D5A0 /* gzip */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = gzip; sourceTree = BUILT_PRODUCTS_DIR; }; + FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libbz2.dylib; path = usr/lib/libbz2.dylib; sourceTree = SDKROOT; }; + FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = liblzma.dylib; path = usr/lib/liblzma.dylib; sourceTree = SDKROOT; }; + FDAD94A51808BC9B00B4D5A0 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + FC8A8B0E14B648D7001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B1614B648E0001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B1E14B648E3001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B2614B648E5001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B2E14B648E7001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B3614B648EA001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B3E14B648EA001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8CC214B658D7001B97AD /* libutil.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B4614B648EB001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B4E14B648EB001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B5614B648EB001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B5E14B648EC001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B6614B648EC001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B6E14B648ED001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8CC614B65C46001B97AD /* libutil.dylib in Frameworks */, + FC8A8CC514B65C3D001B97AD /* libcurses.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B7614B648ED001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B7E14B648ED001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B8614B648ED001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B8E14B648ED001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B9614B648EE001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B9E14B648EE001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BA614B648EE001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BAE14B648EF001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BB614B648EF001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BC614B648EF001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BCE14B648F0001B97AD /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCB1BDB514B645D10070FACB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FDAD94941808BB6D00B4D5A0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FDAD94A21808BC9100B4D5A0 /* libbz2.dylib in Frameworks */, + FDAD94A41808BC9700B4D5A0 /* liblzma.dylib in Frameworks */, + FDAD94A61808BC9B00B4D5A0 /* libz.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + FCB1BDAD14B645D00070FACB = { + isa = PBXGroup; + children = ( + FCB1BDCA14B6460C0070FACB /* chflags */, + FCB1BDCE14B6460C0070FACB /* chmod */, + FCB1BDD414B6460C0070FACB /* chown */, + FCB1BDD914B6460C0070FACB /* cksum */, + FCB1BDE314B6460C0070FACB /* compress */, + FCB1BDF014B6460C0070FACB /* cp */, + FCB1BDF614B6460C0070FACB /* csh */, + FCB1BDF814B6460C0070FACB /* dd */, + FCB1BE0314B6460C0070FACB /* df */, + FCB1BE0814B6460C0070FACB /* du */, + FDAD947F1808BB3A00B4D5A0 /* gzip */, + FCB1BE0C14B6460C0070FACB /* install */, + FCB1BE1114B6460C0070FACB /* ipcrm */, + FCB1BE1514B6460C0070FACB /* ipcs */, + FCB1BE1914B6460C0070FACB /* ln */, + FCB1BE1E14B6460C0070FACB /* ls */, + FCB1BE2814B6460C0070FACB /* mkdir */, + FCB1BE2C14B6460C0070FACB /* mkfifo */, + FCB1BE3014B6460C0070FACB /* mknod */, + FCB1BE3414B6460C0070FACB /* mtree */, + FCB1BE4914B6460C0070FACB /* mv */, + FCB1BE4E14B6460C0070FACB /* pathchk */, + FCB1BE5214B6460C0070FACB /* pax */, + FCB1BE7214B6460C0070FACB /* rm */, + FCB1BE7614B6460C0070FACB /* rmdir */, + FCB1BE7A14B6460C0070FACB /* rmt */, + FCB1BE7E14B6460C0070FACB /* shar */, + FCB1BE8214B6460C0070FACB /* stat */, + FCB1BE8614B6460C0070FACB /* touch */, + FDAD94A71808BCB700B4D5A0 /* Libraries */, + FCB1BDB914B645D10070FACB /* Products */, + ); + indentWidth = 8; + sourceTree = ""; + tabWidth = 8; + usesTabs = 1; + }; + FCB1BDB914B645D10070FACB /* Products */ = { + isa = PBXGroup; + children = ( + FCB1BDB814B645D10070FACB /* chflags */, + FC8A8B1214B648D7001B97AD /* chmod */, + FC8A8B1A14B648E0001B97AD /* chown */, + FC8A8B2214B648E3001B97AD /* cksum */, + FC8A8B2A14B648E5001B97AD /* compress */, + FC8A8B3214B648E7001B97AD /* cp */, + FC8A8B3A14B648EA001B97AD /* dd */, + FC8A8B4214B648EA001B97AD /* df */, + FC8A8B4A14B648EB001B97AD /* du */, + FC8A8B5214B648EB001B97AD /* install */, + FC8A8B5A14B648EB001B97AD /* ipcrm */, + FC8A8B6214B648EC001B97AD /* ipcs */, + FC8A8B6A14B648EC001B97AD /* ln */, + FC8A8B7214B648ED001B97AD /* ls */, + FC8A8B7A14B648ED001B97AD /* mkdir */, + FC8A8B8214B648ED001B97AD /* mkfifo */, + FC8A8B8A14B648ED001B97AD /* mknod */, + FC8A8B9214B648ED001B97AD /* mtree */, + FC8A8B9A14B648EE001B97AD /* mv */, + FC8A8BA214B648EE001B97AD /* pathchk */, + FC8A8BAA14B648EE001B97AD /* pax */, + FC8A8BB214B648EF001B97AD /* rm */, + FC8A8BBA14B648EF001B97AD /* rmdir */, + FC8A8BCA14B648EF001B97AD /* stat */, + FC8A8BD214B648F0001B97AD /* touch */, + FDAD94971808BB6D00B4D5A0 /* gzip */, + ); + name = Products; + sourceTree = ""; + }; + FCB1BDCA14B6460C0070FACB /* chflags */ = { + isa = PBXGroup; + children = ( + FCB1BDCB14B6460C0070FACB /* chflags.1 */, + FCB1BDCC14B6460C0070FACB /* chflags.c */, + ); + path = chflags; + sourceTree = ""; + }; + FCB1BDCE14B6460C0070FACB /* chmod */ = { + isa = PBXGroup; + children = ( + FCB1BDCF14B6460C0070FACB /* chmod.1 */, + FCB1BDD014B6460C0070FACB /* chmod.c */, + FCB1BDD114B6460C0070FACB /* chmod_acl.c */, + FCB1BDD214B6460C0070FACB /* chmod_acl.h */, + ); + path = chmod; + sourceTree = ""; + }; + FCB1BDD414B6460C0070FACB /* chown */ = { + isa = PBXGroup; + children = ( + FCB1BDD514B6460C0070FACB /* chgrp.1 */, + FCB1BDD614B6460C0070FACB /* chown.8 */, + FCB1BDD714B6460C0070FACB /* chown.c */, + ); + path = chown; + sourceTree = ""; + }; + FCB1BDD914B6460C0070FACB /* cksum */ = { + isa = PBXGroup; + children = ( + FC8A8C6614B6535B001B97AD /* sum.1 */, + FCB1BDDA14B6460C0070FACB /* cksum.1 */, + FCB1BDDB14B6460C0070FACB /* cksum.c */, + FCB1BDDC14B6460C0070FACB /* crc.c */, + FCB1BDDD14B6460C0070FACB /* crc32.c */, + FCB1BDDE14B6460C0070FACB /* extern.h */, + FCB1BDE014B6460C0070FACB /* print.c */, + FCB1BDE114B6460C0070FACB /* sum1.c */, + FCB1BDE214B6460C0070FACB /* sum2.c */, + ); + path = cksum; + sourceTree = ""; + }; + FCB1BDE314B6460C0070FACB /* compress */ = { + isa = PBXGroup; + children = ( + FCB1BDE414B6460C0070FACB /* compress.1 */, + FCB1BDE514B6460C0070FACB /* compress.c */, + FCB1BDE614B6460C0070FACB /* doc */, + FCB1BDEB14B6460C0070FACB /* uncompress.1 */, + FCB1BDEC14B6460C0070FACB /* zcat.sh */, + FCB1BDED14B6460C0070FACB /* zopen.3 */, + FCB1BDEE14B6460C0070FACB /* zopen.c */, + FCB1BDEF14B6460C0070FACB /* zopen.h */, + ); + path = compress; + sourceTree = ""; + }; + FCB1BDE614B6460C0070FACB /* doc */ = { + isa = PBXGroup; + children = ( + FCB1BDE714B6460C0070FACB /* NOTES */, + FCB1BDE814B6460C0070FACB /* README */, + FCB1BDE914B6460C0070FACB /* revision.log */, + ); + path = doc; + sourceTree = ""; + }; + FCB1BDF014B6460C0070FACB /* cp */ = { + isa = PBXGroup; + children = ( + FCB1BDF114B6460C0070FACB /* cp.1 */, + FCB1BDF214B6460C0070FACB /* cp.c */, + FCB1BDF314B6460C0070FACB /* extern.h */, + FCB1BDF514B6460C0070FACB /* utils.c */, + ); + path = cp; + sourceTree = ""; + }; + FCB1BDF614B6460C0070FACB /* csh */ = { + isa = PBXGroup; + children = ( + FCB1BDF714B6460C0070FACB /* strpct.c */, + ); + path = csh; + sourceTree = ""; + }; + FCB1BDF814B6460C0070FACB /* dd */ = { + isa = PBXGroup; + children = ( + 0773099A1A3A4DFE00E9B4EA /* dd.entitlements */, + FCB1BDF914B6460C0070FACB /* args.c */, + FCB1BDFA14B6460C0070FACB /* conv.c */, + FCB1BDFB14B6460C0070FACB /* conv_tab.c */, + FCB1BDFC14B6460C0070FACB /* dd.1 */, + FCB1BDFD14B6460C0070FACB /* dd.c */, + FCB1BDFE14B6460C0070FACB /* dd.h */, + FCB1BDFF14B6460C0070FACB /* extern.h */, + FCB1BE0114B6460C0070FACB /* misc.c */, + FCB1BE0214B6460C0070FACB /* position.c */, + ); + path = dd; + sourceTree = ""; + }; + FCB1BE0314B6460C0070FACB /* df */ = { + isa = PBXGroup; + children = ( + FCB1BE0414B6460C0070FACB /* df.1 */, + FCB1BE0514B6460C0070FACB /* df.c */, + FCB1BE0714B6460C0070FACB /* vfslist.c */, + ); + path = df; + sourceTree = ""; + }; + FCB1BE0814B6460C0070FACB /* du */ = { + isa = PBXGroup; + children = ( + FCB1BE0914B6460C0070FACB /* du.1 */, + FCB1BE0A14B6460C0070FACB /* du.c */, + ); + path = du; + sourceTree = ""; + }; + FCB1BE0C14B6460C0070FACB /* install */ = { + isa = PBXGroup; + children = ( + FCB1BE0D14B6460C0070FACB /* install.1 */, + FCB1BE0F14B6460C0070FACB /* pathnames.h */, + FCB1BE1014B6460C0070FACB /* xinstall.c */, + ); + path = install; + sourceTree = ""; + }; + FCB1BE1114B6460C0070FACB /* ipcrm */ = { + isa = PBXGroup; + children = ( + FCB1BE1214B6460C0070FACB /* ipcrm.1 */, + FCB1BE1314B6460C0070FACB /* ipcrm.c */, + ); + path = ipcrm; + sourceTree = ""; + }; + FCB1BE1514B6460C0070FACB /* ipcs */ = { + isa = PBXGroup; + children = ( + FCB1BE1614B6460C0070FACB /* ipcs.1 */, + FCB1BE1714B6460C0070FACB /* ipcs.c */, + ); + path = ipcs; + sourceTree = ""; + }; + FCB1BE1914B6460C0070FACB /* ln */ = { + isa = PBXGroup; + children = ( + FC8A8C7214B65547001B97AD /* link.1 */, + FCB1BE1A14B6460C0070FACB /* ln.1 */, + FCB1BE1B14B6460C0070FACB /* ln.c */, + FCB1BE1D14B6460C0070FACB /* symlink.7 */, + ); + path = ln; + sourceTree = ""; + }; + FCB1BE1E14B6460C0070FACB /* ls */ = { + isa = PBXGroup; + children = ( + FCB1BE1F14B6460C0070FACB /* cmp.c */, + FCB1BE2014B6460C0070FACB /* extern.h */, + FCB1BE2114B6460C0070FACB /* ls.1 */, + FCB1BE2214B6460C0070FACB /* ls.c */, + FCB1BE2314B6460C0070FACB /* ls.h */, + FCB1BE2514B6460C0070FACB /* print.c */, + FCB1BE2614B6460C0070FACB /* util.c */, + FC8A8CC414B65C3D001B97AD /* libcurses.dylib */, + FC8A8CC114B658D6001B97AD /* libutil.dylib */, + ); + path = ls; + sourceTree = ""; + }; + FCB1BE2814B6460C0070FACB /* mkdir */ = { + isa = PBXGroup; + children = ( + FCB1BE2A14B6460C0070FACB /* mkdir.1 */, + FCB1BE2B14B6460C0070FACB /* mkdir.c */, + ); + path = mkdir; + sourceTree = ""; + }; + FCB1BE2C14B6460C0070FACB /* mkfifo */ = { + isa = PBXGroup; + children = ( + FCB1BE2E14B6460C0070FACB /* mkfifo.1 */, + FCB1BE2F14B6460C0070FACB /* mkfifo.c */, + ); + path = mkfifo; + sourceTree = ""; + }; + FCB1BE3014B6460C0070FACB /* mknod */ = { + isa = PBXGroup; + children = ( + FCB1BE3214B6460C0070FACB /* mknod.8 */, + FCB1BE3314B6460C0070FACB /* mknod.c */, + ); + path = mknod; + sourceTree = ""; + }; + FCB1BE3414B6460C0070FACB /* mtree */ = { + isa = PBXGroup; + children = ( + FCB1BE3514B6460C0070FACB /* commoncrypto.c */, + FCB1BE3614B6460C0070FACB /* commoncrypto.h */, + FCB1BE3714B6460C0070FACB /* compare.c */, + FCB1BE3814B6460C0070FACB /* create.c */, + FCB1BE3914B6460C0070FACB /* excludes.c */, + FCB1BE3A14B6460C0070FACB /* extern.h */, + FCB1BE3C14B6460C0070FACB /* misc.c */, + FCB1BE3D14B6460C0070FACB /* mtree.8 */, + FCB1BE3E14B6460C0070FACB /* mtree.c */, + FCB1BE3F14B6460C0070FACB /* mtree.h */, + FCB1BE4014B6460C0070FACB /* spec.c */, + FCB1BE4114B6460C0070FACB /* specspec.c */, + FCB1BE4214B6460C0070FACB /* test */, + FCB1BE4814B6460C0070FACB /* verify.c */, + ); + path = mtree; + sourceTree = ""; + }; + FCB1BE4214B6460C0070FACB /* test */ = { + isa = PBXGroup; + children = ( + FCB1BE4314B6460C0070FACB /* test00.sh */, + FCB1BE4414B6460C0070FACB /* test01.sh */, + FCB1BE4514B6460C0070FACB /* test02.sh */, + FCB1BE4614B6460C0070FACB /* test03.sh */, + FCB1BE4714B6460C0070FACB /* test04.sh */, + ); + path = test; + sourceTree = ""; + }; + FCB1BE4914B6460C0070FACB /* mv */ = { + isa = PBXGroup; + children = ( + FCB1BE4B14B6460C0070FACB /* mv.1 */, + FCB1BE4C14B6460C0070FACB /* mv.c */, + FCB1BE4D14B6460C0070FACB /* pathnames.h */, + ); + path = mv; + sourceTree = ""; + }; + FCB1BE4E14B6460C0070FACB /* pathchk */ = { + isa = PBXGroup; + children = ( + FCB1BE5014B6460C0070FACB /* pathchk.1 */, + FCB1BE5114B6460C0070FACB /* pathchk.c */, + ); + path = pathchk; + sourceTree = ""; + }; + FCB1BE5214B6460C0070FACB /* pax */ = { + isa = PBXGroup; + children = ( + FCB1BE5314B6460C0070FACB /* ar_io.c */, + FCB1BE5414B6460C0070FACB /* ar_subs.c */, + FCB1BE5514B6460C0070FACB /* buf_subs.c */, + FCB1BE5614B6460C0070FACB /* cache.c */, + FCB1BE5714B6460C0070FACB /* cache.h */, + FCB1BE5814B6460C0070FACB /* cpio.1 */, + FCB1BE5914B6460C0070FACB /* cpio.c */, + FCB1BE5A14B6460C0070FACB /* cpio.h */, + FCB1BE5B14B6460C0070FACB /* extern.h */, + FCB1BE5C14B6460C0070FACB /* file_subs.c */, + FCB1BE5D14B6460C0070FACB /* ftree.c */, + FCB1BE5E14B6460C0070FACB /* ftree.h */, + FCB1BE5F14B6460C0070FACB /* gen_subs.c */, + FCB1BE6014B6460C0070FACB /* getoldopt.c */, + FCB1BE6214B6460C0070FACB /* options.c */, + FCB1BE6314B6460C0070FACB /* options.h */, + FCB1BE6414B6460C0070FACB /* pat_rep.c */, + FCB1BE6514B6460C0070FACB /* pat_rep.h */, + FCB1BE6614B6460C0070FACB /* pax.1 */, + FCB1BE6714B6460C0070FACB /* pax.c */, + FCB1BE6814B6460C0070FACB /* pax.h */, + FCB1BE6914B6460C0070FACB /* pax_format.c */, + FCB1BE6A14B6460C0070FACB /* pax_format.h */, + FCB1BE6B14B6460C0070FACB /* sel_subs.c */, + FCB1BE6C14B6460C0070FACB /* sel_subs.h */, + FCB1BE6D14B6460C0070FACB /* tables.c */, + FCB1BE6E14B6460C0070FACB /* tables.h */, + FCB1BE6F14B6460C0070FACB /* tar.c */, + FCB1BE7014B6460C0070FACB /* tar.h */, + FCB1BE7114B6460C0070FACB /* tty_subs.c */, + ); + path = pax; + sourceTree = ""; + }; + FCB1BE7214B6460C0070FACB /* rm */ = { + isa = PBXGroup; + children = ( + FCB1BE7414B6460C0070FACB /* rm.1 */, + FCB1BE7514B6460C0070FACB /* rm.c */, + FC8A8C4F14B650C3001B97AD /* unlink.1 */, + ); + path = rm; + sourceTree = ""; + }; + FCB1BE7614B6460C0070FACB /* rmdir */ = { + isa = PBXGroup; + children = ( + FCB1BE7814B6460C0070FACB /* rmdir.1 */, + FCB1BE7914B6460C0070FACB /* rmdir.c */, + ); + path = rmdir; + sourceTree = ""; + }; + FCB1BE7A14B6460C0070FACB /* rmt */ = { + isa = PBXGroup; + children = ( + FCB1BE7C14B6460C0070FACB /* rmt.8 */, + FCB1BE7D14B6460C0070FACB /* rmt.c */, + ); + path = rmt; + sourceTree = ""; + }; + FCB1BE7E14B6460C0070FACB /* shar */ = { + isa = PBXGroup; + children = ( + FCB1BE8014B6460C0070FACB /* shar.1 */, + FCB1BE8114B6460C0070FACB /* shar.sh */, + ); + path = shar; + sourceTree = ""; + }; + FCB1BE8214B6460C0070FACB /* stat */ = { + isa = PBXGroup; + children = ( + FC8A8C4D14B64EA8001B97AD /* readlink.1 */, + FCB1BE8414B6460C0070FACB /* stat.1 */, + FCB1BE8514B6460C0070FACB /* stat.c */, + ); + path = stat; + sourceTree = ""; + }; + FCB1BE8614B6460C0070FACB /* touch */ = { + isa = PBXGroup; + children = ( + FCB1BE8814B6460C0070FACB /* touch.1 */, + FCB1BE8914B6460C0070FACB /* touch.c */, + ); + path = touch; + sourceTree = ""; + }; + FDAD947F1808BB3A00B4D5A0 /* gzip */ = { + isa = PBXGroup; + children = ( + 3E59B9301D4A767600D3128C /* futimens.c */, + FDAD94801808BB3A00B4D5A0 /* gzexe */, + FDAD94811808BB3A00B4D5A0 /* gzexe.1 */, + FDAD94821808BB3A00B4D5A0 /* gzip.1 */, + FDAD94831808BB3A00B4D5A0 /* gzip.c */, + FDAD94841808BB3A00B4D5A0 /* gzip.plist */, + FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */, + FDAD94861808BB3A00B4D5A0 /* install_scripts.sh */, + FDAD94871808BB3A00B4D5A0 /* unbzip2.c */, + FDAD94881808BB3A00B4D5A0 /* unpack.c */, + FDAD94891808BB3A00B4D5A0 /* unxz.c */, + FDAD948A1808BB3A00B4D5A0 /* zdiff */, + FDAD948B1808BB3A00B4D5A0 /* zdiff.1 */, + FDAD948C1808BB3A00B4D5A0 /* zforce */, + FDAD948D1808BB3A00B4D5A0 /* zforce.1 */, + FDAD948E1808BB3A00B4D5A0 /* zmore */, + FDAD948F1808BB3A00B4D5A0 /* zmore.1 */, + FDAD94901808BB3A00B4D5A0 /* znew */, + FDAD94911808BB3A00B4D5A0 /* znew.1 */, + FDAD94921808BB3A00B4D5A0 /* zuncompress.c */, + ); + path = gzip; + sourceTree = ""; + }; + FDAD94A71808BCB700B4D5A0 /* Libraries */ = { + isa = PBXGroup; + children = ( + FDAD94A11808BC9100B4D5A0 /* libbz2.dylib */, + FDAD94A31808BC9700B4D5A0 /* liblzma.dylib */, + FDAD94A51808BC9B00B4D5A0 /* libz.dylib */, + ); + name = Libraries; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + FC8A8B0B14B648D7001B97AD /* chmod */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B1014B648D7001B97AD /* Build configuration list for PBXNativeTarget "chmod" */; + buildPhases = ( + FC8A8B0C14B648D7001B97AD /* Sources */, + FC8A8B0E14B648D7001B97AD /* Frameworks */, + FC8A8B0F14B648D7001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = chmod; + productName = file_cmds; + productReference = FC8A8B1214B648D7001B97AD /* chmod */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B1414B648E0001B97AD /* chown */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B1814B648E0001B97AD /* Build configuration list for PBXNativeTarget "chown" */; + buildPhases = ( + FC8A8B1514B648E0001B97AD /* Sources */, + FC8A8B1614B648E0001B97AD /* Frameworks */, + FC8A8B1714B648E0001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = chown; + productName = file_cmds; + productReference = FC8A8B1A14B648E0001B97AD /* chown */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B1C14B648E3001B97AD /* cksum */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B2014B648E3001B97AD /* Build configuration list for PBXNativeTarget "cksum" */; + buildPhases = ( + FC8A8B1D14B648E3001B97AD /* Sources */, + FC8A8B1E14B648E3001B97AD /* Frameworks */, + FC8A8B1F14B648E3001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = cksum; + productName = file_cmds; + productReference = FC8A8B2214B648E3001B97AD /* cksum */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B2414B648E5001B97AD /* compress */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B2814B648E5001B97AD /* Build configuration list for PBXNativeTarget "compress" */; + buildPhases = ( + FC8A8B2514B648E5001B97AD /* Sources */, + FC8A8B2614B648E5001B97AD /* Frameworks */, + FC8A8B2714B648E5001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = compress; + productName = file_cmds; + productReference = FC8A8B2A14B648E5001B97AD /* compress */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B2C14B648E7001B97AD /* cp */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B3014B648E7001B97AD /* Build configuration list for PBXNativeTarget "cp" */; + buildPhases = ( + FC8A8B2D14B648E7001B97AD /* Sources */, + FC8A8B2E14B648E7001B97AD /* Frameworks */, + FC8A8B2F14B648E7001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = cp; + productName = file_cmds; + productReference = FC8A8B3214B648E7001B97AD /* cp */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B3414B648EA001B97AD /* dd */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B3814B648EA001B97AD /* Build configuration list for PBXNativeTarget "dd" */; + buildPhases = ( + FC8A8B3514B648EA001B97AD /* Sources */, + FC8A8B3614B648EA001B97AD /* Frameworks */, + FC8A8B3714B648EA001B97AD /* CopyFiles */, + 72E62BA81A3A62960015FC8E /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dd; + productName = file_cmds; + productReference = FC8A8B3A14B648EA001B97AD /* dd */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B3C14B648EA001B97AD /* df */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B4014B648EA001B97AD /* Build configuration list for PBXNativeTarget "df" */; + buildPhases = ( + FC8A8B3D14B648EA001B97AD /* Sources */, + FC8A8B3E14B648EA001B97AD /* Frameworks */, + FC8A8B3F14B648EA001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = df; + productName = file_cmds; + productReference = FC8A8B4214B648EA001B97AD /* df */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B4414B648EB001B97AD /* du */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B4814B648EB001B97AD /* Build configuration list for PBXNativeTarget "du" */; + buildPhases = ( + FC8A8B4514B648EB001B97AD /* Sources */, + FC8A8B4614B648EB001B97AD /* Frameworks */, + FC8A8B4714B648EB001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = du; + productName = file_cmds; + productReference = FC8A8B4A14B648EB001B97AD /* du */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B4C14B648EB001B97AD /* install */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B5014B648EB001B97AD /* Build configuration list for PBXNativeTarget "install" */; + buildPhases = ( + FC8A8B4D14B648EB001B97AD /* Sources */, + FC8A8B4E14B648EB001B97AD /* Frameworks */, + FC8A8B4F14B648EB001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = install; + productName = file_cmds; + productReference = FC8A8B5214B648EB001B97AD /* install */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B5414B648EB001B97AD /* ipcrm */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B5814B648EB001B97AD /* Build configuration list for PBXNativeTarget "ipcrm" */; + buildPhases = ( + FC8A8B5514B648EB001B97AD /* Sources */, + FC8A8B5614B648EB001B97AD /* Frameworks */, + FC8A8B5714B648EB001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ipcrm; + productName = file_cmds; + productReference = FC8A8B5A14B648EB001B97AD /* ipcrm */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B5C14B648EC001B97AD /* ipcs */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B6014B648EC001B97AD /* Build configuration list for PBXNativeTarget "ipcs" */; + buildPhases = ( + FC8A8B5D14B648EC001B97AD /* Sources */, + FC8A8B5E14B648EC001B97AD /* Frameworks */, + FC8A8B5F14B648EC001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ipcs; + productName = file_cmds; + productReference = FC8A8B6214B648EC001B97AD /* ipcs */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B6414B648EC001B97AD /* ln */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B6814B648EC001B97AD /* Build configuration list for PBXNativeTarget "ln" */; + buildPhases = ( + FC8A8B6514B648EC001B97AD /* Sources */, + FC8A8B6614B648EC001B97AD /* Frameworks */, + FC8A8B6714B648EC001B97AD /* CopyFiles */, + FC8A8C7E14B6557E001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ln; + productName = file_cmds; + productReference = FC8A8B6A14B648EC001B97AD /* ln */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B6C14B648ED001B97AD /* ls */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B7014B648ED001B97AD /* Build configuration list for PBXNativeTarget "ls" */; + buildPhases = ( + FC8A8B6D14B648ED001B97AD /* Sources */, + FC8A8B6E14B648ED001B97AD /* Frameworks */, + FC8A8B6F14B648ED001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ls; + productName = file_cmds; + productReference = FC8A8B7214B648ED001B97AD /* ls */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B7414B648ED001B97AD /* mkdir */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B7814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkdir" */; + buildPhases = ( + FC8A8B7514B648ED001B97AD /* Sources */, + FC8A8B7614B648ED001B97AD /* Frameworks */, + FC8A8B7714B648ED001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mkdir; + productName = file_cmds; + productReference = FC8A8B7A14B648ED001B97AD /* mkdir */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B7C14B648ED001B97AD /* mkfifo */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B8014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkfifo" */; + buildPhases = ( + FC8A8B7D14B648ED001B97AD /* Sources */, + FC8A8B7E14B648ED001B97AD /* Frameworks */, + FC8A8B7F14B648ED001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mkfifo; + productName = file_cmds; + productReference = FC8A8B8214B648ED001B97AD /* mkfifo */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B8414B648ED001B97AD /* mknod */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B8814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mknod" */; + buildPhases = ( + FC8A8B8514B648ED001B97AD /* Sources */, + FC8A8B8614B648ED001B97AD /* Frameworks */, + FC8A8B8714B648ED001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mknod; + productName = file_cmds; + productReference = FC8A8B8A14B648ED001B97AD /* mknod */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B8C14B648ED001B97AD /* mtree */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B9014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mtree" */; + buildPhases = ( + FC8A8B8D14B648ED001B97AD /* Sources */, + FC8A8B8E14B648ED001B97AD /* Frameworks */, + FC8A8B8F14B648ED001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mtree; + productName = file_cmds; + productReference = FC8A8B9214B648ED001B97AD /* mtree */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B9414B648EE001B97AD /* mv */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8B9814B648EE001B97AD /* Build configuration list for PBXNativeTarget "mv" */; + buildPhases = ( + FC8A8B9514B648EE001B97AD /* Sources */, + FC8A8B9614B648EE001B97AD /* Frameworks */, + FC8A8B9714B648EE001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mv; + productName = file_cmds; + productReference = FC8A8B9A14B648EE001B97AD /* mv */; + productType = "com.apple.product-type.tool"; + }; + FC8A8B9C14B648EE001B97AD /* pathchk */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BA014B648EE001B97AD /* Build configuration list for PBXNativeTarget "pathchk" */; + buildPhases = ( + FC8A8B9D14B648EE001B97AD /* Sources */, + FC8A8B9E14B648EE001B97AD /* Frameworks */, + FC8A8B9F14B648EE001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pathchk; + productName = file_cmds; + productReference = FC8A8BA214B648EE001B97AD /* pathchk */; + productType = "com.apple.product-type.tool"; + }; + FC8A8BA414B648EE001B97AD /* pax */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BA814B648EE001B97AD /* Build configuration list for PBXNativeTarget "pax" */; + buildPhases = ( + FC8A8BA514B648EE001B97AD /* Sources */, + FC8A8BA614B648EE001B97AD /* Frameworks */, + FC8A8BA714B648EE001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pax; + productName = file_cmds; + productReference = FC8A8BAA14B648EE001B97AD /* pax */; + productType = "com.apple.product-type.tool"; + }; + FC8A8BAC14B648EF001B97AD /* rm */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BB014B648EF001B97AD /* Build configuration list for PBXNativeTarget "rm" */; + buildPhases = ( + FC8A8BAD14B648EF001B97AD /* Sources */, + FC8A8BAE14B648EF001B97AD /* Frameworks */, + FC8A8BAF14B648EF001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rm; + productName = file_cmds; + productReference = FC8A8BB214B648EF001B97AD /* rm */; + productType = "com.apple.product-type.tool"; + }; + FC8A8BB414B648EF001B97AD /* rmdir */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BB814B648EF001B97AD /* Build configuration list for PBXNativeTarget "rmdir" */; + buildPhases = ( + FC8A8BB514B648EF001B97AD /* Sources */, + FC8A8BB614B648EF001B97AD /* Frameworks */, + FC8A8BB714B648EF001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = rmdir; + productName = file_cmds; + productReference = FC8A8BBA14B648EF001B97AD /* rmdir */; + productType = "com.apple.product-type.tool"; + }; + FC8A8BC414B648EF001B97AD /* stat */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BC814B648EF001B97AD /* Build configuration list for PBXNativeTarget "stat" */; + buildPhases = ( + FC8A8BC514B648EF001B97AD /* Sources */, + FC8A8BC614B648EF001B97AD /* Frameworks */, + FC8A8BC714B648EF001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = stat; + productName = file_cmds; + productReference = FC8A8BCA14B648EF001B97AD /* stat */; + productType = "com.apple.product-type.tool"; + }; + FC8A8BCC14B648F0001B97AD /* touch */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC8A8BD014B648F0001B97AD /* Build configuration list for PBXNativeTarget "touch" */; + buildPhases = ( + FC8A8BCD14B648F0001B97AD /* Sources */, + FC8A8BCE14B648F0001B97AD /* Frameworks */, + FC8A8BCF14B648F0001B97AD /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = touch; + productName = file_cmds; + productReference = FC8A8BD214B648F0001B97AD /* touch */; + productType = "com.apple.product-type.tool"; + }; + FCB1BDB714B645D10070FACB /* chflags */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCB1BDC214B645D10070FACB /* Build configuration list for PBXNativeTarget "chflags" */; + buildPhases = ( + FCB1BDB414B645D10070FACB /* Sources */, + FCB1BDB514B645D10070FACB /* Frameworks */, + 590112FE18284E58006881A1 /* ShellScript */, + FCB1BDB614B645D10070FACB /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = chflags; + productName = file_cmds; + productReference = FCB1BDB814B645D10070FACB /* chflags */; + productType = "com.apple.product-type.tool"; + }; + FDAD94961808BB6D00B4D5A0 /* gzip */ = { + isa = PBXNativeTarget; + buildConfigurationList = FDAD949D1808BB6D00B4D5A0 /* Build configuration list for PBXNativeTarget "gzip" */; + buildPhases = ( + FDAD94931808BB6D00B4D5A0 /* Sources */, + FDAD94941808BB6D00B4D5A0 /* Frameworks */, + FDAD94951808BB6D00B4D5A0 /* Install Man Page */, + FDAD94A01808BBF100B4D5A0 /* Install Scripts */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = gzip; + productName = gzip; + productReference = FDAD94971808BB6D00B4D5A0 /* gzip */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FCB1BDAF14B645D00070FACB /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0610; + ORGANIZATIONNAME = "Apple Inc."; + }; + buildConfigurationList = FCB1BDB214B645D00070FACB /* Build configuration list for PBXProject "file_cmds" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = FCB1BDAD14B645D00070FACB; + productRefGroup = FCB1BDB914B645D10070FACB /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FC8A8C8014B655ED001B97AD /* executables */, + FDFF0C501811BA2F00BFC477 /* eOS */, + FCB1BDB714B645D10070FACB /* chflags */, + FC8A8C5B14B652E1001B97AD /* chgrp */, + FC8A8B0B14B648D7001B97AD /* chmod */, + FC8A8B1414B648E0001B97AD /* chown */, + FC8A8B1C14B648E3001B97AD /* cksum */, + FC8A8B2414B648E5001B97AD /* compress */, + FC8A8B2C14B648E7001B97AD /* cp */, + FC8A8B3414B648EA001B97AD /* dd */, + FC8A8B3C14B648EA001B97AD /* df */, + FC8A8B4414B648EB001B97AD /* du */, + FDAD94961808BB6D00B4D5A0 /* gzip */, + FC8A8B4C14B648EB001B97AD /* install */, + FC8A8B5414B648EB001B97AD /* ipcrm */, + FC8A8B5C14B648EC001B97AD /* ipcs */, + FC8A8C7314B6554E001B97AD /* link */, + FC8A8B6414B648EC001B97AD /* ln */, + FC8A8B6C14B648ED001B97AD /* ls */, + FC8A8B7414B648ED001B97AD /* mkdir */, + FC8A8B7C14B648ED001B97AD /* mkfifo */, + FC8A8B8414B648ED001B97AD /* mknod */, + FC8A8B8C14B648ED001B97AD /* mtree */, + FC8A8B9414B648EE001B97AD /* mv */, + FC8A8B9C14B648EE001B97AD /* pathchk */, + FC8A8BA414B648EE001B97AD /* pax */, + FC8A8C4614B64DCD001B97AD /* readlink */, + FC8A8BAC14B648EF001B97AD /* rm */, + FC8A8BB414B648EF001B97AD /* rmdir */, + FC8A8C3C14B64A9D001B97AD /* shar */, + FC8A8BC414B648EF001B97AD /* stat */, + FC8A8C6714B6536D001B97AD /* sum */, + FC8A8BCC14B648F0001B97AD /* touch */, + FC8A8CC814B65F92001B97AD /* uncompress */, + FC8A8C5014B650CF001B97AD /* unlink */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + 590112FE18284E58006881A1 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = "/bin/sh -ex"; + shellScript = "install -d -g ${GROUP} -o ${USER} -m 0755 ${INSTALL_DIR}\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share/man\ninstall -d -g ${GROUP} -o ${USER} -m 0755 ${DSTROOT}/usr/share/man/man{1,7,8}"; + showEnvVarsInLog = 0; + }; + 72E62BA81A3A62960015FC8E /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". ${SRCROOT}/dd/install_symlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8C4C14B64DF9001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/stat", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8C5514B650CF001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/rm", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8C6014B652E1001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/usr/sbin/chown", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8C6C14B6536D001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/cksum", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8C7814B6554E001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/ln", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FC8A8CC714B65CEB001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = "install -d -o root -g wheel -m 0755 \"$DSTROOT\"/usr/bin\ninstall -c -o root -g wheel -m 0755 \"$PROJECT_DIR\"/shar/shar.sh \"$DSTROOT\"/usr/bin/shar"; + showEnvVarsInLog = 0; + }; + FC8A8CCD14B65F92001B97AD /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/compress", + ); + outputPaths = ( + "$(DSTROOT)/$(INSTALL_PATH)/$(PRODUCT_NAME)", + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/hardlink.sh"; + showEnvVarsInLog = 0; + }; + FDAD94A01808BBF100B4D5A0 /* Install Scripts */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Install Scripts"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". ${SRCROOT}/gzip/install_scripts.sh"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + FC8A8B0C14B648D7001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BE614B6495B001B97AD /* chmod_acl.c in Sources */, + FC8A8BE514B64958001B97AD /* chmod.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B1514B648E0001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BE814B64962001B97AD /* chown.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B1D14B648E3001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BF014B6497D001B97AD /* sum2.c in Sources */, + FC8A8BEF14B6497A001B97AD /* sum1.c in Sources */, + FC8A8BEE14B64977001B97AD /* print.c in Sources */, + FC8A8BED14B64975001B97AD /* crc32.c in Sources */, + FC8A8BEC14B64972001B97AD /* crc.c in Sources */, + FC8A8BEB14B64970001B97AD /* cksum.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B2514B648E5001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BF414B6498A001B97AD /* zopen.c in Sources */, + FC8A8BF314B64988001B97AD /* compress.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B2D14B648E7001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BF614B64998001B97AD /* utils.c in Sources */, + FC8A8BF514B64995001B97AD /* cp.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B3514B648EA001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8BFE14B649B1001B97AD /* position.c in Sources */, + FC8A8BFD14B649AE001B97AD /* misc.c in Sources */, + FC8A8BFC14B649AC001B97AD /* dd.c in Sources */, + FC8A8BFA14B649A7001B97AD /* conv_tab.c in Sources */, + FC8A8BF914B649A5001B97AD /* conv.c in Sources */, + FC8A8BF814B649A4001B97AD /* args.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B3D14B648EA001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0114B649D1001B97AD /* df.c in Sources */, + FC8A8CC314B6598F001B97AD /* vfslist.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B4514B648EB001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0214B649D4001B97AD /* du.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B4D14B648EB001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0414B649DD001B97AD /* xinstall.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B5514B648EB001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0714B649E3001B97AD /* ipcrm.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B5D14B648EC001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0814B649E8001B97AD /* ipcs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B6514B648EC001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0B14B649EF001B97AD /* ln.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B6D14B648ED001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C0D14B649F7001B97AD /* cmp.c in Sources */, + FC8A8C0E14B649F9001B97AD /* ls.c in Sources */, + FC8A8C0F14B649FC001B97AD /* print.c in Sources */, + FC8A8C1014B649FE001B97AD /* util.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B7514B648ED001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C1114B64A04001B97AD /* mkdir.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B7D14B648ED001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C1414B64A0A001B97AD /* mkfifo.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B8514B648ED001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C1614B64A0F001B97AD /* mknod.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B8D14B648ED001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8CD114B66E10001B97AD /* crc.c in Sources */, + FC8A8C1714B64A14001B97AD /* commoncrypto.c in Sources */, + FC8A8C1814B64A17001B97AD /* compare.c in Sources */, + FC8A8C1914B64A1A001B97AD /* create.c in Sources */, + FC8A8C1A14B64A22001B97AD /* excludes.c in Sources */, + FC8A8C1B14B64A27001B97AD /* misc.c in Sources */, + FC8A8C1C14B64A2D001B97AD /* mtree.c in Sources */, + FC8A8C1D14B64A31001B97AD /* spec.c in Sources */, + FC8A8C1E14B64A34001B97AD /* specspec.c in Sources */, + FC8A8C1F14B64A38001B97AD /* verify.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B9514B648EE001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C2214B64A4B001B97AD /* mv.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8B9D14B648EE001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C2414B64A53001B97AD /* pathchk.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BA514B648EE001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C2714B64A73001B97AD /* ar_io.c in Sources */, + FC8A8C2814B64A73001B97AD /* ar_subs.c in Sources */, + FC8A8C2914B64A73001B97AD /* buf_subs.c in Sources */, + FC8A8C2A14B64A73001B97AD /* cache.c in Sources */, + FC8A8C2B14B64A73001B97AD /* cpio.c in Sources */, + FC8A8C2C14B64A73001B97AD /* file_subs.c in Sources */, + FC8A8C2D14B64A73001B97AD /* ftree.c in Sources */, + FC8A8C2E14B64A73001B97AD /* gen_subs.c in Sources */, + FC8A8C2F14B64A73001B97AD /* getoldopt.c in Sources */, + FC8A8C3014B64A73001B97AD /* options.c in Sources */, + FC8A8C3114B64A73001B97AD /* pat_rep.c in Sources */, + FC8A8C3214B64A73001B97AD /* pax.c in Sources */, + FC8A8C3314B64A73001B97AD /* pax_format.c in Sources */, + FC8A8C3414B64A73001B97AD /* sel_subs.c in Sources */, + FC8A8C3514B64A73001B97AD /* tables.c in Sources */, + FC8A8C3614B64A73001B97AD /* tar.c in Sources */, + FC8A8C3714B64A73001B97AD /* tty_subs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BAD14B648EF001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C3914B64A7E001B97AD /* rm.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BB514B648EF001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C3B14B64A88001B97AD /* rmdir.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BC514B648EF001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C4214B64AC3001B97AD /* stat.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC8A8BCD14B648F0001B97AD /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8C4314B64AC7001B97AD /* touch.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCB1BDB414B645D10070FACB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC8A8A2814B6486E001B97AD /* chflags.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FDAD94931808BB6D00B4D5A0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3E59B9311D4A767600D3128C /* futimens.c in Sources */, + FDAD949F1808BBB900B4D5A0 /* gzip.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + FC8A8C4A14B64DE1001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BC414B648EF001B97AD /* stat */; + targetProxy = FC8A8C4914B64DE1001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C5914B65238001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BAC14B648EF001B97AD /* rm */; + targetProxy = FC8A8C5814B65238001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C6414B652FA001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1414B648E0001B97AD /* chown */; + targetProxy = FC8A8C6314B652FA001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C7014B6537C001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1C14B648E3001B97AD /* cksum */; + targetProxy = FC8A8C6F14B6537C001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C7C14B65562001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B6414B648EC001B97AD /* ln */; + targetProxy = FC8A8C7B14B65562001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8414B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCB1BDB714B645D10070FACB /* chflags */; + targetProxy = FC8A8C8314B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8614B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C5B14B652E1001B97AD /* chgrp */; + targetProxy = FC8A8C8514B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8814B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B0B14B648D7001B97AD /* chmod */; + targetProxy = FC8A8C8714B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8A14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1414B648E0001B97AD /* chown */; + targetProxy = FC8A8C8914B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8C14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1C14B648E3001B97AD /* cksum */; + targetProxy = FC8A8C8B14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C8E14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B2414B648E5001B97AD /* compress */; + targetProxy = FC8A8C8D14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9014B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B2C14B648E7001B97AD /* cp */; + targetProxy = FC8A8C8F14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9214B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B3414B648EA001B97AD /* dd */; + targetProxy = FC8A8C9114B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9414B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B3C14B648EA001B97AD /* df */; + targetProxy = FC8A8C9314B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9614B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B4414B648EB001B97AD /* du */; + targetProxy = FC8A8C9514B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9814B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B4C14B648EB001B97AD /* install */; + targetProxy = FC8A8C9714B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9A14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B5414B648EB001B97AD /* ipcrm */; + targetProxy = FC8A8C9914B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9C14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B5C14B648EC001B97AD /* ipcs */; + targetProxy = FC8A8C9B14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8C9E14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C7314B6554E001B97AD /* link */; + targetProxy = FC8A8C9D14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CA014B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B6414B648EC001B97AD /* ln */; + targetProxy = FC8A8C9F14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CA214B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B6C14B648ED001B97AD /* ls */; + targetProxy = FC8A8CA114B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CA414B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B7414B648ED001B97AD /* mkdir */; + targetProxy = FC8A8CA314B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CA614B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B7C14B648ED001B97AD /* mkfifo */; + targetProxy = FC8A8CA514B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CA814B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B8414B648ED001B97AD /* mknod */; + targetProxy = FC8A8CA714B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CAA14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B8C14B648ED001B97AD /* mtree */; + targetProxy = FC8A8CA914B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CAC14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B9414B648EE001B97AD /* mv */; + targetProxy = FC8A8CAB14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CAE14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B9C14B648EE001B97AD /* pathchk */; + targetProxy = FC8A8CAD14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CB014B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BA414B648EE001B97AD /* pax */; + targetProxy = FC8A8CAF14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CB214B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C4614B64DCD001B97AD /* readlink */; + targetProxy = FC8A8CB114B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CB414B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BAC14B648EF001B97AD /* rm */; + targetProxy = FC8A8CB314B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CB614B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BB414B648EF001B97AD /* rmdir */; + targetProxy = FC8A8CB514B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CB814B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C3C14B64A9D001B97AD /* shar */; + targetProxy = FC8A8CB714B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CBA14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BC414B648EF001B97AD /* stat */; + targetProxy = FC8A8CB914B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CBC14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C6714B6536D001B97AD /* sum */; + targetProxy = FC8A8CBB14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CBE14B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BCC14B648F0001B97AD /* touch */; + targetProxy = FC8A8CBD14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CC014B655FD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C5014B650CF001B97AD /* unlink */; + targetProxy = FC8A8CBF14B655FD001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CC914B65F92001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BAC14B648EF001B97AD /* rm */; + targetProxy = FC8A8CCA14B65F92001B97AD /* PBXContainerItemProxy */; + }; + FC8A8CD314B67BFD001B97AD /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8CC814B65F92001B97AD /* uncompress */; + targetProxy = FC8A8CD214B67BFD001B97AD /* PBXContainerItemProxy */; + }; + FDAD94AA1808BDAA00B4D5A0 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FDAD94961808BB6D00B4D5A0 /* gzip */; + targetProxy = FDAD94A91808BDAA00B4D5A0 /* PBXContainerItemProxy */; + }; + FDFF0C511811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCB1BDB714B645D10070FACB /* chflags */; + targetProxy = FDFF0C521811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C531811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C5B14B652E1001B97AD /* chgrp */; + targetProxy = FDFF0C541811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C551811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B0B14B648D7001B97AD /* chmod */; + targetProxy = FDFF0C561811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C571811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1414B648E0001B97AD /* chown */; + targetProxy = FDFF0C581811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C591811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B1C14B648E3001B97AD /* cksum */; + targetProxy = FDFF0C5A1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C5B1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B2414B648E5001B97AD /* compress */; + targetProxy = FDFF0C5C1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C5D1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B2C14B648E7001B97AD /* cp */; + targetProxy = FDFF0C5E1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C5F1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B3414B648EA001B97AD /* dd */; + targetProxy = FDFF0C601811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C611811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B3C14B648EA001B97AD /* df */; + targetProxy = FDFF0C621811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C631811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B4414B648EB001B97AD /* du */; + targetProxy = FDFF0C641811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C671811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B4C14B648EB001B97AD /* install */; + targetProxy = FDFF0C681811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C691811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B5414B648EB001B97AD /* ipcrm */; + targetProxy = FDFF0C6A1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C6B1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B5C14B648EC001B97AD /* ipcs */; + targetProxy = FDFF0C6C1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C6D1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C7314B6554E001B97AD /* link */; + targetProxy = FDFF0C6E1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C6F1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B6414B648EC001B97AD /* ln */; + targetProxy = FDFF0C701811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C711811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B6C14B648ED001B97AD /* ls */; + targetProxy = FDFF0C721811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C731811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B7414B648ED001B97AD /* mkdir */; + targetProxy = FDFF0C741811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C751811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B7C14B648ED001B97AD /* mkfifo */; + targetProxy = FDFF0C761811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C771811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B8414B648ED001B97AD /* mknod */; + targetProxy = FDFF0C781811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C791811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B8C14B648ED001B97AD /* mtree */; + targetProxy = FDFF0C7A1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C7B1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B9414B648EE001B97AD /* mv */; + targetProxy = FDFF0C7C1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C7D1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8B9C14B648EE001B97AD /* pathchk */; + targetProxy = FDFF0C7E1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C7F1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BA414B648EE001B97AD /* pax */; + targetProxy = FDFF0C801811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C811811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C4614B64DCD001B97AD /* readlink */; + targetProxy = FDFF0C821811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C831811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BAC14B648EF001B97AD /* rm */; + targetProxy = FDFF0C841811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C851811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BB414B648EF001B97AD /* rmdir */; + targetProxy = FDFF0C861811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C871811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C3C14B64A9D001B97AD /* shar */; + targetProxy = FDFF0C881811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C891811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BC414B648EF001B97AD /* stat */; + targetProxy = FDFF0C8A1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C8B1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C6714B6536D001B97AD /* sum */; + targetProxy = FDFF0C8C1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C8D1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8BCC14B648F0001B97AD /* touch */; + targetProxy = FDFF0C8E1811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C8F1811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8CC814B65F92001B97AD /* uncompress */; + targetProxy = FDFF0C901811BA2F00BFC477 /* PBXContainerItemProxy */; + }; + FDFF0C911811BA2F00BFC477 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC8A8C5014B650CF001B97AD /* unlink */; + targetProxy = FDFF0C921811BA2F00BFC477 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + FC8A8B1114B648D7001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B1914B648E0001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /usr/sbin; + }; + name = Release; + }; + FC8A8B2114B648E3001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8B2914B648E5001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + }; + name = Release; + }; + FC8A8B3114B648E7001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B3914B648EA001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_ENTITLEMENTS = dd/dd.entitlements; + "CODE_SIGN_ENTITLEMENTS[sdk=macosx*]" = ""; + CODE_SIGN_IDENTITY = "-"; + INSTALL_PATH = /bin; + "INSTALL_PATH[sdk=appletvos]" = /usr/local/bin; + "INSTALL_PATH[sdk=iphoneos*]" = /usr/local/bin; + }; + name = Release; + }; + FC8A8B4114B648EA001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B4914B648EB001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + }; + name = Release; + }; + FC8A8B5114B648EB001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + }; + name = Release; + }; + FC8A8B5914B648EB001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8B6114B648EC001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + OTHER_CFLAGS = ( + "-iquote", + "$(SDKROOT)/System/Library/Frameworks/Kernel.framework/PrivateHeaders", + "-iquote", + "$(SDKROOT)/System/Library/Frameworks/System.framework/PrivateHeaders", + ); + WARNING_CFLAGS = ""; + }; + name = Release; + }; + FC8A8B6914B648EC001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B7114B648ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + _DARWIN_USE_64_BIT_INODE, + COLORLS, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B7914B648ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8B8114B648ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8B8914B648ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /sbin; + }; + name = Release; + }; + FC8A8B9114B648ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + _DARWIN_USE_64_BIT_INODE, + ENABLE_MD5, + ENABLE_RMD160, + ENABLE_SHA1, + ENABLE_SHA256, + ); + INSTALL_PATH = /usr/sbin; + "OTHER_LDFLAGS[sdk=macosx*]" = "-lCrashReporterClient"; + }; + name = Release; + }; + FC8A8B9914B648EE001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8BA114B648EE001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8BA914B648EE001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8BB114B648EF001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8BB914B648EF001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8BC914B648EF001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = ( + "$(inherited)", + "HAVE_CONFIG_H=0", + ); + }; + name = Release; + }; + FC8A8BD114B648F0001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8C3E14B64A9D001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8C4814B64DCE001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8C5714B650CF001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8C6214B652E1001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8C6E14B6536D001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8C7A14B6554E001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + INSTALL_PATH = /bin; + }; + name = Release; + }; + FC8A8C8214B655ED001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FC8A8CCF14B65F92001B97AD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FCB1BDC114B645D10070FACB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + _DARWIN_USE_64_BIT_INODE, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx.internal; + VERSIONING_SYSTEM = "apple-generic"; + WARNING_CFLAGS = ( + "-Wall", + "-Werror", + "-Wundef", + ); + }; + name = Release; + }; + FCB1BDC414B645D10070FACB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + FDAD949E1808BB6D00B4D5A0 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FDAD94851808BB3A00B4D5A0 /* gzip.xcconfig */; + buildSettings = { + }; + name = Release; + }; + FDFF0C941811BA2F00BFC477 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + FC8A8B1014B648D7001B97AD /* Build configuration list for PBXNativeTarget "chmod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B1114B648D7001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B1814B648E0001B97AD /* Build configuration list for PBXNativeTarget "chown" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B1914B648E0001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B2014B648E3001B97AD /* Build configuration list for PBXNativeTarget "cksum" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B2114B648E3001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B2814B648E5001B97AD /* Build configuration list for PBXNativeTarget "compress" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B2914B648E5001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B3014B648E7001B97AD /* Build configuration list for PBXNativeTarget "cp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B3114B648E7001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B3814B648EA001B97AD /* Build configuration list for PBXNativeTarget "dd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B3914B648EA001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B4014B648EA001B97AD /* Build configuration list for PBXNativeTarget "df" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B4114B648EA001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B4814B648EB001B97AD /* Build configuration list for PBXNativeTarget "du" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B4914B648EB001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B5014B648EB001B97AD /* Build configuration list for PBXNativeTarget "install" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B5114B648EB001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B5814B648EB001B97AD /* Build configuration list for PBXNativeTarget "ipcrm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B5914B648EB001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B6014B648EC001B97AD /* Build configuration list for PBXNativeTarget "ipcs" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B6114B648EC001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B6814B648EC001B97AD /* Build configuration list for PBXNativeTarget "ln" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B6914B648EC001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B7014B648ED001B97AD /* Build configuration list for PBXNativeTarget "ls" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B7114B648ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B7814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkdir" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B7914B648ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B8014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mkfifo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B8114B648ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B8814B648ED001B97AD /* Build configuration list for PBXNativeTarget "mknod" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B8914B648ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B9014B648ED001B97AD /* Build configuration list for PBXNativeTarget "mtree" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B9114B648ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8B9814B648EE001B97AD /* Build configuration list for PBXNativeTarget "mv" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8B9914B648EE001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BA014B648EE001B97AD /* Build configuration list for PBXNativeTarget "pathchk" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BA114B648EE001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BA814B648EE001B97AD /* Build configuration list for PBXNativeTarget "pax" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BA914B648EE001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BB014B648EF001B97AD /* Build configuration list for PBXNativeTarget "rm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BB114B648EF001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BB814B648EF001B97AD /* Build configuration list for PBXNativeTarget "rmdir" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BB914B648EF001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BC814B648EF001B97AD /* Build configuration list for PBXNativeTarget "stat" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BC914B648EF001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8BD014B648F0001B97AD /* Build configuration list for PBXNativeTarget "touch" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8BD114B648F0001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C3D14B64A9D001B97AD /* Build configuration list for PBXAggregateTarget "shar" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C3E14B64A9D001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C4714B64DCE001B97AD /* Build configuration list for PBXAggregateTarget "readlink" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C4814B64DCE001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C5614B650CF001B97AD /* Build configuration list for PBXAggregateTarget "unlink" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C5714B650CF001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C6114B652E1001B97AD /* Build configuration list for PBXAggregateTarget "chgrp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C6214B652E1001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C6D14B6536D001B97AD /* Build configuration list for PBXAggregateTarget "sum" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C6E14B6536D001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C7914B6554E001B97AD /* Build configuration list for PBXAggregateTarget "link" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C7A14B6554E001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8C8114B655ED001B97AD /* Build configuration list for PBXAggregateTarget "executables" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8C8214B655ED001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC8A8CCE14B65F92001B97AD /* Build configuration list for PBXAggregateTarget "uncompress" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC8A8CCF14B65F92001B97AD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCB1BDB214B645D00070FACB /* Build configuration list for PBXProject "file_cmds" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCB1BDC114B645D10070FACB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCB1BDC214B645D10070FACB /* Build configuration list for PBXNativeTarget "chflags" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCB1BDC414B645D10070FACB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FDAD949D1808BB6D00B4D5A0 /* Build configuration list for PBXNativeTarget "gzip" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FDAD949E1808BB6D00B4D5A0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FDFF0C931811BA2F00BFC477 /* Build configuration list for PBXAggregateTarget "eOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FDFF0C941811BA2F00BFC477 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FCB1BDAF14B645D00070FACB /* Project object */; +} diff --git a/file_cmds/gzip/futimens.c b/file_cmds/gzip/futimens.c new file mode 100644 index 00000000..08345c33 --- /dev/null +++ b/file_cmds/gzip/futimens.c @@ -0,0 +1,100 @@ +/*- + * Copyright (c) 2015 Jilles Tjoelker + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include + +#include +#include +#include +#include + +#ifndef UTIME_NOW +#define UTIME_NOW -1 +#endif + +#ifndef UTIME_OMIT +#define UTIME_OMIT -2 +#endif +int futimens(int fd, const struct timespec times[2]); + +int +futimens(int fd, const struct timespec times[2]) +{ + struct timeval now, tv[2], *tvp; + struct stat sb; + + if (times == NULL || (times[0].tv_nsec == UTIME_NOW && + times[1].tv_nsec == UTIME_NOW)) + tvp = NULL; + else if (times[0].tv_nsec == UTIME_OMIT && + times[1].tv_nsec == UTIME_OMIT) + return (0); + else { + if ((times[0].tv_nsec < 0 || times[0].tv_nsec > 999999999) && + times[0].tv_nsec != UTIME_NOW && + times[0].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + if ((times[1].tv_nsec < 0 || times[1].tv_nsec > 999999999) && + times[1].tv_nsec != UTIME_NOW && + times[1].tv_nsec != UTIME_OMIT) { + errno = EINVAL; + return (-1); + } + tv[0].tv_sec = times[0].tv_sec; + tv[0].tv_usec = times[0].tv_nsec / 1000; + tv[1].tv_sec = times[1].tv_sec; + tv[1].tv_usec = times[1].tv_nsec / 1000; + tvp = tv; + if (times[0].tv_nsec == UTIME_OMIT || + times[1].tv_nsec == UTIME_OMIT) { + if (fstat(fd, &sb) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_OMIT) { + tv[0].tv_sec = sb.st_atimespec.tv_sec; + tv[0].tv_usec = sb.st_atimespec.tv_nsec / 1000; + } + if (times[1].tv_nsec == UTIME_OMIT) { + tv[1].tv_sec = sb.st_mtimespec.tv_sec; + tv[1].tv_usec = sb.st_mtimespec.tv_nsec / 1000; + } + } + if (times[0].tv_nsec == UTIME_NOW || + times[1].tv_nsec == UTIME_NOW) { + if (gettimeofday(&now, NULL) == -1) + return (-1); + if (times[0].tv_nsec == UTIME_NOW) + tv[0] = now; + if (times[1].tv_nsec == UTIME_NOW) + tv[1] = now; + } + } + return (futimes(fd, tvp)); +} diff --git a/file_cmds/gzip/gzexe b/file_cmds/gzip/gzexe new file mode 100644 index 00000000..5809133d --- /dev/null +++ b/file_cmds/gzip/gzexe @@ -0,0 +1,179 @@ +#!/bin/sh - +# +# $NetBSD: gzexe,v 1.3 2004/05/01 08:22:41 wiz Exp $ +# $OpenBSD: gzexe,v 1.3 2003/08/05 18:22:17 deraadt Exp $ +# +#- +# Copyright (c) 2003 Otto Moerbeek +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# $FreeBSD: src/usr.bin/gzip/gzexe,v 1.1 2007/01/26 10:19:07 delphij Exp $ + +# The number of lines plus one in the on-the-fly decompression script +lines=19 + +# A simple string to recognize already compressed files +magic="# compressed by gzexe" + +# Write the decompression script to stdout +header () { + # first section needs variable expansion, second not + cat <<- EOF + #!/bin/sh - + $magic + lines=$lines + EOF + cat <<- 'EOF' + prog=`/usr/bin/basename "$0"` + tmp=`/usr/bin/mktemp -d /tmp/gzexeXXXXXXXXXX` || { + /bin/echo "$prog: cannot create tmp dir"; exit 1 + } + trap '/bin/rm -rf "$tmp"' 0 + if /usr/bin/tail +$lines "$0" | + /usr/bin/gzip -dc > "$tmp/$prog" 2> /dev/null; then + /bin/chmod u+x "$tmp/$prog" + "$tmp/$prog" ${1+"$@"} + ret=$? + else + /bin/echo "$prog: cannot decompress $0" + ret=1 + fi + exit $ret + EOF +} + +# Test if a file is compressed by checking the magic line +compressed () { + test "X`sed -n 2p "$1" 2> /dev/null`" = "X$magic" +} + +# Decompress a file +decompress () { + tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || { + echo "$prog: cannot create tmp file" + return 1 + } + if ! cp "$1" "$tmp"; then + echo "$prog: cannot copy $1 to $tmp" + rm -f "$tmp" + return 1 + fi + if ! tail +$lines "$tmp" | gzip -vdc > "$1"; then + echo "$prog: cannot decompress $1" + cp "$tmp" "$1" + rm -f "$tmp" + return 1 + fi +} + +# Perform some sanity checks on the file +check () { + if test ! -e "$1"; then + echo "$prog: cannot compress non-existing file $1" + return 1 + fi + + if test ! -f "$1"; then + echo "$prog: cannot compress non-regular file $1" + return 1 + fi + + case `basename "$1"` in + sh | mktemp | rm | echo | tail | gzip | chmod) + echo "$prog: cannot compress $1, I depend on it" + return 1 + esac + + if test ! -x "$1"; then + echo "$prog: cannot compress $1, it is not executable" + return 1 + fi + + if test -u "$1" -o -g "$1"; then + echo "$prog: cannot compress $1, it has an s bit set" + return 1 + fi +} + +# Compress a file +compress () { + tmp=`mktemp /tmp/gzexeXXXXXXXXXX` || { + echo "$prog: cannot create tmp file" + return 1 + } + if ! cp "$1" "$tmp"; then + echo "$prog: cannot copy $1 to $tmp" + rm -f "$tmp" + return 1 + fi + if ! cp "$1" "$1"~; then + echo "$prog: cannot create backup copy $1~" + rm -f "$1"~ "$tmp" + return 1 + fi + + # Use cp to overwrite the existing file preserving mode and owner + # if possible. If the file is not writable, this will produce an + # error. + + if header "$1" > "$tmp" && gzip -vc "$1" >> "$tmp"; then + if ! cp "$tmp" "$1"; then + echo "$prog: cannot copy $tmp to $1" + rm -f "$tmp" + return 1 + fi + else + echo "$prog: cannot compress $1" + rm -f "$1"~ "$tmp" + return 1 + fi +} + +# Is the -d flag specified? +dflag= + +# Return value +rc=0 + +if test "X$1" = X-d; then + dflag=1 + shift +fi + +prog=`basename "$0"` +USAGE="usage: $prog [-d] file ..." +if test $# -eq 0; then + echo $USAGE + exit 1 +fi + +while test $# -ne 0; do + if test $dflag; then + if ! compressed "$1"; then + echo "$prog: $1 is not compressed" + rc=1; + elif ! decompress "$1"; then + rc=$? + fi + else + if compressed "$1"; then + echo "$prog: $1 is already compressed" + rc=1; + elif ! check "$1" || ! compress "$1"; then + rc=$? + fi + fi + shift +done +exit $rc diff --git a/file_cmds/gzip/gzexe.1 b/file_cmds/gzip/gzexe.1 new file mode 100644 index 00000000..0c514c4f --- /dev/null +++ b/file_cmds/gzip/gzexe.1 @@ -0,0 +1,73 @@ +.\" $NetBSD: gzexe.1,v 1.3 2003/12/28 12:49:41 wiz Exp $ +.\" $OpenBSD: gzexe.1,v 1.1 2003/07/31 07:32:47 otto Exp $ +.\" +.\" Copyright (c) 2003 Otto Moerbeek +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD: src/usr.bin/gzip/gzexe.1,v 1.1 2007/01/26 10:19:07 delphij Exp $ +.Dd January 26, 2007 +.Dt GZEXE 1 +.Os +.Sh NAME +.Nm gzexe +.Nd create auto-decompressing executables +.Sh SYNOPSIS +.Nm gzexe +.Op Fl d +.Ar +.Sh DESCRIPTION +The +.Nm +utility uses +.Xr gzip 1 +to compress executables, producing executables that decompress on-the-fly +when executed. +This saves disk space, at the cost of slower execution times. +The original executables are saved by copying each of them to a file with +the same name with a +.Sq ~ +suffix appended. +After verifying that the compressed executables work as expected, the backup +files can be removed. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d +Decompress executables previously compressed by +.Nm . +.El +.Pp +The +.Nm +program refuses to compress non-regular or non-executable files, +files with a setuid or setgid bit set, files that are already +compressed using +.Nm +or programs it needs to perform on-the-fly decompression: +.Xr sh 1 , +.Xr mktemp 1 , +.Xr rm 1 , +.Xr echo 1 , +.Xr tail 1 , +.Xr gzip 1 , +and +.Xr chmod 1 . +.Sh SEE ALSO +.Xr gzip 1 +.Sh CAVEATS +The +.Nm +utility replaces files by overwriting them with the generated +compressed executable. +To be able to do this, it is required that the original files are writable. diff --git a/file_cmds/gzip/gzip.1 b/file_cmds/gzip/gzip.1 new file mode 100644 index 00000000..98e0ea25 --- /dev/null +++ b/file_cmds/gzip/gzip.1 @@ -0,0 +1,234 @@ +.\" $NetBSD: gzip.1,v 1.26 2015/10/27 07:36:18 mrg Exp $ +.\" +.\" Copyright (c) 1997, 2003, 2004 Matthew R. Green +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: head/usr.bin/gzip/gzip.1 290073 2015-10-27 21:26:05Z delphij $ +.Dd October 26, 2015 +.Dt GZIP 1 +.Os +.Sh NAME +.Nm gzip +.Nd compression/decompression tool using Lempel-Ziv coding (LZ77) +.Sh SYNOPSIS +.Nm +.Op Fl cdfhkLlNnqrtVv +.Op Fl S Ar suffix +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Nm gunzip +.Op Fl cfhkLNqrtVv +.Op Fl S Ar suffix +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Nm zcat +.Op Fl fhV +.Ar file +.Oo +.Ar file Oo ... +.Oc +.Oc +.Sh DESCRIPTION +The +.Nm +program compresses and decompresses files using Lempel-Ziv coding +(LZ77). +If no +.Ar files +are specified, +.Nm +will compress from standard input, or decompress to standard output. +When in compression mode, each +.Ar file +will be replaced with another file with the suffix, set by the +.Fl S Ar suffix +option, added, if possible. +.Pp +In decompression mode, each +.Ar file +will be checked for existence, as will the file with the suffix +added. +Each +.Ar file +argument must contain a separate complete archive; +when multiple +.Ar files +are indicated, each is decompressed in turn. +.Pp +In the case of +.Nm gzcat +the resulting data is then concatenated in the manner of +.Xr cat 1 . +.Pp +If invoked as +.Nm gunzip +then the +.Fl d +option is enabled. +If invoked as +.Nm zcat +or +.Nm gzcat +then both the +.Fl c +and +.Fl d +options are enabled. +.Pp +This version of +.Nm +is also capable of decompressing files compressed using +.Xr compress 1 , +.Xr bzip2 1 , +or +.Xr xz 1 . +.Sh OPTIONS +The following options are available: +.Bl -tag -width XXrXXXrecursiveX +.It Fl 1 , -fast +.It Fl 2 , 3 , 4 , 5 , 6 , 7 , 8 +.It Fl 9 , -best +These options change the compression level used, with the +.Fl 1 +option being the fastest, with less compression, and the +.Fl 9 +option being the slowest, with optimal compression. +The default compression level is 6. +.It Fl c , -stdout , -to-stdout +This option specifies that output will go to the standard output +stream, leaving files intact. +.It Fl d , -decompress , -uncompress +This option selects decompression rather than compression. +.It Fl f , -force +This option turns on force mode. +This allows files with multiple links, symbolic links to regular files, +overwriting of pre-existing files, reading from or writing to a terminal, +and when combined with the +.Fl c +option, allowing non-compressed data to pass through unchanged. +.It Fl h , -help +This option prints a usage summary and exits. +.It Fl k , -keep +Keep (do not delete) input files during compression +or decompression. +.It Fl L , -license +This option prints +.Nm +license. +.It Fl l , -list +This option displays information about the file's compressed and +uncompressed size, ratio, uncompressed name. +With the +.Fl v +option, it also displays the compression method, CRC, date and time +embedded in the file. +.It Fl N , -name +This option causes the stored filename in the input file to be used +as the output file. +.It Fl n , -no-name +This option stops the filename and timestamp from being stored in +the output file. +.It Fl q , -quiet +With this option, no warnings or errors are printed. +.It Fl r , -recursive +This option is used to +.Nm +the files in a directory tree individually, using the +.Xr fts 3 +library. +.It Fl S Ar suffix , Fl -suffix Ar suffix +This option changes the default suffix from .gz to +.Ar suffix . +.It Fl t , -test +This option will test compressed files for integrity. +.It Fl V , -version +This option prints the version of the +.Nm +program. +.It Fl v , -verbose +This option turns on verbose mode, which prints the compression +ratio for each file compressed. +.El +.Sh ENVIRONMENT +If the environment variable +.Ev GZIP +is set, it is parsed as a white-space separated list of options +handled before any options on the command line. +Options on the command line will override anything in +.Ev GZIP . +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, +1 on errors, +and 2 if a warning occurs. +.Sh SEE ALSO +.Xr bzip2 1 , +.Xr compress 1 , +.Xr xz 1 , +.Xr fts 3 , +.Xr zlib 3 +.Sh HISTORY +The +.Nm +program was originally written by Jean-loup Gailly, licensed under +the GNU Public Licence. +Matthew R. Green wrote a simple front end for +.Nx 1.3 +distribution media, based on the freely re-distributable zlib library. +It was enhanced to be mostly feature-compatible with the original +GNU +.Nm +program for +.Nx 2.0 . +.Pp +This implementation of +.Nm +was ported based on the +.Nx +.Nm , +and first appeared in +.Fx 7.0 . +.Sh AUTHORS +.An -nosplit +This implementation of +.Nm +was written by +.An Matthew R. Green Aq Mt mrg@eterna.com.au +with unpack support written by +.An Xin LI Aq Mt delphij@FreeBSD.org . +.Sh BUGS +According to RFC 1952, the recorded file size is stored in a 32-bit +integer, therefore, it cannot represent files larger than 4GB. +This limitation also applies to +.Fl l +option of +.Nm +utility. diff --git a/file_cmds/gzip/gzip.c b/file_cmds/gzip/gzip.c new file mode 100644 index 00000000..9c9d7905 --- /dev/null +++ b/file_cmds/gzip/gzip.c @@ -0,0 +1,2279 @@ +/* $NetBSD: gzip.c,v 1.109 2015/10/27 07:36:18 mrg Exp $ */ + +/*- + * Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1997, 1998, 2003, 2004, 2006\ + Matthew R. Green. All rights reserved."); +__FBSDID("$FreeBSD: head/usr.bin/gzip/gzip.c 290073 2015-10-27 21:26:05Z delphij $"); +#endif /* not lint */ + +/* + * gzip.c -- GPL free gzip using zlib. + * + * RFC 1950 covers the zlib format + * RFC 1951 covers the deflate format + * RFC 1952 covers the gzip format + * + * TODO: + * - use mmap where possible + * - make bzip2/compress -v/-t/-l support work as well as possible + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +// #include +#define COMPAT_MODE(func, mode) 1 +int futimens(int fd, const struct timespec times[2]); +#endif /* __APPLE__ */ +#include // for booleans +#include "ios_error.h" +// iOS: Until someone install the header file (the library exists) +#define NO_XZ_SUPPORT + +/* what type of file are we dealing with */ +enum filetype { + FT_GZIP, +#ifndef NO_BZIP2_SUPPORT + FT_BZIP2, +#endif +#ifndef NO_COMPRESS_SUPPORT + FT_Z, +#endif +#ifndef NO_PACK_SUPPORT + FT_PACK, +#endif +#ifndef NO_XZ_SUPPORT + FT_XZ, +#endif + FT_LAST, + FT_UNKNOWN +}; + +#ifndef NO_BZIP2_SUPPORT +#include + +#define BZ2_SUFFIX ".bz2" +#define BZIP2_MAGIC "\102\132\150" +#endif + +#ifndef NO_COMPRESS_SUPPORT +#define Z_SUFFIX ".Z" +#define Z_MAGIC "\037\235" +#endif + +#ifndef NO_PACK_SUPPORT +#define PACK_MAGIC "\037\036" +#endif + +#ifndef NO_XZ_SUPPORT +#include +#define XZ_SUFFIX ".xz" +#define XZ_MAGIC "\3757zXZ" +#endif + +#define GZ_SUFFIX ".gz" + +#define BUFLEN (64 * 1024) + +#define GZIP_MAGIC0 0x1F +#define GZIP_MAGIC1 0x8B +#define GZIP_OMAGIC1 0x9E + +#define GZIP_TIMESTAMP (off_t)4 +#define GZIP_ORIGNAME (off_t)10 + +#define HEAD_CRC 0x02 +#define EXTRA_FIELD 0x04 +#define ORIG_NAME 0x08 +#define COMMENT 0x10 + +#define OS_CODE 3 /* Unix */ + +typedef struct { + const char *zipped; + int ziplen; + const char *normal; /* for unzip - must not be longer than zipped */ +} suffixes_t; +static suffixes_t suffixes[] = { +#define SUFFIX(Z, N) {Z, sizeof Z - 1, N} + SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S .xxx */ +#ifndef SMALL + SUFFIX(GZ_SUFFIX, ""), + SUFFIX(".z", ""), + SUFFIX("-gz", ""), + SUFFIX("-z", ""), + SUFFIX("_z", ""), + SUFFIX(".taz", ".tar"), + SUFFIX(".tgz", ".tar"), +#ifndef NO_BZIP2_SUPPORT + SUFFIX(BZ2_SUFFIX, ""), + SUFFIX(".tbz", ".tar"), + SUFFIX(".tbz2", ".tar"), +#endif +#ifndef NO_COMPRESS_SUPPORT + SUFFIX(Z_SUFFIX, ""), +#endif +#ifndef NO_XZ_SUPPORT + SUFFIX(XZ_SUFFIX, ""), +#endif + SUFFIX(GZ_SUFFIX, ""), /* Overwritten by -S "" */ +#endif /* SMALL */ +#undef SUFFIX +}; +#define NUM_SUFFIXES (sizeof suffixes / sizeof suffixes[0]) +#define SUFFIX_MAXLEN 30 + +#ifdef __APPLE__ +static const char gzip_version[] = "Apple gzip "; // GZIP_APPLE_VERSION; +#else +static const char gzip_version[] = "FreeBSD gzip 20150413"; +#endif + +#ifndef SMALL +static const char gzip_copyright[] = \ +" Copyright (c) 1997, 1998, 2003, 2004, 2006 Matthew R. Green\n" +" All rights reserved.\n" +"\n" +" Redistribution and use in source and binary forms, with or without\n" +" modification, are permitted provided that the following conditions\n" +" are met:\n" +" 1. Redistributions of source code must retain the above copyright\n" +" notice, this list of conditions and the following disclaimer.\n" +" 2. Redistributions in binary form must reproduce the above copyright\n" +" notice, this list of conditions and the following disclaimer in the\n" +" documentation and/or other materials provided with the distribution.\n" +"\n" +" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" +" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" +" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" +" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" +" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\n" +" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n" +" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED\n" +" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\n" +" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n" +" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n" +" SUCH DAMAGE."; +#endif + +static int cflag; /* stdout mode */ +static int dflag; /* decompress mode */ +static int lflag; /* list mode */ +static int numflag = 6; /* gzip -1..-9 value */ + +#ifndef SMALL +static int fflag; /* force mode */ +static int kflag; /* don't delete input files */ +static int nflag; /* don't save name/timestamp */ +static int Nflag; /* don't restore name/timestamp */ +static int qflag; /* quiet mode */ +static int rflag; /* recursive mode */ +static int tflag; /* test */ +static int vflag; /* verbose mode */ +static const char *remove_file = NULL; /* file to be removed upon SIGINT */ +#else +#define qflag 0 +#define tflag 0 +#endif + +static int exit_value = 0; /* exit value */ + +static char *infile; /* name of file coming in */ + +#ifdef __APPLE__ +static bool zcat; +#endif + +static void maybe_err(const char *fmt, ...) __printflike(1, 2) ; // __dead2; +#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ + !defined(NO_XZ_SUPPORT) +static void maybe_errx(const char *fmt, ...) __printflike(1, 2) ; // __dead2; +#endif +static void maybe_warn(const char *fmt, ...) __printflike(1, 2); +static void maybe_warnx(const char *fmt, ...) __printflike(1, 2); +static enum filetype file_gettype(u_char *); +#ifdef SMALL +#define gz_compress(if, of, sz, fn, tm) gz_compress(if, of, sz) +#endif +static off_t gz_compress(int, int, off_t *, const char *, uint32_t); +static off_t gz_uncompress(int, int, char *, size_t, off_t *, const char *); +static off_t file_compress(char *, char *, size_t); +static off_t file_uncompress(char *, char *, size_t); +static void handle_pathname(char *); +static void handle_file(char *, struct stat *); +static void handle_stdin(void); +static void handle_stdout(void); +static void print_ratio(off_t, off_t, FILE *); +static void print_list(int fd, off_t, const char *, time_t); +static void usage(void) ; // __dead2; +static void display_version(void) ; // __dead2; +#ifndef SMALL +static void display_license(void); +static void sigint_handler(int); +#endif +static const suffixes_t *check_suffix(char *, int); +static ssize_t read_retry(int, void *, size_t); + +#ifdef SMALL +#define unlink_input(f, sb) unlink(f) +#else +static off_t cat_fd(unsigned char *, size_t, off_t *, int fd); +static void prepend_gzip(char *, int *, char ***); +static void handle_dir(char *); +static void print_verbage(const char *, const char *, off_t, off_t); +static void print_test(const char *, int); +static void copymodes(int fd, const struct stat *, const char *file); +static int check_outfile(const char *outfile); +#endif + +#ifndef NO_BZIP2_SUPPORT +static off_t unbzip2(int, int, char *, size_t, off_t *); +#endif + +#ifndef NO_COMPRESS_SUPPORT +static FILE *zdopen(int); +static off_t zuncompress(FILE *, FILE *, char *, size_t, off_t *); +#endif + +#ifndef NO_PACK_SUPPORT +static off_t unpack(int, int, char *, size_t, off_t *); +#endif + +#ifndef NO_XZ_SUPPORT +static off_t unxz(int, int, char *, size_t, off_t *); +#endif + +#ifdef SMALL +#define getopt_long(a,b,c,d,e) getopt(a,b,c) +#else +static const struct option longopts[] = { + { "stdout", no_argument, 0, 'c' }, + { "to-stdout", no_argument, 0, 'c' }, + { "decompress", no_argument, 0, 'd' }, + { "uncompress", no_argument, 0, 'd' }, + { "force", no_argument, 0, 'f' }, + { "help", no_argument, 0, 'h' }, + { "keep", no_argument, 0, 'k' }, + { "list", no_argument, 0, 'l' }, + { "no-name", no_argument, 0, 'n' }, + { "name", no_argument, 0, 'N' }, + { "quiet", no_argument, 0, 'q' }, + { "recursive", no_argument, 0, 'r' }, + { "suffix", required_argument, 0, 'S' }, + { "test", no_argument, 0, 't' }, + { "verbose", no_argument, 0, 'v' }, + { "version", no_argument, 0, 'V' }, + { "fast", no_argument, 0, '1' }, + { "best", no_argument, 0, '9' }, + { "ascii", no_argument, 0, 'a' }, + { "license", no_argument, 0, 'L' }, + { NULL, no_argument, 0, 0 }, +}; +#endif + +int +gzip_main(int argc, char **argv) +{ + const char *progname = argv[0]; // getprogname(); // getprogname returns Host application +#ifndef SMALL + char *gzip; + int len; +#endif + int ch; + // Initialize all flags: + optind = 1; opterr = 1; optreset = 1; + cflag = dflag = lflag = 0; + numflag = 6; +#ifndef SMALL + fflag = kflag = nflag = Nflag = qflag = rflag = tflag = vflag = 0; +#endif + exit_value = 0; /* exit value */ +#ifdef __APPLE__ + zcat = false; +#endif + +#ifndef SMALL + if ((gzip = getenv("GZIP")) != NULL) + prepend_gzip(gzip, &argc, &argv); + signal(SIGINT, sigint_handler); +#endif + + /* + * XXX + * handle being called `gunzip', `zcat' and `gzcat' + */ + if (strcmp(progname, "gunzip") == 0) + dflag = 1; + else if (strcmp(progname, "zcat") == 0 || + strcmp(progname, "gzcat") == 0) + dflag = cflag = 1; + +#ifdef __APPLE__ + if (strcmp(progname, "zcat") == 0) { + zcat = true; + } +#endif + +#ifdef SMALL +#define OPT_LIST "123456789cdhlV" +#else +#define OPT_LIST "123456789acdfhklLNnqrS:tVv" +#endif + + while ((ch = getopt_long(argc, argv, OPT_LIST, longopts, NULL)) != -1) { + switch (ch) { + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + numflag = ch - '0'; + break; + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'l': + lflag = 1; + dflag = 1; + break; + case 'V': + display_version(); + /* NOTREACHED */ +#ifndef SMALL + case 'a': + fprintf(thread_stderr, "%s: option --ascii ignored on this system\n", progname); + break; + case 'f': + fflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'L': + display_license(); + /* NOT REACHED */ + case 'N': + nflag = 0; + Nflag = 1; + break; + case 'n': + nflag = 1; + Nflag = 0; + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'S': + len = strlen(optarg); + if (len != 0) { + if (len > SUFFIX_MAXLEN) { + errx(1, "incorrect suffix: '%s': too long", optarg); + } + suffixes[0].zipped = optarg; + suffixes[0].ziplen = len; + } else { + suffixes[NUM_SUFFIXES - 1].zipped = ""; + suffixes[NUM_SUFFIXES - 1].ziplen = 0; + } + break; + case 't': + cflag = 1; + tflag = 1; + dflag = 1; + break; + case 'v': + vflag = 1; + break; +#endif + default: + usage(); + /* NOTREACHED */ + } + } + argv += optind; + argc -= optind; + + if (argc == 0) { + if (dflag) /* stdin mode */ + handle_stdin(); + else /* stdout mode */ + handle_stdout(); + } else { + do { + handle_pathname(argv[0]); + } while (*++argv); + } +#ifndef SMALL + if (qflag == 0 && lflag && argc > 1) + print_list(-1, 0, "(totals)", 0); +#endif + exit(exit_value); +} + +/* maybe print a warning */ +void +maybe_warn(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + } + if (exit_value == 0) + exit_value = 1; +} + +/* ... without an errno. */ +void +maybe_warnx(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + } + if (exit_value == 0) + exit_value = 1; +} + +/* maybe print an error */ +void +maybe_err(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarn(fmt, ap); + va_end(ap); + } + exit(2); +} + +#if !defined(NO_BZIP2_SUPPORT) || !defined(NO_PACK_SUPPORT) || \ + !defined(NO_XZ_SUPPORT) +/* ... without an errno. */ +void +maybe_errx(const char *fmt, ...) +{ + va_list ap; + + if (qflag == 0) { + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + } + exit(2); +} +#endif + +#ifndef SMALL +/* split up $GZIP and prepend it to the argument list */ +static void +prepend_gzip(char *gzip, int *argc, char ***argv) +{ + char *s, **nargv, **ac; + int nenvarg = 0, i; + + /* scan how many arguments there are */ + for (s = gzip;;) { + while (*s == ' ' || *s == '\t') + s++; + if (*s == 0) + goto count_done; + nenvarg++; + while (*s != ' ' && *s != '\t') + if (*s++ == 0) + goto count_done; + } +count_done: + /* punt early */ + if (nenvarg == 0) + return; + + *argc += nenvarg; + ac = *argv; + + nargv = (char **)malloc((*argc + 1) * sizeof(char *)); + if (nargv == NULL) + maybe_err("malloc"); + + /* stash this away */ + *argv = nargv; + + /* copy the program name first */ + i = 0; + nargv[i++] = *(ac++); + + s = gzip; + for (;;) { + /* Skip whitespaces. */ + while (*s == ' ' || *s == '\t') + s++; + if (*s == 0) { + goto copy_done; + } + nargv[i++] = s; + /* Find the end of this argument. */ + while (*s != ' ' && *s != '\t') + if (*s++ == 0) + /* Argument followed by NUL. */ + goto copy_done; + /* copy any unterminated args */ + nargv[i-1] = strndup(nargv[i-1], s-nargv[i-1]); + if (nargv[i-1] == NULL) + maybe_err("strndup"); + s++; + } +copy_done: + + /* copy the original arguments and a NULL */ + while (*ac) + nargv[i++] = *(ac++); + nargv[i] = NULL; +} +#endif + +/* compress input to output. Return bytes read, -1 on error */ +static off_t +gz_compress(int in, int out, off_t *gsizep, const char *origname, uint32_t mtime) +{ + z_stream z; + char *outbufp, *inbufp; + off_t in_tot = 0, out_tot = 0; + ssize_t in_size; + int i, error; + uLong crc; +#ifdef SMALL + static char header[] = { GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, 0, + 0, 0, 0, 0, + 0, OS_CODE }; +#endif + + outbufp = malloc(BUFLEN); + inbufp = malloc(BUFLEN); + if (outbufp == NULL || inbufp == NULL) { + maybe_err("malloc failed"); + goto out; + } + + memset(&z, 0, sizeof z); + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = 0; + +#ifdef SMALL + memcpy(outbufp, header, sizeof header); + i = sizeof header; +#else + if (nflag != 0) { + mtime = 0; + origname = ""; + } + + i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c%c%c%s", + GZIP_MAGIC0, GZIP_MAGIC1, Z_DEFLATED, + *origname ? ORIG_NAME : 0, + mtime & 0xff, + (mtime >> 8) & 0xff, + (mtime >> 16) & 0xff, + (mtime >> 24) & 0xff, + numflag == 1 ? 4 : numflag == 9 ? 2 : 0, + OS_CODE, origname); + if (i >= BUFLEN) + /* this need PATH_MAX > BUFLEN ... */ + maybe_err("snprintf"); + if (*origname) + i++; +#endif + + z.next_out = (unsigned char *)outbufp + i; + z.avail_out = BUFLEN - i; + + error = deflateInit2(&z, numflag, Z_DEFLATED, + (-MAX_WBITS), 8, Z_DEFAULT_STRATEGY); + if (error != Z_OK) { + maybe_warnx("deflateInit2 failed"); + in_tot = -1; + goto out; + } + + crc = crc32(0L, Z_NULL, 0); + for (;;) { + if (z.avail_out == 0) { + if (write(out, outbufp, BUFLEN) != BUFLEN) { + maybe_warn("write"); + out_tot = -1; + goto out; + } + + out_tot += BUFLEN; + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + } + + if (z.avail_in == 0) { + in_size = read(in, inbufp, BUFLEN); + if (in_size < 0) { + maybe_warn("read"); + in_tot = -1; + goto out; + } + if (in_size == 0) + break; + + crc = crc32(crc, (const Bytef *)inbufp, (unsigned)in_size); + in_tot += in_size; + z.next_in = (unsigned char *)inbufp; + z.avail_in = in_size; + } + + error = deflate(&z, Z_NO_FLUSH); + if (error != Z_OK && error != Z_STREAM_END) { + maybe_warnx("deflate failed"); + in_tot = -1; + goto out; + } + } + + /* clean up */ + for (;;) { + size_t len; + ssize_t w; + + error = deflate(&z, Z_FINISH); + if (error != Z_OK && error != Z_STREAM_END) { + maybe_warnx("deflate failed"); + in_tot = -1; + goto out; + } + + len = (char *)z.next_out - outbufp; + + w = write(out, outbufp, len); + if (w == -1 || (size_t)w != len) { + maybe_warn("write"); + out_tot = -1; + goto out; + } + out_tot += len; + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + + if (error == Z_STREAM_END) + break; + } + + if (deflateEnd(&z) != Z_OK) { + maybe_warnx("deflateEnd failed"); + in_tot = -1; + goto out; + } + + i = snprintf(outbufp, BUFLEN, "%c%c%c%c%c%c%c%c", + (int)crc & 0xff, + (int)(crc >> 8) & 0xff, + (int)(crc >> 16) & 0xff, + (int)(crc >> 24) & 0xff, + (int)in_tot & 0xff, + (int)(in_tot >> 8) & 0xff, + (int)(in_tot >> 16) & 0xff, + (int)(in_tot >> 24) & 0xff); + if (i != 8) + maybe_err("snprintf"); + if (write(out, outbufp, i) != i) { + maybe_warn("write"); + in_tot = -1; + } else + out_tot += i; + +out: + if (inbufp != NULL) + free(inbufp); + if (outbufp != NULL) + free(outbufp); + if (gsizep) + *gsizep = out_tot; + return in_tot; +} + +/* + * uncompress input to output then close the input. return the + * uncompressed size written, and put the compressed sized read + * into `*gsizep'. + */ +static off_t +gz_uncompress(int in, int out, char *pre, size_t prelen, off_t *gsizep, + const char *filename) +{ + z_stream z; + char *outbufp, *inbufp; + off_t out_tot = -1, in_tot = 0; + uint32_t out_sub_tot = 0; + enum { + GZSTATE_MAGIC0, + GZSTATE_MAGIC1, + GZSTATE_METHOD, + GZSTATE_FLAGS, + GZSTATE_SKIPPING, + GZSTATE_EXTRA, + GZSTATE_EXTRA2, + GZSTATE_EXTRA3, + GZSTATE_ORIGNAME, + GZSTATE_COMMENT, + GZSTATE_HEAD_CRC1, + GZSTATE_HEAD_CRC2, + GZSTATE_INIT, + GZSTATE_READ, + GZSTATE_CRC, + GZSTATE_LEN, + } state = GZSTATE_MAGIC0; + int flags = 0, skip_count = 0; + int error = Z_STREAM_ERROR, done_reading = 0; + uLong crc = 0; + ssize_t wr; + int needmore = 0; + +#define ADVANCE() { z.next_in++; z.avail_in--; } + + if ((outbufp = malloc(BUFLEN)) == NULL) { + maybe_err("malloc failed"); + goto out2; + } + if ((inbufp = malloc(BUFLEN)) == NULL) { + maybe_err("malloc failed"); + goto out1; + } + + memset(&z, 0, sizeof z); + z.avail_in = prelen; + z.next_in = (unsigned char *)pre; + z.avail_out = BUFLEN; + z.next_out = (unsigned char *)outbufp; + z.zalloc = NULL; + z.zfree = NULL; + z.opaque = 0; + + in_tot = prelen; + out_tot = 0; + + for (;;) { + if ((z.avail_in == 0 || needmore) && done_reading == 0) { + ssize_t in_size; + + if (z.avail_in > 0) { + memmove(inbufp, z.next_in, z.avail_in); + } + z.next_in = (unsigned char *)inbufp; + in_size = read(in, z.next_in + z.avail_in, + BUFLEN - z.avail_in); + + if (in_size == -1) { + maybe_warn("failed to read stdin"); + goto stop_and_fail; + } else if (in_size == 0) { + done_reading = 1; + } + + z.avail_in += in_size; + needmore = 0; + + in_tot += in_size; + } + if (z.avail_in == 0) { + if (done_reading && state != GZSTATE_MAGIC0) { + maybe_warnx("%s: unexpected end of file", + filename); + goto stop_and_fail; + } + goto stop; + } + switch (state) { + case GZSTATE_MAGIC0: + if (*z.next_in != GZIP_MAGIC0) { + if (in_tot > 0) { + maybe_warnx("%s: trailing garbage " + "ignored", filename); + exit_value = 2; + goto stop; + } + maybe_warnx("input not gziped (MAGIC0)"); + goto stop_and_fail; + } + ADVANCE(); + state++; + out_sub_tot = 0; + crc = crc32(0L, Z_NULL, 0); + break; + + case GZSTATE_MAGIC1: + if (*z.next_in != GZIP_MAGIC1 && + *z.next_in != GZIP_OMAGIC1) { + maybe_warnx("input not gziped (MAGIC1)"); + goto stop_and_fail; + } + ADVANCE(); + state++; + break; + + case GZSTATE_METHOD: + if (*z.next_in != Z_DEFLATED) { + maybe_warnx("unknown compression method"); + goto stop_and_fail; + } + ADVANCE(); + state++; + break; + + case GZSTATE_FLAGS: + flags = *z.next_in; + ADVANCE(); + skip_count = 6; + state++; + break; + + case GZSTATE_SKIPPING: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_EXTRA: + if ((flags & EXTRA_FIELD) == 0) { + state = GZSTATE_ORIGNAME; + break; + } + skip_count = *z.next_in; + ADVANCE(); + state++; + break; + + case GZSTATE_EXTRA2: + skip_count |= ((*z.next_in) << 8); + ADVANCE(); + state++; + break; + + case GZSTATE_EXTRA3: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_ORIGNAME: + if ((flags & ORIG_NAME) == 0) { + state++; + break; + } + if (*z.next_in == 0) + state++; + ADVANCE(); + break; + + case GZSTATE_COMMENT: + if ((flags & COMMENT) == 0) { + state++; + break; + } + if (*z.next_in == 0) + state++; + ADVANCE(); + break; + + case GZSTATE_HEAD_CRC1: + if (flags & HEAD_CRC) + skip_count = 2; + else + skip_count = 0; + state++; + break; + + case GZSTATE_HEAD_CRC2: + if (skip_count > 0) { + skip_count--; + ADVANCE(); + } else + state++; + break; + + case GZSTATE_INIT: + if (inflateInit2(&z, -MAX_WBITS) != Z_OK) { + maybe_warnx("failed to inflateInit"); + goto stop_and_fail; + } + state++; + break; + + case GZSTATE_READ: + error = inflate(&z, Z_FINISH); + switch (error) { + /* Z_BUF_ERROR goes with Z_FINISH... */ + case Z_BUF_ERROR: + if (z.avail_out > 0 && !done_reading) + continue; + + case Z_STREAM_END: + case Z_OK: + break; + + case Z_NEED_DICT: + maybe_warnx("Z_NEED_DICT error"); + goto stop_and_fail; + case Z_DATA_ERROR: + maybe_warnx("data stream error"); + goto stop_and_fail; + case Z_STREAM_ERROR: + maybe_warnx("internal stream error"); + goto stop_and_fail; + case Z_MEM_ERROR: + maybe_warnx("memory allocation error"); + goto stop_and_fail; + + default: + maybe_warn("unknown error from inflate(): %d", + error); + } + wr = BUFLEN - z.avail_out; + + if (wr != 0) { + crc = crc32(crc, (const Bytef *)outbufp, (unsigned)wr); + if ( +#ifndef SMALL + /* don't write anything with -t */ + tflag == 0 && +#endif + write(out, outbufp, wr) != wr) { + maybe_warn("error writing to output"); + goto stop_and_fail; + } + + out_tot += wr; + out_sub_tot += wr; + } + + if (error == Z_STREAM_END) { + inflateEnd(&z); + state++; + } + + z.next_out = (unsigned char *)outbufp; + z.avail_out = BUFLEN; + + break; + case GZSTATE_CRC: + { + uLong origcrc; + + if (z.avail_in < 4) { + if (!done_reading) { + needmore = 1; + continue; + } + maybe_warnx("truncated input"); + goto stop_and_fail; + } + origcrc = ((unsigned)z.next_in[0] & 0xff) | + ((unsigned)z.next_in[1] & 0xff) << 8 | + ((unsigned)z.next_in[2] & 0xff) << 16 | + ((unsigned)z.next_in[3] & 0xff) << 24; + if (origcrc != crc) { + maybe_warnx("invalid compressed" + " data--crc error"); + goto stop_and_fail; + } + } + + z.avail_in -= 4; + z.next_in += 4; + + if (!z.avail_in && done_reading) { + goto stop; + } + state++; + break; + case GZSTATE_LEN: + { + uLong origlen; + + if (z.avail_in < 4) { + if (!done_reading) { + needmore = 1; + continue; + } + maybe_warnx("truncated input"); + goto stop_and_fail; + } + origlen = ((unsigned)z.next_in[0] & 0xff) | + ((unsigned)z.next_in[1] & 0xff) << 8 | + ((unsigned)z.next_in[2] & 0xff) << 16 | + ((unsigned)z.next_in[3] & 0xff) << 24; + + if (origlen != out_sub_tot) { + maybe_warnx("invalid compressed" + " data--length error"); + goto stop_and_fail; + } + } + + z.avail_in -= 4; + z.next_in += 4; + + if (error < 0) { + maybe_warnx("decompression error"); + goto stop_and_fail; + } + state = GZSTATE_MAGIC0; + break; + } + continue; +stop_and_fail: + out_tot = -1; +stop: + break; + } + if (state > GZSTATE_INIT) + inflateEnd(&z); + + free(inbufp); +out1: + free(outbufp); +out2: + if (gsizep) + *gsizep = in_tot; + return (out_tot); +} + +#ifndef SMALL +/* + * set the owner, mode, flags & utimes using the given file descriptor. + * file is only used in possible warning messages. + */ +static void +copymodes(int fd, const struct stat *sbp, const char *file) +{ + struct timespec times[2]; + struct stat sb; + + /* + * If we have no info on the input, give this file some + * default values and return.. + */ + if (sbp == NULL) { + mode_t mask = umask(022); + + (void)fchmod(fd, DEFFILEMODE & ~mask); + (void)umask(mask); + return; + } + sb = *sbp; + + /* if the chown fails, remove set-id bits as-per compress(1) */ + if (fchown(fd, sb.st_uid, sb.st_gid) < 0) { + if (errno != EPERM) + maybe_warn("couldn't fchown: %s", file); + sb.st_mode &= ~(S_ISUID|S_ISGID); + } + + /* we only allow set-id and the 9 normal permission bits */ + sb.st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + if (fchmod(fd, sb.st_mode) < 0) + maybe_warn("couldn't fchmod: %s", file); + +#ifdef __APPLE__ + times[0] = sb.st_atimespec; + times[1] = sb.st_mtimespec; +#else + times[0] = sb.st_atim; + times[1] = sb.st_mtim; +#endif + if (futimens(fd, times) < 0) + maybe_warn("couldn't futimens: %s", file); + + /* only try flags if they exist already */ + if (sb.st_flags != 0 && fchflags(fd, sb.st_flags) < 0) + maybe_warn("couldn't fchflags: %s", file); +} +#endif + +/* what sort of file is this? */ +static enum filetype +file_gettype(u_char *buf) +{ + + if (buf[0] == GZIP_MAGIC0 && + (buf[1] == GZIP_MAGIC1 || buf[1] == GZIP_OMAGIC1)) + return FT_GZIP; + else +#ifndef NO_BZIP2_SUPPORT + if (memcmp(buf, BZIP2_MAGIC, 3) == 0 && + buf[3] >= '0' && buf[3] <= '9') + return FT_BZIP2; + else +#endif +#ifndef NO_COMPRESS_SUPPORT + if (memcmp(buf, Z_MAGIC, 2) == 0) + return FT_Z; + else +#endif +#ifndef NO_PACK_SUPPORT + if (memcmp(buf, PACK_MAGIC, 2) == 0) + return FT_PACK; + else +#endif +#ifndef NO_XZ_SUPPORT + if (memcmp(buf, XZ_MAGIC, 4) == 0) /* XXX: We only have 4 bytes */ + return FT_XZ; + else +#endif + return FT_UNKNOWN; +} + +#ifndef SMALL +/* check the outfile is OK. */ +static int +check_outfile(const char *outfile) +{ + struct stat sb; + int ok = 1; + + if (lflag == 0 && stat(outfile, &sb) == 0) { + if (fflag) + unlink(outfile); +// else if (isatty(fileno(thread_stdin))) { + else if (ios_isatty(STDIN_FILENO)) { + char ans[10] = { 'n', '\0' }; /* default */ + + fprintf(thread_stderr, "%s already exists -- do you wish to " + "overwrite (y or n)? " , outfile); + fflush(thread_stderr); + (void)fgets(ans, sizeof(ans) - 1, thread_stdin); + if (ans[0] != 'y' && ans[0] != 'Y') { + fprintf(thread_stderr, "\tnot overwriting\n"); + ok = 0; + } else + unlink(outfile); + } else { + maybe_warnx("%s already exists -- skipping", outfile); + ok = 0; + } + } + return ok; +} + +static void +unlink_input(const char *file, const struct stat *sb) +{ + struct stat nsb; + + if (kflag) + return; + bzero(&nsb, sizeof(nsb)); + if (stat(file, &nsb) != 0) + /* Must be gone already */ + return; + if (nsb.st_dev != sb->st_dev || nsb.st_ino != sb->st_ino) + /* Definitely a different file */ + return; + unlink(file); +} + +static void +sigint_handler(int signo __unused) +{ + + if (remove_file != NULL) + unlink(remove_file); + _exit(2); +} +#endif + +static const suffixes_t * +check_suffix(char *file, int xlate) +{ + const suffixes_t *s; + int len = strlen(file); + char *sp; + + for (s = suffixes; s != suffixes + NUM_SUFFIXES; s++) { + /* if it doesn't fit in "a.suf", don't bother */ + if (s->ziplen >= len) + continue; + sp = file + len - s->ziplen; + if (strcmp(s->zipped, sp) != 0) + continue; + if (xlate) + strcpy(sp, s->normal); + return s; + } + return NULL; +} + +#ifdef __APPLE__ +static void +clear_type_and_creator(int fd) +{ + struct attrlist alist; + struct { + u_int32_t length; + char info[32]; + } abuf; + + memset(&alist, 0, sizeof(alist)); + alist.bitmapcount = ATTR_BIT_MAP_COUNT; + alist.commonattr = ATTR_CMN_FNDRINFO; + + if (!fgetattrlist(fd, &alist, &abuf, sizeof(abuf), 0) && abuf.length == sizeof(abuf)) { + memset(abuf.info, 0, 8); + fsetattrlist(fd, &alist, abuf.info, sizeof(abuf.info), 0); + } +} +#endif /* __APPLE__ */ + +/* + * compress the given file: create a corresponding .gz file and remove the + * original. + */ +static off_t +file_compress(char *file, char *outfile, size_t outsize) +{ + int in; + int out; + off_t size, insize; +#ifndef SMALL + struct stat isb, osb; + const suffixes_t *suff; +#endif + + in = open(file, O_RDONLY); + if (in == -1) { + maybe_warn("can't open %s", file); + return (-1); + } + +#ifndef SMALL + bzero(&isb, sizeof(isb)); + if (fstat(in, &isb) != 0) { + maybe_warn("couldn't stat: %s", file); + close(in); + return (-1); + } +#endif + + if (cflag == 0) { +#ifndef SMALL + if (isb.st_nlink > 1 && fflag == 0) { + maybe_warnx("%s has %d other link%s -- skipping", + file, isb.st_nlink - 1, + (isb.st_nlink - 1) == 1 ? "" : "s"); + close(in); + return (-1); + } + + if (fflag == 0 && (suff = check_suffix(file, 0)) && + suff->zipped[0] != 0) { + maybe_warnx("%s already has %s suffix -- unchanged", + file, suff->zipped); + close(in); + return (-1); + } +#endif + + /* Add (usually) .gz to filename */ + if ((size_t)snprintf(outfile, outsize, "%s%s", + file, suffixes[0].zipped) >= outsize) + memcpy(outfile + outsize - suffixes[0].ziplen - 1, + suffixes[0].zipped, suffixes[0].ziplen + 1); + +#ifndef SMALL + if (check_outfile(outfile) == 0) { + close(in); + return (-1); + } +#endif + } + + if (cflag == 0) { + out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (out == -1) { + maybe_warn("could not create output: %s", outfile); + fclose(thread_stdin); + return (-1); + } +#ifndef SMALL + remove_file = outfile; +#endif + } else + out = fileno(thread_stdout); + + insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); + +#ifndef __APPLE__ + (void)close(in); +#endif /* !__APPLE__ */ + + /* + * If there was an error, insize will be -1. + * If we compressed to stdout, just return the size. + * Otherwise stat the file and check it is the correct size. + * We only blow away the file if we can stat the output and it + * has the expected size. + */ + if (cflag != 0) + return (insize == -1 ? -1 : size); + +#ifndef SMALL + if (fstat(out, &osb) != 0) { + maybe_warn("couldn't stat: %s", outfile); + goto bad_outfile; + } + + if (osb.st_size != size) { + maybe_warnx("output file: %s wrong size (%ju != %ju), deleting", + outfile, (uintmax_t)osb.st_size, (uintmax_t)size); + goto bad_outfile; + } + +#ifdef __APPLE__ + fcopyfile(in, out, 0, COPYFILE_ACL | COPYFILE_XATTR); + clear_type_and_creator(out); +#endif /* __APPLE__ */ + copymodes(out, &isb, outfile); + remove_file = NULL; +#endif +#ifdef __APPLE__ + (void)close(in); +#endif /* __APPLE__ */ + if (close(out) == -1) + maybe_warn("couldn't close output"); + + /* output is good, ok to delete input */ + unlink_input(file, &isb); + return (size); + +#ifndef SMALL + bad_outfile: + if (close(out) == -1) + maybe_warn("couldn't close output"); + + maybe_warnx("leaving original %s", file); + unlink(outfile); + return (size); +#endif +} + +/* uncompress the given file and remove the original */ +static off_t +file_uncompress(char *file, char *outfile, size_t outsize) +{ + struct stat isb, osb; + off_t size; + ssize_t rbytes; + unsigned char header1[4]; + enum filetype method; + int fd, ofd, zfd = -1; +#ifndef SMALL + ssize_t rv; + time_t timestamp = 0; + char name[PATH_MAX + 1]; +#endif + + /* gather the old name info */ + + fd = open(file, O_RDONLY); + if (fd < 0) { + maybe_warn("can't open %s", file); + goto lose; + } + + strlcpy(outfile, file, outsize); + if (check_suffix(outfile, 1) == NULL && !(cflag || lflag)) { + maybe_warnx("%s: unknown suffix -- ignored", file); + goto lose; + } + + rbytes = read(fd, header1, sizeof header1); + if (rbytes != sizeof header1) { + /* we don't want to fail here. */ +#ifndef SMALL + if (fflag) + goto lose; +#endif + if (rbytes == -1) + maybe_warn("can't read %s", file); + else + goto unexpected_EOF; + goto lose; + } + + method = file_gettype(header1); +#ifndef SMALL + if (fflag == 0 && method == FT_UNKNOWN) { + maybe_warnx("%s: not in gzip format", file); + goto lose; + } + +#endif + +#ifndef SMALL + if (method == FT_GZIP && Nflag) { + unsigned char ts[4]; /* timestamp */ + + rv = pread(fd, ts, sizeof ts, GZIP_TIMESTAMP); + if (rv >= 0 && rv < (ssize_t)(sizeof ts)) + goto unexpected_EOF; + if (rv == -1) { + if (!fflag) + maybe_warn("can't read %s", file); + goto lose; + } + timestamp = ts[3] << 24 | ts[2] << 16 | ts[1] << 8 | ts[0]; + + if (header1[3] & ORIG_NAME) { + rbytes = pread(fd, name, sizeof(name) - 1, GZIP_ORIGNAME); + if (rbytes < 0) { + maybe_warn("can't read %s", file); + goto lose; + } + if (name[0] != '\0') { + char *dp, *nf; + + /* Make sure that name is NUL-terminated */ + name[rbytes] = '\0'; + + /* strip saved directory name */ + nf = strrchr(name, '/'); + if (nf == NULL) + nf = name; + else + nf++; + + /* preserve original directory name */ + dp = strrchr(file, '/'); + if (dp == NULL) + dp = file; + else + dp++; + snprintf(outfile, outsize, "%.*s%.*s", + (int) (dp - file), + file, (int) rbytes, nf); + } + } + } +#endif + lseek(fd, 0, SEEK_SET); + bzero(&isb, sizeof(isb)); + if (cflag == 0 || lflag) { + if (fstat(fd, &isb) != 0) + goto lose; +#ifndef SMALL + if (isb.st_nlink > 1 && lflag == 0 && fflag == 0) { + maybe_warnx("%s has %d other links -- skipping", + file, isb.st_nlink - 1); + goto lose; + } + if (nflag == 0 && timestamp) + isb.st_mtime = timestamp; + if (check_outfile(outfile) == 0) + goto lose; +#endif + } + + if (cflag == 0 && lflag == 0) { + zfd = open(outfile, O_WRONLY|O_CREAT|O_EXCL, 0600); + if (zfd == fileno(thread_stdout)) { + /* We won't close STDOUT_FILENO later... */ + zfd = dup(zfd); + close(fileno(thread_stdout)); + } + if (zfd == -1) { + maybe_warn("can't open %s", outfile); + goto lose; + } +#ifndef SMALL + remove_file = outfile; +#endif + } else + zfd = fileno(thread_stdout); + + switch (method) { +#ifndef NO_BZIP2_SUPPORT + case FT_BZIP2: + /* XXX */ + if (lflag) { + maybe_warnx("no -l with bzip2 files"); + goto lose; + } + + size = unbzip2(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef NO_COMPRESS_SUPPORT + case FT_Z: { + FILE *in, *out; + + /* XXX */ + if (lflag) { + maybe_warnx("no -l with Lempel-Ziv files"); + goto lose; + } + + if ((in = zdopen(fd)) == NULL) { + maybe_warn("zdopen for read: %s", file); + goto lose; + } + + out = fdopen(dup(zfd), "w"); + if (out == NULL) { + maybe_warn("fdopen for write: %s", outfile); + fclose(in); + goto lose; + } + + size = zuncompress(in, out, NULL, 0, NULL); + /* need to fclose() if ferror() is true... */ + if (ferror(in) | fclose(in)) { + maybe_warn("failed infile fclose"); + unlink(outfile); + (void)fclose(out); + } + if (fclose(out) != 0) { + maybe_warn("failed outfile fclose"); + unlink(outfile); + goto lose; + } + break; + } +#endif + +#ifndef NO_PACK_SUPPORT + case FT_PACK: + if (lflag) { + maybe_warnx("no -l with packed files"); + goto lose; + } + + size = unpack(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef NO_XZ_SUPPORT + case FT_XZ: + if (lflag) { + maybe_warnx("no -l with xz files"); + goto lose; + } + + size = unxz(fd, zfd, NULL, 0, NULL); + break; +#endif + +#ifndef SMALL + case FT_UNKNOWN: + if (lflag) { + maybe_warnx("no -l for unknown filetypes"); + goto lose; + } + size = cat_fd(NULL, 0, NULL, fd); + break; +#endif + default: + if (lflag) { + print_list(fd, isb.st_size, outfile, isb.st_mtime); + close(fd); + return -1; /* XXX */ + } + + size = gz_uncompress(fd, zfd, NULL, 0, NULL, file); + break; + } + + if (close(fd) != 0) + maybe_warn("couldn't close input"); + if (zfd != fileno(thread_stdout) && close(zfd) != 0) + maybe_warn("couldn't close output"); + + if (size == -1) { + if (cflag == 0) + unlink(outfile); + maybe_warnx("%s: uncompress failed", file); + return -1; + } + + /* if testing, or we uncompressed to stdout, this is all we need */ +#ifndef SMALL + if (tflag) + return size; +#endif + /* if we are uncompressing to stdin, don't remove the file. */ + if (cflag) + return size; + + /* + * if we create a file... + */ + /* + * if we can't stat the file don't remove the file. + */ + + ofd = open(outfile, O_RDWR, 0); + if (ofd == -1) { + maybe_warn("couldn't open (leaving original): %s", + outfile); + return -1; + } + if (fstat(ofd, &osb) != 0) { + maybe_warn("couldn't stat (leaving original): %s", + outfile); + close(ofd); + return -1; + } + if (osb.st_size != size) { + maybe_warnx("stat gave different size: %ju != %ju (leaving original)", + (uintmax_t)size, (uintmax_t)osb.st_size); + close(ofd); + unlink(outfile); + return -1; + } +#ifndef SMALL + copymodes(ofd, &isb, outfile); + remove_file = NULL; +#endif + close(ofd); + unlink_input(file, &isb); + return size; + + unexpected_EOF: + maybe_warnx("%s: unexpected end of file", file); + lose: + if (fd != -1) + close(fd); + if (zfd != -1 && zfd != fileno(thread_stdout)) + close(fd); + return -1; +} + +#ifndef SMALL +static off_t +cat_fd(unsigned char * prepend, size_t count, off_t *gsizep, int fd) +{ + char buf[BUFLEN]; + off_t in_tot; + ssize_t w; + + in_tot = count; + // w = write(fileno(thread_stdout), prepend, count); + w = fwrite(prepend, 1, count, thread_stdout); + if (w == -1 || (size_t)w != count) { + maybe_warn("write to stdout"); + return -1; + } + for (;;) { + ssize_t rv; + + rv = read(fd, buf, sizeof buf); + if (rv == 0) + break; + if (rv < 0) { + maybe_warn("read from fd %d", fd); + break; + } + +// if (write(fileno(thread_stdout), buf, rv) != rv) { + if (fwrite(buf, 1, rv, thread_stdout) != rv) { + maybe_warn("write to stdout"); + break; + } + in_tot += rv; + } + + if (gsizep) + *gsizep = in_tot; + return (in_tot); +} +#endif + +static void +handle_stdin(void) +{ + unsigned char header1[4]; + off_t usize, gsize; + enum filetype method; + ssize_t bytes_read; +#ifndef NO_COMPRESS_SUPPORT + FILE *in; +#endif + +#ifndef SMALL +// if (fflag == 0 && lflag == 0 && isatty(fileno(thread_stdin))) { + if (fflag == 0 && lflag == 0 && (ios_isatty(STDIN_FILENO))) { + maybe_warnx("standard input is a terminal -- ignoring"); + return; + } +#endif + + if (lflag) { + struct stat isb; + + /* XXX could read the whole file, etc. */ + if (fstat(fileno(thread_stdin), &isb) < 0) { + maybe_warn("fstat"); + return; + } + print_list(fileno(thread_stdin), isb.st_size, "stdout", isb.st_mtime); + return; + } + + bytes_read = read_retry(fileno(thread_stdin), header1, sizeof header1); + if (bytes_read == -1) { + maybe_warn("can't read stdin"); + return; + } else if (bytes_read != sizeof(header1)) { + maybe_warnx("(stdin): unexpected end of file"); + return; + } + + method = file_gettype(header1); + switch (method) { + default: +#ifndef SMALL + if (fflag == 0) { + maybe_warnx("unknown compression format"); + return; + } + usize = cat_fd(header1, sizeof header1, &gsize, fileno(thread_stdin)); + break; +#endif + case FT_GZIP: + usize = gz_uncompress(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize, "(stdin)"); + break; +#ifndef NO_BZIP2_SUPPORT + case FT_BZIP2: + usize = unbzip2(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif +#ifndef NO_COMPRESS_SUPPORT + case FT_Z: + if ((in = zdopen(fileno(thread_stdin))) == NULL) { + maybe_warnx("zopen of stdin"); + return; + } + + usize = zuncompress(in, thread_stdout, (char *)header1, + sizeof header1, &gsize); + fclose(in); + break; +#endif +#ifndef NO_PACK_SUPPORT + case FT_PACK: + usize = unpack(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif +#ifndef NO_XZ_SUPPORT + case FT_XZ: + usize = unxz(fileno(thread_stdin), fileno(thread_stdout), + (char *)header1, sizeof header1, &gsize); + break; +#endif + } + +#ifndef SMALL + if (vflag && !tflag && usize != -1 && gsize != -1) + print_verbage(NULL, NULL, usize, gsize); + if (vflag && tflag) + print_test("(stdin)", usize != -1); +#endif + +} + +static void +handle_stdout(void) +{ + off_t gsize, usize; + struct stat sb; + time_t systime; + uint32_t mtime; + int ret; + +#ifndef SMALL +// if (fflag == 0 && isatty(fileno(thread_stdout))) { + if (fflag == 0 && (ios_isatty(STDOUT_FILENO))) { + maybe_warnx("standard output is a terminal -- ignoring"); + return; + } +#endif + /* If stdin is a file use its mtime, otherwise use current time */ + ret = fstat(fileno(thread_stdin), &sb); + +#ifndef SMALL + if (ret < 0) { + maybe_warn("Can't stat stdin"); + return; + } +#endif + + if (S_ISREG(sb.st_mode)) + mtime = (uint32_t)sb.st_mtime; + else { + systime = time(NULL); +#ifndef SMALL + if (systime == -1) { + maybe_warn("time"); + return; + } +#endif + mtime = (uint32_t)systime; + } + + usize = gz_compress(fileno(thread_stdin), fileno(thread_stdout), &gsize, "", mtime); +#ifndef SMALL + if (vflag && !tflag && usize != -1 && gsize != -1) + print_verbage(NULL, NULL, usize, gsize); +#endif +} + +/* do what is asked for, for the path name */ +static void +handle_pathname(char *path) +{ + char *opath = path, *s = NULL; + ssize_t len; + int slen; + struct stat sb; + + /* check for stdout/stdin */ + if (path[0] == '-' && path[1] == '\0') { + if (dflag) + handle_stdin(); + else + handle_stdout(); + return; + } + +#ifdef __APPLE__ + if (zcat && COMPAT_MODE("bin/zcat", "Unix2003")) { + char *suffix = strrchr(path, '.'); + if (suffix == NULL || strcmp(suffix, Z_SUFFIX) != 0) { + len = strlen(path); + slen = sizeof(Z_SUFFIX) - 1; + s = malloc(len + slen + 1); + memcpy(s, path, len); + memcpy(s + len, Z_SUFFIX, slen + 1); + path = s; + } + } +#endif + +retry: + if (stat(path, &sb) != 0 || (fflag == 0 && cflag == 0 && + lstat(path, &sb) != 0)) { + /* lets try .gz if we're decompressing */ + if (dflag && s == NULL && errno == ENOENT) { + len = strlen(path); + slen = suffixes[0].ziplen; + s = malloc(len + slen + 1); + if (s == NULL) + maybe_err("malloc"); + memcpy(s, path, len); + memcpy(s + len, suffixes[0].zipped, slen + 1); + path = s; + goto retry; + } +#ifdef __APPLE__ + /* Include actual path for clarity. */ + maybe_warn("can't stat: %s (%s)", opath, path); +#else + maybe_warn("can't stat: %s", opath); +#endif + goto out; + } + + if (S_ISDIR(sb.st_mode)) { +#ifndef SMALL + if (rflag) + handle_dir(path); + else +#endif + maybe_warnx("%s is a directory", path); + goto out; + } + + if (S_ISREG(sb.st_mode)) + handle_file(path, &sb); + else + maybe_warnx("%s is not a regular file", path); + +out: + if (s) + free(s); +} + +/* compress/decompress a file */ +static void +handle_file(char *file, struct stat *sbp) +{ + off_t usize, gsize; + char outfile[PATH_MAX]; + + infile = file; + if (dflag) { + usize = file_uncompress(file, outfile, sizeof(outfile)); +#ifndef SMALL + if (vflag && tflag) + print_test(file, usize != -1); +#endif + if (usize == -1) + return; + gsize = sbp->st_size; + } else { + gsize = file_compress(file, outfile, sizeof(outfile)); + if (gsize == -1) + return; + usize = sbp->st_size; + } + + +#ifndef SMALL + if (vflag && !tflag) + print_verbage(file, (cflag) ? NULL : outfile, usize, gsize); +#endif +} + +#ifndef SMALL +/* this is used with -r to recursively descend directories */ +static void +handle_dir(char *dir) +{ + char *path_argv[2]; + FTS *fts; + FTSENT *entry; + + path_argv[0] = dir; + path_argv[1] = 0; + fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); + if (fts == NULL) { + warn("couldn't fts_open %s", dir); + return; + } + + while ((entry = fts_read(fts))) { + switch(entry->fts_info) { + case FTS_D: + case FTS_DP: + continue; + + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + maybe_warn("%s", entry->fts_path); + continue; + case FTS_F: + handle_file(entry->fts_path, entry->fts_statp); + } + } + (void)fts_close(fts); +} +#endif + +/* print a ratio - size reduction as a fraction of uncompressed size */ +static void +print_ratio(off_t in, off_t out, FILE *where) +{ + int percent10; /* 10 * percent */ + off_t diff; + char buff[8]; + int len; + + diff = in - out/2; + if (diff <= 0) + /* + * Output is more than double size of input! print -99.9% + * Quite possibly we've failed to get the original size. + */ + percent10 = -999; + else { + /* + * We only need 12 bits of result from the final division, + * so reduce the values until a 32bit division will suffice. + */ + while (in > 0x100000) { + diff >>= 1; + in >>= 1; + } + if (in != 0) + percent10 = ((u_int)diff * 2000) / (u_int)in - 1000; + else + percent10 = 0; + } + + len = snprintf(buff, sizeof buff, "%2.2d.", percent10); + /* Move the '.' to before the last digit */ + buff[len - 1] = buff[len - 2]; + buff[len - 2] = '.'; + fprintf(where, "%5s%%", buff); +} + +#ifndef SMALL +/* print compression statistics, and the new name (if there is one!) */ +static void +print_verbage(const char *file, const char *nfile, off_t usize, off_t gsize) +{ + if (file) + fprintf(thread_stderr, "%s:%s ", file, + strlen(file) < 7 ? "\t\t" : "\t"); + print_ratio(usize, gsize, thread_stderr); + if (nfile) + fprintf(thread_stderr, " -- replaced with %s", nfile); + fprintf(thread_stderr, "\n"); + fflush(thread_stderr); +} + +/* print test results */ +static void +print_test(const char *file, int ok) +{ + + if (exit_value == 0 && ok == 0) + exit_value = 1; + fprintf(thread_stderr, "%s:%s %s\n", file, + strlen(file) < 7 ? "\t\t" : "\t", ok ? "OK" : "NOT OK"); + fflush(thread_stderr); +} +#endif + +/* print a file's info ala --list */ +/* eg: + compressed uncompressed ratio uncompressed_name + 354841 1679360 78.8% /usr/pkgsrc/distfiles/libglade-2.0.1.tar +*/ +static void +print_list(int fd, off_t out, const char *outfile, time_t ts) +{ + static int first = 1; +#ifndef SMALL + static off_t in_tot, out_tot; + uint32_t crc = 0; +#endif + off_t in = 0, rv; + + if (first) { +#ifndef SMALL + if (vflag) + fprintf(thread_stdout, "method crc date time "); +#endif + if (qflag == 0) + fprintf(thread_stdout, " compressed uncompressed " + "ratio uncompressed_name\n"); + } + first = 0; + + /* print totals? */ +#ifndef SMALL + if (fd == -1) { + in = in_tot; + out = out_tot; + } else +#endif + { + /* read the last 4 bytes - this is the uncompressed size */ + rv = lseek(fd, (off_t)(-8), SEEK_END); + if (rv != -1) { + unsigned char buf[8]; + uint32_t usize; + + rv = read(fd, (char *)buf, sizeof(buf)); + if (rv == -1) + maybe_warn("read of uncompressed size"); + else if (rv != sizeof(buf)) + maybe_warnx("read of uncompressed size"); + + else { + usize = buf[4] | buf[5] << 8 | + buf[6] << 16 | buf[7] << 24; + in = (off_t)usize; +#ifndef SMALL + crc = buf[0] | buf[1] << 8 | + buf[2] << 16 | buf[3] << 24; +#endif + } + } + } + +#ifndef SMALL + if (vflag && fd == -1) + fprintf(thread_stdout, " "); + else if (vflag) { + char *date = ctime(&ts); + + /* skip the day, 1/100th second, and year */ + date += 4; + date[12] = 0; + fprintf(thread_stdout, "%5s %08x %11s ", "defla"/*XXX*/, crc, date); + } + in_tot += in; + out_tot += out; +#else + (void)&ts; /* XXX */ +#endif + fprintf(thread_stdout, "%12llu %12llu ", (unsigned long long)out, (unsigned long long)in); + print_ratio(in, out, thread_stdout); + fprintf(thread_stdout, " %s\n", outfile); +} + +/* display the usage of NetBSD gzip */ +static void +usage(void) +{ + + fprintf(thread_stderr, "%s\n", gzip_version); + fprintf(thread_stderr, +#ifdef SMALL + "usage: %s [-" OPT_LIST "] [ [ ...]]\n", +#else + "usage: %s [-123456789acdfhklLNnqrtVv] [-S .suffix] [ [ ...]]\n" + " -1 --fast fastest (worst) compression\n" + " -2 .. -8 set compression level\n" + " -9 --best best (slowest) compression\n" + " -c --stdout write to stdout, keep original files\n" + " --to-stdout\n" + " -d --decompress uncompress files\n" + " --uncompress\n" + " -f --force force overwriting & compress links\n" + " -h --help display this help\n" + " -k --keep don't delete input files during operation\n" + " -l --list list compressed file contents\n" + " -N --name save or restore original file name and time stamp\n" + " -n --no-name don't save original file name or time stamp\n" + " -q --quiet output no warnings\n" + " -r --recursive recursively compress files in directories\n" + " -S .suf use suffix .suf instead of .gz\n" + " --suffix .suf\n" + " -t --test test compressed file\n" + " -V --version display program version\n" + " -v --verbose print extra statistics\n", +#endif + ios_progname()); + exit(0); +} + +#ifndef SMALL +/* display the license information of FreeBSD gzip */ +static void +display_license(void) +{ + +#ifdef __APPLE__ + fprintf(thread_stderr, "%s (based on FreeBSD gzip 20150113)\n", gzip_version); +#else + fprintf(thread_stderr, "%s (based on NetBSD gzip 20150113)\n", gzip_version); +#endif + fprintf(thread_stderr, "%s\n", gzip_copyright); + exit(0); +} +#endif + +/* display the version of NetBSD gzip */ +static void +display_version(void) +{ + + fprintf(thread_stderr, "%s\n", gzip_version); + exit(0); +} + +#ifndef NO_BZIP2_SUPPORT +#include "unbzip2.c" +#endif +#ifndef NO_COMPRESS_SUPPORT +#include "zuncompress.c" +#endif +#ifndef NO_PACK_SUPPORT +#include "unpack.c" +#endif +#ifndef NO_XZ_SUPPORT +#include "unxz.c" +#endif + +static ssize_t +read_retry(int fd, void *buf, size_t sz) +{ + char *cp = buf; + size_t left = MIN(sz, (size_t) SSIZE_MAX); + + while (left > 0) { + ssize_t ret; + + ret = read(fd, cp, left); + if (ret == -1) { + return ret; + } else if (ret == 0) { + break; /* EOF */ + } + cp += ret; + left -= ret; + } + + return sz - left; +} diff --git a/file_cmds/gzip/gzip.plist b/file_cmds/gzip/gzip.plist new file mode 100644 index 00000000..a0190fd9 --- /dev/null +++ b/file_cmds/gzip/gzip.plist @@ -0,0 +1,29 @@ + + + + + OpenSourceImportDate + 2012-10-25 + OpenSourceLicense + bsd + OpenSourceLicenseFile + gzip.txt + OpenSourceModifications + + gzip.c: adjust --version output + gzip.c: timespec portability fixes + gzip.c: preserve ACLs/EAs (3753276) + gzip.c: clear type/creator (4123341) + gzip.c, gzip.1: zcat conformance (3675040) + zmore: work as zless or zmore + + OpenSourceProject + gzip + OpenSourceSCM + cvs -d :pserver:anoncvs@anoncvs.fr.FreeBSD.org:/home/ncvs co src/usr.bin/gzip + OpenSourceVersion + 2012-10-19 + OpenSourceWebsiteURL + http://svnweb.freebsd.org/base/head/usr.bin/gzip/ + + diff --git a/file_cmds/gzip/gzip.xcconfig b/file_cmds/gzip/gzip.xcconfig new file mode 100644 index 00000000..382cbbf7 --- /dev/null +++ b/file_cmds/gzip/gzip.xcconfig @@ -0,0 +1,49 @@ +PRODUCT_NAME = $(TARGET_NAME) +GZIP_PREFIX = /usr/local +GZIP_PREFIX[sdk=macosx*] = /usr +INSTALL_PATH = $(GZIP_PREFIX)/bin + +GCC_PREPROCESSOR_DEFINITIONS = GZIP_APPLE_VERSION=\"$(RC_ProjectSourceVersion)\" + +// Make it build in the GUI +SUPPORTED_PLATFORMS = iphoneos macosx +ARCHS[sdk=macosx*] = x86_64 +RUN_CLANG_STATIC_ANALYZER = YES + +// Versioning +CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion) +VERSIONING_SYSTEM = apple-generic +VERSION_INFO_PREFIX = __ + +// Other Stuff +ALWAYS_SEARCH_USER_PATHS = NO +APPLY_RULES_IN_COPY_FILES = YES +CODE_SIGN_IDENTITY = - +DEAD_CODE_STRIPPING = YES +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym +STRIP_INSTALLED_PRODUCT = YES +USE_HEADERMAP = NO + +// Warnings +//CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES +//CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES +GCC_TREAT_WARNINGS_AS_ERRORS = YES +//GCC_WARN_64_TO_32_BIT_CONVERSION = YES +GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES +GCC_WARN_ABOUT_MISSING_NEWLINE = YES +GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES +GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES +GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES +GCC_WARN_SHADOW = YES +GCC_WARN_SIGN_COMPARE = YES +GCC_WARN_UNINITIALIZED_AUTOS = YES +GCC_WARN_UNKNOWN_PRAGMAS = YES +GCC_WARN_UNUSED_FUNCTION = YES +GCC_WARN_UNUSED_LABEL = YES +GCC_WARN_UNUSED_PARAMETER = YES +GCC_WARN_UNUSED_VARIABLE = YES + +// Used by install_scripts.sh +GZIP_SCRIPTS = gzexe zdiff zforce zmore znew +GZIP_LINKS = gzip gunzip gzip gzcat gzip zcat zdiff zcmp zmore zless diff --git a/file_cmds/gzip/install_scripts.sh b/file_cmds/gzip/install_scripts.sh new file mode 100644 index 00000000..6e8e550d --- /dev/null +++ b/file_cmds/gzip/install_scripts.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -e + +INSTALL_MAN1_DIR=${INSTALL_ROOT}${GZIP_PREFIX}/share/man/man1 + +install -d -m 0755 "${INSTALL_MAN1_DIR}" +for script in ${GZIP_SCRIPTS}; do + printf "Installing ${script} ...\n" + install -m 0755 ${SRCROOT}/gzip/${script} ${INSTALL_DIR}/${script} + install -m 0644 ${SRCROOT}/gzip/${script}.1 ${INSTALL_MAN1_DIR}/${script}.1 +done + +set ${GZIP_LINKS} +while [ $# -ge 2 ]; do + l=$1 + shift + t=$1 + shift + printf "Creating link: ${t} -> ${l} ...\n" + ln -f ${INSTALL_DIR}/${l} ${INSTALL_DIR}/${t} + ln -f ${INSTALL_MAN1_DIR}/${l}.1 ${INSTALL_MAN1_DIR}/${t}.1 +done diff --git a/file_cmds/gzip/unbzip2.c b/file_cmds/gzip/unbzip2.c new file mode 100644 index 00000000..4192e9bc --- /dev/null +++ b/file_cmds/gzip/unbzip2.c @@ -0,0 +1,141 @@ +/* $NetBSD: unbzip2.c,v 1.13 2009/12/05 03:23:37 mrg Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Simon Burge. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/gzip/unbzip2.c,v 1.5 2010/04/07 22:54:53 delphij Exp $ + */ + +/* This file is #included by gzip.c */ + +static off_t +unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) +{ + int ret, end_of_file, cold = 0; + off_t bytes_out = 0; + bz_stream bzs; + static char *inbuf, *outbuf; + + if (inbuf == NULL) + inbuf = malloc(BUFLEN); + if (outbuf == NULL) + outbuf = malloc(BUFLEN); + if (inbuf == NULL || outbuf == NULL) + maybe_err("malloc"); + + bzs.bzalloc = NULL; + bzs.bzfree = NULL; + bzs.opaque = NULL; + + end_of_file = 0; + ret = BZ2_bzDecompressInit(&bzs, 0, 0); + if (ret != BZ_OK) + maybe_errx("bzip2 init"); + + /* Prepend. */ + bzs.avail_in = prelen; + bzs.next_in = pre; + + if (bytes_in) + *bytes_in = prelen; + + while (ret == BZ_OK) { + if (bzs.avail_in == 0 && !end_of_file) { + ssize_t n; + + n = read(in, inbuf, BUFLEN); + if (n < 0) + maybe_err("read"); + if (n == 0) + end_of_file = 1; + bzs.next_in = inbuf; + bzs.avail_in = n; + if (bytes_in) + *bytes_in += n; + } + + bzs.next_out = outbuf; + bzs.avail_out = BUFLEN; + ret = BZ2_bzDecompress(&bzs); + + switch (ret) { + case BZ_STREAM_END: + case BZ_OK: + if (ret == BZ_OK && end_of_file) { + /* + * If we hit this after a stream end, consider + * it as the end of the whole file and don't + * bail out. + */ + if (cold == 1) + ret = BZ_STREAM_END; + else + maybe_errx("truncated file"); + } + cold = 0; + if (!tflag && bzs.avail_out != BUFLEN) { + ssize_t n; + + n = write(out, outbuf, BUFLEN - bzs.avail_out); + if (n < 0) + maybe_err("write"); + bytes_out += n; + } + if (ret == BZ_STREAM_END && !end_of_file) { + if (BZ2_bzDecompressEnd(&bzs) != BZ_OK || + BZ2_bzDecompressInit(&bzs, 0, 0) != BZ_OK) + maybe_errx("bzip2 re-init"); + cold = 1; + ret = BZ_OK; + } + break; + + case BZ_DATA_ERROR: + maybe_warnx("bzip2 data integrity error"); + break; + + case BZ_DATA_ERROR_MAGIC: + maybe_warnx("bzip2 magic number error"); + break; + + case BZ_MEM_ERROR: + maybe_warnx("bzip2 out of memory"); + break; + + default: + maybe_warnx("unknown bzip2 error: %d", ret); + break; + } + } + + if (ret != BZ_STREAM_END || BZ2_bzDecompressEnd(&bzs) != BZ_OK) + return (-1); + + return (bytes_out); +} + diff --git a/file_cmds/gzip/unpack.c b/file_cmds/gzip/unpack.c new file mode 100644 index 00000000..dbad5882 --- /dev/null +++ b/file_cmds/gzip/unpack.c @@ -0,0 +1,329 @@ +/*- + * Copyright (c) 2009 Xin LI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/gzip/unpack.c,v 1.3 2010/10/16 15:24:04 bcr Exp $ + */ + +/* This file is #included by gzip.c */ + +/* + * pack(1) file format: + * + * The first 7 bytes is the header: + * 00, 01 - Signature (US, RS), we already validated it earlier. + * 02..05 - Uncompressed size + * 06 - Level for the huffman tree (<=24) + * + * pack(1) will then store symbols (leaf) nodes count in each huffman + * tree levels, each level would consume 1 byte (See [1]). + * + * After the symbol count table, there is the symbol table, storing + * symbols represented by corresponding leaf node. EOB is not being + * explicitly transmitted (not necessary anyway) in the symbol table. + * + * Compressed data goes after the symbol table. + * + * NOTES + * + * [1] If we count EOB into the symbols, that would mean that we will + * have at most 256 symbols in the huffman tree. pack(1) rejects empty + * file and files that just repeats one character, which means that we + * will have at least 2 symbols. Therefore, pack(1) would reduce the + * last level symbol count by 2 which makes it a number in + * range [0..254], so all levels' symbol count would fit into 1 byte. + */ + +#define PACK_HEADER_LENGTH 7 +#define HTREE_MAXLEVEL 24 + +/* + * unpack descriptor + * + * Represent the huffman tree in a similar way that pack(1) would + * store in a packed file. We store all symbols in a linear table, + * and store pointers to each level's first symbol. In addition to + * that, maintain two counts for each level: inner nodes count and + * leaf nodes count. + */ +typedef struct { + int symbol_size; /* Size of the symbol table */ + int treelevels; /* Levels for the huffman tree */ + + int *symbolsin; /* Table of leaf symbols count in + each level */ + int *inodesin; /* Table of internal nodes count in + each level */ + + char *symbol; /* The symbol table */ + char *symbol_eob; /* Pointer to the EOB symbol */ + char **tree; /* Decoding huffman tree (pointers to + first symbol of each tree level */ + + off_t uncompressed_size; /* Uncompressed size */ + FILE *fpIn; /* Input stream */ + FILE *fpOut; /* Output stream */ +} unpack_descriptor_t; + +/* + * Release resource allocated to an unpack descriptor. + * + * Caller is responsible to make sure that all of these pointers are + * initialized (in our case, they all point to valid memory block). + * We don't zero out pointers here because nobody else would ever + * reference the memory block without scrubbing them. + */ +static void +unpack_descriptor_fini(unpack_descriptor_t *unpackd) +{ + + free(unpackd->symbolsin); + free(unpackd->inodesin); + free(unpackd->symbol); + free(unpackd->tree); + + fclose(unpackd->fpIn); + fclose(unpackd->fpOut); +} + +/* + * Recursively fill the internal node count table + */ +static void +unpackd_fill_inodesin(const unpack_descriptor_t *unpackd, int level) +{ + + /* + * The internal nodes would be 1/2 of total internal nodes and + * leaf nodes in the next level. For the last level there + * would be no internal node by definition. + */ + if (level < unpackd->treelevels) { + unpackd_fill_inodesin(unpackd, level + 1); + unpackd->inodesin[level] = (unpackd->inodesin[level + 1] + + unpackd->symbolsin[level + 1]) / 2; + } else + unpackd->inodesin[level] = 0; +} + +/* + * Update counter for accepted bytes + */ +static void +accepted_bytes(off_t *bytes_in, off_t newbytes) +{ + + if (bytes_in != NULL) + (*bytes_in) += newbytes; +} + +/* + * Read file header and construct the tree. Also, prepare the buffered I/O + * for decode routine. + * + * Return value is uncompressed size. + */ +static void +unpack_parse_header(int in, int out, char *pre, size_t prelen, off_t *bytes_in, + unpack_descriptor_t *unpackd) +{ + unsigned char hdr[PACK_HEADER_LENGTH]; /* buffer for header */ + ssize_t bytesread; /* Bytes read from the file */ + int i, j, thisbyte; + + /* Prepend the header buffer if we already read some data */ + if (prelen != 0) + memcpy(hdr, pre, prelen); + + /* Read in and fill the rest bytes of header */ + bytesread = read(in, hdr + prelen, PACK_HEADER_LENGTH - prelen); + if (bytesread < 0) + maybe_err("Error reading pack header"); + + accepted_bytes(bytes_in, PACK_HEADER_LENGTH); + + /* Obtain uncompressed length (bytes 2,3,4,5)*/ + unpackd->uncompressed_size = 0; + for (i = 2; i <= 5; i++) { + unpackd->uncompressed_size <<= 8; + unpackd->uncompressed_size |= hdr[i]; + } + + /* Get the levels of the tree */ + unpackd->treelevels = hdr[6]; + if (unpackd->treelevels > HTREE_MAXLEVEL || unpackd->treelevels < 1) + maybe_errx("Huffman tree has insane levels"); + + /* Let libc take care for buffering from now on */ + if ((unpackd->fpIn = fdopen(in, "r")) == NULL) + maybe_err("Can not fdopen() input stream"); + if ((unpackd->fpOut = fdopen(out, "w")) == NULL) + maybe_err("Can not fdopen() output stream"); + + /* Allocate for the tables of bounds and the tree itself */ + unpackd->inodesin = + calloc(unpackd->treelevels, sizeof(*(unpackd->inodesin))); + unpackd->symbolsin = + calloc(unpackd->treelevels, sizeof(*(unpackd->symbolsin))); + unpackd->tree = + calloc(unpackd->treelevels, (sizeof (*(unpackd->tree)))); + if (unpackd->inodesin == NULL || unpackd->symbolsin == NULL || + unpackd->tree == NULL) + maybe_err("calloc"); + + /* We count from 0 so adjust to match array upper bound */ + unpackd->treelevels--; + + /* Read the levels symbol count table and calculate total */ + unpackd->symbol_size = 1; /* EOB */ + for (i = 0; i <= unpackd->treelevels; i++) { + if ((thisbyte = fgetc(unpackd->fpIn)) == EOF) + maybe_err("File appears to be truncated"); + unpackd->symbolsin[i] = (unsigned char)thisbyte; + unpackd->symbol_size += unpackd->symbolsin[i]; + } + accepted_bytes(bytes_in, unpackd->treelevels); + if (unpackd->symbol_size > 256) + maybe_errx("Bad symbol table"); + + /* Allocate for the symbol table, point symbol_eob at the beginning */ + unpackd->symbol_eob = unpackd->symbol = calloc(1, unpackd->symbol_size); + if (unpackd->symbol == NULL) + maybe_err("calloc"); + + /* + * Read in the symbol table, which contain [2, 256] symbols. + * In order to fit the count in one byte, pack(1) would offset + * it by reducing 2 from the actual number from the last level. + * + * We adjust the last level's symbol count by 1 here, because + * the EOB symbol is not being transmitted explicitly. Another + * adjustment would be done later afterward. + */ + unpackd->symbolsin[unpackd->treelevels]++; + for (i = 0; i <= unpackd->treelevels; i++) { + unpackd->tree[i] = unpackd->symbol_eob; + for (j = 0; j < unpackd->symbolsin[i]; j++) { + if ((thisbyte = fgetc(unpackd->fpIn)) == EOF) + maybe_errx("Symbol table truncated"); + *unpackd->symbol_eob++ = (char)thisbyte; + } + accepted_bytes(bytes_in, unpackd->symbolsin[i]); + } + + /* Now, take account for the EOB symbol as well */ + unpackd->symbolsin[unpackd->treelevels]++; + + /* + * The symbolsin table has been constructed now. + * Calculate the internal nodes count table based on it. + */ + unpackd_fill_inodesin(unpackd, 0); +} + +/* + * Decode huffman stream, based on the huffman tree. + */ +static void +unpack_decode(const unpack_descriptor_t *unpackd, off_t *bytes_in) +{ + int thislevel, thiscode, thisbyte, inlevelindex; + int i; + off_t bytes_out = 0; + const char *thissymbol; /* The symbol pointer decoded from stream */ + + /* + * Decode huffman. Fetch every bytes from the file, get it + * into 'thiscode' bit-by-bit, then output the symbol we got + * when one has been found. + * + * Assumption: sizeof(int) > ((max tree levels + 1) / 8). + * bad things could happen if not. + */ + thislevel = 0; + thiscode = thisbyte = 0; + + while ((thisbyte = fgetc(unpackd->fpIn)) != EOF) { + accepted_bytes(bytes_in, 1); + + /* + * Split one bit from thisbyte, from highest to lowest, + * feed the bit into thiscode, until we got a symbol from + * the tree. + */ + for (i = 7; i >= 0; i--) { + thiscode = (thiscode << 1) | ((thisbyte >> i) & 1); + + /* Did we got a symbol? (referencing leaf node) */ + if (thiscode >= unpackd->inodesin[thislevel]) { + inlevelindex = + thiscode - unpackd->inodesin[thislevel]; + if (inlevelindex > unpackd->symbolsin[thislevel]) + maybe_errx("File corrupt"); + + thissymbol = + &(unpackd->tree[thislevel][inlevelindex]); + if ((thissymbol == unpackd->symbol_eob) && + (bytes_out == unpackd->uncompressed_size)) + goto finished; + + fputc((*thissymbol), unpackd->fpOut); + bytes_out++; + + /* Prepare for next input */ + thislevel = 0; thiscode = 0; + } else { + thislevel++; + if (thislevel > unpackd->treelevels) + maybe_errx("File corrupt"); + } + } + } + +finished: + if (bytes_out != unpackd->uncompressed_size) + maybe_errx("Premature EOF"); +} + +/* Handler for pack(1)'ed file */ +static off_t +unpack(int in, int out, char *pre, size_t prelen, off_t *bytes_in) +{ + unpack_descriptor_t unpackd; + + in = dup(in); + if (in == -1) + maybe_err("dup"); + out = dup(out); + if (out == -1) + maybe_err("dup"); + + unpack_parse_header(in, out, pre, prelen, bytes_in, &unpackd); + unpack_decode(&unpackd, bytes_in); + unpack_descriptor_fini(&unpackd); + + /* If we reached here, the unpack was successful */ + return (unpackd.uncompressed_size); +} + diff --git a/file_cmds/gzip/unxz.c b/file_cmds/gzip/unxz.c new file mode 100644 index 00000000..df48ae3a --- /dev/null +++ b/file_cmds/gzip/unxz.c @@ -0,0 +1,153 @@ +/* $NetBSD: unxz.c,v 1.5 2011/09/30 01:32:21 christos Exp $ */ + +/*- + * Copyright (c) 2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include +__FBSDID("$FreeBSD: src/usr.bin/gzip/unxz.c,v 1.2 2011/10/16 07:35:26 delphij Exp $"); + +#include +#include +#include +#include +#include + +static off_t +unxz(int i, int o, char *pre, size_t prelen, off_t *bytes_in) +{ + lzma_stream strm = LZMA_STREAM_INIT; + static const int flags = LZMA_TELL_UNSUPPORTED_CHECK|LZMA_CONCATENATED; + lzma_ret ret; + lzma_action action = LZMA_RUN; + off_t bytes_out, bp; + uint8_t ibuf[BUFSIZ]; + uint8_t obuf[BUFSIZ]; + + if (bytes_in == NULL) + bytes_in = &bp; + + strm.next_in = ibuf; + memcpy(ibuf, pre, prelen); + strm.avail_in = read(i, ibuf + prelen, sizeof(ibuf) - prelen); + if (strm.avail_in == (size_t)-1) + maybe_err("read failed"); + strm.avail_in += prelen; + *bytes_in = strm.avail_in; + + if ((ret = lzma_stream_decoder(&strm, UINT64_MAX, flags)) != LZMA_OK) + maybe_errx("Can't initialize decoder (%d)", ret); + + strm.next_out = NULL; + strm.avail_out = 0; + if ((ret = lzma_code(&strm, LZMA_RUN)) != LZMA_OK) + maybe_errx("Can't read headers (%d)", ret); + + bytes_out = 0; + strm.next_out = obuf; + strm.avail_out = sizeof(obuf); + + for (;;) { + if (strm.avail_in == 0) { + strm.next_in = ibuf; + strm.avail_in = read(i, ibuf, sizeof(ibuf)); + switch (strm.avail_in) { + case (size_t)-1: + maybe_err("read failed"); + /*NOTREACHED*/ + case 0: + action = LZMA_FINISH; + break; + default: + *bytes_in += strm.avail_in; + break; + } + } + + ret = lzma_code(&strm, action); + + // Write and check write error before checking decoder error. + // This way as much data as possible gets written to output + // even if decoder detected an error. + if (strm.avail_out == 0 || ret != LZMA_OK) { + const size_t write_size = sizeof(obuf) - strm.avail_out; + + if (write(o, obuf, write_size) != (ssize_t)write_size) + maybe_err("write failed"); + + strm.next_out = obuf; + strm.avail_out = sizeof(obuf); + bytes_out += write_size; + } + + if (ret != LZMA_OK) { + if (ret == LZMA_STREAM_END) { + // Check that there's no trailing garbage. + if (strm.avail_in != 0 || read(i, ibuf, 1)) + ret = LZMA_DATA_ERROR; + else { + lzma_end(&strm); + return bytes_out; + } + } + + const char *msg; + switch (ret) { + case LZMA_MEM_ERROR: + msg = strerror(ENOMEM); + break; + + case LZMA_FORMAT_ERROR: + msg = "File format not recognized"; + break; + + case LZMA_OPTIONS_ERROR: + // FIXME: Better message? + msg = "Unsupported compression options"; + break; + + case LZMA_DATA_ERROR: + msg = "File is corrupt"; + break; + + case LZMA_BUF_ERROR: + msg = "Unexpected end of input"; + break; + + case LZMA_MEMLIMIT_ERROR: + msg = "Reached memory limit"; + break; + + default: + maybe_errx("Unknown error (%d)", ret); + break; + } + maybe_errx("%s", msg); + + } + } +} diff --git a/file_cmds/gzip/zdiff b/file_cmds/gzip/zdiff new file mode 100644 index 00000000..87863c0b --- /dev/null +++ b/file_cmds/gzip/zdiff @@ -0,0 +1,142 @@ +#!/bin/sh - +# +# $NetBSD: zdiff,v 1.5 2010/04/14 20:30:28 joerg Exp $ +# +# $OpenBSD: zdiff,v 1.2 2003/07/29 07:42:44 otto Exp $ +# +#- +# Copyright (c) 2003 Todd C. Miller +# Copyright (c) 2010 Joerg Sonnenberger +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Sponsored in part by the Defense Advanced Research Projects +# Agency (DARPA) and Air Force Research Laboratory, Air Force +# Materiel Command, USAF, under agreement number F39502-99-1-0512. +# +# $FreeBSD: src/usr.bin/gzip/zdiff,v 1.2 2011/05/23 09:02:44 delphij Exp $ + +# Set $prog based on $0 +case $0 in + *cmp) prog=cmp + ;; + *) prog=diff + ;; +esac +USAGE="usage: $0 [options] file1 [file2]" + +check_suffix() { + case "$1" in + *[._-][Zz]) + setvar $2 "${1%??}" + setvar $3 "gzip -cdqf" + ;; + *[._-]bz) + setvar $2 "${1%???}" + setvar $3 "bzip2 -cdqf" + ;; + *[._-]gz) + setvar $2 "${1%???}" + setvar $3 "gzip -cdqf" + ;; + *[._-]xz) + setvar $2 "${1%???}" + setvar $3 "xz -cdqf" + ;; + *[._-]bz2) + setvar $2 "${1%????}" + setvar $3 "bzip2 -cdqf" + ;; + *[._-]lzma) + setvar $2 "${1%?????}" + setvar $3 "xz -cdqf" + ;; + *.t[ag]z) + setvar $2 "${1%??}"ar + setvar $3 "gzip -cdqf" + ;; + *.tbz) + setvar $2 "${1%??}"ar + setvar $3 "bzip2 -cdqf" + ;; + *.tbz2) + setvar $2 "${1%???}"ar + setvar $3 "bzip2 -cdqf" + ;; + *.t[lx]z) + setvar $2 "${1%??}"ar + setvar $3 "xz -cdqf" + ;; + *) + setvar $2 "$1" + setvar $3 "" + ;; + esac +} + + +# Pull out any command line flags so we can pass them to diff/cmp +# XXX - assumes there is no optarg +flags= +while test $# -ne 0; do + case "$1" in + --) + shift + break + ;; + -) + break + ;; + -*) + flags="$flags $1" + shift + ;; + *) + break + ;; + esac +done + +if [ $# -eq 1 ]; then + # One file given, compare compressed to uncompressed + files="$1" + check_suffix "$1" files filt + if [ -z "$filt" ]; then + echo "z$prog: unknown suffix" 1>&2 + exit 1 + fi + $filt -- "$1" | $prog $flags -- - "$files" + status=$? +elif [ $# -eq 2 ]; then + # Two files given, compare the two uncompressing as needed + check_suffix "$1" files filt + check_suffix "$2" files2 filt2 + if [ -z "$filt" -a -z "$filt2" ]; then + $prog $flags -- "$1" "$2" + elif [ -z "$filt" -a -n "$filt2" -a "$1" != "-" ]; then + $filt2 -- "$2" | $prog $flags -- "$1" - + elif [ -n "$filt" -a -z "$filt2" -a "$2" != "-" ]; then + $filt -- "$1" | $prog $flags -- - "$2" + else + tmp=`mktemp -t z$prog.XXXXXXXXXX` || exit 1 + trap "rm -f $tmp" 0 1 2 3 13 15 + ${filt2:-cat} -- "$2" > $tmp || exit $? + ${filt:-cat} -- "$1" | $prog $flags -- - "$tmp" + fi + status=$? +else + echo "$USAGE" 1>&2 + exit 1 +fi + +exit $status diff --git a/file_cmds/gzip/zdiff.1 b/file_cmds/gzip/zdiff.1 new file mode 100644 index 00000000..2e0ebea4 --- /dev/null +++ b/file_cmds/gzip/zdiff.1 @@ -0,0 +1,142 @@ +.\" $NetBSD: zdiff.1,v 1.5 2010/04/14 19:52:05 wiz Exp $ +.\" $OpenBSD: zdiff.1,v 1.2 2003/07/13 17:39:14 millert Exp $ +.\" +.\" Copyright (c) 2003 Todd C. Miller +.\" Copyright (c) 2010 Joerg Sonnenberger +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Sponsored in part by the Defense Advanced Research Projects +.\" Agency (DARPA) and Air Force Research Laboratory, Air Force +.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. +.\" +.\" $FreeBSD: src/usr.bin/gzip/zdiff.1,v 1.2 2011/05/23 09:02:44 delphij Exp $ +.Dd May 23, 2011 +.Dt ZDIFF 1 +.Os +.Sh NAME +.Nm zcmp , +.Nm zdiff +.Nd compare compressed files +.Sh SYNOPSIS +.Nm zcmp +.Op Ar options +.Ar file +.Op Ar file2 +.Nm zdiff +.Op Ar options +.Ar file +.Op Ar file2 +.Sh DESCRIPTION +.Nm zcmp +and +.Nm zdiff +are filters that invoke +.Xr cmp 1 +or +.Xr diff 1 +respectively to compare compressed files. +Any +.Ar options +that are specified are passed to +.Xr cmp 1 +or +.Xr diff 1 . +.Pp +If only +.Ar file1 +is specified, it is compared against a file with the same name, but +with the extension removed. +When both +.Ar file1 +or +.Ar file2 +are specified, either file may be compressed. +.Pp +Extensions handled by +.Xr gzip 1 : +.Bl -bullet -compact +.It +z, Z, +.It +gz, +.It +taz, +.It +tgz. +.El +.Pp +Extensions handled by +.Xr bzip2 1 : +.Bl -bullet -compact +.It +bz, +.It +bz2, +.It +tbz, +.It +tbz2. +.El +.Pp +Extensions handled by +.Xr xz 1 : +.Bl -bullet -compact +.It +lzma, +.It +xz, +.It +tlz, +.It +txz. +.El +.Sh ENVIRONMENT +.Bl -tag -width "TMPDIR" +.It Ev TMPDIR +Directory in which to place temporary files. +If unset, +.Pa /tmp +is used. +.El +.Sh FILES +.Bl -tag -width "/tmp/zdiff.XXXXXXXXXX" -compact +.It Pa /tmp/zcmp.XXXXXXXXXX +Temporary file for +.Nm zcmp . +.It Pa /tmp/zdiff.XXXXXXXXXX +Temporary file for +.Nm zdiff . +.El +.Sh SEE ALSO +.Xr bzip2 1 , +.Xr cmp 1 , +.Xr diff 1 , +.Xr gzip 1 , +.Xr xz 1 +.Sh CAVEATS +.Nm zcmp +and +.Nm zdiff +rely solely on the file extension to determine what is, or is not, +a compressed file. +Consequently, the following are not supported as arguments: +.Bl -dash +.It +directories +.It +device special files +.It +filenames indicating the standard input +.Pq Dq \- +.El diff --git a/file_cmds/gzip/zforce b/file_cmds/gzip/zforce new file mode 100644 index 00000000..243cbdf5 --- /dev/null +++ b/file_cmds/gzip/zforce @@ -0,0 +1,55 @@ +#!/bin/sh - +# +# $NetBSD: zforce,v 1.2 2003/12/28 12:43:43 wiz Exp $ +# $OpenBSD: zforce,v 1.2 2003/08/05 18:22:17 deraadt Exp $ +# +#- +# Copyright (c) 2003 Otto Moerbeek +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# $FreeBSD: src/usr.bin/gzip/zforce,v 1.1 2007/01/26 10:19:07 delphij Exp $ +prog=`basename $0` +USAGE="usage: $prog file ..." +if test $# -eq 0; then + echo $USAGE + exit 1 +fi + +ret=0 + +while test $# -ne 0; do + case "$1" in + *[._-]gz) + shift + ;; + *.t[ag]z) + shift + ;; + *) + if file "$1" | + grep -q "gzip compressed data" 2> /dev/null + then + n="$1".gz + if mv "$1" "$n" 2> /dev/null; then + echo "$1" -- renamed to "$n" + else + ret=1 + echo $prog: cannot rename "$1" to "$n" + fi + fi + shift + ;; + esac +done +exit $ret diff --git a/file_cmds/gzip/zforce.1 b/file_cmds/gzip/zforce.1 new file mode 100644 index 00000000..933d6810 --- /dev/null +++ b/file_cmds/gzip/zforce.1 @@ -0,0 +1,53 @@ +.\" $NetBSD: zforce.1,v 1.2 2003/12/28 12:43:43 wiz Exp $ +.\" $OpenBSD: zforce.1,v 1.1 2003/07/29 11:50:09 otto Exp $ +.\" +.\" Copyright (c) 2003 Otto Moerbeek +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD: src/usr.bin/gzip/zforce.1,v 1.1 2007/01/26 10:19:07 delphij Exp $ +.Dd January 26, 2007 +.Dt ZFORCE 1 +.Os +.Sh NAME +.Nm zforce +.Nd force gzip files to have a .gz suffix +.Sh SYNOPSIS +.Nm zforce +.Ar +.Sh DESCRIPTION +The +.Nm +utility renames +.Xr gzip 1 +files to have a +.Sq .gz +suffix, so that +.Xr gzip 1 +will not compress them twice. +This can be useful if file names were truncated during a file transfer. +Files that have an existing +.Sq .gz , +.Sq -gz , +.Sq _gz , +.Sq .tgz +or +.Sq .taz +suffix, or that have not been compressed by +.Xr gzip 1 , +are ignored. +.Sh SEE ALSO +.Xr gzip 1 +.Sh CAVEATS +.Nm +overwrites existing files without warning. diff --git a/file_cmds/gzip/zmore b/file_cmds/gzip/zmore new file mode 100644 index 00000000..dbdbd3f8 --- /dev/null +++ b/file_cmds/gzip/zmore @@ -0,0 +1,82 @@ +#!/bin/sh - +# +# $NetBSD: zmore,v 1.5 2013/12/06 13:33:15 pettai Exp $ +# +# $OpenBSD: zmore,v 1.6 2008/08/20 09:22:02 mpf Exp $ +# +#- +# Copyright (c) 2003 Todd C. Miller +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# Sponsored in part by the Defense Advanced Research Projects +# Agency (DARPA) and Air Force Research Laboratory, Air Force +# Materiel Command, USAF, under agreement number F39502-99-1-0512. +# +# $FreeBSD: head/usr.bin/gzip/zmore 273507 2014-10-23 01:22:29Z delphij $ + +# Pull out any command line flags so we can pass them to more/less +flags= +while test $# -ne 0; do + case "$1" in + --) + shift + break + ;; + -*) + flags="$flags $1" + shift + ;; + *) + break + ;; + esac +done + +if [ `basename $0` = "zless" ] ; then + pager=${PAGER-less} +else + pager=${PAGER-more} +fi + +# No files means read from stdin +if [ $# -eq 0 ]; then + gzip -cdfq 2>&1 | $pager $flags + exit 0 +fi + +oterm=`stty -g 2>/dev/null` +while test $# -ne 0; do + gzip -cdfq "$1" 2>&1 | $pager $flags + prev="$1" + shift + if tty -s && test -n "$oterm" -a $# -gt 0; then + #echo -n "--More--(Next file: $1)" + echo -n "$prev (END) - Next: $1 " + trap "stty $oterm 2>/dev/null" 0 1 2 3 13 15 + stty cbreak -echo 2>/dev/null + REPLY=`dd bs=1 count=1 2>/dev/null` + stty $oterm 2>/dev/null + trap - 0 1 2 3 13 15 + echo + case "$REPLY" in + s) + shift + ;; + e|q) + break + ;; + esac + fi +done +exit 0 diff --git a/file_cmds/gzip/zmore.1 b/file_cmds/gzip/zmore.1 new file mode 100644 index 00000000..4723f224 --- /dev/null +++ b/file_cmds/gzip/zmore.1 @@ -0,0 +1,110 @@ +.\" $NetBSD: zmore.1,v 1.4 2013/11/12 21:58:37 pettai Exp $ +.\" $OpenBSD: zmore.1,v 1.10 2009/08/16 09:41:08 sobrado Exp $ +.\" +.\" Copyright (c) 2003 Todd C. Miller +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" Sponsored in part by the Defense Advanced Research Projects +.\" Agency (DARPA) and Air Force Research Laboratory, Air Force +.\" Materiel Command, USAF, under agreement number F39502-99-1-0512. +.\" +.\" $FreeBSD: head/usr.bin/gzip/zmore.1 273507 2014-10-23 01:22:29Z delphij $ +.Dd October 22, 2014 +.Dt ZMORE 1 +.Os +.Sh NAME +.Nm zmore , +.Nm zless +.Nd view compressed files +.Sh SYNOPSIS +.Nm zmore +.Op Ar flags +.Op Ar +.Nm zless +.Op Ar flags +.Op Ar +.Sh DESCRIPTION +.Nm +is a filter that allows the viewing of files compressed with Lempel-Ziv +encoding. +Such files generally have a +.Dq Z +or +.Dq gz +extension (both the +.Xr compress 1 +and +.Xr gzip 1 +formats are supported). +Any +.Ar flags +that are specified are passed to the user's preferred +.Ev PAGER +(which is +.Pa /usr/bin/more +by default). +.Pp +.Nm zless +is equivalent to +.Nm zmore +but uses +.Xr less 1 +as a pager instead of +.Xr more 1 . +.Pp +When multiple files are specified, +.Nm +will pause at the end of each file and present the following prompt to the user: +.Bd -literal -offset indent +prev_file (END) - Next: next_file +.Ed +.Pp +Where +.Sy prev_file +is the file that was just displayed and +.Sy next_file +is the next file to be displayed. +The following keys are recognized at the prompt: +.Bl -tag -width "e or q" -offset indent +.It Ic e No or Ic q +quit +.Nm zmore . +.It Ic s +skip the next file (or exit if the next file is the last). +.El +.Pp +If no files are specified, +.Nm +will read from the standard input. +In this mode +.Nm +will assume +.Xr gzip 1 +style compression since there is no suffix on which to make a decision. +.Sh ENVIRONMENT +.Bl -tag -width "PAGER" +.It Ev PAGER +Program used to display files. +If unset, +.Pa /usr/bin/more +is used +.Pq Nm zmore +or +.Pa /usr/bin/less +.Pq Nm zless . +.El +.Sh SEE ALSO +.Xr compress 1 , +.Xr less 1 , +.Xr more 1 diff --git a/file_cmds/gzip/znew b/file_cmds/gzip/znew new file mode 100644 index 00000000..4a84f580 --- /dev/null +++ b/file_cmds/gzip/znew @@ -0,0 +1,137 @@ +#!/bin/sh - +# +# $NetBSD: znew,v 1.3 2008/04/27 09:07:13 nakayama Exp $ +# $OpenBSD: znew,v 1.2 2003/08/05 18:22:17 deraadt Exp $ +# +#- +# Copyright (c) 2003 Otto Moerbeek +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +# +# $FreeBSD: src/usr.bin/gzip/znew,v 1.3 2008/06/30 23:53:15 delphij Exp $ + +# Return 0 if the first arg file size is smaller than the second, 1 otherwise. +smaller () { + a=`du -k "$1" | awk '{ print $1 }'` + b=`du -k "$2" | awk '{ print $1 }'` + test $a -lt $b +} + +# Check gzip integrity if the -t flag is specified +checkfile () { + if test $tflag -eq 1; then + gzip -qt < "$1" + fi +} + +# Decompress a file and then gzip it +process () { + prefix="${1%.Z}" + filez="$prefix".Z + filegz="$prefix".gz + + if test ! -e "$filez"; then + echo "$prog: $filez does not exist" + return 1 + fi + if test ! -f "$filez"; then + echo "$prog: $filez is not a regular file" + return 1 + fi + if test -e "$filegz" -a $fflag -eq 0; then + echo "$prog: $filegz already exists" + return 1 + fi + + tmp=`mktemp /tmp/znewXXXXXXXXXX` || { + echo "$prog: cannot create tmp file" + return 1 + } + trap 'rm -f "$tmp"; exit 1' HUP INT QUIT PIPE TERM + + # Do the actual work, producing a file "$tmp" + if uncompress -f -c < "$filez" | gzip -f -c $gzipflags > "$tmp"; then + if test $kflag -eq 1 && smaller "$filez" "$tmp"; then + echo -n "$prog: $filez is smaller than $filegz" + echo "; keeping it" + rm -f "$tmp" + return 0 + fi + if ! checkfile "$tmp"; then + echo "$prog: integrity check of $tmp failed" + rm -f "$tmp" + return 1; + fi + + # Try to keep the mode of the original file + if ! cp -fp "$filez" "$filegz"; then + echo "$prog: warning: could not keep mode of $filez" + fi + if ! cp "$tmp" "$filegz" 2> /dev/null; then + echo "$prog: warning: could not keep mode of $filez" + if ! cp -f "$tmp" "$filegz" 2> /dev/null; then + echo "$prog: could not copy $tmp to $filegz" + rm -f "$filegz" "$tmp" + return 1 + fi + fi + if ! touch -fr "$filez" "$filegz"; then + echo -n "$prog: warning: could not keep timestamp of " + echo "$filez" + fi + rm -f "$filez" "$tmp" + else + echo "$prog: failed to process $filez" + rm -f "$tmp" + return 1 + fi +} + +prog=`basename "$0"` +usage="usage: $prog [-ftv9K] file ..." + +fflag=0 +tflag=0 +kflag=0 +gzipflags= + +# -P flag is recognized to maintain compatibility, but ignored. Pipe mode is +# always used +while getopts :ftv9PK i; do + case $i in + f) fflag=1;; + t) tflag=1;; + v) gzipflags="-v $gzipflags";; + 9) gzipflags="-9 $gzipflags";; + P) ;; + K) kflag=1;; + \?) echo "$usage"; exit 1;; + esac +done + +shift $((OPTIND - 1)) + +if test $# -eq 0; then + echo "$usage" + exit 1 +fi + +rc=0 + +while test $# -ne 0; do + if ! process "$1"; then + rc=$? + fi + shift +done +exit $rc diff --git a/file_cmds/gzip/znew.1 b/file_cmds/gzip/znew.1 new file mode 100644 index 00000000..61060ca0 --- /dev/null +++ b/file_cmds/gzip/znew.1 @@ -0,0 +1,71 @@ +.\" $NetBSD: znew.1,v 1.2 2003/12/28 12:43:43 wiz Exp $ +.\" $OpenBSD: znew.1,v 1.1 2003/08/02 20:52:50 otto Exp $ +.\" +.\" Copyright (c) 2003 Otto Moerbeek +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.\" $FreeBSD: src/usr.bin/gzip/znew.1,v 1.1 2007/01/26 10:19:07 delphij Exp $ +.Dd January 26, 2007 +.Dt ZNEW 1 +.Os +.Sh NAME +.Nm znew +.Nd convert compressed files to gzipped files +.Sh SYNOPSIS +.Nm +.Op Fl ftv9K +.Ar +.Sh DESCRIPTION +The +.Nm +utility uncompresses files compressed by +.Xr compress 1 +and recompresses them with +.Xr gzip 1 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +Overwrite existing +.Sq .gz +files. +Unless this option is specified, +.Nm +refuses to overwrite existing files. +.It Fl t +Test integrity of the gzipped file before deleting the original file. +If the integrity check fails, the original +.Sq .Z +file is not removed. +.It Fl v +Print a report specifying the achieved compression ratios. +.It Fl 9 +Use the -9 mode of +.Xr gzip 1 , +achieving better compression at the cost of slower execution. +.It Fl K +Keep the original +.Sq .Z +file if it uses less disk blocks than the gzipped one. +A disk block is 1024 bytes. +.El +.Sh SEE ALSO +.Xr gzip 1 +.Sh CAVEATS +The +.Nm +utility tries to maintain the file mode of the original file. +If the original file is not writable, it is not able to do that and +.Nm +will print a warning. diff --git a/file_cmds/gzip/zuncompress.c b/file_cmds/gzip/zuncompress.c new file mode 100644 index 00000000..06df5976 --- /dev/null +++ b/file_cmds/gzip/zuncompress.c @@ -0,0 +1,396 @@ +/* $NetBSD: zuncompress.c,v 1.11 2011/08/16 13:55:02 joerg Exp $ */ + +/*- + * Copyright (c) 1985, 1986, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: NetBSD: zopen.c,v 1.8 2003/08/07 11:13:29 agc Exp + * $FreeBSD: src/usr.bin/gzip/zuncompress.c,v 1.7 2012/10/19 14:49:42 ed Exp $ + */ + +/* This file is #included by gzip.c */ + +static int zread(void *, char *, int); + +#define tab_prefixof(i) (zs->zs_codetab[i]) +#define tab_suffixof(i) ((char_type *)(zs->zs_htab))[i] +#define de_stack ((char_type *)&tab_suffixof(1 << BITS)) + +#define BITS 16 /* Default bits. */ +#define HSIZE 69001 /* 95% occupancy */ /* XXX may not need HSIZE */ +#define BIT_MASK 0x1f /* Defines for third byte of header. */ +#define BLOCK_MASK 0x80 +#define CHECK_GAP 10000 /* Ratio check interval. */ +#define BUFSIZE (64 * 1024) + +/* + * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + * a fourth header byte (for expansion). + */ +#define INIT_BITS 9 /* Initial number of bits/code. */ + +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* First free entry. */ +#define CLEAR 256 /* Table clear output code. */ + + +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + +typedef long code_int; +typedef long count_int; +typedef u_char char_type; + +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ + +static char_type rmask[9] = + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static off_t total_compressed_bytes; +static size_t compressed_prelen; +static char *compressed_pre; + +struct s_zstate { + FILE *zs_fp; /* File stream for I/O */ + char zs_mode; /* r or w */ + enum { + S_START, S_MIDDLE, S_EOF + } zs_state; /* State of computation */ + int zs_n_bits; /* Number of bits/code. */ + int zs_maxbits; /* User settable max # bits/code. */ + code_int zs_maxcode; /* Maximum code, given n_bits. */ + code_int zs_maxmaxcode; /* Should NEVER generate this code. */ + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; + code_int zs_hsize; /* For dynamic table sizing. */ + code_int zs_free_ent; /* First unused entry. */ + /* + * Block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ + int zs_block_compress; + int zs_clear_flg; + long zs_ratio; + count_int zs_checkpoint; + int zs_offset; + long zs_in_count; /* Length of input. */ + long zs_bytes_out; /* Length of compressed output. */ + long zs_out_count; /* # of codes output (for debugging). */ + char_type zs_buf[BITS]; + union { + struct { + long zs_fcode; + code_int zs_ent; + code_int zs_hsize_reg; + int zs_hshift; + } w; /* Write parameters */ + struct { + char_type *zs_stackp; + int zs_finchar; + code_int zs_code, zs_oldcode, zs_incode; + int zs_roffset, zs_size; + char_type zs_gbuf[BITS]; + } r; /* Read parameters */ + } u; +}; + +static code_int getcode(struct s_zstate *zs); + +static off_t +zuncompress(FILE *in, FILE *out, char *pre, size_t prelen, + off_t *compressed_bytes) +{ + off_t bin, bout = 0; + char *buf; + + buf = malloc(BUFSIZE); + if (buf == NULL) + return -1; + + /* XXX */ + compressed_prelen = prelen; + if (prelen != 0) + compressed_pre = pre; + else + compressed_pre = NULL; + + while ((bin = fread(buf, 1, BUFSIZE, in)) != 0) { + if (tflag == 0 && (off_t)fwrite(buf, 1, bin, out) != bin) { + free(buf); + return -1; + } + bout += bin; + } + + if (compressed_bytes) + *compressed_bytes = total_compressed_bytes; + + free(buf); + return bout; +} + +static int +zclose(void *zs) +{ + free(zs); + /* We leave the caller to close the fd passed to zdopen() */ + return 0; +} + +FILE * +zdopen(int fd) +{ + struct s_zstate *zs; + + if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL) + return (NULL); + + zs->zs_state = S_START; + + /* XXX we can get rid of some of these */ + zs->zs_hsize = HSIZE; /* For dynamic table sizing. */ + zs->zs_free_ent = 0; /* First unused entry. */ + zs->zs_block_compress = BLOCK_MASK; + zs->zs_clear_flg = 0; /* XXX we calloc()'d this structure why = 0? */ + zs->zs_ratio = 0; + zs->zs_checkpoint = CHECK_GAP; + zs->zs_in_count = 1; /* Length of input. */ + zs->zs_out_count = 0; /* # of codes output (for debugging). */ + zs->u.r.zs_roffset = 0; + zs->u.r.zs_size = 0; + + /* + * Layering compress on top of stdio in order to provide buffering, + * and ensure that reads and write work with the data specified. + */ + if ((zs->zs_fp = fdopen(fd, "r")) == NULL) { + free(zs); + return NULL; + } + + return funopen(zs, zread, NULL, NULL, zclose); +} + +/* + * Decompress read. This routine adapts to the codes in the file building + * the "string" table on-the-fly; requiring no table to be stored in the + * compressed file. The tables used herein are shared with those of the + * compress() routine. See the definitions above. + */ +static int +zread(void *cookie, char *rbp, int num) +{ + u_int count, i; + struct s_zstate *zs; + u_char *bp, header[3]; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)rbp; + switch (zs->zs_state) { + case S_START: + zs->zs_state = S_MIDDLE; + break; + case S_MIDDLE: + goto middle; + case S_EOF: + goto eof; + } + + /* Check the magic number */ + for (i = 0; i < 3 && compressed_prelen; i++, compressed_prelen--) + header[i] = *compressed_pre++; + + if (fread(header + i, 1, sizeof(header) - i, zs->zs_fp) != + sizeof(header) - i || + memcmp(header, magic_header, sizeof(magic_header)) != 0) { + errno = EFTYPE; + return (-1); + } + total_compressed_bytes = 0; + zs->zs_maxbits = header[2]; /* Set -b from file. */ + zs->zs_block_compress = zs->zs_maxbits & BLOCK_MASK; + zs->zs_maxbits &= BIT_MASK; + zs->zs_maxmaxcode = 1L << zs->zs_maxbits; + if (zs->zs_maxbits > BITS || zs->zs_maxbits < 12) { + errno = EFTYPE; + return (-1); + } + /* As above, initialize the first 256 entries in the table. */ + zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS); + for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0; zs->u.r.zs_code--) { + tab_prefixof(zs->u.r.zs_code) = 0; + tab_suffixof(zs->u.r.zs_code) = (char_type) zs->u.r.zs_code; + } + zs->zs_free_ent = zs->zs_block_compress ? FIRST : 256; + + zs->u.r.zs_oldcode = -1; + zs->u.r.zs_stackp = de_stack; + + while ((zs->u.r.zs_code = getcode(zs)) > -1) { + + if ((zs->u.r.zs_code == CLEAR) && zs->zs_block_compress) { + for (zs->u.r.zs_code = 255; zs->u.r.zs_code >= 0; + zs->u.r.zs_code--) + tab_prefixof(zs->u.r.zs_code) = 0; + zs->zs_clear_flg = 1; + zs->zs_free_ent = FIRST; + zs->u.r.zs_oldcode = -1; + continue; + } + zs->u.r.zs_incode = zs->u.r.zs_code; + + /* Special case for KwKwK string. */ + if (zs->u.r.zs_code >= zs->zs_free_ent) { + if (zs->u.r.zs_code > zs->zs_free_ent || + zs->u.r.zs_oldcode == -1) { + /* Bad stream. */ + errno = EINVAL; + return (-1); + } + *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar; + zs->u.r.zs_code = zs->u.r.zs_oldcode; + } + /* + * The above condition ensures that code < free_ent. + * The construction of tab_prefixof in turn guarantees that + * each iteration decreases code and therefore stack usage is + * bound by 1 << BITS - 256. + */ + + /* Generate output characters in reverse order. */ + while (zs->u.r.zs_code >= 256) { + *zs->u.r.zs_stackp++ = tab_suffixof(zs->u.r.zs_code); + zs->u.r.zs_code = tab_prefixof(zs->u.r.zs_code); + } + *zs->u.r.zs_stackp++ = zs->u.r.zs_finchar = tab_suffixof(zs->u.r.zs_code); + + /* And put them out in forward order. */ +middle: do { + if (count-- == 0) + return (num); + *bp++ = *--zs->u.r.zs_stackp; + } while (zs->u.r.zs_stackp > de_stack); + + /* Generate the new entry. */ + if ((zs->u.r.zs_code = zs->zs_free_ent) < zs->zs_maxmaxcode && + zs->u.r.zs_oldcode != -1) { + tab_prefixof(zs->u.r.zs_code) = (u_short) zs->u.r.zs_oldcode; + tab_suffixof(zs->u.r.zs_code) = zs->u.r.zs_finchar; + zs->zs_free_ent = zs->u.r.zs_code + 1; + } + + /* Remember previous code. */ + zs->u.r.zs_oldcode = zs->u.r.zs_incode; + } + zs->zs_state = S_EOF; +eof: return (num - count); +} + +/*- + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ +static code_int +getcode(struct s_zstate *zs) +{ + code_int gcode; + int r_off, bits, i; + char_type *bp; + + bp = zs->u.r.zs_gbuf; + if (zs->zs_clear_flg > 0 || zs->u.r.zs_roffset >= zs->u.r.zs_size || + zs->zs_free_ent > zs->zs_maxcode) { + /* + * If the next entry will be too big for the current gcode + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if (zs->zs_free_ent > zs->zs_maxcode) { + zs->zs_n_bits++; + if (zs->zs_n_bits == zs->zs_maxbits) /* Won't get any bigger now. */ + zs->zs_maxcode = zs->zs_maxmaxcode; + else + zs->zs_maxcode = MAXCODE(zs->zs_n_bits); + } + if (zs->zs_clear_flg > 0) { + zs->zs_maxcode = MAXCODE(zs->zs_n_bits = INIT_BITS); + zs->zs_clear_flg = 0; + } + /* XXX */ + for (i = 0; i < zs->zs_n_bits && compressed_prelen; i++, compressed_prelen--) + zs->u.r.zs_gbuf[i] = *compressed_pre++; + zs->u.r.zs_size = fread(zs->u.r.zs_gbuf + i, 1, zs->zs_n_bits - i, zs->zs_fp); + zs->u.r.zs_size += i; + if (zs->u.r.zs_size <= 0) /* End of file. */ + return (-1); + zs->u.r.zs_roffset = 0; + + total_compressed_bytes += zs->u.r.zs_size; + + /* Round size down to integral number of codes. */ + zs->u.r.zs_size = (zs->u.r.zs_size << 3) - (zs->zs_n_bits - 1); + } + r_off = zs->u.r.zs_roffset; + bits = zs->zs_n_bits; + + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + + /* Get first part (low order bits). */ + gcode = (*bp++ >> r_off); + bits -= (8 - r_off); + r_off = 8 - r_off; /* Now, roffset into gcode word. */ + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + gcode |= *bp++ << r_off; + r_off += 8; + bits -= 8; + } + + /* High order bits. */ + gcode |= (*bp & rmask[bits]) << r_off; + zs->u.r.zs_roffset += zs->zs_n_bits; + + return (gcode); +} + diff --git a/file_cmds/install/install.1 b/file_cmds/install/install.1 new file mode 100644 index 00000000..76f36978 --- /dev/null +++ b/file_cmds/install/install.1 @@ -0,0 +1,253 @@ +.\" Copyright (c) 1987, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" From: @(#)install.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/xinstall/install.1,v 1.21 2001/05/30 09:45:47 ru Exp $ +.\" +.Dd May 7, 2001 +.Dt INSTALL 1 +.Os +.Sh NAME +.Nm install +.Nd install binaries +.Sh SYNOPSIS +.Nm install +.Op Fl bCcMpSsv +.Op Fl B Ar suffix +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl m Ar mode +.Op Fl o Ar owner +.Ar file1 file2 +.Nm install +.Op Fl bCcMpSsv +.Op Fl B Ar suffix +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl m Ar mode +.Op Fl o Ar owner +.Ar file1 ... fileN directory +.Nm install +.Fl d +.Op Fl v +.Op Fl g Ar group +.Op Fl m Ar mode +.Op Fl o Ar owner +.Ar directory ... +.Sh DESCRIPTION +The file(s) are copied +to the target file or directory. +If the destination is a directory, then the +.Ar file +is copied into +.Ar directory +with its original filename. +If the target file already exists, it is +either renamed to +.Ar file Ns Pa .old +if the +.Fl b +option is given +or overwritten +if permissions allow. +An alternate backup suffix may be specified via the +.Fl B +option's argument. +.Pp +The options are as follows: +.Bl -tag -width indent +.\" ========== +.It Fl B Ar suffix +Use +.Ar suffix +as the backup suffix if +.Fl b +is given. +.\" ========== +.It Fl b +Back up any existing files before overwriting them by renaming +them to +.Ar file Ns Pa .old . +See +.Fl B +for specifying a different backup suffix. +.\" ========== +.It Fl C +Copy the file. +If the target file already exists and the files are the same, +then don't change the modification time of the target. +.\" ========== +.It Fl c +Copy the file. +This is actually the default. +The +.Fl c +option is only included for backwards compatibility. +.\" ========== +.It Fl d +Create directories. +Missing parent directories are created as required. +.\" ========== +.It Fl f +Specify the target's file flags; see +.Xr chflags 1 +for a list of possible flags and their meanings. +.\" ========== +.It Fl g +Specify a group. +A numeric GID is allowed. +.\" ========== +.It Fl M +Disable all use of +.Xr mmap 2 . +.\" ========== +.It Fl m +Specify an alternate mode. +The default mode is set to rwxr-xr-x (0755). +The specified mode may be either an octal or symbolic value; see +.Xr chmod 1 +for a description of possible mode values. +.\" ========== +.It Fl o +Specify an owner. +A numeric UID is allowed. +.\" ========== +.It Fl p +Preserve the modification time. +Copy the file, as if the +.Fl C +(compare and copy) option is specified, +except if the target file doesn't already exist or is different, +then preserve the modification time of the file. +.\" ========== +.It Fl S +Safe copy. +Normally, +.Nm install +unlinks an existing target before installing the new file. +With the +.Fl S +flag a temporary file is used and then renamed to be +the target. +The reason this is safer is that if the copy or +rename fails, the existing target is left untouched. +.\" ========== +.It Fl s +.Nm install +exec's the command +.Xr strip 1 +to strip binaries so that +.Nm install +can be portable over a large +number of systems and binary types. +.\" ========== +.It Fl v +Causes +.Nm install +to show when +.Fl C +actually installs something. +.El +.Pp +By default, +.Nm install +preserves all file flags, with the exception of the +.Dq nodump +flag. +.Pp +The +.Nm install +utility attempts to prevent moving a file onto itself. +.Pp +Installing +.Pa /dev/null +creates an empty file. +.Sh DIAGNOSTICS +The +.Nm install +utility exits 0 on success, and 1 otherwise. +.Sh FILES +.Bl -tag -width INS@XXXX -compact +.It Pa INS@XXXX +If either +.Fl S +option is specified, or the +.Fl C +or +.Fl p +option is used in conjuction with the +.Fl s +option, temporary files named +.Pa INS@XXXX , +where +.Pa XXXX +is decided by +.Xr mkstemp 3 , +are created in the target directory. +.El +.Sh COMPATIBILITY +Historically +.Nm install +moved files by default. +The default was changed to copy in +.Fx 4.4 . +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr mv 1 , +.Xr strip 1 , +.Xr mmap 2 , +.Xr chown 8 +.Sh HISTORY +The +.Nm install +utility appeared in +.Bx 4.2 . +.Sh BUGS +Temporary files may be left in the target directory if +.Nm install +exits abnormally. +.Pp +File flags cannot be set by +.Xr fchflags 2 +over a NFS file system. Other file systems do not have a concept of flags. +.Nm install +will only warn when flags could not be set on a file system +that does not support them. +.Pp +.Nm install +with +.Fl v +falsely says a file is copied when +.Fl C +snaps hard links. diff --git a/file_cmds/install/pathnames.h b/file_cmds/install/pathnames.h new file mode 100644 index 00000000..853b83c0 --- /dev/null +++ b/file_cmds/install/pathnames.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#define _PATH_STRIP "/usr/bin/strip" diff --git a/file_cmds/install/xinstall.c b/file_cmds/install/xinstall.c new file mode 100644 index 00000000..05b65009 --- /dev/null +++ b/file_cmds/install/xinstall.c @@ -0,0 +1,819 @@ +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#endif +__used static const char rcsid[] = + "$FreeBSD: src/usr.bin/xinstall/xinstall.c,v 1.43 2001/05/30 07:08:49 ru Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif /* __APPLE__ */ + +#include "pathnames.h" + +/* Bootstrap aid - this doesn't exist in most older releases */ +#ifndef MAP_FAILED +#define MAP_FAILED ((void *)-1) /* from */ +#endif + +#define DIRECTORY 0x01 /* Tell install it's a directory. */ +#define SETFLAGS 0x02 /* Tell install to set flags. */ +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) +#define BACKUP_SUFFIX ".old" + +struct passwd *pp; +struct group *gp; +gid_t gid; +uid_t uid; +int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose; +mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; +char *suffix = BACKUP_SUFFIX; + +void copy __P((int, char *, int, char *, off_t)); +int compare __P((int, const char *, size_t, int, const char *, size_t)); +int create_newfile __P((char *, int, struct stat *)); +int create_tempfile __P((char *, char *, size_t)); +void install __P((char *, char *, u_long, u_int)); +void install_dir __P((char *)); +u_long numeric_id __P((char *, char *)); +void strip __P((char *)); +int trymmap __P((int)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat from_sb, to_sb; + mode_t *set; + u_long fset = 0; + int ch, no_target; + u_int iflags; + char *flags, *group, *owner, *to_name; + + iflags = 0; + group = owner = NULL; + while ((ch = getopt(argc, argv, "B:bCcdf:g:Mm:o:pSsv")) != -1) + switch((char)ch) { + case 'B': + suffix = optarg; + /* FALLTHROUGH */ + case 'b': + dobackup = 1; + break; + case 'C': + docompare = 1; + break; + case 'c': + /* For backwards compatibility. */ + break; + case 'd': + dodir = 1; + break; + case 'f': + flags = optarg; + if (strtofflags(&flags, &fset, NULL)) + errx(EX_USAGE, "%s: invalid flag", flags); + iflags |= SETFLAGS; + break; + case 'g': + group = optarg; + break; + case 'M': + nommap = 1; + break; + case 'm': + if (!(set = setmode(optarg))) + errx(EX_USAGE, "invalid file mode: %s", + optarg); + mode = getmode(set, 0); + free(set); + break; + case 'o': + owner = optarg; + break; + case 'p': + docompare = dopreserve = 1; + break; + case 'S': + safecopy = 1; + break; + case 's': + dostrip = 1; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* some options make no sense when creating directories */ + if ((safecopy || dostrip) && dodir) + usage(); + + /* + * Older versions allowed -d -C combo. Issue a warning + * for now, but turn this into an error before 4.5-RELEASE. + */ + if (docompare && dodir) + warnx("the -d and -C options may not be specified together"); + + /* must have at least two arguments, except when creating directories */ + if (argc < 2 && !dodir) + usage(); + + /* need to make a temp copy so we can compare stripped version */ + if (docompare && dostrip) + safecopy = 1; + + /* get group and owner id's */ + if (group != NULL) { + if ((gp = getgrnam(group)) != NULL) + gid = gp->gr_gid; + else + gid = (uid_t)numeric_id(group, "group"); + } else + gid = (gid_t)-1; + + if (owner != NULL) { + if ((pp = getpwnam(owner)) != NULL) + uid = pp->pw_uid; + else + uid = (uid_t)numeric_id(owner, "user"); + } else + uid = (uid_t)-1; + + if (dodir) { + for (; *argv != NULL; ++argv) + install_dir(*argv); + exit(EX_OK); + /* NOTREACHED */ + } + + no_target = stat(to_name = argv[argc - 1], &to_sb); + if (!no_target && S_ISDIR(to_sb.st_mode)) { + for (; *argv != to_name; ++argv) + install(*argv, to_name, fset, iflags | DIRECTORY); + exit(EX_OK); + /* NOTREACHED */ + } + + /* can't do file1 file2 directory/file */ + if (argc != 2) + usage(); + + if (!no_target) { + if (stat(*argv, &from_sb)) + err(EX_OSERR, "%s", *argv); + if (!S_ISREG(to_sb.st_mode)) { + errno = EFTYPE; + err(EX_OSERR, "%s", to_name); + } + if (to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(EX_USAGE, + "%s and %s are the same file", *argv, to_name); + } + install(*argv, to_name, fset, iflags); + exit(EX_OK); + /* NOTREACHED */ +} + +u_long +numeric_id(name, type) + char *name, *type; +{ + u_long val; + char *ep; + + /* + * XXX + * We know that uid_t's and gid_t's are unsigned longs. + */ + errno = 0; + val = strtoul(name, &ep, 10); + if (errno) + err(EX_NOUSER, "%s", name); + if (*ep != '\0') + errx(EX_NOUSER, "unknown %s %s", type, name); + return (val); +} + +/* + * install -- + * build a path name and install the file + */ +void +install(from_name, to_name, fset, flags) + char *from_name, *to_name; + u_long fset; + u_int flags; +{ + struct stat from_sb, temp_sb, to_sb; + struct utimbuf utb; + int devnull, files_match, from_fd=0, serrno, target; + int tempcopy, temp_fd, to_fd=0; + char backup[MAXPATHLEN], *p, pathbuf[MAXPATHLEN], tempfile[MAXPATHLEN]; + + files_match = 0; + + /* If try to install NULL file to a directory, fails. */ + if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { + if (stat(from_name, &from_sb)) + err(EX_OSERR, "%s", from_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 { + devnull = 1; + } + + target = stat(to_name, &to_sb) == 0; + + /* Only install to regular files. */ + if (target && !S_ISREG(to_sb.st_mode)) { + errno = EFTYPE; + warn("%s", to_name); + return; + } + + /* Only copy safe if the target exists. */ + tempcopy = safecopy && target; + + if (!devnull && (from_fd = open(from_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", from_name); + + /* If we don't strip, we can compare first. */ + if (docompare && !dostrip && target) { + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + if (devnull) + files_match = to_sb.st_size == 0; + else + files_match = !(compare(from_fd, from_name, + (size_t)from_sb.st_size, to_fd, + to_name, (size_t)to_sb.st_size)); + + /* Close "to" file unless we match. */ + if (!files_match) + (void)close(to_fd); + } + + if (!files_match) { + if (tempcopy) { + to_fd = create_tempfile(to_name, tempfile, + sizeof(tempfile)); + if (to_fd < 0) + err(EX_OSERR, "%s", tempfile); + } else { + if ((to_fd = create_newfile(to_name, target, + &to_sb)) < 0) + err(EX_OSERR, "%s", to_name); + if (verbose) + (void)printf("install: %s -> %s\n", + from_name, to_name); + } + if (!devnull) + copy(from_fd, from_name, to_fd, + tempcopy ? tempfile : to_name, from_sb.st_size); + } + + if (dostrip) { + strip(tempcopy ? tempfile : to_name); + + /* + * Re-open our fd on the target, in case we used a strip + * that does not work in-place -- like GNU binutils strip. + */ + close(to_fd); + to_fd = open(tempcopy ? tempfile : to_name, O_RDONLY, 0); + if (to_fd < 0) + err(EX_OSERR, "stripping %s", to_name); + } + + /* + * Compare the stripped temp file with the target. + */ + if (docompare && dostrip && target) { + temp_fd = to_fd; + + /* Re-open to_fd using the real target name. */ + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + + if (fstat(temp_fd, &temp_sb)) { + serrno = errno; + (void)unlink(tempfile); + errno = serrno; + err(EX_OSERR, "%s", tempfile); + } + + if (compare(temp_fd, tempfile, (size_t)temp_sb.st_size, to_fd, + to_name, (size_t)to_sb.st_size) == 0) { + /* + * If target has more than one link we need to + * replace it in order to snap the extra links. + * Need to preserve target file times, though. + */ + if (to_sb.st_nlink != 1) { + utb.actime = to_sb.st_atime; + utb.modtime = to_sb.st_mtime; + (void)utime(tempfile, &utb); + } else { + files_match = 1; + (void)unlink(tempfile); + } + (void) close(temp_fd); + } + } + + /* + * Move the new file into place if doing a safe copy + * and the files are different (or just not compared). + */ + if (tempcopy && !files_match) { + /* Try to turn off the immutable bits. */ + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS); + if (dobackup) { + if (snprintf(backup, MAXPATHLEN, "%s%s", to_name, + suffix) != strlen(to_name) + strlen(suffix)) { + unlink(tempfile); + errx(EX_OSERR, "%s: backup filename too long", + to_name); + } + if (verbose) + (void)printf("install: %s -> %s\n", to_name, backup); + if (rename(to_name, backup) < 0) { + serrno = errno; + unlink(tempfile); + errno = serrno; + err(EX_OSERR, "rename: %s to %s", to_name, + backup); + } + } + if (verbose) + (void)printf("install: %s -> %s\n", from_name, to_name); + if (rename(tempfile, to_name) < 0) { + serrno = errno; + unlink(tempfile); + errno = serrno; + err(EX_OSERR, "rename: %s to %s", + tempfile, to_name); + } + + /* Re-open to_fd so we aren't hosed by the rename(2). */ + (void) close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, 0)) < 0) + err(EX_OSERR, "%s", to_name); + } + +#ifdef __APPLE__ + /* in case mtime is modified */ + if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) && + fcopyfile(from_fd, to_fd, NULL, COPYFILE_XATTR) < 0) { + warn("%s: unable to copy extended attributes from %s", to_name, from_name); + } +#endif /* __APPLE__ */ + /* + * Preserve the timestamp of the source file if necessary. + */ + if (dopreserve && !files_match && !devnull) { + utb.actime = from_sb.st_atime; + utb.modtime = from_sb.st_mtime; + (void)utime(to_name, &utb); + } + + if (fstat(to_fd, &to_sb) == -1) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s", to_name); + } + + /* + * Set owner, group, mode for target; do the chown first, + * chown may lose the setuid bits. + */ + if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || + (uid != (uid_t)-1 && uid != to_sb.st_uid) || + (mode != to_sb.st_mode)) { + /* Try to turn off the immutable bits. */ + if (to_sb.st_flags & NOCHANGEBITS) + (void)fchflags(to_fd, to_sb.st_flags & ~NOCHANGEBITS); + } + + if ((gid != (gid_t)-1 && gid != to_sb.st_gid) || + (uid != (uid_t)-1 && uid != to_sb.st_uid)) + if (fchown(to_fd, uid, gid) == -1) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR,"%s: chown/chgrp", to_name); + } + + if (mode != to_sb.st_mode) + if (fchmod(to_fd, mode)) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s: chmod", to_name); + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + * NFS does not support flags. Ignore ENOTSUP flags if we're just + * trying to turn off UF_NODUMP. If we're trying to set real flags, + * then warn if the the fs doesn't support it, otherwise fail. + */ + if (!devnull && fchflags(to_fd, + flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { + if (flags & SETFLAGS) { + if (errno == ENOTSUP) + warn("%s: chflags", to_name); + else { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s: chflags", to_name); + } + } + } +#ifdef __APPLE__ + /* the ACL could prevent credential/permission system calls later on... */ + if (!devnull && (S_ISLNK(from_sb.st_mode) || S_ISREG(from_sb.st_mode)) && + (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL) < 0)) { + warn("%s: unable to copy ACL from %s", to_name, from_name); + } +#endif /* __APPLE__ */ + + (void)close(to_fd); + if (!devnull) + (void)close(from_fd); +} +#if TARGET_OS_EMBEDDED +#define BUFFER_SIZE 128*1024 +#else /* !TARGET_OS_EMBEDDED */ +#define BUFFER_SIZE MAXBSIZE +#endif /* TARGET_OS_EMBEDDED */ +/* + * compare -- + * compare two files; non-zero means files differ + */ +int +compare(int from_fd, const char *from_name, size_t from_len, + int to_fd, const char *to_name, size_t to_len) +{ + char *p, *q; + int rv; + int done_compare; + + rv = 0; + if (from_len != to_len) + return 1; + + if (from_len <= 8 * 1024 * 1024) { + done_compare = 0; + if (trymmap(from_fd) && trymmap(to_fd)) { + p = mmap(NULL, from_len, PROT_READ, MAP_SHARED, from_fd, (off_t)0); + if (p == (char *)MAP_FAILED) + goto out; + q = mmap(NULL, from_len, PROT_READ, MAP_SHARED, to_fd, (off_t)0); + if (q == (char *)MAP_FAILED) { + munmap(p, from_len); + goto out; + } + + rv = memcmp(p, q, from_len); + munmap(p, from_len); + munmap(q, from_len); + done_compare = 1; + } + out: + if (!done_compare) { + char buf1[BUFFER_SIZE]; + char buf2[BUFFER_SIZE]; + int n1, n2; + + rv = 0; + lseek(from_fd, 0, SEEK_SET); + lseek(to_fd, 0, SEEK_SET); + while (rv == 0) { + n1 = read(from_fd, buf1, sizeof(buf1)); + if (n1 == 0) + break; /* EOF */ + else if (n1 > 0) { + n2 = read(to_fd, buf2, n1); + if (n2 == n1) + rv = memcmp(buf1, buf2, n1); + else + rv = 1; /* out of sync */ + } else + rv = 1; /* read failure */ + } + lseek(from_fd, 0, SEEK_SET); + lseek(to_fd, 0, SEEK_SET); + } + } else + rv = 1; /* don't bother in this case */ + + return rv; +} + +/* + * create_tempfile -- + * create a temporary file based on path and open it + */ +int +create_tempfile(path, temp, tsize) + char *path; + char *temp; + size_t tsize; +{ + char *p; + + (void)strncpy(temp, path, tsize); + temp[tsize - 1] = '\0'; + if ((p = strrchr(temp, '/')) != NULL) + p++; + else + p = temp; + (void)strncpy(p, "INS@XXXX", &temp[tsize - 1] - p); + temp[tsize - 1] = '\0'; + return (mkstemp(temp)); +} + +/* + * create_newfile -- + * create a new file, overwriting an existing one if necessary + */ +int +create_newfile(path, target, sbp) + char *path; + int target; + struct stat *sbp; +{ + char backup[MAXPATHLEN]; + + if (target) { + /* + * Unlink now... avoid ETXTBSY errors later. Try to turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ + if (sbp->st_flags & NOCHANGEBITS) + (void)chflags(path, sbp->st_flags & ~NOCHANGEBITS); + + if (dobackup) { + if (snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix) != strlen(path) + strlen(suffix)) + errx(EX_OSERR, "%s: backup filename too long", + path); + (void)snprintf(backup, MAXPATHLEN, "%s%s", + path, suffix); + if (verbose) + (void)printf("install: %s -> %s\n", + path, backup); + if (rename(path, backup) < 0) + err(EX_OSERR, "rename: %s to %s", path, backup); + } else + (void)unlink(path); + } + + return (open(path, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)); +} + +/* + * copy -- + * copy from one file to another + */ +void +copy(from_fd, from_name, to_fd, to_name, size) + register int from_fd, to_fd; + char *from_name, *to_name; + off_t size; +{ + register int nr, nw; + int serrno; + char *p, buf[BUFFER_SIZE]; + int done_copy; + + /* Rewind file descriptors. */ + if (lseek(from_fd, (off_t)0, SEEK_SET) == (off_t)-1) + err(EX_OSERR, "lseek: %s", from_name); + if (lseek(to_fd, (off_t)0, SEEK_SET) == (off_t)-1) + err(EX_OSERR, "lseek: %s", to_name); + + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files. This is really a minor hack, but it + * wins some CPU back. + */ + done_copy = 0; + if (size <= 8 * 1048576 && trymmap(from_fd) && + (p = mmap(NULL, (size_t)size, PROT_READ, MAP_SHARED, + from_fd, (off_t)0)) != (char *)MAP_FAILED) { + if ((nw = write(to_fd, p, size)) != size) { + serrno = errno; + (void)unlink(to_name); + errno = nw > 0 ? EIO : serrno; + err(EX_OSERR, "%s", to_name); + } + done_copy = 1; + } + if (!done_copy) { + while ((nr = read(from_fd, buf, sizeof(buf))) > 0) + if ((nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errno = nw > 0 ? EIO : serrno; + err(EX_OSERR, "%s", to_name); + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errno = serrno; + err(EX_OSERR, "%s", from_name); + } + } +} + +/* + * strip -- + * use strip(1) to strip the target file + */ +void +strip(to_name) + char *to_name; +{ + pid_t pid; + int error; + extern char** environ; + char *const argv[] = { "xcrun", "strip", "-", to_name, NULL }; + + if (0 == (error = posix_spawnp(&pid, "xcrun", NULL, NULL, argv, environ))) { + int status = 0; + pid_t child = waitpid(pid, &status, 0); + if ((child == -1) || status) { + unlink(to_name); + errx(EX_SOFTWARE, "child process failed: xcrun strip - %s", to_name); + } + } else { + errno = error; + err(EX_OSERR, "xcrun strip - %s", to_name); + } +} + +/* + * install_dir -- + * build directory heirarchy + */ +void +install_dir(path) + char *path; +{ + register char *p; + struct stat sb; + int ch; + + for (p = path;; ++p) + if (!*p || (p != path && *p == '/')) { + ch = *p; + *p = '\0'; + if (stat(path, &sb)) { + if (errno != ENOENT || mkdir(path, 0755) < 0) { + err(EX_OSERR, "mkdir %s", path); + /* NOTREACHED */ + } else if (verbose) + (void)printf("install: mkdir %s\n", + path); + } else if (!S_ISDIR(sb.st_mode)) + errx(EX_OSERR, "%s exists but is not a directory", path); + if (!(*p = ch)) + break; + } + + if ((gid != (gid_t)-1 || uid != (uid_t)-1) && chown(path, uid, gid)) + warn("chown %u:%u %s", uid, gid, path); + if (chmod(path, mode)) + warn("chmod %o %s", mode, path); +} + +/* + * usage -- + * print a usage message and die + */ +void +usage() +{ + (void)fprintf(stderr, "\ +usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\ + [-o owner] file1 file2\n\ + install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n\ + [-o owner] file1 ... fileN directory\n\ + install -d [-v] [-g group] [-m mode] [-o owner] directory ...\n"); + exit(EX_USAGE); + /* NOTREACHED */ +} + +/* + * trymmap -- + * return true (1) if mmap should be tried, false (0) if not. + */ +int +trymmap(fd) + int fd; +{ +/* + * The ifdef is for bootstrapping - f_fstypename doesn't exist in + * pre-Lite2-merge systems. + */ +#ifdef MFSNAMELEN + struct statfs stfs; + + if (nommap || fstatfs(fd, &stfs) != 0) + return (0); + if (strcmp(stfs.f_fstypename, "ufs") == 0 || + strcmp(stfs.f_fstypename, "cd9660") == 0) + return (1); +#endif + return (0); +} diff --git a/file_cmds/ipcrm/ipcrm.1 b/file_cmds/ipcrm/ipcrm.1 new file mode 100644 index 00000000..55ff23ca --- /dev/null +++ b/file_cmds/ipcrm/ipcrm.1 @@ -0,0 +1,84 @@ +.\" Copyright (c) 1994 Adam Glass +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. The name of the Author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL Adam Glass BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: ipcrm.1,v 1.3 2005/04/12 23:51:24 nicolai Exp $ +.\"" +.Dd August 8, 1994 +.Dt ipcrm 1 +.Os +.Sh NAME +.Nm ipcrm +.Nd remove the specified message queues, semaphore sets, and shared memory +segments +.Sh SYNOPSIS +.Nm ipcrm +.Op Fl M Ar shmkey +.Op Fl m Ar shmid +.Op Fl Q Ar msgkey +.Op Fl q Ar msqid +.Op Fl S Ar semkey +.Op Fl s Ar semid +.Ar ... +.Sh DESCRIPTION +.Nm Ipcrm +removes the specified message queues, semaphores and shared memory +segments. These System V IPC objects can be specified by their +creation id or any associated key. +.Pp +The following options are used to specify which IPC objects will be removed. +Any number and combination of these options can be used: +.Bl -tag -width indent +.It Fl M Ar shmkey +Mark the shared memory segment associated with key +.Nm shmkey +for removal. +This marked segment will be destroyed after the last detach. +.It Fl m Ar shmid +Mark the shared memory segment associated with id +.Nm shmid +for removal. +This marked segment will be destroyed after the last detach. +.It Fl Q Ar msgkey +Remove the message queue associated with key +.Nm msgkey +from the system. +.It Fl q Ar msqid +Remove the message queue associated with the id +.Nm msqid +from the system. +.It Fl S Ar semkey +Remove the semaphore set associated with key +.Nm semkey +from the system. +.It Fl s Ar semid +Removes the semaphore set associated with id +.Nm semid +from the system. +.El +.Pp +The identifiers and keys associated with these System V IPC objects can be +determined by using +.Xr ipcs 1 +. +.Sh SEE ALSO +.Xr ipcs 1 diff --git a/file_cmds/ipcrm/ipcrm.c b/file_cmds/ipcrm/ipcrm.c new file mode 100644 index 00000000..e90aaa19 --- /dev/null +++ b/file_cmds/ipcrm/ipcrm.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 1994 Adam Glass + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Adam Glass. + * 4. The name of the Author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY Adam Glass ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL Adam Glass BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char rcsid[] = + "$Id: ipcrm.c,v 1.3 2005/02/03 07:31:33 josborne Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IPC_TO_STR(x) (x == 'Q' ? "msq" : (x == 'M' ? "shm" : "sem")) +#define IPC_TO_STRING(x) (x == 'Q' ? "message queue" : \ + (x == 'M' ? "shared memory segment" : "semaphore")) + +int signaled; + +static void usage(void) +{ + fprintf(stderr, "%s\n%s\n", + "usage: ipcrm [-q msqid] [-m shmid] [-s semid]", + " [-Q msgkey] [-M shmkey] [-S semkey] ..."); + exit(1); +} + +static int msgrm(key_t key, int id) +{ + if (key) { + id = msgget(key, 0); + if (id == -1) + return -1; + } + return msgctl(id, IPC_RMID, NULL); +} + +static int shmrm(key_t key, int id) +{ + if (key) { + id = shmget(key, 0, 0); + if (id == -1) + return -1; + } + return shmctl(id, IPC_RMID, NULL); +} + +static int semrm(key_t key, int id) +{ + if (key) { + id = semget(key, 0, 0); + if (id == -1) + return -1; + } + return semctl(id, 0, IPC_RMID); +} + +static void not_configured(__unused int unused) +{ + signaled++; +} + +int main(argc, argv) + int argc; + char *argv[]; + +{ + int c, result, errflg, target_id; + key_t target_key; + char *en; + + errflg = 0; + signal(SIGSYS, not_configured); + while ((c = getopt(argc, argv, ":q:m:s:Q:M:S:")) != -1) { + + signaled = 0; + switch (c) { + case 'q': + case 'm': + case 's': + target_id = (int)strtol(optarg, &en, 0); + if (*en) { + warnx("%s: '%s' is not a number", + IPC_TO_STRING(toupper(c)), optarg); + continue; + } + if (c == 'q') + result = msgrm(0, target_id); + else if (c == 'm') + result = shmrm(0, target_id); + else + result = semrm(0, target_id); + if (result < 0) { + errflg++; + if (!signaled) + warn("%sid(%d): ", IPC_TO_STR(toupper(c)), target_id); + else + warnx("%ss are not configured in the running kernel", + IPC_TO_STRING(toupper(c))); + } + break; + case 'Q': + case 'M': + case 'S': + target_key = (key_t)strtol(optarg, &en, 0); + if (*en) { + warnx("%s: '%s' is not a number", IPC_TO_STRING(c), optarg); + continue; + } + if (target_key == IPC_PRIVATE) { + warnx("can't remove private %ss", IPC_TO_STRING(c)); + continue; + } + if (c == 'Q') + result = msgrm(target_key, 0); + else if (c == 'M') + result = shmrm(target_key, 0); + else + result = semrm(target_key, 0); + if (result < 0) { + errflg++; + if (!signaled) + warn("%s key(%d): ", IPC_TO_STRING(c), target_key); + else + warnx("%ss are not configured in the running kernel", + IPC_TO_STRING(c)); + } + break; + case ':': + fprintf(stderr, "option -%c requires an argument\n", optopt); + usage(); + case '?': + fprintf(stderr, "unrecognized option: -%c\n", optopt); + usage(); + } + } + + if (optind != argc) { + fprintf(stderr, "unknown argument: %s\n", argv[optind]); + usage(); + } + exit(errflg); +} diff --git a/file_cmds/ipcs/ipcs.1 b/file_cmds/ipcs/ipcs.1 new file mode 100644 index 00000000..f0a6df4b --- /dev/null +++ b/file_cmds/ipcs/ipcs.1 @@ -0,0 +1,132 @@ +.\" +.\" Copyright (c) 1994 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by SigmaSoft, Th. Lockert. +.\" 3. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/ipcs/ipcs.1,v 1.16 2003/05/21 21:07:28 ru Exp $ +.\" +.Dd June 18, 1994 +.Dt "IPCS" 1 +.Os +.Sh NAME +.Nm ipcs +.Nd report System V interprocess communication facilities status +.Sh SYNOPSIS +.Nm ipcs +.Op Fl abcMmopQqSsTt +.Sh DESCRIPTION +The +.Nm ipcs +utility provides information on System V interprocess communication +(IPC) facilities on the system. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Show the maximum amount of information possible when +displaying active semaphores, message queues, +and shared memory segments. +(This is shorthand for specifying the +.Fl b , +.Fl c , +.Fl o , +.Fl p , +and +.Fl t +options.) +.It Fl b +Show the maximum allowed sizes for active semaphores, message queues, +and shared memory segments. The +.Dq maximum allowed size +is the maximum number of bytes in a message on a message queue, +the size of a shared memory segment, +or the number of semaphores in a set of semaphores. +.It Fl c +Show the creator's name and group for active semaphores, message queues, +and shared memory segments. +.It Fl M +Display system information about shared memory. +.It Fl m +Display information about active shared memory segments. +.It Fl o +Show outstanding usage for active message queues, +and shared memory segments. The +.Dq outstanding usage +is the number of messages in a message queue, or the number +of processes attached to a shared memory segment. +.It Fl p +Show the process ID information for active semaphores, message queues, +and shared memory segments. The +.Dq process ID information +is the last process to send a message to or receive a message from +a message queue, +the process that created a semaphore, or the last process to attach +or detach a shared memory segment. +.It Fl Q +Display system information about messages queues. +.It Fl q +Display information about active message queues. +.It Fl S +Display system information about semaphores. +.It Fl s +Display information about active semaphores. +.It Fl T +Display system information about shared memory, message queues +and semaphores. +.It Fl t +Show access times for active semaphores, message queues, +and shared memory segments. The access times is the time +of the last control operation on an IPC object, +the last send or receive of a message, +the last attach or detach of a shared memory segment, +or the last operation on a semaphore. +.El +.Pp +If none of the +.Fl M , +.Fl m , +.Fl Q , +.Fl q , +.Fl S , +or +.Fl s +options are specified, information about all active IPC facilities is +listed. +.Sh RESTRICTIONS +System data structures may change while +.Nm ipcs +is running; the output of +.Nm ipcs +is not guaranteed to be consistent. +.Sh BUGS +This manual page is woefully incomplete, because it does not +at all attempt to explain the information printed by +.Nm ipcs . +.Sh SEE ALSO +.Xr ipcrm 1 +.Sh AUTHORS +.An Thorsten Lockert Aq tholo@sigmasoft.com diff --git a/file_cmds/ipcs/ipcs.c b/file_cmds/ipcs/ipcs.c new file mode 100644 index 00000000..986b229e --- /dev/null +++ b/file_cmds/ipcs/ipcs.c @@ -0,0 +1,553 @@ +/* + * Copyright (c) 1994 SigmaSoft, Th. Lockert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sys/types.h" +#include +#include +#include +#include +#include +#include +#include "sys/ipcs.h" +#define KERNEL 1 /* To get new ipc_perm and __(sem|shm|msg)ds_new */ +#include "sys/ipc.h" +#include "sys/sem_internal.h" +#include "sys/shm_internal.h" +#include "sys/msg.h" + + +/* The following is a kludge, until the problem of multiple inclusions + of ipc.h is taken care of. */ +#ifndef IXSEQ_TO_IPCID +#define IXSEQ_TO_IPCID(ix,perm) (((perm._seq) << 16L) | (ix & 0xffff)) +#endif + +static char * +fmt_perm(u_short mode, char write_char) +{ + static char buffer[100]; + + buffer[0] = '-'; + buffer[1] = '-'; + buffer[2] = ((mode & 0400) ? 'r' : '-'); + buffer[3] = ((mode & 0200) ? write_char : '-'); + buffer[4] = '-'; + buffer[5] = ((mode & 0040) ? 'r' : '-'); + buffer[6] = ((mode & 0020) ? write_char : '-'); + buffer[7] = '-'; + buffer[8] = ((mode & 0004) ? 'r' : '-'); + buffer[9] = ((mode & 0002) ? write_char : '-'); + buffer[10] = '-'; + buffer[11] = '\0'; + return (&buffer[0]); +} + +static void +cvt_time(time_t t, char *buf) +{ + struct tm *tm; + + if (t == 0) { + strcpy(buf, "no-entry"); + } else { + tm = localtime(&t); + if (tm != NULL) { + sprintf(buf, "%2d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + } +} +#define SHMINFO 1 +#define SHMTOTAL 2 +#define MSGINFO 4 +#define MSGTOTAL 8 +#define SEMINFO 16 +#define SEMTOTAL 32 + +#define BIGGEST 1 +#define CREATOR 2 +#define OUTSTANDING 4 +#define PID 8 +#define TIME 16 + +static void +usage(void) +{ + errx(EX_USAGE, "%s","usage: ipcs [-abcmopqstMQST]\n"); +} + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int display = 0; + int option = 0; + int exit_val = 0; + time_t now; + char datestring[100]; + int i; + + while ((i = getopt(argc, argv, "MmQqSsabcoptT")) != -1) + switch (i) { + case 'M': + display = SHMTOTAL; + break; + case 'm': + display |= SHMINFO; + break; + case 'Q': + display = MSGTOTAL; + break; + case 'q': + display |= MSGINFO; + break; + case 'S': + display = SEMTOTAL; + break; + case 's': + display |= SEMINFO; + break; + case 'T': + display = SHMTOTAL | MSGTOTAL | SEMTOTAL; + break; + case 'a': + option |= BIGGEST | CREATOR | OUTSTANDING | PID | TIME; + break; + case 'b': + option |= BIGGEST; + break; + case 'c': + option |= CREATOR; + break; + case 'o': + option |= OUTSTANDING; + break; + case 'p': + option |= PID; + break; + case 't': + option |= TIME; + break; + default: + usage(); + } + if (display == 0) + display = SHMINFO | MSGINFO | SEMINFO; + now = time(0); + struct tm* tm = localtime(&now); + if (tm == NULL) { + now = 0; + tm = localtime(&now); + } + if (0 == strftime(datestring, sizeof(datestring), "%a %b %e %H:%M:%S %Z %Y", tm)) + errx(1, "strftime failed\n"); + printf("IPC status from as of %s\n", datestring); + if ((display & (MSGINFO | MSGTOTAL))) { + if (display & MSGTOTAL) { + struct IPCS_command ic; + struct msginfo msginfo; + size_t ic_size = sizeof(ic); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_MSG_CONF; + ic.ipcs_cursor = 0; /* 0 for fw. compat. */ + ic.ipcs_data = &msginfo; + ic.ipcs_datalen = sizeof(msginfo); + + if (sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size)) { + if (errno != EPERM) { + char buffer[1024]; + snprintf(buffer, 1024, "sysctlbyname(IPCS_MSG_SYSCTL, op=CONF, &ic, &%ld) datalen=%d", + sizeof(ic), ic.ipcs_datalen); + perror(buffer); + } else + perror("sysctlbyname IPCS_MSG_SYSCTL"); + } + + printf("msginfo:\n"); + printf("\tmsgmax: %6d\t(max characters in a message)\n", + msginfo.msgmax); + printf("\tmsgmni: %6d\t(# of message queues)\n", + msginfo.msgmni); + printf("\tmsgmnb: %6d\t(max characters in a message queue)\n", + msginfo.msgmnb); + printf("\tmsgtql: %6d\t(max # of messages in system)\n", + msginfo.msgtql); + printf("\tmsgssz: %6d\t(size of a message segment)\n", + msginfo.msgssz); + printf("\tmsgseg: %6d\t(# of message segments in system)\n\n", + msginfo.msgseg); + } + if (display & MSGINFO) { + struct IPCS_command ic; + struct __msqid_ds_new ds; + struct __msqid_ds_new *msqptr = &ds; + size_t ic_size = sizeof(ic); + + printf("T ID KEY MODE OWNER GROUP"); + if (option & CREATOR) + printf(" CREATOR CGROUP"); + if (option & OUTSTANDING) + printf(" CBYTES QNUM"); + if (option & BIGGEST) + printf(" QBYTES"); + if (option & PID) + printf(" LSPID LRPID"); + if (option & TIME) + printf(" STIME RTIME CTIME"); + printf("\nMessage Queues:\n"); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_MSG_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_datalen = sizeof(*msqptr); + ic.ipcs_data = msqptr; + + memset(msqptr, 0, sizeof(*msqptr)); + + while(!(sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + ic.ipcs_data = msqptr; + + if (msqptr->msg_qbytes != 0) { + char stime_buf[100], rtime_buf[100], + ctime_buf[100]; + + cvt_time(msqptr->msg_stime, stime_buf); + cvt_time(msqptr->msg_rtime, rtime_buf); + cvt_time(msqptr->msg_ctime, ctime_buf); + + printf("q %6d 0x%08x %s %8s %8s", + IXSEQ_TO_IPCID((ic.ipcs_cursor-1), msqptr->msg_perm), + (int)msqptr->msg_perm._key, + fmt_perm(msqptr->msg_perm.mode, 'w'), + user_from_uid(msqptr->msg_perm.uid, 0), + group_from_gid(msqptr->msg_perm.gid, 0)); + + if (option & CREATOR) + printf(" %8s %8s", + user_from_uid(msqptr->msg_perm.cuid, 0), + group_from_gid(msqptr->msg_perm.cgid, 0)); + + if (option & OUTSTANDING) + printf(" %6lu %6lu", + msqptr->msg_cbytes, + msqptr->msg_qnum); + + if (option & BIGGEST) + printf(" %6lu", + msqptr->msg_qbytes); + + if (option & PID) + printf(" %6d %6d", + msqptr->msg_lspid, + msqptr->msg_lrpid); + + if (option & TIME) + printf(" %s %s %s", + stime_buf, + rtime_buf, + ctime_buf); + + printf("\n"); + } + memset(msqptr, 0, sizeof(*msqptr)); + errno = 0; + } + + if (errno != ENOENT && errno != ERANGE) { + if (errno != EPERM) { + errx(1, "sysctlbyname(IPCS_MSG_SYSCTL, op=ITER, &ic, &%ld) datalen=%d failed:%s\n", + sizeof(ic), ic.ipcs_datalen, strerror(errno)); + } else + errx(1, "sysctlbyname IPCS_MSG_SYSCTL: %s", strerror(errno)); + } + printf("\n"); + } + } else + if (display & (MSGINFO | MSGTOTAL)) { + errx(1, "%s", "SVID messages facility not configured in the system\n"); + } + + if ((display & (SHMINFO | SHMTOTAL))) { + if (display & SHMTOTAL) { + struct IPCS_command ic; + struct shminfo shminfo; + size_t ic_size = sizeof(ic); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SHM_CONF; + ic.ipcs_cursor = 0; /* 0 for fw. compat. */ + ic.ipcs_data = &shminfo; + ic.ipcs_datalen = sizeof(shminfo); + + if (sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size)) { + if (errno != EPERM) { + errx(1, "sysctlbyname(IPCS_SHM_SYSCTL, op=CONF, &ic, &%ld) datalen=%d failed: %s\n", + sizeof(ic), ic.ipcs_datalen, strerror(errno)); + } else + errx(1, "sysctlbyname: %s", strerror(errno)); + } + printf("shminfo:\n"); + printf("\tshmmax: %7lld\t(max shared memory segment size)\n", + shminfo.shmmax); + printf("\tshmmin: %7lld\t(min shared memory segment size)\n", + shminfo.shmmin); + printf("\tshmmni: %7lld\t(max number of shared memory identifiers)\n", + shminfo.shmmni); + printf("\tshmseg: %7lld\t(max shared memory segments per process)\n", + shminfo.shmseg); + printf("\tshmall: %7lld\t(max amount of shared memory in pages)\n\n", + shminfo.shmall); + } + if (display & SHMINFO) { + struct IPCS_command ic; + struct __shmid_ds_new ds; + struct __shmid_ds_new *shmptr = &ds; + size_t ic_size = sizeof(ic); + + printf("T ID KEY MODE OWNER GROUP"); + if (option & CREATOR) + printf(" CREATOR CGROUP"); + if (option & OUTSTANDING) + printf(" NATTCH"); + if (option & BIGGEST) + printf(" SEGSZ"); + if (option & PID) + printf(" CPID LPID"); + if (option & TIME) + printf(" ATIME DTIME CTIME"); + printf("\nShared Memory:\n"); + { /* XXX */ + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SHM_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_datalen = sizeof(*shmptr); + ic.ipcs_data = shmptr; + memset(shmptr, 0, sizeof(*shmptr)); + + while(!(sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + ic.ipcs_data = shmptr; /* xnu workaround */ + + if (shmptr->shm_perm.mode & 0x0800) { + char atime_buf[100], dtime_buf[100], + ctime_buf[100]; + + cvt_time(shmptr->shm_atime, atime_buf); + cvt_time(shmptr->shm_dtime, dtime_buf); + cvt_time(shmptr->shm_ctime, ctime_buf); + + printf("m %6d 0x%08x %s %8s %8s", + IXSEQ_TO_IPCID((ic.ipcs_cursor-1), shmptr->shm_perm), + (int)shmptr->shm_perm._key, + fmt_perm(shmptr->shm_perm.mode, 'w'), + user_from_uid(shmptr->shm_perm.uid, 0), + group_from_gid(shmptr->shm_perm.gid, 0)); + + if (option & CREATOR) + printf(" %8s %8s", + user_from_uid(shmptr->shm_perm.cuid, 0), + group_from_gid(shmptr->shm_perm.cgid, 0)); + + if (option & OUTSTANDING) + printf(" %6d", + shmptr->shm_nattch); + + if (option & BIGGEST) + printf(" %6ld", + shmptr->shm_segsz); + + if (option & PID) + printf(" %6d %6d", + shmptr->shm_cpid, + shmptr->shm_lpid); + + if (option & TIME) + printf(" %s %s %s", + atime_buf, + dtime_buf, + ctime_buf); + + printf("\n"); + } + memset(shmptr, 0, sizeof(*shmptr)); + errno = 0; + } + + if (errno != ENOENT && errno != ERANGE) { + if (errno != EPERM) { + errx(1, "sysctlbyname(IPCS_SHM_SYSCTL, op=ITER, &ic, &%ld) datalen=%d failed:%s\n", + sizeof(ic), ic.ipcs_datalen, strerror(errno)); + } else + errx(1, "sysctlbyname: %s", strerror(errno)); + } + } /* XXX */ + printf("\n"); + } + } +else + if (display & (SHMINFO | SHMTOTAL)) { + errx(1, "%s", "SVID shared memory facility not configured in the system\n"); + } + + if ((display & (SEMINFO | SEMTOTAL))) { + if (display & SEMTOTAL) { + struct IPCS_command ic; + struct seminfo seminfo; + size_t ic_size = sizeof(ic); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SEM_CONF; + ic.ipcs_cursor = 0; /* 0 for fw. compat. */ + ic.ipcs_data = &seminfo; + ic.ipcs_datalen = sizeof(seminfo); + + if (sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size)) { + if (errno != EPERM) { + char buffer[1024]; + snprintf(buffer, 1024, "sysctlbyname(IPCS_SEM_SYSCTL, op=CONF, &ic, &%ld) datalen=%d", + sizeof(ic), ic.ipcs_datalen); + perror(buffer); + } else + perror("sysctlbyname IPCS_SEM_SYSCTL/SEM_CONF"); + } + + printf("seminfo:\n"); + printf("\tsemmap: %6d\t(# of entries in semaphore map)\n", + seminfo.semmap); + printf("\tsemmni: %6d\t(# of semaphore identifiers)\n", + seminfo.semmni); + printf("\tsemmns: %6d\t(# of semaphores in system)\n", + seminfo.semmns); + printf("\tsemmnu: %6d\t(# of undo structures in system)\n", + seminfo.semmnu); + printf("\tsemmsl: %6d\t(max # of semaphores per id)\n", + seminfo.semmsl); + printf("\tsemopm: %6d\t(max # of operations per semop call)\n", + seminfo.semopm); + printf("\tsemume: %6d\t(max # of undo entries per process)\n", + seminfo.semume); + printf("\tsemusz: %6d\t(size in bytes of undo structure)\n", + seminfo.semusz); + printf("\tsemvmx: %6d\t(semaphore maximum value)\n", + seminfo.semvmx); + printf("\tsemaem: %6d\t(adjust on exit max value)\n\n", + seminfo.semaem); + } + if (display & SEMINFO) { + struct IPCS_command ic; + struct __semid_ds_new ds; + struct __semid_ds_new *semaptr = &ds; + size_t ic_size = sizeof(ic); + + printf("T ID KEY MODE OWNER GROUP"); + if (option & CREATOR) + printf(" CREATOR CGROUP"); + if (option & BIGGEST) + printf(" NSEMS"); + if (option & TIME) + printf(" OTIME CTIME"); + printf("\nSemaphores:\n"); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SEM_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_datalen = sizeof(*semaptr); + ic.ipcs_data = semaptr; + + memset(semaptr, 0, sizeof(*semaptr)); + + while(!(sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + ic.ipcs_data = semaptr; /* xnu workaround */ + + if ((semaptr->sem_perm.mode & SEM_ALLOC) != 0) { + char ctime_buf[100], otime_buf[100]; + + cvt_time(semaptr->sem_otime, otime_buf); + cvt_time(semaptr->sem_ctime, ctime_buf); + + printf("s %6d 0x%08x %s %8s %8s", + IXSEQ_TO_IPCID((ic.ipcs_cursor-1), semaptr->sem_perm), + (int)semaptr->sem_perm._key, + fmt_perm(semaptr->sem_perm.mode, 'a'), + user_from_uid(semaptr->sem_perm.uid, 0), + group_from_gid(semaptr->sem_perm.gid, 0)); + + if (option & CREATOR) + printf(" %8s %8s", + user_from_uid(semaptr->sem_perm.cuid, 0), + group_from_gid(semaptr->sem_perm.cgid, 0)); + + if (option & BIGGEST) + printf(" %6d", + semaptr->sem_nsems); + + if (option & TIME) + printf(" %s %s", + otime_buf, + ctime_buf); + + printf("\n"); + } + memset(semaptr, 0, sizeof(*semaptr)); + errno = 0; + } + + if (errno != ENOENT && errno != ERANGE) { + if (errno != EPERM) { + errx(1, "sysctlbyname(IPCS_SEM_SYSCTL/ITER, op=ITER, &ic, &%ld) datalen=%d failed: %s\n", + sizeof(ic), ic.ipcs_datalen, strerror(errno)); + } else + errx(1, "sysctlbyname: IPCS_SEM_SYSCTL %s", strerror(errno)); + } + printf("\n"); + } + } else + if (display & (SEMINFO | SEMTOTAL)) { + errx(1, "%s", "SVID semaphores facility not configured in the system\n"); + } + + exit(exit_val); +} diff --git a/file_cmds/ln/link.1 b/file_cmds/ln/link.1 new file mode 100644 index 00000000..bfb5b6e2 --- /dev/null +++ b/file_cmds/ln/link.1 @@ -0,0 +1 @@ +.so man1/ln.1 diff --git a/file_cmds/ln/ln.1 b/file_cmds/ln/ln.1 new file mode 100644 index 00000000..37100d37 --- /dev/null +++ b/file_cmds/ln/ln.1 @@ -0,0 +1,233 @@ +.\"- +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ln.1 8.2 (Berkeley) 12/30/93 +.\" $FreeBSD: src/bin/ln/ln.1,v 1.31 2006/02/14 11:08:05 glebius Exp $ +.\" +.Dd February 14, 2006 +.Dt LN 1 +.Os +.Sh NAME +.Nm link , +.Nm ln +.Nd make links +.Sh SYNOPSIS +.Nm ln +.Op Fl Ffhinsv +.Ar source_file +.Op Ar target_file +.Nm ln +.Op Fl Ffhinsv +.Ar source_file ... +.Ar target_dir +.Nm link +.Ar source_file Ar target_file +.Sh DESCRIPTION +The +.Nm ln +utility creates a new directory entry (linked file) which has the +same modes as the original file. +It is useful for maintaining multiple copies of a file in many places +at once without using up storage for the +.Dq copies ; +instead, a link +.Dq points +to the original copy. +There are two types of links; hard links and symbolic links. +How a link +.Dq points +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 h +If the +.Ar target_file +or +.Ar target_dir +is a symbolic link, do not follow it. +This is most useful with the +.Fl f +option, to replace a symlink which may point to a directory. +.\" ========== +.It Fl f +If the target file already exists, +then unlink it so that the link may occur. +(The +.Fl f +option overrides any previous +.Fl i +options.) +.\" ========== +.It Fl i +Cause +.Nm ln +to write a prompt to standard error if the target file exists. +If the response from the standard input begins with the character +.Sq Li y +or +.Sq Li Y , +then unlink the target file so that the link may occur. +Otherwise, do not attempt the link. +(The +.Fl i +option overrides any previous +.Fl f +options.) +.\" ========== +.It Fl n +Same as +.Fl h , +for compatibility with other +.Nm ln +implementations. +.\" ========== +.It Fl s +Create a symbolic link. +.\" ========== +.It Fl v +Cause +.Nm ln +to be verbose, showing files as they are processed. +.El +.Pp +By default, +.Nm ln +makes +.Em hard +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. +.Pp +A symbolic link contains the name of the file to +which it is linked. +The referenced file is used when an +.Xr open 2 +operation is performed on the link. +A +.Xr stat 2 +on a symbolic link will return the linked-to file; an +.Xr lstat 2 +must be done to obtain information about the link. +The +.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 ln +creates a link to an existing file +.Ar source_file . +If +.Ar target_file +is given, the link has that name; +.Ar target_file +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 . +.Pp +Given more than two arguments, +.Nm ln +makes links in +.Ar target_dir +to all the named source files. +The links made will have the same name as the files being linked to. +.Pp +When the utility is called as +.Nm link , +exactly two arguments must be supplied, +neither of which may specify a directory. +No options may be supplied in this simple mode of operation, +which performs a +.Xr link 2 +operation using the two passed arguments. +.Sh COMPATIBILITY +The +.Fl h , +.Fl i , +.Fl n +and +.Fl v +options are non-standard and their use in scripts is not recommended. +They are provided solely for compatibility with other +.Nm ln +implementations. +.Pp +The +.Fl F +option is +.Fx +extention and should not be used in portable scripts. +.Sh SEE ALSO +.Xr link 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm ln +utility conforms to +.St -p1003.2-92 . +.Pp +The simplified +.Nm link +command conforms to +.St -susv2 . +.Sh HISTORY +An +.Nm ln +command appeared in +.At v1 . diff --git a/file_cmds/ln/ln.c b/file_cmds/ln/ln.c new file mode 100644 index 00000000..21c2445a --- /dev/null +++ b/file_cmds/ln/ln.c @@ -0,0 +1,269 @@ +/*- + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/ln/ln.c,v 1.34 2006/02/14 11:08:05 glebius Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +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 sflag; /* Symbolic, not hard, link. */ +static int vflag; /* Verbose output. */ + /* System link call. */ +static int (*linkf)(const char *, const char *); +static char linkch; + +static int linkit(const char *, const char *, int); +static void usage(void); + +int +ln_main(int argc, char *argv[]) +{ + struct stat sb; + char *p, *sourcedir; + int ch, exitval; + + fflag = Fflag = hflag = iflag = sflag = vflag = 0; + optind = 1; opterr = 1; optreset = 1; + if (argc < 1) + usage(); + /* + * Test for the special case where the utility is called as + * "link", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + if (strcmp(p, "link") == 0) { + while (getopt(argc, argv, "") != -1) + usage(); + argc -= optind; + argv += optind; + if (argc != 2) + usage(); + linkf = link; + return(linkit(argv[0], argv[1], 0)); + exit(linkit(argv[0], argv[1], 0)); + } + + while ((ch = getopt(argc, argv, "Ffhinsv")) != -1) + switch (ch) { + case 'F': + Fflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'h': + case 'n': + hflag = 1; + break; + case 'i': + iflag = 1; + fflag = 0; + break; + case 's': + sflag = 1; + break; + case 'v': + vflag = 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; + + switch(argc) { + case 0: + usage(); + /* NOTREACHED */ + case 1: /* ln target */ + exit(linkit(argv[0], ".", 1)); + case 2: /* ln target source */ + 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)) { + /* + * We were asked not to follow symlinks, but found one at + * the target--simulate "not a directory" error + */ + errno = ENOTDIR; + err(1, "%s", sourcedir); + } + if (stat(sourcedir, &sb)) { + err(1, "%s", sourcedir); + } + if (!S_ISDIR(sb.st_mode)) + usage(); + for (exitval = 0; *argv != sourcedir; ++argv) + exitval |= linkit(*argv, sourcedir, 1); + exit(exitval); +} + +static int +linkit(const char *target, const char *source, int isdir) +{ + struct stat sb; + const char *p; + int ch, exists, first; + char path[PATH_MAX]; + char bbuf[PATH_MAX]; + + if (!sflag) { + /* If target doesn't exist, quit now. */ + if (stat(target, &sb)) { + warn("%s", target); + return (1); + } + /* Only symbolic links to directories. */ + if (S_ISDIR(sb.st_mode)) { + errno = EISDIR; + warn("%s", target); + return (1); + } + } + + /* + * If the source is a directory (and not a symlink if hflag), + * append the target's name. + */ + if (isdir || + (lstat(source, &sb) == 0 && S_ISDIR(sb.st_mode)) || + (!hflag && stat(source, &sb) == 0 && S_ISDIR(sb.st_mode))) { + if (strlcpy(bbuf, target, sizeof(bbuf)) >= sizeof(bbuf) || + (p = basename(bbuf)) == NULL || + snprintf(path, sizeof(path), "%s/%s", source, p) >= + (ssize_t)sizeof(path)) { + errno = ENAMETOOLONG; + warn("%s", target); + return (1); + } + source = path; + } + + exists = !lstat(source, &sb); + /* + * If the file exists, then unlink it forcibly if -f was specified + * and interactively if -i was specified. + */ + if (fflag && exists) { + if (Fflag && S_ISDIR(sb.st_mode)) { + if (rmdir(source)) { + warn("%s", source); + return (1); + } + } else if (unlink(source)) { + warn("%s", source); + return (1); + } + } else if (iflag && exists) { + fflush(thread_stdout); + fprintf(thread_stderr, "replace %s? ", source); + fflush(thread_stderr); + + first = ch = getchar(); + while(ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + fprintf(thread_stderr, "not replaced\n"); + return (1); + } + + if (Fflag && S_ISDIR(sb.st_mode)) { + if (rmdir(source)) { + warn("%s", source); + return (1); + } + } else if (unlink(source)) { + warn("%s", source); + return (1); + } + } + + /* Attempt the link. */ + if ((*linkf)(target, source)) { + warn("%s", source); + return (1); + } + if (vflag) + (void)fprintf(thread_stdout, "%s %c> %s\n", source, linkch, target); + return (0); +} + +static void +usage(void) +{ + (void)fprintf(thread_stderr, "%s\n%s\n%s\n", + "usage: ln [-Ffhinsv] source_file [target_file]", + " ln [-Ffhinsv] source_file ... target_dir", + " link source_file target_file"); + exit(1); +} diff --git a/file_cmds/ln/symlink.7 b/file_cmds/ln/symlink.7 new file mode 100644 index 00000000..7c6c7576 --- /dev/null +++ b/file_cmds/ln/symlink.7 @@ -0,0 +1,457 @@ +.\"- +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)symlink.7 8.3 (Berkeley) 3/31/94 +.\" $FreeBSD: src/bin/ln/symlink.7,v 1.30 2005/02/13 22:25:09 ru Exp $ +.\" +.Dd March 31, 1994 +.Dt SYMLINK 7 +.Os +.Sh NAME +.Nm symlink +.Nd symbolic link handling +.Sh SYMBOLIC LINK HANDLING +Symbolic links are files that act as pointers to other files. +To understand their behavior, you must first understand how hard links +work. +A hard link to a file is indistinguishable from the original file because +it is a reference to the object underlying the original file name. +Changes to a file are independent of the name used to reference the +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. +For this reason, symbolic links may reference directories and may span +file systems. +.Pp +Because a symbolic link and its referenced object coexist in the file system +name space, confusion can arise in distinguishing between the link itself +and the referenced object. +Historically, commands and system calls have adopted their own link +following conventions in a somewhat ad-hoc fashion. +Rules for more a uniform approach, as they are implemented in this system, +are outlined here. +It is important that local applications conform to these rules, too, +so that the user interface can be as consistent as possible. +.Pp +Symbolic links are handled either by operating on the link itself, +or by operating on the object referenced by the link. +In the latter case, +an application or system call is said to +.Dq follow +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 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 +exceeded.) +.Pp +There are three separate areas that need to be discussed. +They are as follows: +.Pp +.Bl -enum -compact -offset indent +.It +Symbolic links used as file name arguments for system calls. +.It +Symbolic links specified as command line arguments to utilities that +are not traversing a file tree. +.It +Symbolic links encountered by utilities that are traversing a file tree +(either specified on the command line or encountered as part of the +file hierarchy walk). +.El +.Ss System calls. +The first area is symbolic links used as file name arguments for +system calls. +.Pp +Except as noted below, all system calls follow symbolic links. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the system call +.Dq Li open("slink" ...\&) +would return a file descriptor to the file +.Dq afile . +.Pp +There are nine 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 lstat 2 , +.Xr lutimes 3 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr rmdir 2 , +and +.Xr unlink 2 . +Because +.Xr remove 3 +is an alias for +.Xr unlink 2 , +it also does not follow symbolic links. +When +.Xr rmdir 2 +is applied to a symbolic link, it fails with the error +.Er ENOTDIR . +.Pp +The owner and group of an existing symbolic link can be changed by +means of the +.Xr lchown 2 +system call. +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 3 +system calls, respectively. +Of these, only the flags are used by the system; +the access permissions and ownership are ignored. +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the system call +.Xr chown 2 +has been changed to follow symbolic links. +The +.Xr lchown 2 +system call was added later when the limitations of the new +.Xr chown 2 +became apparent. +.Ss Commands not traversing a file tree. +The second area is symbolic links, specified as command line file +name arguments, to commands which are not traversing a file tree. +.Pp +Except as noted below, commands follow symbolic links named as command +line arguments. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the command +.Dq Li cat slink +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 +.Dq Li "chown file" +is included in this rule, while the command +.Dq Li "chown -R file" +is not. +(The latter is described in the third area, below.) +.Pp +If it is explicitly intended that the command operate on the symbolic +link instead of following the symbolic link, e.g., it is desired that +.Dq Li "chown slink" +change the ownership of the file that +.Dq Li slink +is, whether it is a symbolic link or not, the +.Fl h +option should be used. +In the above example, +.Dq Li "chown root slink" +would change the ownership of the file referenced by +.Dq Li slink , +while +.Dq Li "chown -h root slink" +would change the ownership of +.Dq Li slink +itself. +.Pp +There are four exceptions to this rule. +The +.Xr mv 1 +and +.Xr rm 1 +commands do not follow symbolic links named as arguments, +but respectively attempt to rename and delete them. +(Note, if the symbolic link references a file via a relative path, +moving it to another directory may very well cause it to stop working, +since the path may no longer be correct.) +.Pp +The +.Xr ls 1 +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 +.Fl R +option is not specified), +the +.Nm ls +command follows symbolic links named as arguments if the +.Fl H +or +.Fl L +option is specified, +or if the +.Fl F , +.Fl d +or +.Fl l +options are not specified. +(The +.Nm ls +command is the only command where the +.Fl H +and +.Fl L +options affect its behavior even though it is not doing a walk of +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 +.Fl L +option is specified. +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the +.Nm chown +and +.Nm chgrp +commands follow symbolic links specified on the command line. +.Ss Commands traversing a file tree. +The following commands either optionally or always traverse file trees: +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ls 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 +and +.Xr chown 8 . +.Pp +It is important to realize that the following rules apply equally to +symbolic links encountered during the file tree traversal and symbolic +links listed as command line arguments. +.Pp +The first rule applies to symbolic links that reference files that are +not of type directory. +Operations that apply to symbolic links are performed on the links +themselves, but otherwise the links are ignored. +.Pp +The command +.Dq Li "rm -r slink directory" +will remove +.Dq Li slink , +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 +.Nm rm +affect the file which +.Dq Li slink +references in any way. +.Pp +The second rule applies to symbolic links that reference files of type +directory. +Symbolic links which reference files of type directory are never +.Dq followed +by default. +This is often referred to as a +.Dq physical +walk, as opposed to a +.Dq logical +walk (where symbolic links referencing directories are followed). +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, regardless +of the type of file they reference, by specifying the +.Fl H +(for +.Dq half\-logical ) +flag. +This flag is intended to make the command line name space look +like the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl H +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -HR user slink" +will traverse the file hierarchy rooted in the file pointed to by +.Dq Li slink . +Note, the +.Fl H +is not the same as the previously discussed +.Fl h +flag. +The +.Fl H +flag causes symbolic links specified on the command line to be +dereferenced both for the purposes of the action to be performed +and the tree walk, and it is as if the user had specified the +name of the file to which the symbolic link pointed. +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, as well as +any symbolic links encountered during the traversal, regardless of +the type of file they reference, by specifying the +.Fl L +(for +.Dq logical ) +flag. +This flag is intended to make the entire name space look like +the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl L +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -LR user slink" +will change the owner of the file referenced by +.Dq Li slink . +If +.Dq Li slink +references a directory, +.Nm chown +will traverse the file hierarchy rooted in the directory that it +references. +In addition, if any symbolic links are encountered in any file tree that +.Nm chown +traverses, they will be treated in the same fashion as +.Dq Li slink . +.Pp +As consistently as possible, you can specify the default behavior by +specifying the +.Fl P +(for +.Dq physical ) +flag. +This flag is intended to make the entire name space look like the +physical name space. +.Pp +For commands that do not by default do file tree traversals, the +.Fl H , +.Fl L +and +.Fl P +flags are ignored if the +.Fl R +flag is not also specified. +In addition, you may specify the +.Fl H , +.Fl L +and +.Fl P +options more than once; the last one specified determines the +command's behavior. +This is intended to permit you to alias commands to behave one way +or the other, and then override that behavior on the command line. +.Pp +The +.Xr ls 1 +and +.Xr rm 1 +commands have exceptions to these rules. +The +.Nm rm +command operates on the symbolic link, and not the file it references, +and therefore never follows a symbolic link. +The +.Nm rm +command does not support the +.Fl H , +.Fl L +or +.Fl P +options. +.Pp +To maintain compatibility with historic systems, +the +.Nm ls +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 +.Fl L +flag is specified, +.Nm ls +follows all symbolic links, +regardless of their type, +whether specified on the command line or encountered in the tree walk. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ln 1 , +.Xr ls 1 , +.Xr mv 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 , +.Xr lchflags 2 , +.Xr lchmod 2 , +.Xr lchown 2 , +.Xr lstat 2 , +.Xr lutimes 3 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr symlink 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr remove 3 , +.Xr chown 8 diff --git a/file_cmds/ls/cmp.c b/file_cmds/ls/cmp.c new file mode 100644 index 00000000..2ad5edd9 --- /dev/null +++ b/file_cmds/ls/cmp.c @@ -0,0 +1,226 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/cmp.c,v 1.12 2002/06/30 05:13:54 obrien Exp $"); + + +#include +#include + +#include +#include + +#include "ls.h" +#include "extern.h" + +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ + defined(_XOPEN_SOURCE) || defined(__NetBSD__) +#define ATIMENSEC_CMP(x, op, y) ((x)->st_atimensec op (y)->st_atimensec) +#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctimensec op (y)->st_ctimensec) +#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtimensec op (y)->st_mtimensec) +#define BTIMENSEC_CMP(x, op, y) ((x)->st_birthtimensec op (y)->st_birthtimensec) +#else +#define ATIMENSEC_CMP(x, op, y) \ + ((x)->st_atimespec.tv_nsec op (y)->st_atimespec.tv_nsec) +#define CTIMENSEC_CMP(x, op, y) \ + ((x)->st_ctimespec.tv_nsec op (y)->st_ctimespec.tv_nsec) +#define MTIMENSEC_CMP(x, op, y) \ + ((x)->st_mtimespec.tv_nsec op (y)->st_mtimespec.tv_nsec) +#define BTIMENSEC_CMP(x, op, y) \ + ((x)->st_birthtimespec.tv_nsec op (y)->st_birthtimespec.tv_nsec) +#endif + +int +namecmp(const FTSENT *a, const FTSENT *b) +{ + return (strcoll(a->fts_name, b->fts_name)); +} + +int +revnamecmp(const FTSENT *a, const FTSENT *b) +{ + return (strcoll(b->fts_name, a->fts_name)); +} + +int +modcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revmodcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (-1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +acccmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revacccmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (-1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +statcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revstatcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (-1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +sizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (-1); + else + return (namecmp(a, b)); +} + +int +revsizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (-1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (1); + else + return (revnamecmp(a, b)); +} + +int +birthcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime) + return (1); + else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime) + return (-1); + else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revbirthcmp(const FTSENT *a, const FTSENT *b) +{ + if (b->fts_statp->st_birthtime > a->fts_statp->st_birthtime) + return (-1); + else if (b->fts_statp->st_birthtime < a->fts_statp->st_birthtime) + return (1); + else if (BTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (BTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} diff --git a/file_cmds/ls/extern.h b/file_cmds/ls/extern.h new file mode 100644 index 00000000..93d4691d --- /dev/null +++ b/file_cmds/ls/extern.h @@ -0,0 +1,68 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)extern.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/ls/extern.h,v 1.19 2002/05/19 02:51:36 tjr Exp $ + */ + +int acccmp(const FTSENT *, const FTSENT *); +int revacccmp(const FTSENT *, const FTSENT *); +int modcmp(const FTSENT *, const FTSENT *); +int revmodcmp(const FTSENT *, const FTSENT *); +int namecmp(const FTSENT *, const FTSENT *); +int revnamecmp(const FTSENT *, const FTSENT *); +int statcmp(const FTSENT *, const FTSENT *); +int revstatcmp(const FTSENT *, const FTSENT *); +int sizecmp (const FTSENT *, const FTSENT *); +int revsizecmp (const FTSENT *, const FTSENT *); +int birthcmp(const FTSENT *, const FTSENT *); +int revbirthcmp(const FTSENT *, const FTSENT *); + +void printcol(DISPLAY *); +void printlong(DISPLAY *); +void printscol(DISPLAY *); +void printstream(DISPLAY *); +void ls_usage(void); +int prn_normal(const char *); +size_t len_octal(const char *, int); +int prn_octal(const char *); +int prn_printable(const char *); +#ifdef COLORLS +void parsecolors(const char *cs); +void colorquit(int); + +extern char *ansi_fgcol; +extern char *ansi_bgcol; +extern char *ansi_coloff; +extern char *attrs_off; +extern char *enter_bold; +#endif diff --git a/file_cmds/ls/ls.1 b/file_cmds/ls/ls.1 new file mode 100644 index 00000000..12e8af53 --- /dev/null +++ b/file_cmds/ls/ls.1 @@ -0,0 +1,706 @@ +.\" Copyright (c) 1980, 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgment: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)ls.1 8.7 (Berkeley) 7/29/94 +.\" $FreeBSD: src/bin/ls/ls.1,v 1.69 2002/08/21 17:32:34 trhodes Exp $ +.\" +.Dd May 19, 2002 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm ls +.Op Fl ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1 +.Op Ar +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm ls +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm ls +displays the names of files contained +within that directory, as well as any requested, associated +information. +.Pp +If no operands are given, the contents of the current +directory are displayed. +If more than one operand is given, +non-directory operands are displayed first; directory +and non-directory operands are sorted separately and in +lexicographical order. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl @ +Display extended attribute keys and sizes in long +.Pq Fl l +output. +.It Fl 1 +(The numeric digit +.Dq one . ) +Force output to be +one entry per line. +This is the default when +output is not to a terminal. +.It Fl A +List all entries except for +.Pa \&. +and +.Pa .. . +Always set for the super-user. +.It Fl a +Include directory entries whose names begin with a +dot +.Pq Pa \&. . +.It Fl B +Force printing of non-printable characters (as defined by +.Xr ctype 3 +and current locale settings) in file names as +.Li \e Ns Va xxx , +where +.Va xxx +is the numeric value of the character in octal. +.It Fl b +As +.Fl B , +but use +.Tn C +escape codes whenever possible. +.It Fl C +Force multi-column output; this is the default when output is to a terminal. +.It Fl c +Use time when file status was last changed for sorting +.Pq Fl t +or long printing +.Pq Fl l . +.It Fl d +Directories are listed as plain files (not searched recursively). +.It Fl e +Print the Access Control List (ACL) associated with the file, if present, in long +.Pq Fl l +output. +.It Fl F +Display a slash +.Pq Ql / +immediately after each pathname that is a directory, +an asterisk +.Pq Ql * +after each that is executable, +an at sign +.Pq Ql @ +after each symbolic link, +an equals sign +.Pq Ql = +after each socket, +a percent sign +.Pq Ql % +after each whiteout, +and a vertical bar +.Pq Ql \&| +after each that is a +.Tn FIFO . +.It Fl f +Output is not sorted. +This option turns on the +.Fl a +option. +.It Fl G +Enable colorized output. +This option is equivalent to defining +.Ev CLICOLOR +in the environment. +(See below.) +.It Fl g +This option is only available for compatibility with POSIX; +it is used to display the group name in the long +.Pq Fl l +format output (the owner name is suppressed). +.It Fl H +Symbolic links on the command line are followed. +This option is assumed if +none of the +.Fl F , d , +or +.Fl l +options are specified. +.It Fl h +When used with the +.Fl l +option, use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte +and Petabyte in order to reduce the number of digits to three or less +using base 2 for sizes. +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl k +If the +.Fl s +option is specified, print the file size allocation in kilobytes, +not blocks. +This option overrides the environment variable +.Ev BLOCKSIZE . +.It Fl L +Follow all symbolic links to final target and list the file or directory the link references +rather than the link itself. +This option cancels the +.Fl P +option. +.It Fl l +(The lowercase letter +.Dq ell . ) +List in long format. +(See below.) +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the long listing. +.It Fl m +Stream output format; list files across the page, separated by commas. +.It Fl n +Display user and group IDs numerically, +rather than converting to a user or group name in a long +.Pq Fl l +output. +This option turns on the +.Fl l +option. +.It Fl O +Include the file flags in a long +.Pq Fl l +output. +.It Fl o +List in long format, but omit the group id. +.It Fl P +If argument is a symbolic link, list the link itself rather than the +object the link references. +This option cancels the +.Fl H +and +.Fl L +options. +.It Fl p +Write a slash +.Pq Ql / +after each filename if that file is a directory. +.It Fl q +Force printing of non-graphic characters in file names as +the character +.Ql \&? ; +this is the default when output is to a terminal. +.It Fl R +Recursively list subdirectories encountered. +.It Fl r +Reverse the order of the sort to get reverse +lexicographical order or the oldest entries first (or largest files +last, if combined with sort by size +.It Fl S +Sort files by size +.It Fl s +Display the number of file system blocks actually used by each file, in units +of 512 bytes, where partial units are rounded up to the next integer value. +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the listing. +The environment variable +.Ev BLOCKSIZE +overrides the unit size of 512 bytes. +.It Fl T +When used with the +.Fl l +(lowercase letter +.Dq ell ) +option, display complete time information for the file, including +month, day, hour, minute, second, and year. +.It Fl t +Sort by time modified (most recently modified +first) before sorting the operands by lexicographical +order. +.It Fl u +Use time of last access, +instead of last modification +of the file for sorting +.Pq Fl t +or long printing +.Pq Fl l . +.It Fl U +Use time of file creation, instead of last modification for sorting +.Pq Fl t +or long output +.Pq Fl l . +.It Fl v +Force unedited printing of non-graphic characters; this is the default when +output is not to a terminal. +.It Fl W +Display whiteouts when scanning directories. +.Pq Fl S +flag). +.It Fl w +Force raw printing of non-printable characters. +This is the default +when output is not to a terminal. +.It Fl x +The same as +.Fl C , +except that the multi-column output is produced with entries sorted +across, rather than down, the columns. +.El +.Pp +The +.Fl 1 , C , x , +and +.Fl l +options all override each other; +the last one specified determines the format used. +.Pp +The +.Fl c +and +.Fl u +options override each other; the last one specified determines +the file time used. +.Pp +The +.Fl B , b , w , +and +.Fl q +options all override each other; +the last one specified determines the format used +for non-printable characters. +.Pp +The +.Fl H , L +and +.Fl P +options all override each other (either partially or fully); they +are applied in the order specified. +.Pp +By default, +.Nm ls +lists one entry per line to standard +output; the exceptions are to terminals or when the +.Fl C +or +.Fl x +options are specified. +.Pp +File information is displayed with one or more +.Ao blank Ac Ns s +separating the information associated with the +.Fl i , s , +and +.Fl l +options. +.Ss The Long Format +If the +.Fl l +option is given, the following information +is displayed for each file: +file mode, +number of links, owner name, group name, +number of bytes in the file, abbreviated +month, day-of-month file was last modified, +hour file last modified, minute file last +modified, and the pathname. +In addition, for each directory whose contents are displayed, +the total number of 512-byte blocks used by the files in the directory +is displayed on a line by itself, +immediately before the information for the files in the directory. +If the file or directory has extended attributes, +the permissions field printed by the +.Fl l +option is followed by a '@' character. +Otherwise, if the file or directory has extended security information +(such as an access control list), +the permissions field printed by the +.Fl l +option is followed by a '+' character. +.Pp +If the modification time of the file +is more than 6 months in the past or future, +then the year of the last modification +is displayed in place of the hour and minute fields. +.Pp +If the owner or group names are not a known user or group name, +or the +.Fl n +option is given, +the numeric ID's are displayed. +.Pp +If the file is a character special or block special file, +the major and minor device numbers for the file are displayed +in the size field. +If the file is a symbolic link, +the pathname of the linked-to file is preceded by +.Dq Li -> . +.Pp +The file mode printed under the +.Fl l +option consists of the +entry type, owner permissions, and group permissions. +The entry type character describes the type of file, +as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy b +Block special file. +.It Sy c +Character special file. +.It Sy d +Directory. +.It Sy l +Symbolic link. +.It Sy s +Socket link. +.It Sy p +.Tn FIFO . +.It Sy \- +Regular file. +.El +.Pp +The next three fields +are three characters each: +owner permissions, +group permissions, and +other permissions. +Each field has three character positions: +.Bl -enum -offset indent +.It +If +.Sy r , +the file is readable; if +.Sy \- , +it is not readable. +.It +If +.Sy w , +the file is writable; if +.Sy \- , +it is not writable. +.It +The first of the following that applies: +.Bl -tag -width 4n -offset indent +.It Sy S +If in the owner permissions, the file is not executable and +set-user-ID mode is set. +If in the group permissions, the file is not executable +and set-group-ID mode is set. +.It Sy s +If in the owner permissions, the file is executable +and set-user-ID mode is set. +If in the group permissions, the file is executable +and setgroup-ID mode is set. +.It Sy x +The file is executable or the directory is +searchable. +.It Sy \- +The file is neither readable, writable, executable, +nor set-user-ID nor set-group-ID mode, nor sticky. +(See below.) +.El +.Pp +These next two apply only to the third character in the last group +(other permissions). +.Bl -tag -width 4n -offset indent +.It Sy T +The sticky bit is set +(mode +.Li 1000 ) , +but not execute or search permission. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.It Sy t +The sticky bit is set (mode +.Li 1000 ) , +and is searchable or executable. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.El +.El +.Sh EXAMPLES +The following is how to do an +.Nm ls +listing sorted by increasing size +.Pp +.Dl "ls -lrS" +.Sh DIAGNOSTICS +.Ex -std +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm ls : +.Bl -tag -width ".Ev CLICOLOR_FORCE" +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It Ev CLICOLOR +Use +\*[Ai] +color sequences to distinguish file types. +See +.Ev LSCOLORS +below. +In addition to the file types mentioned in the +.Fl F +option some extra attributes (setuid bit set, etc.) are also displayed. +The colorization is dependent on a terminal type with the proper +.Xr termcap 5 +capabilities. +The default +.Dq Li cons25 +console has the proper capabilities, +but to display the colors in an +.Xr xterm 1 , +for example, +the +.Ev TERM +variable must be set to +.Dq Li xterm-color . +Other terminal types may require similar adjustments. +Colorization +is silently disabled if the output isn't directed to a terminal +unless the +.Ev CLICOLOR_FORCE +variable is defined. +.It Ev CLICOLOR_FORCE +Color sequences are normally disabled if the output isn't directed to +a terminal. +This can be overridden by setting this flag. +The +.Ev TERM +variable still needs to reference a color capable terminal however +otherwise it is not possible to determine which color sequences to +use. +.It Ev COLUMNS +If this variable contains a string representing a +decimal integer, it is used as the +column position width for displaying +multiple-text-column output. +The +.Nm ls +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C +and +.Fl x . ) +.It Ev LANG +The locale to use when determining the order of day and month in the long +.Fl l +format output. +See +.Xr environ 7 +for more information. +.It Ev LSCOLORS +The value of this variable describes what color to use for which +attribute when colors are enabled with +.Ev CLICOLOR . +This string is a concatenation of pairs of the format +.Ar f Ns Ar b , +where +.Ar f +is the foreground color and +.Ar b +is the background color. +.Pp +The color designators are as follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy a +black +.It Sy b +red +.It Sy c +green +.It Sy d +brown +.It Sy e +blue +.It Sy f +magenta +.It Sy g +cyan +.It Sy h +light grey +.It Sy A +bold black, usually shows up as dark grey +.It Sy B +bold red +.It Sy C +bold green +.It Sy D +bold brown, usually shows up as yellow +.It Sy E +bold blue +.It Sy F +bold magenta +.It Sy G +bold cyan +.It Sy H +bold light grey; looks like bright white +.It Sy x +default foreground or background +.El +.Pp +Note that the above are standard +\*[Ai] +colors. +The actual display may differ +depending on the color capabilities of the terminal in use. +.Pp +The order of the attributes are as follows: +.Pp +.Bl -enum -offset indent -compact +.It +directory +.It +symbolic link +.It +socket +.It +pipe +.It +executable +.It +block special +.It +character special +.It +executable with setuid bit set +.It +executable with setgid bit set +.It +directory writable to others, with sticky bit +.It +directory writable to others, without sticky bit +.El +.Pp +The default is +.Qq "exfxcxdxbxegedabagacad" , +i.e. blue foreground and +default background for regular directories, black foreground and red +background for setuid executables, etc. +.It Ev LS_COLWIDTHS +If this variable is set, it is considered to be a +colon-delimited list of minimum column widths. +Unreasonable +and insufficient widths are ignored (thus zero signifies +a dynamically sized column). +Not all columns have changeable widths. +The fields are, +in order: inode, block count, number of links, user name, +group name, flags, file size, file name. +.It Ev TERM +The +.Ev CLICOLOR +functionality depends on a terminal type with color capabilities. +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh COMPATIBILITY +The group field is now automatically included in the long listing for +files in order to be compatible with the +.St -p1003.2 +specification. +.Sh LEGACY DESCRIPTION +In legacy mode, the +.Fl f +option does not turn on the +.Fl a +option and the +.Fl g , +.Fl n , +and +.Fl o +options do not turn on the +.Fl l +option. +.Pp +Also, the +.Fl o +option causes the file flags to be included in a long (-l) output; +there is no +.Fl O +option. +.Pp +When +.Fl H +is specified (and not overridden by +.Fl L +or +.Fl P ) +and a file argument is a symlink +that resolves to a non-directory file, +the output will reflect the nature of the link, +rather than that of the file. +In legacy operation, the output will describe the file. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chmod 1 , +.Xr sort 1 , +.Xr xterm 1 , +.Xr compat 5 , +.Xr termcap 5 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm ls +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +An +.Nm ls +command appeared in +.At v1 . +.Sh BUGS +To maintain backward compatibility, the relationships between the many +options are quite complex. diff --git a/file_cmds/ls/ls.c b/file_cmds/ls/ls.c new file mode 100644 index 00000000..a4745b29 --- /dev/null +++ b/file_cmds/ls/ls.c @@ -0,0 +1,1021 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)ls.c 8.5 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef COLORLS +#include "termcap.h" +#include +#endif +#ifdef __APPLE__ +#include +#include +#include +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ +#include "ls.h" +#include "extern.h" +#include "ios_error.h" + +/* + * Upward approximation of the maximum number of characters needed to + * represent a value of integral type t as a string, excluding the + * NUL terminator, with provision for a sign. + */ +#define STRBUF_SIZEOF(t) (1 + CHAR_BIT * sizeof(t) / 3 + 1) + +static void display(FTSENT *, FTSENT *); +static u_quad_t makenines(u_quad_t); +static int mastercmp(const FTSENT **, const FTSENT **); +static void traverse(int, char **, int); + +static void (*printfcn)(DISPLAY *); +static int (*sortfcn)(const FTSENT *, const FTSENT *); + +__thread long blocksize; /* block size units */ +__thread int termwidth = 80; /* default terminal width */ +static int output; /* If anything output. */ + +/* flags */ +__thread int f_accesstime; /* use time of last access */ +__thread int f_birthtime; /* use time of file birth */ +__thread int f_flags; /* show flags associated with a file */ +__thread int f_humanval; /* show human-readable file sizes */ +__thread int f_inode; /* print inode */ +static int f_kblocks; /* print size in kilobytes */ +static int f_listdir; /* list actual directory, not contents */ +static int f_listdot; /* list files beginning with . */ +__thread int f_longform; /* long listing format */ +__thread int f_nonprint; /* show unprintables as ? */ +static int f_nosort; /* don't sort output */ +__thread int f_notabs; /* don't use tab-separated multi-col output */ +__thread int f_numericonly; /* don't convert uid/gid to name */ +__thread int f_octal; /* show unprintables as \xxx */ +__thread int f_octal_escape; /* like f_octal but use C escapes if possible */ +static int f_recursive; /* ls subdirectories also */ +static int f_reversesort; /* reverse whatever sort is used */ +__thread int f_sectime; /* print the real time for all files */ +static int f_singlecol; /* use single column output */ +__thread int f_size; /* list size in short listing */ +__thread int f_slash; /* similar to f_type, but only for dirs */ +__thread int f_sortacross; /* sort across rows, not down columns */ +__thread int f_statustime; /* use time of last mode change */ +__thread int f_stream; /* stream the output, separate with commas */ +static int f_timesort; /* sort by time vice name */ +static int f_sizesort; /* sort by size */ +__thread int f_type; /* add type character for non-regular files */ +static int f_whiteout; /* show whiteout entries */ +__thread int f_acl; /* show ACLs in long listing */ +__thread int f_xattr; /* show extended attributes in long listing */ +__thread int f_group; /* show group */ +__thread int f_owner; /* show owner */ +#ifdef COLORLS +__thread int f_color; /* add type in color for non-regular files */ + +char *ansi_bgcol; /* ANSI sequence to set background colour */ +char *ansi_fgcol; /* ANSI sequence to set foreground colour */ +char *ansi_coloff; /* ANSI sequence to reset colours */ +char *attrs_off; /* ANSI sequence to turn off attributes */ +char *enter_bold; /* ANSI sequence to set color to bold mode */ +#endif + +static void initializeAllFlags() +{ + termwidth = 80; + f_accesstime = 0; /* use time of last access */ + f_birthtime = 0; /* use time of file birth */ + f_flags = 0; /* show flags associated with a file */ + f_humanval = 0; /* show human-readable file sizes */ + f_inode = 0; /* print inode */ + f_kblocks = 0; /* print size in kilobytes */ + f_listdir = 0; /* list actual directory, not contents */ + f_listdot = 0; /* list files beginning with . */ + f_longform = 0; /* long listing format */ + f_nonprint = 0; /* show unprintables as ? */ + f_nosort = 0; /* don't sort output */ + f_notabs = 0; /* don't use tab-separated multi-col output */ + f_numericonly = 0; /* don't convert uid/gid to name */ + f_octal = 0; /* show unprintables as \xxx */ + f_octal_escape = 0; /* like f_octal but use C escapes if possible */ + f_recursive = 0; /* ls subdirectories also */ + f_reversesort = 0; /* reverse whatever sort is used */ + f_sectime = 0; /* print the real time for all files */ + f_singlecol = 0; /* use single column output */ + f_size = 0; /* list size in short listing */ + f_slash = 0; /* similar to f_type, but only for dirs */ + f_sortacross = 0; /* sort across rows, not down columns */ + f_statustime = 0; /* use time of last mode change */ + f_stream = 0; /* stream the output, separate with commas */ + f_timesort = 0; /* sort by time vice name */ + f_sizesort = 0; /* sort by size */ + f_type = 0; /* add type character for non-regular files */ + f_whiteout = 0; /* show whiteout entries */ + f_acl = 0; /* show ACLs in long listing */ + f_xattr = 0; /* show extended attributes in long listing */ + f_group = 0; /* show group */ + f_owner = 0; /* show owner */ +#ifdef COLORLS + f_color = 0; /* add type in color for non-regular files */ +#endif + optind = 1; + opterr = 1; + optreset = 1; + output = 0; +} + + +static int rval; + +int +ls_main(int argc, char *argv[]) +{ + static char dot[] = ".", *dotav[] = {dot, NULL}; + struct winsize win; + int ch, fts_options, notused; + char *p; +#ifdef COLORLS + char termcapbuf[1024]; /* termcap definition buffer */ + char tcapbuf[512]; /* capability buffer */ + char *bp = tcapbuf; +#endif + + if (argc < 1) + ls_usage(); + // re-initialize all flags: + initializeAllFlags(); + rval = 0; + (void)setlocale(LC_ALL, ""); + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + // iOS: we *are* a terminal, but file(thread_stdout) doesn't tell it + if (ios_isatty(STDOUT_FILENO)) { + termwidth = 80; + if ((p = getenv("COLUMNS")) != NULL && *p != '\0') + termwidth = atoi(p); + else if (ioctl(fileno(thread_stdout), TIOCGWINSZ, &win) != -1 && + win.ws_col > 0) + termwidth = win.ws_col; + f_nonprint = 1; + } else { + f_singlecol = 1; + /* retrieve environment variable, in case of explicit -C */ + p = getenv("COLUMNS"); + if (p) + termwidth = atoi(p); + } + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1@ABCFGHLOPRSTUWabcdefghiklmnopqrstuvwx")) + != -1) { + switch (ch) { + /* + * The -1, -C, -x and -l options all override each other so + * shell aliasing works right. + */ + case '1': + f_singlecol = 1; + f_longform = 0; + f_stream = 0; + break; + case 'B': + f_nonprint = 0; + f_octal = 1; + f_octal_escape = 0; + break; + case 'C': + f_sortacross = f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + break; + case 'x': + f_sortacross = 1; + f_longform = 0; + f_singlecol = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = f_birthtime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = f_birthtime = 0; + break; + case 'U': + f_birthtime = 1; + f_statustime = f_accesstime = 0; + break; + case 'F': + f_type = 1; + f_slash = 0; + break; + case 'H': + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options &= ~FTS_LOGICAL; + fts_options |= FTS_PHYSICAL; + fts_options |= FTS_COMFOLLOWDIR; + } else + fts_options |= FTS_COMFOLLOW; + break; + case 'G': + setenv("CLICOLOR", "", 1); + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); + } + break; + case 'P': + fts_options &= ~(FTS_COMFOLLOW|FTS_COMFOLLOWDIR); + fts_options &= ~FTS_LOGICAL; + fts_options |= FTS_PHYSICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + fts_options |= FTS_SEEDOT; + f_listdot = 1; + } + break; + case 'g': /* Compatibility with Unix03 */ + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_group = 1; + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } + break; + case 'h': + f_humanval = 1; + break; + case 'i': + f_inode = 1; + break; + case 'k': + f_kblocks = 1; + break; + case 'm': + f_stream = 1; + f_singlecol = 0; + f_longform = 0; + break; + case 'n': + f_numericonly = 1; + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } + break; + case 'o': + if (COMPAT_MODE("bin/ls", "Unix2003")) { + f_owner = 1; + f_longform = 1; + f_singlecol = 0; + f_stream = 0; + } else { + f_flags = 1; + } + break; + case 'p': + f_slash = 1; + f_type = 1; + break; + case 'q': + f_nonprint = 1; + f_octal = 0; + f_octal_escape = 0; + break; + case 'r': + f_reversesort = 1; + break; + case 'S': + /* Darwin 1.4.1 compatibility */ + f_sizesort = 1; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + f_timesort = 1; + break; + case 'W': + f_whiteout = 1; + break; + case 'v': + /* Darwin 1.4.1 compatibility */ + f_nonprint = 0; + break; + case 'b': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 1; + break; + case 'w': + f_nonprint = 0; + f_octal = 0; + f_octal_escape = 0; + break; + case 'e': + f_acl = 1; + break; + case '@': + f_xattr = 1; + break; + case 'O': + f_flags = 1; + break; + default: + case '?': + ls_usage(); + } + } + argc -= optind; + argv += optind; + + /* Enabling of colours is conditional on the environment. */ + if (getenv("CLICOLOR") && + ((ios_isatty(STDOUT_FILENO)) || getenv("CLICOLOR_FORCE"))) { +#ifdef COLORLS +#ifndef TARGET_OS_IPHONE + if (tgetent(termcapbuf, getenv("TERM")) == 1) { + ansi_fgcol = tgetstr("AF", &bp); + ansi_bgcol = tgetstr("AB", &bp); + attrs_off = tgetstr("me", &bp); + enter_bold = tgetstr("md", &bp); + + /* To switch colours off use 'op' if + * available, otherwise use 'oc', or + * don't do colours at all. */ + ansi_coloff = tgetstr("op", &bp); + if (!ansi_coloff) + ansi_coloff = tgetstr("oc", &bp); + if (ansi_fgcol && ansi_bgcol && ansi_coloff) + f_color = 1; + } +#else /* TARGET_OS_IPHONE */ + ansi_fgcol = "\033[3" ; // tgetstr("AF", &bp); + ansi_bgcol = "\033[4" ; // tgetstr("AB", &bp); + attrs_off = "\033[0m"; // tgetstr("me", &bp); + enter_bold = "\033[1m"; // tgetstr("md", &bp); + ansi_coloff = "\033[39;49m"; // tgetstr("op", &bp); + if (ansi_fgcol && ansi_bgcol && ansi_coloff) + f_color = 1; +#endif + } +#else + (void)fprintf(thread_stderr, "Color support not compiled in.\n"); +#endif /*COLORLS*/ + +#ifdef COLORLS + if (f_color) { + /* + * We can't put tabs and color sequences together: + * column number will be incremented incorrectly + * for "stty oxtabs" mode. + */ +#ifndef TARGET_OS_IPHONE + f_notabs = 1; +#endif + // (void)signal(SIGINT, colorquit); + // (void)signal(SIGQUIT, colorquit); + parsecolors(getenv("LSCOLORS")); + } +#endif + + /* + * If not -F, -i, -l, -s or -t options, don't require stat + * information, unless in color mode in which case we do + * need this to determine which colors to display. + */ + if (!f_inode && !f_longform && !f_size && !f_timesort && !f_type && !f_sizesort +#ifdef COLORLS + && !f_color +#endif + ) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type && !f_inode) + fts_options |= FTS_COMFOLLOW; + + /* + * If -W, show whiteout entries + */ +#ifdef FTS_WHITEOUT + if (f_whiteout) + fts_options |= FTS_WHITEOUT; +#endif + + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + if (f_kblocks) + blocksize = 2; + else { + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } + } + /* Select a sort function. */ + if (f_reversesort) { + if (f_sizesort) + sortfcn = revsizecmp; + else if (!f_timesort) + sortfcn = revnamecmp; + else if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else if (f_birthtime) + sortfcn = revbirthcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + } else { + if (f_sizesort) + sortfcn = sizecmp; + else if (!f_timesort) + sortfcn = namecmp; + else if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else if (f_birthtime) + sortfcn = birthcmp; + else /* Use modification time. */ + sortfcn = modcmp; + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_longform) + printfcn = printlong; + else if (f_stream) + printfcn = printstream; + else + printfcn = printcol; + + if (argc) + traverse(argc, argv, fts_options); + else + traverse(1, dotav, fts_options); + // reset signal: + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + exit(rval); +} + + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(int argc, char *argv[], int options) +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options, error; + + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) { + err(1, "fts_open"); + } + + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) { + fts_close(ftsp); + return; + } + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + if (COMPAT_MODE("bin/ls", "Unix2003")) { + rval = 1; + } + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) { + fts_set(ftsp, p, FTS_SKIP); + break; + } + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (output) + (void)fprintf(thread_stdout, "\n%s:\n", p->fts_path); + else if (argc > 1) { + (void)fprintf(thread_stdout, "%s:\n", p->fts_path); + output = 1; + } + chp = fts_children(ftsp, ch_options); + if (COMPAT_MODE("bin/ls", "Unix2003") && ((options & FTS_LOGICAL)!=0)) { + FTSENT *curr; + for (curr = chp; curr; curr = curr->fts_link) { + if (curr->fts_info == FTS_SLNONE) + curr->fts_number = NO_PRINT; + } + } + display(p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_SLNONE: /* Same as default unless Unix conformance */ + if (COMPAT_MODE("bin/ls", "Unix2003")) { + if ((options & FTS_LOGICAL)!=0) { /* -L was specified */ + warnx("%s: %s", p->fts_name, strerror(p->fts_errno ?: ENOENT)); + rval = 1; + } + } + break; + default: + break; + } + error = errno; + fts_close(ftsp); + errno = error; + + if (errno) { + err(1, "fts_read"); + } +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(FTSENT *p, FTSENT *list) +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + off_t maxsize; + u_int64_t btotal, maxblock; + u_long lattrlen, maxlen, maxnlink, maxlattr; + ino_t maxinode; + int bcfile, maxflags; + gid_t maxgroup; + uid_t maxuser; + size_t flen, ulen, glen; + char *initmax; + int entries, needstats; + const char *user, *group; + char *flags, *lattr = NULL; + char buf[STRBUF_SIZEOF(u_quad_t) + 1]; + char ngroup[STRBUF_SIZEOF(uid_t) + 1]; + char nuser[STRBUF_SIZEOF(gid_t) + 1]; +#ifdef __APPLE__ + acl_entry_t dummy; + ssize_t xattr_size; + char *filename; + char path[MAXPATHLEN+1]; +#endif // __APPLE__ + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signaled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + btotal = 0; + initmax = getenv("LS_COLWIDTHS"); + /* Fields match -lios order. New ones should be added at the end. */ + maxlattr = maxblock = maxinode = maxlen = maxnlink = + maxuser = maxgroup = maxflags = maxsize = 0; + if (initmax != NULL && *initmax != '\0') { + char *initmax2, *jinitmax; + int ninitmax; + + /* Fill-in "::" as "0:0:0" for the sake of scanf. */ + jinitmax = initmax2 = malloc(strlen(initmax) * 2 + 2); + if (jinitmax == NULL) { + err(1, "malloc"); + } + if (*initmax == ':') + strcpy(initmax2, "0:"), initmax2 += 2; + else + *initmax2++ = *initmax, *initmax2 = '\0'; + for (initmax++; *initmax != '\0'; initmax++) { + if (initmax[-1] == ':' && initmax[0] == ':') { + *initmax2++ = '0'; + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } else { + *initmax2++ = initmax[0]; + initmax2[1] = '\0'; + } + } + if (initmax2[-1] == ':') + strcpy(initmax2, "0"); + + ninitmax = sscanf(jinitmax, +#if _DARWIN_FEATURE_64_BIT_INODE + " %llu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", +#else + " %lu : %qu : %lu : %i : %i : %i : %qu : %lu : %lu ", +#endif + &maxinode, &maxblock, &maxnlink, &maxuser, + &maxgroup, &maxflags, &maxsize, &maxlen, &maxlattr); + f_notabs = 1; + switch (ninitmax) { + case 0: + maxinode = 0; + /* FALLTHROUGH */ + case 1: + maxblock = 0; + /* FALLTHROUGH */ + case 2: + maxnlink = 0; + /* FALLTHROUGH */ + case 3: + maxuser = 0; + /* FALLTHROUGH */ + case 4: + maxgroup = 0; + /* FALLTHROUGH */ + case 5: + maxflags = 0; + /* FALLTHROUGH */ + case 6: + maxsize = 0; + /* FALLTHROUGH */ + case 7: + maxlen = 0; + /* FALLTHROUGH */ + case 8: + maxlattr = 0; + /* FALLTHROUGH */ +#ifdef COLORLS + if (!f_color) +#endif + f_notabs = 0; + /* FALLTHROUGH */ + default: + break; + } + maxinode = makenines(maxinode); + maxblock = makenines(maxblock); + maxnlink = makenines(maxnlink); + maxsize = makenines(maxsize); + } + bcfile = 0; + flags = NULL; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + rval = 1; + continue; + } + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (f_octal || f_octal_escape) { + u_long t = len_octal(cur->fts_name, cur->fts_namelen); + + if (t > maxlen) + maxlen = t; + } + if (needstats) { + sp = cur->fts_statp; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (sp->st_size > maxsize) + maxsize = sp->st_size; + + btotal += sp->st_blocks; + if (f_longform) { + if (f_numericonly) { + (void)snprintf(nuser, sizeof(nuser), + "%u", sp->st_uid); + (void)snprintf(ngroup, sizeof(ngroup), + "%u", sp->st_gid); + user = nuser; + group = ngroup; + } else { + user = user_from_uid(sp->st_uid, 0); + group = group_from_gid(sp->st_gid, 0); + } + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = fflagstostr(sp->st_flags); + if (flags != NULL && *flags == '\0') { + free(flags); + flags = strdup("-"); + } + if (flags == NULL) { + err(1, "fflagstostr"); + } + flen = strlen(flags); + if (flen > (size_t)maxflags) + maxflags = flen; + } else + flen = 0; + lattr = NULL; + lattrlen = 0; + + if ((np = calloc(1, sizeof(NAMES) + lattrlen + + ulen + glen + flen + 4)) == NULL) + { + err(1, "malloc"); + } + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); +#ifdef __APPLE__ + if (cur->fts_level == FTS_ROOTLEVEL) { + filename = cur->fts_name; + } else { + snprintf(path, sizeof(path), "%s/%s", cur->fts_parent->fts_accpath, cur->fts_name); + filename = path; + } + xattr_size = listxattr(filename, NULL, 0, XATTR_NOFOLLOW); + if (xattr_size < 0) { + xattr_size = 0; + } + if ((xattr_size > 0) && f_xattr) { + /* collect sizes */ + np->xattr_names = malloc(xattr_size); + listxattr(filename, np->xattr_names, xattr_size, XATTR_NOFOLLOW); + for (char *name = np->xattr_names; name < np->xattr_names + xattr_size; + name += strlen(name)+1) { + np->xattr_sizes = reallocf(np->xattr_sizes, (np->xattr_count+1) * sizeof(np->xattr_sizes[0])); + np->xattr_sizes[np->xattr_count] = getxattr(filename, name, 0, 0, 0, XATTR_NOFOLLOW); + np->xattr_count++; + } + } + /* symlinks can not have ACLs */ + np->acl = acl_get_link_np(filename, ACL_TYPE_EXTENDED); + if (np->acl) { + if (acl_get_entry(np->acl, ACL_FIRST_ENTRY, &dummy) == -1) { + acl_free(np->acl); + np->acl = NULL; + } + } + if (xattr_size > 0) { + np->mode_suffix = '@'; + } else if (np->acl) { + np->mode_suffix = '+'; + } else { + np->mode_suffix = ' '; + } + if (!f_acl) { + acl_free(np->acl); + np->acl = NULL; + } +#endif // __APPLE__ + if (S_ISCHR(sp->st_mode) || + S_ISBLK(sp->st_mode)) + bcfile = 1; + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + free(flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.bcfile = bcfile; + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_lattr = maxlattr; + d.s_group = maxgroup; +#if _DARWIN_FEATURE_64_BIT_INODE + (void)snprintf(buf, sizeof(buf), "%llu", maxinode); +#else + (void)snprintf(buf, sizeof(buf), "%lu", maxinode); +#endif + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%lu", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", (u_int64_t)maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; + } + printfcn(&d); + output = 1; + + if (f_longform) { + for (cur = list; cur; cur = cur->fts_link) { + np = cur->fts_pointer; + if (np) { + if (np->acl) { + acl_free(np->acl); + } + free(np->xattr_names); + free(np->xattr_sizes); + free(np); + cur->fts_pointer = NULL; + } + } + } +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp(const FTSENT **a, const FTSENT **b) +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) + return (namecmp(*a, *b)); + + if (a_info != b_info && + (*a)->fts_level == FTS_ROOTLEVEL && !f_listdir) { + if (a_info == FTS_D) + return (1); + if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} + +/* + * Makenines() returns (10**n)-1. This is useful for converting a width + * into a number that wide in decimal. + */ +static u_quad_t +makenines(u_quad_t n) +{ + u_long i; + u_quad_t reg; + + reg = 1; + /* Use a loop instead of pow(), since all values of n are small. */ + for (i = 0; i < n; i++) + reg *= 10; + reg--; + + return reg; +} diff --git a/file_cmds/ls/ls.h b/file_cmds/ls/ls.h new file mode 100644 index 00000000..691ab6cc --- /dev/null +++ b/file_cmds/ls/ls.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)ls.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/ls/ls.h,v 1.18 2002/05/19 02:51:36 tjr Exp $ + */ + +#define NO_PRINT 1 + +extern __thread long blocksize; /* block size units */ + +extern __thread int f_accesstime; /* use time of last access */ +extern __thread int f_birthtime; /* use time of file birth */ +extern __thread int f_flags; /* show flags associated with a file */ +extern __thread int f_humanval; /* show human-readable file sizes */ +extern __thread int f_inode; /* print inode */ +extern __thread int f_longform; /* long listing format */ +extern __thread int f_octal; /* print unprintables in octal */ +extern __thread int f_octal_escape; /* like f_octal but use C escapes if possible */ +extern __thread int f_nonprint; /* show unprintables as ? */ +extern __thread int f_sectime; /* print the real time for all files */ +extern __thread int f_size; /* list size in short listing */ +extern __thread int f_slash; /* append a '/' if the file is a directory */ +extern __thread int f_sortacross; /* sort across rows, not down columns */ +extern __thread int f_statustime; /* use time of last mode change */ +extern __thread int f_notabs; /* don't use tab-separated multi-col output */ +extern __thread int f_type; /* add type character for non-regular files */ +extern __thread int f_acl; /* print ACLs in long format */ +extern __thread int f_xattr; /* print extended attributes in long format */ +extern __thread int f_group; /* list group without owner */ +extern __thread int f_owner; /* list owner without group */ +#ifdef COLORLS +extern __thread int f_color; /* add type in color for non-regular files */ +#endif +extern __thread int f_numericonly; /* don't convert uid/gid to name */ + +#ifdef __APPLE__ +#include +#endif // __APPLE__ + +typedef struct { + FTSENT *list; + u_int64_t btotal; + int bcfile; + int entries; + int maxlen; + u_int s_block; + u_int s_flags; + u_int s_lattr; + u_int s_group; + u_int s_inode; + u_int s_nlink; + u_int s_size; + u_int s_user; +} DISPLAY; + +typedef struct { + char *user; + char *group; + char *flags; +#ifndef __APPLE__ + char *lattr; +#else + char *xattr_names; /* f_xattr */ + int *xattr_sizes; + acl_t acl; /* f_acl */ + int xattr_count; + char mode_suffix; /* @ | + | */ +#endif /* __APPLE__ */ + char data[1]; +} NAMES; diff --git a/file_cmds/ls/print.c b/file_cmds/ls/print.c new file mode 100644 index 00000000..09f79d56 --- /dev/null +++ b/file_cmds/ls/print.c @@ -0,0 +1,1029 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)print.c 8.4 (Berkeley) 4/17/94"; +#endif /* not lint */ +#endif +#include +__RCSID("$FreeBSD: src/bin/ls/print.c,v 1.57 2002/08/29 14:29:09 keramida Exp $"); + +#include +#include +#ifdef __APPLE__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include +#include +#include "libutil.h" +#include +#include +#include +#include +#include +#ifdef COLORLS +#include +#include "termcap.h" +#include +#endif +#include /* intmax_t */ +#include +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "ls.h" +#include "extern.h" +#include +#include "ios_error.h" + + +static int printaname(FTSENT *, u_long, u_long); +static void printlink(FTSENT *); +static void printtime(time_t); +static int printtype(u_int); +static void printsize(size_t, off_t); +#ifdef COLORLS +static void endcolor(int); +static int colortype(mode_t); +#endif + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +#ifdef COLORLS +/* Most of these are taken from */ +typedef enum Colors { + C_DIR, /* directory */ + C_LNK, /* symbolic link */ + C_SOCK, /* socket */ + C_FIFO, /* pipe */ + C_EXEC, /* executable */ + C_BLK, /* block special */ + C_CHR, /* character special */ + C_SUID, /* setuid executable */ + C_SGID, /* setgid executable */ + C_WSDIR, /* directory writeble to others, with sticky + * bit */ + C_WDIR, /* directory writeble to others, without + * sticky bit */ + C_NUMCOLORS /* just a place-holder */ +} Colors; + +static const char *defcolors = "exfxcxdxbxegedabagacad"; + +/* colors for file types */ +static struct { + int num[2]; + int bold; +} colors[C_NUMCOLORS]; +#endif + +void +printscol(DISPLAY *dp) +{ + FTSENT *p; + + assert(dp); + if (COMPAT_MODE("bin/ls", "Unix2003") && (dp->list != NULL)) { + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + } + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +/* + * print name in current style + */ +static int +printname(const char *name) +{ + if (f_octal || f_octal_escape) + return prn_octal(name); + else if (f_nonprint) + return prn_printable(name); + else + return prn_normal(name); +} + +/* + * print access control list + */ +static struct { + acl_perm_t perm; + char *name; + int flags; +#define ACL_PERM_DIR (1<<0) +#define ACL_PERM_FILE (1<<1) +} acl_perms[] = { + {ACL_READ_DATA, "read", ACL_PERM_FILE}, + {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR}, + {ACL_WRITE_DATA, "write", ACL_PERM_FILE}, + {ACL_ADD_FILE, "add_file", ACL_PERM_DIR}, + {ACL_EXECUTE, "execute", ACL_PERM_FILE}, + {ACL_SEARCH, "search", ACL_PERM_DIR}, + {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_APPEND_DATA, "append", ACL_PERM_FILE}, + {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR}, + {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR}, + {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR}, + {0, NULL, 0} +}; + +static struct { + acl_flag_t flag; + char *name; + int flags; +} acl_flags[] = { + {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR}, + {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR}, + {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR}, + {0, NULL, 0} +}; + +// mbr_identifier_translate source code +#define MBR_REC_TYPE_USER 1 +#define MBR_REC_TYPE_GROUP 2 + +/* only supported by mbr_identifier_translate for target type */ +#define ID_TYPE_UID_OR_GID 30 +#define ID_TYPE_NAME 31 +#define ID_TYPE_WINDOWS_FQN 32 + +static const uuid_t _user_compat_prefix = {0xff, 0xff, 0xee, 0xee, 0xdd, 0xdd, 0xcc, 0xcc, 0xbb, 0xbb, 0xaa, 0xaa, 0x00, 0x00, 0x00, 0x00}; +static const uuid_t _group_compat_prefix = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00}; + +#define COMPAT_PREFIX_LEN (sizeof(uuid_t) - sizeof(id_t)) + +static int +_ios_mbr_od_available(void) +{ +#if DS_AVAILABLE + xpc_pipe_t pipe = _mbr_xpc_pipe(false); + if (pipe != NULL) { + xpc_release(pipe); + return 1; + } +#endif + return 0; +} + +static int +ios_mbr_identifier_translate(int id_type, const void *identifier, size_t identifier_size, int target_type, void **result, int *rec_type) +{ +#if DS_AVAILABLE + xpc_object_t payload, reply; +#endif + id_t tempID; + size_t identifier_len; + int rc = EIO; + + if (identifier == NULL || result == NULL || identifier_size == 0) return EIO; + + if (identifier_size == -1) { + identifier_size = strlen(identifier); + } else { + /* 10898647: For types that are known to be strings, send the smallest necessary amount of data. */ + switch (id_type) { + case ID_TYPE_USERNAME: + case ID_TYPE_GROUPNAME: + case ID_TYPE_GROUP_NFS: + case ID_TYPE_USER_NFS: + case ID_TYPE_X509_DN: + case ID_TYPE_KERBEROS: + case ID_TYPE_NAME: + identifier_len = strlen(identifier); + if (identifier_size > identifier_len) { + identifier_size = identifier_len; + } + break; + } + } + + switch (target_type) { + case ID_TYPE_GID: + case ID_TYPE_UID: + case ID_TYPE_UID_OR_GID: + /* shortcut UUIDs using compatibilty prefixes */ + if (id_type == ID_TYPE_UUID) { + const uint8_t *uu = identifier; + + if (identifier_size != sizeof(uuid_t)) return EINVAL; + + if (memcmp(uu, _user_compat_prefix, COMPAT_PREFIX_LEN) == 0) { + id_t *tempRes = malloc(sizeof(*tempRes)); + memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID)); + (*tempRes) = ntohl(tempID); + (*result) = tempRes; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_USER; + } + return 0; + } else if (memcmp(uu, _group_compat_prefix, COMPAT_PREFIX_LEN) == 0) { + id_t *tempRes = malloc(sizeof(*tempRes)); + memcpy(&tempID, &uu[COMPAT_PREFIX_LEN], sizeof(tempID)); + (*tempRes) = ntohl(tempID); + (*result) = tempRes; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_GROUP; + } + return 0; + } + } + break; + + case ID_TYPE_UUID: + /* if this is a UID or GID translation, we shortcut UID/GID 0 */ + /* or if no OD, we return compatibility UUIDs */ + switch (id_type) { + case ID_TYPE_UID: + if (identifier_size != sizeof(tempID)) return EINVAL; + + tempID = *((id_t *) identifier); + if ((tempID == 0) || (_ios_mbr_od_available() == 0)) { + uint8_t *tempUU = malloc(sizeof(uuid_t)); + uuid_copy(tempUU, _user_compat_prefix); + *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID); + (*result) = tempUU; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_USER; + } + return 0; + } + break; + + case ID_TYPE_GID: + if (identifier_size != sizeof(tempID)) return EINVAL; + + tempID = *((id_t *) identifier); + if ((tempID == 0) || (_ios_mbr_od_available() == 0)) { + uint8_t *tempUU = malloc(sizeof(uuid_t)); + uuid_copy(tempUU, _group_compat_prefix); + *((id_t *) &tempUU[COMPAT_PREFIX_LEN]) = htonl(tempID); + (*result) = tempUU; + if (rec_type != NULL) { + (*rec_type) = MBR_REC_TYPE_GROUP; + } + return 0; + } + break; + } + break; + } + +#if DS_AVAILABLE + payload = xpc_dictionary_create(NULL, NULL, 0); + if (payload == NULL) return EIO; + + xpc_dictionary_set_int64(payload, "requesting", target_type); + xpc_dictionary_set_int64(payload, "type", id_type); + xpc_dictionary_set_data(payload, "identifier", identifier, identifier_size); + + reply = _od_rpc_call("mbr_identifier_translate", payload, _mbr_xpc_pipe); + if (reply != NULL) { + const void *reply_id; + size_t idLen; + + rc = (int) xpc_dictionary_get_int64(reply, "error"); + if (rc == 0) { + reply_id = xpc_dictionary_get_data(reply, "identifier", &idLen); + if (reply_id != NULL) { + char *identifier = malloc(idLen); + + memcpy(identifier, reply_id, idLen); // should already be NULL terminated, etc. + (*result) = identifier; + + if (rec_type != NULL) { + (*rec_type) = (int) xpc_dictionary_get_int64(reply, "rectype"); + } + } else { + (*result) = NULL; + rc = ENOENT; + } + } + + xpc_release(reply); + } + + xpc_release(payload); +#endif + + return rc; +} + + +static char * +uuid_to_name(uuid_t *uu) +{ + int type; + char *name = NULL; + char *recname = NULL; + +#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */ + name = (char *) malloc(MAXNAMETAG); + + if (NULL == name) { + err(1, "malloc"); + } + + if (f_numericonly) { + goto errout; + } + + if (ios_mbr_identifier_translate(ID_TYPE_UUID, *uu, sizeof(*uu), ID_TYPE_NAME, (void **) &recname, &type)) { + goto errout; + } + + snprintf(name, MAXNAMETAG, "%s:%s", (type == MBR_REC_TYPE_USER ? "user" : "group"), recname); + free(recname); + + return name; +errout: + uuid_unparse_upper(*uu, name); + + return name; +} + +static void +printxattr(DISPLAY *dp, int count, char *buf, int sizes[]) +{ + for (int i = 0; i < count; i++) { + putchar('\t'); + printname(buf); + putchar('\t'); + printsize(dp->s_size, sizes[i]); + putchar('\n'); + buf += strlen(buf) + 1; + } +} + +static void +printacl(acl_t acl, int isdir) +{ + acl_entry_t entry = NULL; + int index; + uuid_t *applicable; + char *name = NULL; + acl_tag_t tag; + acl_flagset_t flags; + acl_permset_t perms; + char *type; + int i, first; + + + for (index = 0; + acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY : ACL_NEXT_ENTRY, &entry) == 0; + index++) { + if (acl_get_tag_type(entry, &tag) != 0) + continue; + if (acl_get_flagset_np(entry, &flags) != 0) + continue; + if (acl_get_permset(entry, &perms) != 0) + continue; + if ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL) + continue; + name = uuid_to_name(applicable); + acl_free(applicable); + switch(tag) { + case ACL_EXTENDED_ALLOW: + type = "allow"; + break; + case ACL_EXTENDED_DENY: + type = "deny"; + break; + default: + type = "unknown"; + } + + (void)fprintf(thread_stdout, " %d: %s%s %s ", + index, + name, + acl_get_flag_np(flags, ACL_ENTRY_INHERITED) ? " inherited" : "", + type); + + if (name) + free(name); + + for (i = 0, first = 0; acl_perms[i].name != NULL; i++) { + if (acl_get_perm_np(perms, acl_perms[i].perm) == 0) + continue; + if (!(acl_perms[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) + continue; + (void)fprintf(thread_stdout, "%s%s", first++ ? "," : "", acl_perms[i].name); + } + for (i = 0; acl_flags[i].name != NULL; i++) { + if (acl_get_flag_np(flags, acl_flags[i].flag) == 0) + continue; + if (!(acl_flags[i].flags & (isdir ? ACL_PERM_DIR : ACL_PERM_FILE))) + continue; + (void)fprintf(thread_stdout, "%s%s", first++ ? "," : "", acl_flags[i].name); + } + + (void)putchar('\n'); + } + +} + +void +printlong(DISPLAY *dp) +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; +#ifdef COLORLS + int color_printed = 0; +#endif + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) +#if _DARWIN_FEATURE_64_BIT_INODE + (void)fprintf(thread_stdout, "%*llu ", dp->s_inode, (u_quad_t)sp->st_ino); +#else + (void)fprintf(thread_stdout, "%*lu ", dp->s_inode, (u_long)sp->st_ino); +#endif + if (f_size) + (void)fprintf(thread_stdout, "%*qu ", + dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize)); + strmode(sp->st_mode, buf); + np = p->fts_pointer; +#ifdef __APPLE__ + buf[10] = '\0'; /* make +/@ abut the mode */ + char str[2] = { np->mode_suffix, '\0' }; +#endif /* __APPLE__ */ + if (f_group && f_owner) { /* means print neither */ +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u ", buf, str, dp->s_nlink, + sp->st_nlink); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u ", buf, dp->s_nlink, + sp->st_nlink); +#endif /* __APPLE__ */ + } + else if (f_group) { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#endif /* __APPLE__ */ + } + else if (f_owner) { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user); +#endif /* __APPLE__ */ + } + else { +#ifdef __APPLE__ + (void)fprintf(thread_stdout, "%s%s %*u %-*s %-*s ", buf, str, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); +#else /* ! __APPLE__ */ + (void)fprintf(thread_stdout, "%s %*u %-*s %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); +#endif /* ! __APPLE__ */ + } + if (f_flags) + (void)fprintf(thread_stdout, "%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) + (void)fprintf(thread_stdout, "%3d, 0x%08x ", + major(sp->st_rdev), + (u_int)minor(sp->st_rdev)); + else + (void)fprintf(thread_stdout, "%3d, %3d ", + major(sp->st_rdev), minor(sp->st_rdev)); + else if (dp->bcfile) + (void)fprintf(thread_stdout, "%*s%*qu ", + 8 - dp->s_size, "", dp->s_size, (u_int64_t)sp->st_size); + else + printsize(dp->s_size, sp->st_size); + if (f_accesstime) + printtime(sp->st_atime); + else if (f_statustime) + printtime(sp->st_ctime); + else if (f_birthtime) + printtime(sp->st_birthtime); + else + printtime(sp->st_mtime); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + (void)printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); +#ifdef __APPLE__ + if (np->xattr_count && f_xattr) { + printxattr(dp, np->xattr_count, np->xattr_names, np->xattr_sizes); + } + if (np->acl != NULL && f_acl) { + printacl(np->acl, S_ISDIR(sp->st_mode)); + } +#endif /* __APPLE__ */ + } +} + +void +printstream(DISPLAY *dp) +{ + FTSENT *p; + extern __thread int termwidth; + int chcnt; + + for (p = dp->list, chcnt = 0; p; p = p->fts_link) { + if (p->fts_number == NO_PRINT) + continue; + if (strlen(p->fts_name) + chcnt + + (p->fts_link ? 2 : 0) >= (unsigned)termwidth) { + putchar('\n'); + chcnt = 0; + } + chcnt += printaname(p, dp->s_inode, dp->s_block); + if (p->fts_link) { + fprintf(thread_stdout, ", "); + chcnt += 2; + } + } + if (chcnt) + putchar('\n'); +} + +void +printcol(DISPLAY *dp) +{ + extern __thread int termwidth; + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base; + int chcnt; + int cnt; + int col; + int colwidth; + int endcol; + int num; + int numcols; + int numrows; + int row; + int tabwidth; + + if (f_notabs) + tabwidth = 1; + else + tabwidth = 8; + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if ((lastentries == -1) || (dp->entries > lastentries)) { + lastentries = dp->entries; + if ((array = realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn(NULL); + printscol(dp); + return; + } + } + memset(array, 0, dp->entries * sizeof(FTSENT *)); + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth = (colwidth + tabwidth) & ~(tabwidth - 1); + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + numcols = termwidth / colwidth; + numrows = num / numcols; + if (num % numcols) + ++numrows; + + assert(dp->list); + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)fprintf(thread_stdout, "total %qu\n", (u_int64_t)howmany(dp->btotal, blocksize)); + + base = 0; + for (row = 0; row < numrows; ++row) { + endcol = colwidth; + if (!f_sortacross) + base = row; + for (col = 0, chcnt = 0; col < numcols; ++col) { + assert(base < dp->entries); + chcnt += printaname(array[base], dp->s_inode, dp->s_block); + if (f_sortacross) + base++; + else + base += numrows; + if (base >= num) + break; + while ((cnt = ((chcnt + tabwidth) & ~(tabwidth - 1))) + <= endcol) { + if (f_sortacross && col + 1 >= numcols) + break; + (void)putchar(f_notabs ? ' ' : '\t'); + chcnt = cnt; + } + endcol += colwidth; + } + (void)putchar('\n'); + } +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(FTSENT *p, u_long inodefield, u_long sizefield) +{ + struct stat *sp; + int chcnt; +#ifdef COLORLS + int color_printed = 0; +#endif + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) +#if _DARWIN_FEATURE_64_BIT_INODE + chcnt += fprintf(thread_stdout, "%*llu ", (int)inodefield, (u_quad_t)sp->st_ino); +#else + chcnt += fprintf(thread_stdout, "%*lu ", (int)inodefield, (u_long)sp->st_ino); +#endif + if (f_size) + chcnt += fprintf(thread_stdout, "%*qu ", + (int)sizefield, (u_int64_t)howmany(sp->st_blocks, blocksize)); +#ifdef COLORLS + if (f_color) + color_printed = colortype(sp->st_mode); +#endif + chcnt += printname(p->fts_name); +#ifdef COLORLS + if (f_color && color_printed) + endcolor(0); +#endif + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(time_t ftime) +{ + char longstring[80]; + static time_t now; + const char *format; + static int d_first = -1; + + if (d_first < 0) + d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + if (now == 0) + now = time(NULL); + +#define SIXMONTHS ((365 / 2) * 86400) + if (f_sectime) + /* mmm dd hh:mm:ss yyyy || dd mmm hh:mm:ss yyyy */ + format = d_first ? "%e %b %T %Y " : "%b %e %T %Y "; + else if (COMPAT_MODE("bin/ls", "Unix2003")) { + if (ftime + SIXMONTHS > now && ftime <= now) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = d_first ? "%e %b %R " : "%b %e %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = d_first ? "%e %b %Y " : "%b %e %Y "; + } + else if (ftime + SIXMONTHS > now && ftime < now + SIXMONTHS) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = d_first ? "%e %b %R " : "%b %e %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = d_first ? "%e %b %Y " : "%b %e %Y "; + strftime(longstring, sizeof(longstring), format, localtime(&ftime)); + fputs(longstring, thread_stdout); +} + +static int +printtype(u_int mode) +{ + + if (f_slash) { + if ((mode & S_IFMT) == S_IFDIR) { + (void)putchar('/'); + return (1); + } + return (0); + } + + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); + case S_IFWHT: + (void)putchar('%'); + return (1); + default: + break; + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +#ifdef COLORLS +static int +putch(int c) +{ + (void)putchar(c); + return 0; +} + +static int +writech(int c) +{ + char tmp = c; + + //(void)write(fileno(thread_stdout), &tmp, 1); + fwrite(&tmp, 1, 1, thread_stdout); + return 0; +} + +static void +printcolor(Colors c) +{ + char ansiseq[10]; + + if (colors[c].bold) + fprintf(thread_stdout, "%s", enter_bold); + // tputs(enter_bold, 1, putch); + + if (colors[c].num[0] != -1) { +#ifndef TARGET_OS_IPHONE + ansiseq = tgoto(ansi_fgcol, 0, colors[c].num[0]); +#else + sprintf(ansiseq, "%s%dm", ansi_fgcol,colors[c].num[0]); +#endif + if (ansiseq) + fprintf(thread_stdout, "%s", ansiseq); + // tputs(ansiseq, 1, putch); + } + if (colors[c].num[1] != -1) { +#ifndef TARGET_OS_IPHONE + ansiseq = tgoto(ansi_bgcol, 0, colors[c].num[1]); +#else + sprintf(ansiseq, "%s%dm", ansi_bgcol,colors[c].num[1]); +#endif + if (ansiseq) + fprintf(thread_stdout, "%s", ansiseq); + // tputs(ansiseq, 1, putch); + } +} + +static void +endcolor(int sig) +{ + fprintf(thread_stdout, "%s", ansi_coloff); + fprintf(thread_stdout, "%s", attrs_off); + // tputs(ansi_coloff, 1, sig ? writech : putch); + // tputs(attrs_off, 1, sig ? writech : putch); +} + +static int +colortype(mode_t mode) +{ + switch (mode & S_IFMT) { + case S_IFDIR: + if (mode & S_IWOTH) + if (mode & S_ISTXT) + printcolor(C_WSDIR); + else + printcolor(C_WDIR); + else + printcolor(C_DIR); + return (1); + case S_IFLNK: + printcolor(C_LNK); + return (1); + case S_IFSOCK: + printcolor(C_SOCK); + return (1); + case S_IFIFO: + printcolor(C_FIFO); + return (1); + case S_IFBLK: + printcolor(C_BLK); + return (1); + case S_IFCHR: + printcolor(C_CHR); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + if (mode & S_ISUID) + printcolor(C_SUID); + else if (mode & S_ISGID) + printcolor(C_SGID); + else + printcolor(C_EXEC); + return (1); + } + return (0); +} + +void +parsecolors(const char *cs) +{ + int i; + int j; + int len; + char c[2]; + short legacy_warn = 0; + + if (cs == NULL) + cs = ""; /* LSCOLORS not set */ + len = strlen(cs); + for (i = 0; i < C_NUMCOLORS; i++) { + colors[i].bold = 0; + + if (len <= 2 * i) { + c[0] = defcolors[2 * i]; + c[1] = defcolors[2 * i + 1]; + } else { + c[0] = cs[2 * i]; + c[1] = cs[2 * i + 1]; + } + for (j = 0; j < 2; j++) { + /* Legacy colours used 0-7 */ + if (c[j] >= '0' && c[j] <= '7') { + colors[i].num[j] = c[j] - '0'; + if (!legacy_warn) { + fprintf(thread_stderr, + "warn: LSCOLORS should use " + "characters a-h instead of 0-9 (" + "see the manual page)\n"); + } + legacy_warn = 1; + } else if (c[j] >= 'a' && c[j] <= 'h') + colors[i].num[j] = c[j] - 'a'; + else if (c[j] >= 'A' && c[j] <= 'H') { + colors[i].num[j] = c[j] - 'A'; + colors[i].bold = 1; + } else if (tolower((unsigned char)c[j] == 'x')) + colors[i].num[j] = -1; + else { + fprintf(thread_stderr, + "error: invalid character '%c' in LSCOLORS" + " env var\n", c[j]); + colors[i].num[j] = -1; + } + } + } +} + +void +colorquit(int sig) +{ + endcolor(sig); + + // (void)signal(sig, SIG_DFL); + // back to default behaviour on both signals: + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + ios_exit(0); + // (void)kill(getpid(), sig); +} + +#endif /* COLORLS */ + +static void +printlink(FTSENT *p) +{ + int lnklen; + char name[MAXPATHLEN + 1]; + char path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(thread_stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)fprintf(thread_stdout, " -> "); + (void)printname(path); +} + +static void +printsize(size_t width, off_t bytes) +{ + + if (f_humanval) { + char buf[5]; + + humanize_number(buf, sizeof(buf), (int64_t)bytes, "", + HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL); + (void)fprintf(thread_stdout, "%5s ", buf); + } else + (void)fprintf(thread_stdout, "%*jd ", (u_int)width, (intmax_t)bytes); +} diff --git a/file_cmds/ls/util.c b/file_cmds/ls/util.c new file mode 100644 index 00000000..4e80de39 --- /dev/null +++ b/file_cmds/ls/util.c @@ -0,0 +1,245 @@ +/*- + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)util.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/bin/ls/util.c,v 1.38 2005/06/03 11:05:58 dd Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" +#include +#include "ios_error.h" + +int +prn_normal(const char *s) +{ +#ifdef TARGET_OS_IPHONE + // This is a bit extreme, but we need to keep the chars together for + // the rest of the pipeline + fprintf(thread_stdout, "%s", s); + return strlen(s); +#else + mbstate_t mbs; + wchar_t wc; + int i, n; + size_t clen; + + memset(&mbs, 0, sizeof(mbs)); + n = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + if (clen == (size_t)-2) { + n += fprintf(thread_stdout, "%s", s); + break; + } + if (clen == (size_t)-1) { + memset(&mbs, 0, sizeof(mbs)); + putchar((unsigned char)*s); + s++; + n++; + continue; + } + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + s += clen; + if (iswprint(wc)) + n += wcwidth(wc); + } + return (n); +#endif +} + +int +prn_printable(const char *s) +{ + mbstate_t mbs; + wchar_t wc; + int i, n; + size_t clen; + +#ifdef TARGET_OS_IPHONE + fprintf(thread_stdout, "%s", s); + return strlen(s); +#else + memset(&mbs, 0, sizeof(mbs)); + n = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + if (clen == (size_t)-1) { + putchar('?'); + s++; + n++; + memset(&mbs, 0, sizeof(mbs)); + continue; + } + if (clen == (size_t)-2) { + putchar('?'); + n++; + break; + } + if (!iswprint(wc)) { + putchar('?'); + s += clen; + n++; + continue; + } + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + s += clen; + n += wcwidth(wc); + } + return (n); +#endif +} + +/* + * The fts system makes it difficult to replace fts_name with a different- + * sized string, so we just calculate the real length here and do the + * conversion in prn_octal() + * + * XXX when using f_octal_escape (-b) rather than f_octal (-B), the + * length computed by len_octal may be too big. I just can't be buggered + * to fix this as an efficient fix would involve a lookup table. Same goes + * for the rather inelegant code in prn_octal. + * + * DES 1998/04/23 + */ + +size_t +len_octal(const char *s, int len) +{ + mbstate_t mbs; + wchar_t wc; + size_t clen, r; + + memset(&mbs, 0, sizeof(mbs)); + r = 0; + while (len != 0 && (clen = mbrtowc(&wc, s, len, &mbs)) != 0) { + if (clen == (size_t)-1) { + r += 4; + s++; + len--; + memset(&mbs, 0, sizeof(mbs)); + continue; + } + if (clen == (size_t)-2) { + r += 4 * len; + break; + } + if (iswprint(wc)) + r++; + else + r += 4 * clen; + s += clen; + } + return (r); +} + +int +prn_octal(const char *s) +{ + static const char esc[] = "\\\\\"\"\aa\bb\ff\nn\rr\tt\vv"; + const char *p; + mbstate_t mbs; + wchar_t wc; + size_t clen; + unsigned char ch; + int goodchar, i, len, prtlen; + + memset(&mbs, 0, sizeof(mbs)); + len = 0; + while ((clen = mbrtowc(&wc, s, MB_LEN_MAX, &mbs)) != 0) { + goodchar = clen != (size_t)-1 && clen != (size_t)-2; + if (goodchar && iswprint(wc) && wc != L'\"' && wc != L'\\') { + for (i = 0; i < (int)clen; i++) + putchar((unsigned char)s[i]); + len += wcwidth(wc); + } else if (goodchar && f_octal_escape && wc >= 0 && + wc <= (wchar_t)UCHAR_MAX && + (p = strchr(esc, (char)wc)) != NULL) { + putchar('\\'); + putchar(p[1]); + len += 2; + } else { + if (goodchar) + prtlen = clen; + else if (clen == (size_t)-1) + prtlen = 1; + else + prtlen = strlen(s); + for (i = 0; i < prtlen; i++) { + ch = (unsigned char)s[i]; + putchar('\\'); + putchar('0' + (ch >> 6)); + putchar('0' + ((ch >> 3) & 7)); + putchar('0' + (ch & 7)); + len += 4; + } + } + if (clen == (size_t)-2) + break; + if (clen == (size_t)-1) { + memset(&mbs, 0, sizeof(mbs)); + s++; + } else + s += clen; + } + return (len); +} + +void +ls_usage(void) +{ + (void)fprintf(thread_stderr, +#ifdef COLORLS + "usage: ls [-ABCFGHLOPRSTUWabcdefghiklmnopqrstuwx1]" +#else + "usage: ls [-ABCFHLOPRSTUWabcdefghiklmnopqrstuwx1]" +#endif + " [file ...]\n"); + exit(1); +} diff --git a/file_cmds/mkdir/mkdir.1 b/file_cmds/mkdir/mkdir.1 new file mode 100644 index 00000000..2903a002 --- /dev/null +++ b/file_cmds/mkdir/mkdir.1 @@ -0,0 +1,108 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94 +.\" $FreeBSD: src/bin/mkdir/mkdir.1,v 1.17 2002/06/30 06:50:16 tjr Exp $ +.\" +.Dd January 25, 1994 +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm +.Op Fl pv +.Op Fl m Ar mode +.Ar directory_name ... +.Sh DESCRIPTION +The +.Nm +utility creates the directories named as operands, in the order specified, +using mode +.Li rwxrwxrwx (\&0777) +as modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Pp +.Bl -tag -width indent +.It Fl m Ar mode +Set the file permission bits of the final created directory to +the specified mode. +The +.Ar mode +argument can be in any of the formats specified to the +.Xr chmod 1 +command. +If a symbolic mode is specified, the operation characters +.Dq + +and +.Dq - +are interpreted relative to an initial mode of +.Dq a=rwx . +.It Fl p +Create intermediate directories as required. +If this option is not specified, the full path prefix of each +operand must already exist. +On the other hand, with this option specified, no error will +be reported if a directory given as an operand already exists. +Intermediate directories are created with permission bits of +.Li rwxrwxrwx (\&0777) +as modified by the current umask, plus write and search +permission for the owner. +.It Fl v +Be verbose when creating directories, listing them as they are created. +.El +.Pp +The user must have write permission in the parent directory. +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr rmdir 1 +.Sh COMPATIBILITY +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/file_cmds/mkdir/mkdir.c b/file_cmds/mkdir/mkdir.c new file mode 100644 index 00000000..b3e45f8e --- /dev/null +++ b/file_cmds/mkdir/mkdir.c @@ -0,0 +1,150 @@ +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/mkdir/mkdir.c,v 1.26 2002/06/30 05:13:54 obrien Exp $"); + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static void usage(void); + +static int vflag; + +int +mkdir_main(int argc, char *argv[]) +{ + int ch, exitval, success, pflag; + mode_t omode, *set = (mode_t *)NULL; + char *mode; + // initialize flag + vflag = 0; + optind = 1; opterr = 1; optreset = 1; + + pflag = 0; + mode = NULL; + while ((ch = getopt(argc, argv, "m:pv")) != -1) + switch(ch) { + case 'm': + mode = optarg; + break; + case 'p': + pflag = 1; + break; + case 'v': + vflag = 1; + break; + case '?': + default: + usage(); + } + +// argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + if (mode == NULL) { + omode = S_IRWXU | S_IRWXG | S_IRWXO; + } else { + if ((set = setmode(mode)) == NULL) { + errx(1, "invalid file mode: %s", mode); + } + omode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); + free(set); + } + + for (exitval = 0; *argv != NULL; ++argv) { + success = 1; + if (pflag) { + int status = mkpath_np(*argv, omode); + if (status && status != EEXIST) { + fprintf(thread_stderr, "mkdir: %s: %s\n", *argv, strerror(status)); + // warnc(status, "%s", *argv); + success = 0; + } + } else if (mkdir(*argv, omode) < 0) { + if (errno == ENOTDIR || errno == ENOENT) + warn("%s", dirname(*argv)); + else + warn("%s", *argv); + success = 0; + } else if (vflag) + (void)fprintf(thread_stdout, "mkdir: created directory '%s'\n", *argv); + + if (!success) + exitval = 1; + /* + * The mkdir() and umask() calls both honor only the low + * nine bits, so if you try to set a mode including the + * sticky, setuid, setgid bits you lose them. Don't do + * this unless the user has specifically requested a mode, + * as chmod will (obviously) ignore the umask. + */ + if (success && mode != NULL && chmod(*argv, omode) == -1) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); // don't exit from main thread +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "usage: mkdir [-pv] [-m mode] directory ...\n"); + exit (EX_USAGE); +} diff --git a/file_cmds/mkfifo/mkfifo.1 b/file_cmds/mkfifo/mkfifo.1 new file mode 100644 index 00000000..39ca9823 --- /dev/null +++ b/file_cmds/mkfifo/mkfifo.1 @@ -0,0 +1,103 @@ +.\" $NetBSD: mkfifo.1,v 1.6 1997/10/19 05:11:52 lukem Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkfifo.1 8.2 (Berkeley) 1/5/94 +.\" +.Dd January 5, 1994 +.Dt MKFIFO 1 +.Os BSD 4.4 +.Sh NAME +.Nm mkfifo +.Nd make fifos +.Sh SYNOPSIS +.Nm mkfifo +.Op Fl m Ar mode +.Ar fifo_name ... +.Sh DESCRIPTION +.Nm mkfifo +creates the fifos requested, in the order specified. +By default, +the resulting fifos have mode +.Li \&0666 +(rw-rw-rw-), limited by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl m +Set the file permission bits of newly-created fifos to +.Ar mode , +without respect to the current umask. +.Pp +The mode is specified as in +.Xr chmod 1 . +In symbolic mode strings, the +.Dq + +and +.Dq - +operators are interpreted relative to an assumed initial mode of +.Dq a=rw +.El +.Pp +.Nm mkfifo +requires write permission in the parent directory. +.Pp +.Nm mkfifo +exits with 0 if successful, and with >0 if an error occurred. +.Sh LEGACY DESCRIPTION +In legacy mode, the fifo's file permission bits +are always limited by the current umask. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr mkdir 1 , +.Xr rm 1 , +.Xr umask 1 , +.Xr mkfifo 2 , +.Xr umask 2 , +.Xr compat 5 , +.Xr mknod 8 +.Sh STANDARDS +The +.Nm mkfifo +utility is expected to be +.St -p1003.2-92 +compliant. +.Sh HISTORY +.Nm mkfifo +command appeared in +.Bx 4.4 . diff --git a/file_cmds/mkfifo/mkfifo.c b/file_cmds/mkfifo/mkfifo.c new file mode 100644 index 00000000..c06fc5a1 --- /dev/null +++ b/file_cmds/mkfifo/mkfifo.c @@ -0,0 +1,128 @@ +/* $NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $ */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkfifo.c 8.2 (Berkeley) 1/5/94"; +#endif +__RCSID("$NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +int main __P((int, char **)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, exitval; + void * set; + mode_t mode = 0; + int m_used = 0; + + setlocale (LC_ALL, ""); + + /* The default mode is the value of the bitwise inclusive or of + S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH + modified by the file creation mask */ + if (!COMPAT_MODE("bin/mkfifo", "Unix2003")) { + mode = 0666 & ~umask(0); + } + + while ((ch = getopt(argc, argv, "m:")) != -1) + switch(ch) { + case 'm': + m_used = 1; + if (!(set = setmode(optarg))) { + errx(1, "invalid file mode."); + /* NOTREACHED */ + } + /* In symbolic mode strings, the + and - operators are + interpreted relative to an assumed initial mode of + a=rw. */ + mode = getmode (set, 0666); + break; + case '?': + default: + usage(); + } +// argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + if (COMPAT_MODE("bin/mkfifo", "Unix2003")) { + mode_t maskbits = umask(0); /* now must disable umask so -m mode won't be masked again */ + if (!m_used) + mode = 0666 & ~maskbits; + } + + for (exitval = 0; *argv; ++argv) { + if (mkfifo(*argv, mode) < 0) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: mkfifo [-m mode] fifoname ...\n"); + exit(1); +} diff --git a/file_cmds/mknod/mknod.8 b/file_cmds/mknod/mknod.8 new file mode 100644 index 00000000..eb4f2927 --- /dev/null +++ b/file_cmds/mknod/mknod.8 @@ -0,0 +1,137 @@ +.\" $NetBSD: mknod.8,v 1.15 1998/09/11 07:20:48 mycroft Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd September 11, 1998 +.Dt MKNOD 8 +.Os NetBSD 1.4 +.Sh NAME +.Nm mknod +.Nd make device special file +.Sh SYNOPSIS +.Nm +.Op Fl F Ar format +.Ar name +.Op Cm c | Cm b +.Ar major minor +.Nm +.Op Fl F Ar format +.Ar name +.Op Cm c | Cm b +.Ar major unit subunit +.Nm +.Ar name +.Op Cm c | Cm b +.Ar number +.Nm +.Ar name +.Ar w +.Sh DESCRIPTION +The +.Nm +command creates device special files. +.Pp +To make nodes manually, the required arguments are: +.Pp +.Bl -tag -width majorx +.It Ar name +Device name, for example +.Dq sd +for a SCSI disk on an HP300 or a +.Dq pty +for pseudo-devices. +.It Cm b | Cm c | Cm w +Type of device. If the +device is a block type device such as a tape or disk drive which needs +both cooked and raw special files, +the type is +.Cm b . +Whiteout nodes are type +.Cm w . +All other devices are character type devices, such as terminal +and pseudo devices, and are type +.Cm c . +.It Ar major +The major device number is an integer number which tells the kernel +which device driver entry point to use. +.It Ar minor +The minor device number tells the kernel which one of several similar +devices the node corresponds to; for example, it may be a specific serial +port or pty. +.It Ar unit and subunit +The unit and subunit numbers select a subset of a device; for example, the +unit may specify a particular SCSI disk, and the subunit a partition on +that disk. (Currently this form of specification is only supported by the +.Ar bsdos +format, for compatibility with the +.Bsx +.Xr mknod 8 . ) +.El +.Pp +Device numbers for different operating systems may be packed in a different +format. To create device nodes that may be used by such an operating system +(e.g. in an exported file system used for netbooting), the +.Fl F +option is used. The following formats are recognized: +native, +386bsd, +4bsd, +bsdos, +freebsd, +hpux, +isc, +linux, +netbsd, +osf1, +sco, +solaris, +sunos, +svr3, +svr4 and +ultrix. +.Pp +Alternatively, a single opaque device number may be specified. +.Sh SEE ALSO +.Xr mkfifo 1 , +.Xr mkfifo 2 , +.Xr mknod 2 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +The +.Fl F +option appeared in +.Nx 1.4 . diff --git a/file_cmds/mknod/mknod.c b/file_cmds/mknod/mknod.c new file mode 100644 index 00000000..c4848af5 --- /dev/null +++ b/file_cmds/mknod/mknod.c @@ -0,0 +1,405 @@ +/* $NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.\n"); +__RCSID("$NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char *[])); +static void usage __P((void)); +typedef dev_t pack_t __P((int, u_long [])); + + +pack_t pack_native; + +dev_t +pack_native(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if (major(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +pack_t pack_netbsd; + +dev_t +pack_netbsd(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if (major_netbsd(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_netbsd(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +pack_t pack_freebsd; + +dev_t +pack_freebsd(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if (major_freebsd(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_freebsd(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +pack_t pack_8_8; + +dev_t +pack_8_8(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if (major_8_8(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_8_8(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +pack_t pack_12_20; + +dev_t +pack_12_20(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_12_20(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +pack_t pack_14_18; + +dev_t +pack_14_18(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if (major_14_18(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_14_18(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +pack_t pack_8_24; + +dev_t +pack_8_24(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if (major_8_24(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_8_24(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +pack_t pack_bsdos; + +dev_t +pack_bsdos(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_12_20(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if (major_12_12_8(dev) != numbers[0]) + errx(1, "invalid major number"); + if (unit_12_12_8(dev) != numbers[1]) + errx(1, "invalid unit number"); + if (subunit_12_12_8(dev) != numbers[2]) + errx(1, "invalid subunit number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +struct format { + char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +int compare_format __P((const void *, const void *)); + +int +compare_format(key, element) + const void *key; + const void *element; +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +int +main(argc, argv) + int argc; + char **argv; +{ + char *name; + mode_t mode; + dev_t dev; + pack_t *pack; + u_long numbers[8]; + struct format *format; + char *p; + int n; + int ch; + + pack = pack_native; + + while ((ch = getopt(argc, argv, "F:")) != -1) { + switch (ch) { + case 'F': + format = bsearch(optarg, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + errx(1, "invalid format: %s", optarg); + pack = format->pack; + break; + + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 2 || argc > 10) + usage(); + + name = *argv; + argc--; + argv++; + + mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + if (*argv[0] == 'c') + mode |= S_IFCHR; + else if (*argv[0] == 'b') + mode |= S_IFBLK; + else if (*argv[0] == 'w') + mode |= S_IFWHT; + else + errx(1, "node type must be 'b' or 'c' or 'w'."); + argc--; + argv++; + + for (n = 0; n < argc; n++) { + if (S_ISWHT(mode)) { + errx(1, "whiteout nodes have no device numbers."); + } + numbers[n] = strtoul(argv[n], &p, 0); + if ((p && *p != '\0') || (numbers[n] == ULONG_MAX && errno == ERANGE)) + errx(1, "invalid number: %s", argv[n]); + } + + if (S_ISWHT(mode)) + dev = 0; + else if (argc == 1) + dev = (dev_t)numbers[0]; + else + dev = (*pack)(argc, numbers); + +#if 0 + printf("name: %s\nmode: %05o\ndev: %08x\n", name, mode, dev); +#else + if (mknod(name, mode, dev) < 0) + err(1, "%s", name); +#endif + + exit(0); +} + +void +usage() +{ + + fprintf(stderr, "usage: mknod [-F format] name [b | c] major minor\n"); + fprintf(stderr, " mknod [-F format] name [b | c] major unit subunit\n"); + fprintf(stderr, " mknod name [b | c] number\n"); + fprintf(stderr, " mknod name w\n"); + exit(1); +} diff --git a/file_cmds/mtree/commoncrypto.c b/file_cmds/mtree/commoncrypto.c new file mode 100644 index 00000000..f7556312 --- /dev/null +++ b/file_cmds/mtree/commoncrypto.c @@ -0,0 +1,239 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "commoncrypto.h" + +const int kSHA256NullTerminatedBuffLen = 65; +static const char hex[] = "0123456789abcdef"; + +/* Functions for SHA256_File_XATTRs */ +#define SHA256_Data(d, s, b) Digest_Data(kCCDigestSHA256, d, s, b) +char *Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf); +void Quicksort(char **array, int num); + +/* Generic version of libmd's *_File() functions. */ +char * +Digest_File(CCDigestAlg algorithm, const char *filename, char *buf) +{ + int fd; + __block CCDigestCtx ctx; + dispatch_queue_t queue; + dispatch_semaphore_t sema; + dispatch_io_t io; + __block int s_error = 0; + uint8_t digest[32]; // SHA256 is the biggest + size_t i, length; + + /* dispatch_io_create_with_path requires an absolute path */ + fd = open(filename, O_RDONLY); + if (fd < 0) { + return NULL; + } + + (void)fcntl(fd, F_NOCACHE, 1); + + (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); + + queue = dispatch_queue_create("com.apple.mtree.io", NULL); + os_assert(queue); + sema = dispatch_semaphore_create(0); + os_assert(sema); + + io = dispatch_io_create(DISPATCH_IO_STREAM, fd, queue, ^(int error) { + if (error != 0) { + s_error = error; + } + (void)close(fd); + (void)dispatch_semaphore_signal(sema); + }); + os_assert(io); + dispatch_io_read(io, 0, SIZE_MAX, queue, ^(__unused bool done, dispatch_data_t data, int error) { + if (data != NULL) { + (void)dispatch_data_apply(data, ^(__unused dispatch_data_t region, __unused size_t offset, const void *buffer, size_t size) { + (void)os_assumes_zero(CCDigestUpdate(&ctx, buffer, size)); + return (bool)true; + }); + } + + if (error != 0) { + s_error = error; + } + }); + dispatch_release(io); // it will close on its own + + (void)dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER); + + dispatch_release(queue); + dispatch_release(sema); + + if (s_error != 0) { + errno = s_error; + return NULL; + } + + /* Finalize and convert to hex. */ + (void)os_assumes_zero(CCDigestFinal(&ctx, digest)); + length = CCDigestOutputSize(&ctx); + os_assert(length <= sizeof(digest)); + for (i = 0; i < length; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + + return buf; +} + +char *SHA256_Path_XATTRs(char *path, char *buf) +{ + char *xattrsSummary = NULL; + int options = XATTR_SHOWCOMPRESSION | XATTR_NOFOLLOW; + ssize_t nameBufSize = listxattr(path, NULL, 0, options); + if (nameBufSize > 0) { + char *nameBuf = malloc(nameBufSize); + + listxattr(path, nameBuf, nameBufSize, options); + + size_t xattrsLen = 1; + size_t xattrIndex = 0; + char **xattrs = malloc(xattrsLen * sizeof(char *)); + char *nextName = nameBuf; + while (nextName < nameBuf + nameBufSize) + { + char *name = nextName; + if (xattrIndex == xattrsLen) { + xattrsLen *= 2; + xattrs = realloc(xattrs, xattrsLen * sizeof(char *)); + } + xattrs[xattrIndex++] = name; + nextName += strlen(name) + 1; + } + + // sort the xattr array as they're not guaranteed to come in the same order + qsort_b(xattrs, xattrIndex, sizeof(char *), ^(const void *l, const void *r) { + char *left = *(char **)l; + char *right = *(char **)r; + return strcmp(left, right); + }); + + // gather the data for the xattrs + bool didAddXATTR = false; + int xattrBufLen = kSHA256NullTerminatedBuffLen; + void *xattrBuf = malloc(xattrBufLen); // resized if necessary + char *digest; + ssize_t result = 0; + char *oldSummary = NULL; + for (int i = 0; i < xattrIndex; i++) { + char *name = xattrs[i]; + ssize_t xlen = getxattr(path, name, NULL, 0, 0, options); + if (xlen > xattrBufLen) { + xattrBufLen = xlen; + xattrBuf = realloc(xattrBuf, xattrBufLen); + } + bzero(xattrBuf, xattrBufLen); + result = getxattr(path, name, xattrBuf, xattrBufLen, 0, options); + if (result < 0) + err(1, "SHA256_Path_XATTRs getxattr of \"%s\" at path \"%s\" failed with error", name, path ); + + digest = SHA256_Data(xattrBuf, xattrBufLen, buf); + if (!digest) + err(1, "%s", xattrsSummary); + if (!didAddXATTR) + { + didAddXATTR = true; + asprintf(&xattrsSummary, "%s:%s", name, digest); + } else { + oldSummary = xattrsSummary; + asprintf(&xattrsSummary, "%s, %s:%s", oldSummary, name, digest); + free(oldSummary); + } + } + + free(xattrBuf); + free(nameBuf); + free(xattrs); + + digest = SHA256_Data(xattrsSummary, strlen(xattrsSummary) * sizeof(char), buf); + if (!digest) + err(1, "%s", xattrsSummary); + + free(xattrsSummary); + return digest; + } + return kNone; +} + +char *SHA256_Path_ACL(char *path, char *buf) +{ + int result = 0; + char *data = NULL; + char *digest = NULL; + + struct attrlist list = { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_EXTENDED_SECURITY, + }; + + struct ACLBuf { + uint32_t len; + attribute_set_t returned_attrs; + attrreference_t acl; + char buf[8192]; // current acls are up to 3116 bytes, but they may increase in the future + } __attribute__((aligned(4), packed)); + + struct ACLBuf aclBuf; + + result = getattrlist(path, &list, &aclBuf, sizeof(aclBuf), FSOPT_NOFOLLOW); + + if (result) + err(1, "SHA256_Path_ACL: getattrlist"); + + // if the path does not have an acl, return none + if ( ( ! ( aclBuf.returned_attrs.commonattr & ATTR_CMN_EXTENDED_SECURITY ) ) + || ( aclBuf.acl.attr_length == 0 ) ) { + return kNone; + } + + data = ((char*)&aclBuf.acl) + aclBuf.acl.attr_dataoffset; + + digest = SHA256_Data(data, aclBuf.acl.attr_length, buf); + if (!digest) + err(1, "SHA256_Path_ACL: SHA256_Data"); + + return digest; +} + +/* Functions for Digest_Path_* */ +char * +Digest_Data(CCDigestAlg algorithm, void *data, size_t size, char *buf) { + + uint8_t digest[32]; // SHA256 is the biggest + CCDigestCtx ctx; + size_t i, length; + + (void)os_assumes_zero(CCDigestInit(algorithm, &ctx)); + (void)os_assumes_zero(CCDigestUpdate(&ctx, data, size)); + + /* Finalize and convert to hex. */ + (void)os_assumes_zero(CCDigestFinal(&ctx, digest)); + length = CCDigestOutputSize(&ctx); + os_assert(length <= sizeof(digest)); + for (i = 0; i < length; i++) { + buf[i+i] = hex[digest[i] >> 4]; + buf[i+i+1] = hex[digest[i] & 0x0f]; + } + buf[i+i] = '\0'; + + return buf; +} + diff --git a/file_cmds/mtree/commoncrypto.h b/file_cmds/mtree/commoncrypto.h new file mode 100644 index 00000000..a894749a --- /dev/null +++ b/file_cmds/mtree/commoncrypto.h @@ -0,0 +1,15 @@ +#include + +#define kNone "none" + +extern const int kSHA256NullTerminatedBuffLen; + +#define MD5File(f, b) Digest_File(kCCDigestMD5, f, b) +#define SHA1_File(f, b) Digest_File(kCCDigestSHA1, f, b) +#define RIPEMD160_File(f, b) Digest_File(kCCDigestRMD160, f, b) +#define SHA256_File(f, b) Digest_File(kCCDigestSHA256, f, b) + +char *Digest_File(CCDigestAlg algorithm, const char *filename, char *buf); + +char *SHA256_Path_XATTRs(char *path, char *buf); +char *SHA256_Path_ACL(char *path, char *buf); \ No newline at end of file diff --git a/file_cmds/mtree/compare.c b/file_cmds/mtree/compare.c new file mode 100644 index 00000000..7413d2fd --- /dev/null +++ b/file_cmds/mtree/compare.c @@ -0,0 +1,483 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/compare.c,v 1.34 2005/03/29 11:44:17 tobez Exp $"); + +#include +#include +#include + +#include +#include +#include +#include +#ifndef __APPLE__ +#ifdef ENABLE_MD5 +#include +#endif +#ifdef ENABLE_RMD160 +#include +#endif +#ifdef ENABLE_SHA1 +#include +#endif +#ifdef ENABLE_SHA256 +#include +#endif +#endif /* !__APPLE__ */ +#include +#include +#include +#include +#include + +#include "mtree.h" +#include "extern.h" + +#ifdef __APPLE__ +#include "commoncrypto.h" +#endif /* __APPLE__ */ + +#define INDENTNAMELEN 8 +#define LABEL \ + if (!label++) { \ + len = printf("%s changed\n", RP(p)); \ + tab = "\t"; \ + } + +int +compare(char *name __unused, NODE *s, FTSENT *p) +{ + struct timeval tv[2]; + uint32_t val; + int fd, label; + off_t len; + char *cp; + const char *tab = ""; + char *fflags, *badflags; + u_long flags; + + label = 0; + switch(s->type) { + case F_BLOCK: + if (!S_ISBLK(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_CHAR: + if (!S_ISCHR(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_DIR: + if (!S_ISDIR(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_FIFO: + if (!S_ISFIFO(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_FILE: + if (!S_ISREG(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_LINK: + if (!S_ISLNK(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_SOCK: + if (!S_ISSOCK(p->fts_statp->st_mode)) { +typeerr: LABEL; + (void)printf("\ttype expected %s found %s\n", + ftype(s->type), inotype(p->fts_statp->st_mode)); + return (label); + } + break; + } + /* Set the uid/gid first, then set the mode. */ + if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { + LABEL; + (void)printf("%suser expected %lu found %lu", + tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid); + if (uflag) + if (chown(p->fts_accpath, s->st_uid, -1)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } + if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { + LABEL; + (void)printf("%sgid expected %lu found %lu", + tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid); + if (uflag) + if (chown(p->fts_accpath, -1, s->st_gid)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } + if (s->flags & F_MODE && + !S_ISLNK(p->fts_statp->st_mode) && + s->st_mode != (p->fts_statp->st_mode & MBITS)) { + LABEL; + (void)printf("%spermissions expected %#o found %#o", + tab, s->st_mode, p->fts_statp->st_mode & MBITS); + if (uflag) + if (chmod(p->fts_accpath, s->st_mode)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } + if (s->flags & F_NLINK && s->type != F_DIR && + s->st_nlink != p->fts_statp->st_nlink) { + LABEL; + (void)printf("%slink_count expected %u found %u\n", + tab, s->st_nlink, p->fts_statp->st_nlink); + tab = "\t"; + } + if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size && + !S_ISDIR(p->fts_statp->st_mode)) { + LABEL; + (void)printf("%ssize expected %jd found %jd\n", tab, + (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size); + tab = "\t"; + } + if ((s->flags & F_TIME) && + ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) || + (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) { + LABEL; + (void)printf("%smodification time expected %.24s.%09ld ", + tab, ctime(&s->st_mtimespec.tv_sec), s->st_mtimespec.tv_nsec); + (void)printf("found %.24s.%09ld", + ctime(&p->fts_statp->st_mtimespec.tv_sec), p->fts_statp->st_mtimespec.tv_nsec); + if (uflag) { + tv[0].tv_sec = s->st_mtimespec.tv_sec; + tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000; + tv[1] = tv[0]; + if (utimes(p->fts_accpath, tv)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + } else + (void)printf("\n"); + tab = "\t"; + } + if (s->flags & F_CKSUM) { + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { + LABEL; + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else if (crc(fd, &val, &len)) { + (void)close(fd); + LABEL; + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + (void)close(fd); + if (s->cksum != val) { + LABEL; + (void)printf("%scksum expected %lu found %lu\n", + tab, s->cksum, (unsigned long)val); + tab = "\t"; + } + } + } + if (s->flags & F_FLAGS) { + // There are unpublished flags that should not fail comparison + // we convert to string and back to filter them out + fflags = badflags = flags_to_string(p->fts_statp->st_flags); + if (strcmp("none", fflags) == 0) { + flags = 0; + } else if (strtofflags(&badflags, &flags, NULL) != 0) + errx(1, "invalid flag %s", badflags); + free(fflags); + if (s->st_flags != flags) { + LABEL; + fflags = flags_to_string(s->st_flags); + (void)printf("%sflags expected \"%s\"", tab, fflags); + free(fflags); + + fflags = flags_to_string(flags); + (void)printf(" found \"%s\"", fflags); + free(fflags); + + if (uflag) + if (chflags(p->fts_accpath, (u_int)s->st_flags)) + (void)printf(" not modified: %s\n", + strerror(errno)); + else + (void)printf(" modified\n"); + else + (void)printf("\n"); + tab = "\t"; + } + } +#ifdef ENABLE_MD5 + if (s->flags & F_MD5) { + char *new_digest, buf[33]; + + new_digest = MD5File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sMD5: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->md5digest)) { + LABEL; + printf("%sMD5 expected %s found %s\n", tab, s->md5digest, + new_digest); + tab = "\t"; + } + } +#endif /* ENABLE_MD5 */ +#ifdef ENABLE_SHA1 + if (s->flags & F_SHA1) { + char *new_digest, buf[41]; + + new_digest = SHA1_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->sha1digest)) { + LABEL; + printf("%sSHA-1 expected %s found %s\n", + tab, s->sha1digest, new_digest); + tab = "\t"; + } + } +#endif /* ENABLE_SHA1 */ +#ifdef ENABLE_RMD160 + if (s->flags & F_RMD160) { + char *new_digest, buf[41]; + + new_digest = RIPEMD160_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sRIPEMD160: %s: %s\n", tab, + p->fts_accpath, strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->rmd160digest)) { + LABEL; + printf("%sRIPEMD160 expected %s found %s\n", + tab, s->rmd160digest, new_digest); + tab = "\t"; + } + } +#endif /* ENABLE_RMD160 */ +#ifdef ENABLE_SHA256 + if (s->flags & F_SHA256) { + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; + + new_digest = SHA256_File(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath, + strerror(errno)); + tab = "\t"; + } else if (strcmp(new_digest, s->sha256digest)) { + LABEL; + printf("%sSHA-256 expected %s found %s\n", + tab, s->sha256digest, new_digest); + tab = "\t"; + } + } +#endif /* ENABLE_SHA256 */ + + if (s->flags & F_SLINK && + strcmp(cp = rlink(p->fts_accpath), s->slink)) { + LABEL; + (void)printf("%slink_ref expected %s found %s\n", + tab, s->slink, cp); + } + if ((s->flags & F_BTIME) && + ((s->st_birthtimespec.tv_sec != p->fts_statp->st_birthtimespec.tv_sec) || + (s->st_birthtimespec.tv_nsec != p->fts_statp->st_birthtimespec.tv_nsec))) { + LABEL; + (void)printf("%sbirth time expected %.24s.%09ld ", + tab, ctime(&s->st_birthtimespec.tv_sec), s->st_birthtimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_birthtimespec.tv_sec), p->fts_statp->st_birthtimespec.tv_nsec); + tab = "\t"; + } + if ((s->flags & F_ATIME) && + ((s->st_atimespec.tv_sec != p->fts_statp->st_atimespec.tv_sec) || + (s->st_atimespec.tv_nsec != p->fts_statp->st_atimespec.tv_nsec))) { + LABEL; + (void)printf("%saccess time expected %.24s.%09ld ", + tab, ctime(&s->st_atimespec.tv_sec), s->st_atimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_atimespec.tv_sec), p->fts_statp->st_atimespec.tv_nsec); + tab = "\t"; + } + if ((s->flags & F_CTIME) && + ((s->st_ctimespec.tv_sec != p->fts_statp->st_ctimespec.tv_sec) || + (s->st_ctimespec.tv_nsec != p->fts_statp->st_ctimespec.tv_nsec))) { + LABEL; + (void)printf("%smetadata modification time expected %.24s.%09ld ", + tab, ctime(&s->st_ctimespec.tv_sec), s->st_ctimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&p->fts_statp->st_ctimespec.tv_sec), p->fts_statp->st_ctimespec.tv_nsec); + tab = "\t"; + } + if (s->flags & F_PTIME) { + int supported; + struct timespec ptimespec = ptime(p->fts_accpath, &supported); + if (!supported) { + LABEL; + (void)printf("%stime added to parent folder expected %.24s.%09ld found that it is not supported\n", + tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec); + tab = "\t"; + } else if ((s->st_ptimespec.tv_sec != ptimespec.tv_sec) || + (s->st_ptimespec.tv_nsec != ptimespec.tv_nsec)) { + LABEL; + (void)printf("%stime added to parent folder expected %.24s.%09ld ", + tab, ctime(&s->st_ptimespec.tv_sec), s->st_ptimespec.tv_nsec); + (void)printf("found %.24s.%09ld\n", + ctime(&ptimespec.tv_sec), ptimespec.tv_nsec); + tab = "\t"; + } + } + if (s->flags & F_XATTRS) { + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; + new_digest = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sxattrsdigest missing, expected: %s\n", tab, s->xattrsdigest); + tab = "\t"; + } else if (strcmp(new_digest, s->xattrsdigest)) { + LABEL; + printf("%sxattrsdigest expected %s found %s\n", + tab, s->xattrsdigest, new_digest); + tab = "\t"; + } + } + if ((s->flags & F_INODE) && + (p->fts_statp->st_ino != s->st_ino)) { + LABEL; + (void)printf("%sinode expected %llu found %llu\n", + tab, s->st_ino, p->fts_ino); + tab = "\t"; + } + if (s->flags & F_ACL) { + char *new_digest, buf[kSHA256NullTerminatedBuffLen]; + new_digest = SHA256_Path_ACL(p->fts_accpath, buf); + if (!new_digest) { + LABEL; + printf("%sacldigest missing, expected: %s\n", tab, s->acldigest); + tab = "\t"; + } else if (strcmp(new_digest, s->acldigest)) { + LABEL; + printf("%sacldigest expected %s found %s\n", + tab, s->acldigest, new_digest); + tab = "\t"; + } + } + + return (label); +} + +const char * +inotype(u_int type) +{ + switch(type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); + case S_IFSOCK: + return ("socket"); + default: + return ("unknown"); + } + /* NOTREACHED */ +} + +const char * +ftype(u_int type) +{ + switch(type) { + case F_BLOCK: + return ("block"); + case F_CHAR: + return ("char"); + case F_DIR: + return ("dir"); + case F_FIFO: + return ("fifo"); + case F_FILE: + return ("file"); + case F_LINK: + return ("link"); + case F_SOCK: + return ("socket"); + default: + return ("unknown"); + } + /* NOTREACHED */ +} + +char * +rlink(char *name) +{ + static char lbuf[MAXPATHLEN]; + ssize_t len; + + if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1) + err(1, "line %d: %s", lineno, name); + lbuf[len] = '\0'; + return (lbuf); +} diff --git a/file_cmds/mtree/create.c b/file_cmds/mtree/create.c new file mode 100644 index 00000000..66992a73 --- /dev/null +++ b/file_cmds/mtree/create.c @@ -0,0 +1,508 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/create.c,v 1.37 2005/03/29 11:44:17 tobez Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef __APPLE__ +#ifdef ENABLE_MD5 +#include +#endif +#ifdef ENABLE_SHA1 +#include +#endif +#ifdef ENABLE_RMD160 +#include +#endif +#ifdef ENABLE_SHA256 +#include +#endif +#endif /* !__APPLE__ */ +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +#ifdef __APPLE__ +#include "commoncrypto.h" +#endif /* __APPLE__ */ + +#define INDENTNAMELEN 15 +#define MAXLINELEN 80 + +static gid_t gid; +static uid_t uid; +static mode_t mode; +static u_long flags = 0xffffffff; +static char *xattrs = kNone; +static char *acl = kNone; + +static int dsort(const FTSENT **, const FTSENT **); +static void output(int, int *, const char *, ...) __printflike(3, 4); +static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *, char **, char **); +static void statf(int, FTSENT *); + +void +cwalk(void) +{ + FTS *t; + FTSENT *p; + time_t cl; + char *argv[2], host[MAXHOSTNAMELEN]; + char dot[] = "."; + int indent = 0; + char *path; + + if (!nflag) { + (void)time(&cl); + (void)gethostname(host, sizeof(host)); + (void)printf( + "#\t user: %s\n#\tmachine: %s\n", + getlogin(), host); + (void)printf( + "#\t tree: %s\n#\t date: %s", + fullpath, ctime(&cl)); + } + + argv[0] = dot; + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) + err(1, "fts_open()"); + while ((p = fts_read(t))) { + if (iflag) + indent = p->fts_level * 4; + if (check_excludes(p->fts_name, p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } + switch(p->fts_info) { + case FTS_D: + if (!dflag) + (void)printf("\n"); + if (!nflag) { + path = escape_path(p->fts_path); + (void)printf("# %s\n", path); + free(path); + } + statd(t, p, &uid, &gid, &mode, &flags, &xattrs, &acl); + statf(indent, p); + break; + case FTS_DP: + if (!nflag && (p->fts_level > 0)) { + path = escape_path(p->fts_path); + (void)printf("%*s# %s\n", indent, "", path); + free(path); + } + (void)printf("%*s..\n", indent, ""); + if (!dflag) + (void)printf("\n"); + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + break; + default: + if (!dflag) + statf(indent, p); + break; + + } + } + (void)fts_close(t); + if (sflag && keys & F_CKSUM) + warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); +} + +static void +statf(int indent, FTSENT *p) +{ + struct group *gr; + struct passwd *pw; + uint32_t val; + off_t len; + int fd, offset; + char *fflags; + char *escaped_name; + + escaped_name = calloc(1, p->fts_namelen * 4 + 1); + if (escaped_name == NULL) + errx(1, "statf(): calloc() failed"); + strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB); + + if (iflag || S_ISDIR(p->fts_statp->st_mode)) + offset = printf("%*s%s", indent, "", escaped_name); + else + offset = printf("%*s %s", indent, "", escaped_name); + + free(escaped_name); + + if (offset > (INDENTNAMELEN + indent)) + offset = MAXLINELEN; + else + offset += printf("%*s", (INDENTNAMELEN + indent) - offset, ""); + + if (!S_ISREG(p->fts_statp->st_mode) && !dflag) + output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode)); + if (p->fts_statp->st_uid != uid) { + if (keys & F_UNAME) { + pw = getpwuid(p->fts_statp->st_uid); + if (pw != NULL) + output(indent, &offset, "uname=%s", pw->pw_name); + else if (wflag) + warnx("Could not get uname for uid=%u", + p->fts_statp->st_uid); + else + errx(1, + "Could not get uname for uid=%u", + p->fts_statp->st_uid); + } + if (keys & F_UID) + output(indent, &offset, "uid=%u", p->fts_statp->st_uid); + } + if (p->fts_statp->st_gid != gid) { + if (keys & F_GNAME) { + gr = getgrgid(p->fts_statp->st_gid); + if (gr != NULL) + output(indent, &offset, "gname=%s", gr->gr_name); + else if (wflag) + warnx("Could not get gname for gid=%u", + p->fts_statp->st_gid); + else + errx(1, + "Could not get gname for gid=%u", + p->fts_statp->st_gid); + } + if (keys & F_GID) + output(indent, &offset, "gid=%u", p->fts_statp->st_gid); + } + if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) + output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS); + if (keys & F_NLINK && p->fts_statp->st_nlink != 1) + output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink); + if (keys & F_SIZE) + output(indent, &offset, "size=%jd", + (intmax_t)p->fts_statp->st_size); + if (keys & F_TIME) + output(indent, &offset, "time=%ld.%09ld", + (long)p->fts_statp->st_mtimespec.tv_sec, + p->fts_statp->st_mtimespec.tv_nsec); + if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || + crc(fd, &val, &len)) + err(1, "%s", p->fts_accpath); + (void)close(fd); + output(indent, &offset, "cksum=%lu", (unsigned long)val); + } +#ifdef ENABLE_MD5 + if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[33]; + + digest = MD5File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "md5digest=%s", digest); + } +#endif /* ENABLE_MD5 */ +#ifdef ENABLE_SHA1 + if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[41]; + + digest = SHA1_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "sha1digest=%s", digest); + } +#endif /* ENABLE_SHA1 */ +#ifdef ENABLE_RMD160 + if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[41]; + + digest = RIPEMD160_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "ripemd160digest=%s", digest); + } +#endif /* ENABLE_RMD160 */ +#ifdef ENABLE_SHA256 + if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) { + char *digest, buf[kSHA256NullTerminatedBuffLen]; + + digest = SHA256_File(p->fts_accpath, buf); + if (!digest) + err(1, "%s", p->fts_accpath); + output(indent, &offset, "sha256digest=%s", digest); + } +#endif /* ENABLE_SHA256 */ + if (keys & F_SLINK && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) { + char visbuf[MAXPATHLEN * 4]; + char *s = rlink(p->fts_accpath); + strvis(visbuf, s, VIS_WHITE | VIS_OCTAL); + output(indent, &offset, "link=%s", visbuf); + } + if (keys & F_FLAGS && p->fts_statp->st_flags != flags) { + fflags = flags_to_string(p->fts_statp->st_flags); + output(indent, &offset, "flags=%s", fflags); + free(fflags); + } + if (keys & F_BTIME) { + output(indent, &offset, "btime=%ld.%09ld", + p->fts_statp->st_birthtimespec.tv_sec, + p->fts_statp->st_birthtimespec.tv_nsec); + } + // only check access time on regular files, as traversing a folder will update its access time + if (keys & F_ATIME && S_ISREG(p->fts_statp->st_mode)) { + output(indent, &offset, "atime=%ld.%09ld", + p->fts_statp->st_atimespec.tv_sec, + p->fts_statp->st_atimespec.tv_nsec); + } + if (keys & F_CTIME) { + output(indent, &offset, "ctime=%ld.%09ld", + p->fts_statp->st_ctimespec.tv_sec, + p->fts_statp->st_ctimespec.tv_nsec); + } + // date added to parent folder is only supported for files and directories + if (keys & F_PTIME && (S_ISREG(p->fts_statp->st_mode) || + S_ISDIR(p->fts_statp->st_mode))) { + int supported; + struct timespec ptimespec = ptime(p->fts_accpath, &supported); + if (supported) { + output(indent, &offset, "ptime=%ld.%09ld", + ptimespec.tv_sec, + ptimespec.tv_nsec); + } + } + if (keys & F_XATTRS) { + char *digest, buf[kSHA256NullTerminatedBuffLen]; + + digest = SHA256_Path_XATTRs(p->fts_accpath, buf); + if (digest && (strcmp(digest, xattrs) != 0)) { + output(indent, &offset, "xattrsdigest=%s", digest); + } + } + if (keys & F_INODE) { + output(indent, &offset, "inode=%llu", p->fts_statp->st_ino); + } + if (keys & F_ACL) { + char *digest, buf[kSHA256NullTerminatedBuffLen]; + + digest = SHA256_Path_ACL(p->fts_accpath, buf); + if (digest && (strcmp(digest, acl) != 0)) { + output(indent, &offset, "acldigest=%s", digest); + } + } + + (void)putchar('\n'); +} + +#define MAXGID 5000 +#define MAXUID 5000 +#define MAXMODE MBITS + 1 +#define MAXFLAGS 256 +#define MAXS 16 + +static int +statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags, char **pxattrs, char **pacl) +{ + FTSENT *p; + gid_t sgid; + uid_t suid; + mode_t smode; + u_long sflags; + struct group *gr; + struct passwd *pw; + gid_t savegid = *pgid; + uid_t saveuid = *puid; + mode_t savemode = *pmode; + u_long saveflags = *pflags; + char *savexattrs = *pxattrs; + char *saveacl = *pacl; + u_short maxgid, maxuid, maxmode, maxflags; + u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS]; + char *fflags; + static int first = 1; + + if ((p = fts_children(t, 0)) == NULL) { + if (errno) + err(1, "%s", RP(parent)); + return (1); + } + + bzero(g, sizeof(g)); + bzero(u, sizeof(u)); + bzero(m, sizeof(m)); + bzero(f, sizeof(f)); + + maxuid = maxgid = maxmode = maxflags = 0; + for (; p; p = p->fts_link) { + if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) { + smode = p->fts_statp->st_mode & MBITS; + if (smode < MAXMODE && ++m[smode] > maxmode) { + savemode = smode; + maxmode = m[smode]; + } + sgid = p->fts_statp->st_gid; + if (sgid < MAXGID && ++g[sgid] > maxgid) { + savegid = sgid; + maxgid = g[sgid]; + } + suid = p->fts_statp->st_uid; + if (suid < MAXUID && ++u[suid] > maxuid) { + saveuid = suid; + maxuid = u[suid]; + } + + /* + * XXX + * note that we don't count the most common xattr/acl digest + * so set will always the default value (none) + */ + + /* + * XXX + * note that the below will break when file flags + * are extended beyond the first 4 bytes of each + * half word of the flags + */ +#define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0)) + sflags = p->fts_statp->st_flags; + if (FLAGS2IDX(sflags) < MAXFLAGS && + ++f[FLAGS2IDX(sflags)] > maxflags) { + saveflags = sflags; + maxflags = f[FLAGS2IDX(sflags)]; + } + } + } + /* + * If the /set record is the same as the last one we do not need to output + * a new one. So first we check to see if anything changed. Note that we + * always output a /set record for the first directory. + */ + if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) || + (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) || + ((keys & F_MODE) && (*pmode != savemode)) || + ((keys & F_FLAGS) && (*pflags != saveflags)) || + (first)) { + first = 0; + if (dflag) + (void)printf("/set type=dir"); + else + (void)printf("/set type=file"); + if (keys & F_UNAME) { + pw = getpwuid(saveuid); + if (pw != NULL) + (void)printf(" uname=%s", pw->pw_name); + else if (wflag) + warnx( "Could not get uname for uid=%u", saveuid); + else + errx(1, "Could not get uname for uid=%u", saveuid); + } + if (keys & F_UID) + (void)printf(" uid=%lu", (u_long)saveuid); + if (keys & F_GNAME) { + gr = getgrgid(savegid); + if (gr != NULL) + (void)printf(" gname=%s", gr->gr_name); + else if (wflag) + warnx("Could not get gname for gid=%u", savegid); + else + errx(1, "Could not get gname for gid=%u", savegid); + } + if (keys & F_GID) + (void)printf(" gid=%lu", (u_long)savegid); + if (keys & F_MODE) + (void)printf(" mode=%#o", savemode); + if (keys & F_NLINK) + (void)printf(" nlink=1"); + if (keys & F_FLAGS) { + fflags = flags_to_string(saveflags); + (void)printf(" flags=%s", fflags); + free(fflags); + } + if (keys & F_XATTRS) + (void)printf(" xattrsdigest=%s", savexattrs); + if (keys & F_ACL) + (void)printf(" acldigest=%s", saveacl); + (void)printf("\n"); + *puid = saveuid; + *pgid = savegid; + *pmode = savemode; + *pflags = saveflags; + *pxattrs = savexattrs; + *pacl = saveacl; + } + return (0); +} + +static int +dsort(const FTSENT **a, const FTSENT **b) +{ + if (S_ISDIR((*a)->fts_statp->st_mode)) { + if (!S_ISDIR((*b)->fts_statp->st_mode)) + return (1); + } else if (S_ISDIR((*b)->fts_statp->st_mode)) + return (-1); + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +#include + +void +output(int indent, int *offset, const char *fmt, ...) +{ + va_list ap; + char buf[1024]; + va_start(ap, fmt); + (void)vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (*offset + strlen(buf) > MAXLINELEN - 3) { + (void)printf(" \\\n%*s", INDENTNAMELEN + indent, ""); + *offset = INDENTNAMELEN + indent; + } + *offset += printf(" %s", buf) + 1; +} diff --git a/file_cmds/mtree/excludes.c b/file_cmds/mtree/excludes.c new file mode 100644 index 00000000..89047711 --- /dev/null +++ b/file_cmds/mtree/excludes.c @@ -0,0 +1,112 @@ +/* + * Copyright 2000 Massachusetts Institute of Technology + * + * Permission to use, copy, modify, and distribute this software and + * its documentation for any purpose and without fee is hereby + * granted, provided that both the above copyright notice and this + * permission notice appear in all copies, that both the above + * copyright notice and this permission notice appear in all + * supporting documentation, and that the name of M.I.T. not be used + * in advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. M.I.T. makes + * no representations about the suitability of this software for any + * purpose. It is provided "as is" without express or implied + * warranty. + * + * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS + * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT + * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/excludes.c,v 1.8 2003/10/21 08:27:05 phk Exp $"); + +#include +#include /* XXX for mtree.h */ +#include + +#include +#include +#include +#include +#include +#include + +#include "mtree.h" /* XXX for extern.h */ +#include "extern.h" + +/* + * We're assuming that there won't be a whole lot of excludes, + * so it's OK to use a stupid algorithm. + */ +struct exclude { + LIST_ENTRY(exclude) link; + const char *glob; + int pathname; +}; +static LIST_HEAD(, exclude) excludes; + +void +init_excludes(void) +{ + LIST_INIT(&excludes); +} + +void +read_excludes_file(const char *name) +{ + FILE *fp; + char *line, *str; + struct exclude *e; + size_t len; + + fp = fopen(name, "r"); + if (fp == 0) + err(1, "%s", name); + + while ((line = fgetln(fp, &len)) != 0) { + if (line[len - 1] == '\n') + len--; + if (len == 0) + continue; + + str = malloc(len + 1); + e = malloc(sizeof *e); + if (str == 0 || e == 0) + errx(1, "memory allocation error"); + e->glob = str; + memcpy(str, line, len); + str[len] = '\0'; + if (strchr(str, '/')) + e->pathname = 1; + else + e->pathname = 0; + LIST_INSERT_HEAD(&excludes, e, link); + } + fclose(fp); +} + +int +check_excludes(const char *fname, const char *path) +{ + struct exclude *e; + + /* fnmatch(3) has a funny return value convention... */ +#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0) + + LIST_FOREACH(e, &excludes, link) { + if ((e->pathname && MATCH(e->glob, path)) + || MATCH(e->glob, fname)) + return 1; + } + return 0; +} diff --git a/file_cmds/mtree/extern.h b/file_cmds/mtree/extern.h new file mode 100644 index 00000000..ff40b840 --- /dev/null +++ b/file_cmds/mtree/extern.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.sbin/mtree/extern.h,v 1.13 2004/01/11 19:38:48 phk Exp $ + */ +extern uint32_t crc_total; + +#ifdef _FTS_H_ +int compare(char *, NODE *, FTSENT *); +#endif +int crc(int, uint32_t *, off_t *); +void cwalk(void); +char *flags_to_string(u_long); +char *escape_path(char *string); +struct timespec ptime(char *path, int *supported); + +const char *inotype(u_int); +u_int parsekey(char *, int *); +char *rlink(char *); +NODE *mtree_readspec(FILE *fi); +int mtree_verifyspec(FILE *fi); +int mtree_specspec(FILE *fi, FILE *fj); + +int check_excludes(const char *, const char *); +void init_excludes(void); +void read_excludes_file(const char *); +const char * ftype(u_int type); + +extern int ftsoptions; +extern u_int keys; +extern int lineno; +extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag; +#ifdef MAXPATHLEN +extern char fullpath[MAXPATHLEN]; +#endif diff --git a/file_cmds/mtree/misc.c b/file_cmds/mtree/misc.c new file mode 100644 index 00000000..48518688 --- /dev/null +++ b/file_cmds/mtree/misc.c @@ -0,0 +1,175 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif /*not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/misc.c,v 1.16 2005/03/29 11:44:17 tobez Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" +#import +#include + +typedef struct _key { + const char *name; /* key name */ + u_int val; /* value */ + +#define NEEDVALUE 0x01 + u_int flags; +} KEY; + +/* NB: the following table must be sorted lexically. */ +static KEY keylist[] = { + {"acldigest", F_ACL, NEEDVALUE}, + {"atime", F_ATIME, NEEDVALUE}, + {"btime", F_BTIME, NEEDVALUE}, + {"cksum", F_CKSUM, NEEDVALUE}, + {"ctime", F_CTIME, NEEDVALUE}, + {"flags", F_FLAGS, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"inode", F_INODE, NEEDVALUE}, + {"link", F_SLINK, NEEDVALUE}, +#ifdef ENABLE_MD5 + {"md5digest", F_MD5, NEEDVALUE}, +#endif + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"nochange", F_NOCHANGE, 0}, + {"ptime", F_PTIME, NEEDVALUE}, +#ifdef ENABLE_RMD160 + {"ripemd160digest", F_RMD160, NEEDVALUE}, +#endif +#ifdef ENABLE_SHA1 + {"sha1digest", F_SHA1, NEEDVALUE}, +#endif +#ifdef ENABLE_SHA256 + {"sha256digest", F_SHA256, NEEDVALUE}, +#endif + {"size", F_SIZE, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE}, + {"xattrsdigest", F_XATTRS, NEEDVALUE}, +}; + +int keycompare(const void *, const void *); + +u_int +parsekey(char *name, int *needvaluep) +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + errx(1, "line %d: unknown keyword %s", lineno, name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + return (k->val); +} + +int +keycompare(const void *a, const void *b) +{ + return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name)); +} + +char * +flags_to_string(u_long fflags) +{ + char *string; + + string = fflagstostr(fflags); + if (string != NULL && *string == '\0') { + free(string); + string = strdup("none"); + } + if (string == NULL) + err(1, NULL); + + return string; +} + +// escape path and always return a new string so it can be freed +char * +escape_path(char *string) +{ + char *escapedPath = calloc(1, strlen(string) * 4 + 1); + if (escapedPath == NULL) + errx(1, "escape_path(): calloc() failed"); + strvis(escapedPath, string, VIS_NL | VIS_CSTYLE | VIS_OCTAL); + + return escapedPath; +} + +struct ptimebuf { + uint32_t length; + attribute_set_t returned_attrs; + struct timespec st_ptimespec; +} __attribute__((aligned(4), packed)); + +// ptime is not supported on root filesystems or HFS filesystems older than the feature being introduced +struct timespec +ptime(char *path, int *supported) { + + int ret = 0; + struct ptimebuf buf; + struct attrlist list = { + .bitmapcount = ATTR_BIT_MAP_COUNT, + .commonattr = ATTR_CMN_RETURNED_ATTRS | ATTR_CMN_ADDEDTIME, + }; + ret = getattrlist(path, &list, &buf, sizeof(buf), FSOPT_NOFOLLOW); + if (ret) { + err(1, "ptime: getattrlist"); + } + + *supported = 0; + if (buf.returned_attrs.commonattr & ATTR_CMN_ADDEDTIME) { + *supported = 1; + } + + return buf.st_ptimespec; + +} diff --git a/file_cmds/mtree/mtree.8 b/file_cmds/mtree/mtree.8 new file mode 100644 index 00000000..1aa529a7 --- /dev/null +++ b/file_cmds/mtree/mtree.8 @@ -0,0 +1,393 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93 +.\" $FreeBSD: src/usr.sbin/mtree/mtree.8,v 1.53 2005/07/31 03:30:47 keramida Exp $ +.\" +.Dd March 29, 2005 +.Dt MTREE 8 +.Os +.Sh NAME +.Nm mtree +.Nd map a directory hierarchy +.Sh SYNOPSIS +.Nm mtree +.Op Fl LPUcdeinqruxw +.Bk -words +.Op Fl f Ar spec +.Ek +.Bk -words +.Op Fl f Ar spec +.Ek +.Bk -words +.Op Fl K Ar keywords +.Ek +.Bk -words +.Op Fl k Ar keywords +.Ek +.Bk -words +.Op Fl p Ar path +.Ek +.Bk -words +.Op Fl s Ar seed +.Ek +.Bk -words +.Op Fl X Ar exclude-list +.Ek +.Sh DESCRIPTION +The +.Nm mtree +utility compares the file hierarchy rooted in the current directory against a +specification read from the standard input. +Messages are written to the standard output for any files whose +characteristics do not match the specifications, or which are +missing from either the file hierarchy or the specification. +.Pp +The options are as follows: +.Bl -tag -width flag +.\" ========== +.It Fl c +Print a specification for the file hierarchy to the standard output. +.\" ========== +.It Fl d +Ignore everything except directory type files. +.\" ========== +.It Fl e +Do not complain about files that are in the file hierarchy, but not in the +specification. +.\" ========== +.It Fl f Ar file +Read the specification from +.Ar file , +instead of from the standard input. +.Pp +If this option is specified twice, +the two specifications are compared with each other, +rather than to the file hierarchy. +The specifications be sorted like output generated using +.Fl c . +The output format in this case is somewhat remniscent of +.Xr comm 1 , +having "in first spec only", "in second spec only", and "different" +columns, prefixed by zero, one and two TAB characters respectively. +Each entry in the "different" column occupies two lines, +one from each specification. +.\" ========== +.It Fl i +Indent the output 4 spaces each time a directory level is descended when +create a specification with the +.Fl c +option. +This does not affect either the /set statements or the comment before each +directory. +It does however affect the comment before the close of each directory. +.\" ========== +.It Fl K Ar keywords +Add the specified (whitespace or comma separated) +.Ar keywords +to the current set of keywords. +.\" ========== +.It Fl k Ar keywords +Use the ``type'' keyword plus the specified (whitespace or comma separated) +.Ar keywords +instead of the current set of keywords. +.\" ========== +.It Fl L +Follow all symbolic links in the file hierarchy. +.\" ========== +.It Fl n +Do not emit pathname comments when creating a specification. +Normally +a comment is emitted before each directory and before the close of that +directory when using the +.Fl c +option. +.\" ========== +.It Fl P +Do not follow symbolic links in the file hierarchy, instead consider +the symbolic link itself in any comparisons. +This is the default. +.\" ========== +.It Fl p Ar path +Use the file hierarchy rooted in +.Ar path , +instead of the current directory. +.\" ========== +.It Fl q +Quiet mode. +Do not complain when a +.Dq missing +directory cannot be created because it already exists. +This occurs when the directory is a symbolic link. +.\" ========== +.It Fl r +Remove any files in the file hierarchy that are not described in the +specification. +.\" ========== +.It Fl s Ar seed +Display a single checksum to the standard error output that represents all +of the files for which the keyword +.Cm cksum +was specified. +The checksum is seeded with the specified value. +.\" ========== +.It Fl U +Modify the owner, group, permissions, and modification time of existing +files to match the specification and create any missing directories or +symbolic links. +User, group and permissions must all be specified for missing directories +to be created. +Corrected mismatches are not considered errors. +.\" ========== +.It Fl u +Same as +.Fl U +except a status of 2 is returned if the file hierarchy did not match +the specification. +.\" ========== +.It Fl w +Make some error conditions non-fatal warnings. +.\" ========== +.It Fl X Ar exclude-list +The specified file contains +.Xr fnmatch 3 +patterns matching files to be excluded from +the specification, one to a line. +If the pattern contains a +.Ql \&/ +character, it will be matched against entire pathnames (relative to +the starting directory); otherwise, +it will be matched against basenames only. +No comments are allowed in +the +.Ar exclude-list +file. +.\" ========== +.It Fl x +Do not descend below mount points in the file hierarchy. +.El +.Pp +Specifications are mostly composed of ``keywords'', i.e., strings +that specify values relating to files. +No keywords have default values, and if a keyword has no value set, no +checks based on it are performed. +.Pp +Currently supported keywords are as follows: +.Bl -tag -width Cm +.It Cm cksum +The checksum of the file using the default algorithm specified by +the +.Xr cksum 1 +utility. +.It Cm flags +The file flags as a symbolic name. +See +.Xr chflags 1 +for information on these names. +If no flags are to be set the string +.Dq none +may be used to override the current default. +.It Cm ignore +Ignore any file hierarchy below this file. +.It Cm gid +The file group as a numeric value. +.It Cm gname +The file group as a symbolic name. +.It Cm md5digest +The MD5 message digest of the file. +.It Cm sha1digest +The +.Tn FIPS +160-1 +.Pq Dq Tn SHA-1 +message digest of the file. +.It Cm ripemd160digest +The +.Tn RIPEMD160 +message digest of the file. +.It Cm mode +The current file's permissions as a numeric (octal) or symbolic +value. +.It Cm nlink +The number of hard links the file is expected to have. +.It Cm nochange +Make sure this file or directory exists but otherwise ignore all attributes. +.It Cm uid +The file owner as a numeric value. +.It Cm uname +The file owner as a symbolic name. +.It Cm size +The size, in bytes, of the file. +.It Cm link +The file the symbolic link is expected to reference. +.It Cm time +The last modification time of the file. +.It Cm btime +The creation (birth) time of the file. +.It Cm atime +The last access time of the file. +.It Cm ctime +The last metadata modification time of the file. +.It Cm ptime +The time the file was added to its parent folder. +.It Cm inode +The inode number of the file. +.It Cm xattrsdigest +Digest of the extended attributes of the file. +.It Cm acldigest +Digest of the access control list of the file. +.It Cm type +The type of the file; may be set to any one of the following: +.Pp +.Bl -tag -width Cm -compact +.It Cm block +block special device +.It Cm char +character special device +.It Cm dir +directory +.It Cm fifo +fifo +.It Cm file +regular file +.It Cm link +symbolic link +.It Cm socket +socket +.El +.El +.Pp +The default set of keywords are +.Cm flags , +.Cm gid , +.Cm mode , +.Cm nlink , +.Cm size , +.Cm link , +.Cm time , +and +.Cm uid . +.Pp +There are four types of lines in a specification. +.Pp +The first type of line sets a global value for a keyword, and consists of +the string ``/set'' followed by whitespace, followed by sets of keyword/value +pairs, separated by whitespace. +Keyword/value pairs consist of a keyword, followed by an equals sign +(``=''), followed by a value, without whitespace characters. +Once a keyword has been set, its value remains unchanged until either +reset or unset. +.Pp +The second type of line unsets keywords and consists of the string +``/unset'', followed by whitespace, followed by one or more keywords, +separated by whitespace. +.Pp +The third type of line is a file specification and consists of a file +name, followed by whitespace, followed by zero or more whitespace +separated keyword/value pairs. +The file name may be preceded by whitespace characters. +The file name may contain any of the standard file name matching +characters (``['', ``]'', ``?'' or ``*''), in which case files +in the hierarchy will be associated with the first pattern that +they match. +.Pp +Each of the keyword/value pairs consist of a keyword, followed by an +equals sign (``=''), followed by the keyword's value, without +whitespace characters. +These values override, without changing, the global value of the +corresponding keyword. +.Pp +All paths are relative. +Specifying a directory will cause subsequent files to be searched +for in that directory hierarchy. +Which brings us to the last type of line in a specification: a line +containing only the string +.Dq Pa ..\& +causes the current directory +path to ascend one level. +.Pp +Empty lines and lines whose first non-whitespace character is a hash +mark (``#'') are ignored. +.Pp +The +.Nm mtree +utility exits with a status of 0 on success, 1 if any error occurred, +and 2 if the file hierarchy did not match the specification. +A status of 2 is converted to a status of 0 if the +.Fl U +option is used. +.Sh FILES +.Bl -tag -width /etc/mtree -compact +.It Pa /etc/mtree +system specification directory +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The +.Fl d +and +.Fl u +options can be used in combination to create directory hierarchies +for distributions and other such things; the files in +.Pa /etc/mtree +were used to create almost all directories in this +.Fx +distribution. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cksum 1 , +.Xr md5 1 , +.Xr stat 2 , +.Xr fts 3 , +.Xr md5 3 , +.Xr chown 8 +.Sh HISTORY +The +.Nm mtree +utility appeared in +.Bx 4.3 Reno . +The +.Tn MD5 +digest capability was added in +.Fx 2.1 , +in response to the widespread use of programs which can spoof +.Xr cksum 1 . +The +.Tn SHA-1 +and +.Tn RIPEMD160 +digests were added in +.Fx 4.0 , +as new attacks have demonstrated weaknesses in +.Tn MD5 . +Support for file flags was added in +.Fx 4.0 , +and mostly comes from +.Nx . diff --git a/file_cmds/mtree/mtree.c b/file_cmds/mtree/mtree.c new file mode 100644 index 00000000..22bb6f00 --- /dev/null +++ b/file_cmds/mtree/mtree.c @@ -0,0 +1,191 @@ +/*- + * Copyright (c) 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/mtree.c,v 1.29 2004/06/04 19:29:28 ru Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +int ftsoptions = FTS_PHYSICAL; +int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag; +u_int keys; +char fullpath[MAXPATHLEN]; + +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int ch; + char *dir, *p; + int status; + FILE *spec1, *spec2; + + dir = NULL; + keys = KEYDEFAULT; + init_excludes(); + spec1 = stdin; + spec2 = NULL; + + while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:")) != -1) + switch((char)ch) { + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'e': + eflag = 1; + break; + case 'f': + if (spec1 == stdin) { + spec1 = fopen(optarg, "r"); + if (spec1 == NULL) + err(1, "%s", optarg); + } else if (spec2 == NULL) { + spec2 = fopen(optarg, "r"); + if (spec2 == NULL) + err(1, "%s", optarg); + } else + usage(); + break; + case 'i': + iflag = 1; + break; + case 'K': + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); + break; + case 'k': + keys = F_TYPE; + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); + break; + case 'L': + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + break; + case 'n': + nflag = 1; + break; + case 'P': + ftsoptions &= ~FTS_LOGICAL; + ftsoptions |= FTS_PHYSICAL; + break; + case 'p': + dir = optarg; + break; + case 'q': + qflag = 1; + break; + case 'r': + rflag = 1; + break; + case 's': + sflag = 1; + crc_total = (uint32_t)~strtoul(optarg, &p, 0); + if (*p) + errx(1, "illegal seed value -- %s", optarg); + break; + case 'U': + Uflag = 1; + uflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'w': + wflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case 'X': + read_excludes_file(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; +// argv += optind; + + if (argc) + usage(); + + if (dir && chdir(dir)) + err(1, "%s", dir); + + if ((cflag || sflag) && !getwd(fullpath)) + errx(1, "%s", fullpath); + + if (cflag) { + cwalk(); + exit(0); + } + if (spec2 != NULL) + status = mtree_specspec(spec1, spec2); + else + status = mtree_verifyspec(spec1); + if (Uflag & (status == MISMATCHEXIT)) + status = 0; + exit(status); +} + +static void +usage(void) +{ + (void)fprintf(stderr, +"usage: mtree [-LPUcdeinqruxw] [-f spec] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n" +"\t[-X excludes]\n"); + exit(1); +} diff --git a/file_cmds/mtree/mtree.h b/file_cmds/mtree/mtree.h new file mode 100644 index 00000000..02d17284 --- /dev/null +++ b/file_cmds/mtree/mtree.h @@ -0,0 +1,111 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mtree.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.sbin/mtree/mtree.h,v 1.7 2005/03/29 11:44:17 tobez Exp $ + */ + +#include +#include + +#define KEYDEFAULT \ + (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID | F_FLAGS) + +#define MISMATCHEXIT 2 + +typedef struct _node { + struct _node *parent, *child; /* up, down */ + struct _node *prev, *next; /* left, right */ + off_t st_size; /* size */ + struct timespec st_mtimespec; /* last modification time */ + u_long cksum; /* check sum */ + char *md5digest; /* MD5 digest */ + char *sha1digest; /* SHA-1 digest */ + char *sha256digest; /* SHA-256 digest */ + char *rmd160digest; /* RIPEMD160 digest */ + char *slink; /* symbolic link reference */ + uid_t st_uid; /* uid */ + gid_t st_gid; /* gid */ +#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) + mode_t st_mode; /* mode */ + u_long st_flags; /* flags */ + nlink_t st_nlink; /* link count */ + struct timespec st_birthtimespec; /* birth time (creation time) */ + struct timespec st_atimespec; /* access time */ + struct timespec st_ctimespec; /* metadata modification time */ + struct timespec st_ptimespec; /* time added to parent folder */ + char *xattrsdigest; /* digest of extended attributes */ + ino_t st_ino; /* inode */ + char *acldigest; /* digest of access control list */ + +#define F_CKSUM 0x00000001 /* check sum */ +#define F_DONE 0x00000002 /* directory done */ +#define F_GID 0x00000004 /* gid */ +#define F_GNAME 0x00000008 /* group name */ +#define F_IGN 0x00000010 /* ignore */ +#define F_MAGIC 0x00000020 /* name has magic chars */ +#define F_MODE 0x00000040 /* mode */ +#define F_NLINK 0x00000080 /* number of links */ +#define F_SIZE 0x00000100 /* size */ +#define F_SLINK 0x00000200 /* The file the symbolic link is expected to reference */ +#define F_TIME 0x00000400 /* modification time (mtime) */ +#define F_TYPE 0x00000800 /* file type */ +#define F_UID 0x00001000 /* uid */ +#define F_UNAME 0x00002000 /* user name */ +#define F_VISIT 0x00004000 /* file visited */ +#define F_MD5 0x00008000 /* MD5 digest */ +#define F_NOCHANGE 0x00010000 /* If owner/mode "wrong", do */ + /* not change */ +#define F_SHA1 0x00020000 /* SHA-1 digest */ +#define F_RMD160 0x00040000 /* RIPEMD160 digest */ +#define F_FLAGS 0x00080000 /* file flags */ +#define F_SHA256 0x00100000 /* SHA-256 digest */ +#define F_BTIME 0x00200000 /* creation time */ +#define F_ATIME 0x00400000 /* access time */ +#define F_CTIME 0x00800000 /* metadata modification time (ctime) */ +#define F_PTIME 0x01000000 /* time added to parent folder */ +#define F_XATTRS 0x02000000 /* digest of extended attributes */ +#define F_INODE 0x04000000 /* inode */ +#define F_ACL 0x08000000 /* digest of access control list */ + u_int flags; /* items set */ + +#define F_BLOCK 0x001 /* block special */ +#define F_CHAR 0x002 /* char special */ +#define F_DIR 0x004 /* directory */ +#define F_FIFO 0x008 /* fifo */ +#define F_FILE 0x010 /* regular file */ +#define F_LINK 0x020 /* symbolic link */ +#define F_SOCK 0x040 /* socket */ + u_char type; /* file type */ + + char name[1]; /* file name (must be last) */ +} NODE; + +#define RP(p) \ + ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ + (p)->fts_path + 2 : (p)->fts_path) diff --git a/file_cmds/mtree/spec.c b/file_cmds/mtree/spec.c new file mode 100644 index 00000000..332accc1 --- /dev/null +++ b/file_cmds/mtree/spec.c @@ -0,0 +1,376 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/spec.c,v 1.22 2005/03/29 11:44:17 tobez Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +int lineno; /* Current spec line number. */ + +static void set(char *, NODE *); +static void unset(char *, NODE *); + +NODE * +mtree_readspec(FILE *fi) +{ + NODE *centry, *last; + char *p; + NODE ginfo, *root; + int c_cur, c_next; + char buf[2048]; + + centry = last = root = NULL; + bzero(&ginfo, sizeof(ginfo)); + c_cur = c_next = 0; + for (lineno = 1; fgets(buf, sizeof(buf), fi); + ++lineno, c_cur = c_next, c_next = 0) { + /* Skip empty lines. */ + if (buf[0] == '\n') + continue; + + /* Find end of line. */ + if ((p = index(buf, '\n')) == NULL) + errx(1, "line %d too long", lineno); + + /* See if next line is continuation line. */ + if (p[-1] == '\\') { + --p; + c_next = 1; + } + + /* Null-terminate the line. */ + *p = '\0'; + + /* Skip leading whitespace. */ + for (p = buf; *p && isspace(*p); ++p); + + /* If nothing but whitespace or comment char, continue. */ + if (!*p || *p == '#') + continue; + +#ifdef DEBUG + (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); +#endif + if (c_cur) { + set(p, centry); + continue; + } + + /* Grab file name, "$", "set", or "unset". */ + if ((p = strtok(p, "\n\t ")) == NULL) + errx(1, "line %d: missing field", lineno); + + if (p[0] == '/') + switch(p[1]) { + case 's': + if (strcmp(p + 1, "set")) + break; + set(NULL, &ginfo); + continue; + case 'u': + if (strcmp(p + 1, "unset")) + break; + unset(NULL, &ginfo); + continue; + } + + if (index(p, '/')) + errx(1, "line %d: slash character in file name", + lineno); + + if (!strcmp(p, "..")) { + /* Don't go up, if haven't gone down. */ + if (!root) + goto noparent; + if (last->type != F_DIR || last->flags & F_DONE) { + if (last == root) + goto noparent; + last = last->parent; + } + last->flags |= F_DONE; + continue; + +noparent: errx(1, "line %d: no parent node", lineno); + } + + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + errx(1, "calloc"); + *centry = ginfo; +#define MAGIC "?*[" + if (strpbrk(p, MAGIC)) + centry->flags |= F_MAGIC; + if (strunvis(centry->name, p) == -1) + errx(1, "filename %s is ill-encoded", p); + set(NULL, centry); + + if (!root) { + last = root = centry; + root->parent = root; + } else if (last->type == F_DIR && !(last->flags & F_DONE)) { + centry->parent = last; + last = last->child = centry; + } else { + centry->parent = last->parent; + centry->prev = last; + last = last->next = centry; + } + } + return (root); +} + +static void +set(char *t, NODE *ip) +{ + int type; + char *kw, *val = NULL; + struct group *gr; + struct passwd *pw; + mode_t *m; + int value; + char *ep; + + for (; (kw = strtok(t, "= \t\n")); t = NULL) { + ip->flags |= type = parsekey(kw, &value); + if ((value == 0) || (val = strtok(NULL, " \t\n")) == NULL) + errx(1, "line %d: missing value", lineno); + switch(type) { + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid checksum %s", + lineno, val); + break; + case F_MD5: + ip->md5digest = strdup(val); + if(!ip->md5digest) + errx(1, "strdup"); + break; + case F_SHA1: + ip->sha1digest = strdup(val); + if(!ip->sha1digest) + errx(1, "strdup"); + break; + case F_SHA256: + ip->sha256digest = strdup(val); + if(!ip->sha256digest) + errx(1, "strdup"); + break; + case F_RMD160: + ip->rmd160digest = strdup(val); + if(!ip->rmd160digest) + errx(1, "strdup"); + break; + case F_FLAGS: + if (strcmp("none", val) == 0) + ip->st_flags = 0; + else if (strtofflags(&val, &ip->st_flags, NULL) != 0) + errx(1, "line %d: invalid flag %s",lineno, val); + break; + case F_GID: + ip->st_gid = (gid_t)strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid gid %s", lineno, val); + break; + case F_GNAME: + if ((gr = getgrnam(val)) == NULL) + errx(1, "line %d: unknown group %s", lineno, val); + ip->st_gid = gr->gr_gid; + break; + case F_IGN: + /* just set flag bit */ + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + errx(1, "line %d: invalid file mode %s", + lineno, val); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid link count %s", + lineno, val); + break; + case F_SIZE: + ip->st_size = strtoq(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid size %s", + lineno, val); + break; + case F_SLINK: + ip->slink = malloc(strlen(val) + 1); + if (ip->slink == NULL) + errx(1, "malloc"); + if (strunvis(ip->slink, val) == -1) + errx(1, "symlink %s is ill-encoded", val); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_mtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_TYPE: + switch(*val) { + case 'b': + if (!strcmp(val, "block")) + ip->type = F_BLOCK; + break; + case 'c': + if (!strcmp(val, "char")) + ip->type = F_CHAR; + break; + case 'd': + if (!strcmp(val, "dir")) + ip->type = F_DIR; + break; + case 'f': + if (!strcmp(val, "file")) + ip->type = F_FILE; + if (!strcmp(val, "fifo")) + ip->type = F_FIFO; + break; + case 'l': + if (!strcmp(val, "link")) + ip->type = F_LINK; + break; + case 's': + if (!strcmp(val, "socket")) + ip->type = F_SOCK; + break; + default: + errx(1, "line %d: unknown file type %s", + lineno, val); + } + break; + case F_UID: + ip->st_uid = (uid_t)strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid uid %s", lineno, val); + break; + case F_UNAME: + if ((pw = getpwnam(val)) == NULL) + errx(1, "line %d: unknown user %s", lineno, val); + ip->st_uid = pw->pw_uid; + break; + case F_BTIME: + ip->st_birthtimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_birthtimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_ATIME: + ip->st_atimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_atimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_CTIME: + ip->st_ctimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_ctimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_PTIME: + ip->st_ptimespec.tv_sec = strtoul(val, &ep, 10); + if (*ep != '.') + errx(1, "line %d: invalid time %s", + lineno, val); + val = ep + 1; + ip->st_ptimespec.tv_nsec = strtoul(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid time %s", + lineno, val); + break; + case F_XATTRS: + ip->xattrsdigest = strdup(val); + if(!ip->xattrsdigest) + err(1, "strdup"); + break; + case F_INODE: + ip->st_ino = (ino_t)strtoull(val, &ep, 10); + if (*ep) + errx(1, "line %d: invalid inode %s", lineno, val); + break; + case F_ACL: + ip->acldigest = strdup(val); + if(!ip->acldigest) + err(1, "strdup"); + } + } +} + +static void +unset(char *t, NODE *ip) +{ + char *p; + + while ((p = strtok(t, "\n\t "))) + ip->flags &= ~parsekey(p, NULL); +} diff --git a/file_cmds/mtree/specspec.c b/file_cmds/mtree/specspec.c new file mode 100644 index 00000000..bbcd795c --- /dev/null +++ b/file_cmds/mtree/specspec.c @@ -0,0 +1,289 @@ +/*- + * Copyright (c) 2003 Poul-Henning Kamp + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/specspec.c,v 1.6 2005/03/29 11:44:17 tobez Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +#define FF(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d)) +#define FS(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d)) +#define FM(a, b, c, d) \ + (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d)) + +static void +shownode(NODE *n, int f, char const *path) +{ + struct group *gr; + struct passwd *pw; + + printf("%s%s %s", path, n->name, ftype(n->type)); + if (f & F_CKSUM) + printf(" cksum=%lu", n->cksum); + if (f & F_GID) + printf(" gid=%d", n->st_gid); + if (f & F_GNAME) { + gr = getgrgid(n->st_gid); + if (gr == NULL) + printf(" gid=%d", n->st_gid); + else + printf(" gname=%s", gr->gr_name); + } + if (f & F_MODE) + printf(" mode=%o", n->st_mode); + if (f & F_NLINK) + printf(" nlink=%d", n->st_nlink); + if (f & F_SIZE) + printf(" size=%jd", (intmax_t)n->st_size); + if (f & F_TIME) + printf(" time=%ld.%09ld", n->st_mtimespec.tv_sec, n->st_mtimespec.tv_nsec); + if (f & F_UID) + printf(" uid=%d", n->st_uid); + if (f & F_UNAME) { + pw = getpwuid(n->st_uid); + if (pw == NULL) + printf(" uid=%d", n->st_uid); + else + printf(" uname=%s", pw->pw_name); + } + if (f & F_MD5) + printf(" md5digest=%s", n->md5digest); + if (f & F_SHA1) + printf(" sha1digest=%s", n->sha1digest); + if (f & F_RMD160) + printf(" rmd160digest=%s", n->rmd160digest); + if (f & F_SHA256) + printf(" sha256digest=%s", n->sha256digest); + if (f & F_FLAGS) + printf(" flags=%s", flags_to_string(n->st_flags)); + if (f & F_BTIME) + printf(" btime=%ld.%09ld", n->st_birthtimespec.tv_sec, n->st_birthtimespec.tv_nsec); + if (f & F_ATIME) + printf(" atime=%ld.%09ld", n->st_atimespec.tv_sec, n->st_atimespec.tv_nsec); + if (f & F_CTIME) + printf(" ctime=%ld.%09ld", n->st_ctimespec.tv_sec, n->st_ctimespec.tv_nsec); + if (f & F_PTIME) + printf(" ptime=%ld.%09ld", n->st_ptimespec.tv_sec, n->st_ptimespec.tv_nsec); + if (f & F_XATTRS) + printf(" xattrsdigest=%s", n->xattrsdigest); + if (f & F_INODE) + printf(" inode=%llu", n->st_ino); + if (f & F_ACL) + printf(" acldigest=%s", n->acldigest); + + printf("\n"); +} + +static int +mismatch(NODE *n1, NODE *n2, int differ, char const *path) +{ + + if (n2 == NULL) { + shownode(n1, differ, path); + return (1); + } + if (n1 == NULL) { + printf("\t"); + shownode(n2, differ, path); + return (1); + } + if (!(differ & keys)) + return(0); + printf("\t\t"); + shownode(n1, differ, path); + printf("\t\t"); + shownode(n2, differ, path); + return (1); +} + +static int +compare_nodes(NODE *n1, NODE *n2, char const *path) +{ + int differs; + + if (n1 != NULL && n1->type == F_LINK) + n1->flags &= ~F_MODE; + if (n2 != NULL && n2->type == F_LINK) + n2->flags &= ~F_MODE; + differs = 0; + if ((n1 == NULL) && (n2 == NULL)) { + return 0; + } else if (n1 == NULL) { + differs = n2->flags; + mismatch(n1, n2, differs, path); + return (1); + } else if (n2 == NULL) { + differs = n1->flags; + mismatch(n1, n2, differs, path); + return (1); + } else if (n1->type != n2->type) { + differs = 0; + mismatch(n1, n2, differs, path); + return (1); + } + if (FF(n1, n2, F_CKSUM, cksum)) + differs |= F_CKSUM; + if (FF(n1, n2, F_GID, st_gid)) + differs |= F_GID; + if (FF(n1, n2, F_GNAME, st_gid)) + differs |= F_GNAME; + if (FF(n1, n2, F_MODE, st_mode)) + differs |= F_MODE; + if (FF(n1, n2, F_NLINK, st_nlink)) + differs |= F_NLINK; + if (FF(n1, n2, F_SIZE, st_size)) + differs |= F_SIZE; + if (FS(n1, n2, F_SLINK, slink)) + differs |= F_SLINK; + if (FM(n1, n2, F_TIME, st_mtimespec)) + differs |= F_TIME; + if (FF(n1, n2, F_UID, st_uid)) + differs |= F_UID; + if (FF(n1, n2, F_UNAME, st_uid)) + differs |= F_UNAME; + if (FS(n1, n2, F_MD5, md5digest)) + differs |= F_MD5; + if (FS(n1, n2, F_SHA1, sha1digest)) + differs |= F_SHA1; + if (FS(n1, n2, F_RMD160, rmd160digest)) + differs |= F_RMD160; + if (FS(n1, n2, F_SHA256, sha256digest)) + differs |= F_SHA256; + if (FF(n1, n2, F_FLAGS, st_flags)) + differs |= F_FLAGS; + if (FM(n1, n2, F_BTIME, st_birthtimespec)) + differs |= F_BTIME; + if (FM(n1, n2, F_ATIME, st_atimespec)) + differs |= F_ATIME; + if (FM(n1, n2, F_CTIME, st_ctimespec)) + differs |= F_CTIME; + if (FM(n1, n2, F_PTIME, st_ptimespec)) + differs |= F_PTIME; + if (FS(n1, n2, F_XATTRS, xattrsdigest)) + differs |= F_XATTRS; + if (FF(n1, n2, F_INODE, st_ino)) + differs |= F_INODE; + if (FS(n1, n2, F_ACL, acldigest)) + differs |= F_ACL; + + if (differs) { + mismatch(n1, n2, differs, path); + return (1); + } + return (0); +} +static int +walk_in_the_forest(NODE *t1, NODE *t2, char const *path) +{ + int r, i; + NODE *c1, *c2, *n1, *n2; + char *np; + + r = 0; + + if (t1 != NULL) + c1 = t1->child; + else + c1 = NULL; + if (t2 != NULL) + c2 = t2->child; + else + c2 = NULL; + while (c1 != NULL || c2 != NULL) { + n1 = n2 = NULL; + if (c1 != NULL) + n1 = c1->next; + if (c2 != NULL) + n2 = c2->next; + if (c1 != NULL && c2 != NULL) { + if (c1->type != F_DIR && c2->type == F_DIR) { + n2 = c2; + c2 = NULL; + } else if (c1->type == F_DIR && c2->type != F_DIR) { + n1 = c1; + c1 = NULL; + } else { + i = strcmp(c1->name, c2->name); + if (i > 0) { + n1 = c1; + c1 = NULL; + } else if (i < 0) { + n2 = c2; + c2 = NULL; + } + } + } + if (c1 == NULL && c2->type == F_DIR) { + asprintf(&np, "%s%s/", path, c2->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else if (c2 == NULL && c1->type == F_DIR) { + asprintf(&np, "%s%s/", path, c1->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else if (c1 == NULL || c2 == NULL) { + i = compare_nodes(c1, c2, path); + } else if (c1->type == F_DIR && c2->type == F_DIR) { + asprintf(&np, "%s%s/", path, c1->name); + i = walk_in_the_forest(c1, c2, np); + free(np); + i += compare_nodes(c1, c2, path); + } else { + i = compare_nodes(c1, c2, path); + } + r += i; + c1 = n1; + c2 = n2; + } + return (r); +} + +int +mtree_specspec(FILE *fi, FILE *fj) +{ + int rval; + NODE *root1, *root2; + + root1 = mtree_readspec(fi); + root2 = mtree_readspec(fj); + rval = walk_in_the_forest(root1, root2, ""); + rval += compare_nodes(root1, root2, ""); + if (rval > 0) + return (MISMATCHEXIT); + return (0); +} diff --git a/file_cmds/mtree/test/test00.sh b/file_cmds/mtree/test/test00.sh new file mode 100644 index 00000000..a2e4b280 --- /dev/null +++ b/file_cmds/mtree/test/test00.sh @@ -0,0 +1,67 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD: src/usr.sbin/mtree/test/test00.sh,v 1.3 2003/11/05 22:26:08 phk Exp $ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + + +mkdir ${TMP}/mt/foo +mkdir ${TMP}/mr/\* +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '*' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\* + +mkdir -p ${TMP}/mt/foo +mkdir ${TMP}/mr/\[f\]oo +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '[' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\[f\]oo + +mkdir -p ${TMP}/mt/foo +mkdir ${TMP}/mr/\?oo +mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1 +if [ -d ${TMP}/mt/foo ] ; then + echo "ERROR Mtree create fell for filename with '?' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\?oo + +mkdir ${TMP}/mr/\# +mtree -c -p ${TMP}/mr > ${TMP}/_ +if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then + true +else + echo "ERROR Mtree create fell for filename with '#' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +if [ ! -d ${TMP}/mt/\# ] ; then + echo "ERROR Mtree update failed to create name with '#' char" 1>&2 + rm -rf ${TMP} + exit 1 +fi +rmdir ${TMP}/mr/\# + +rm -rf ${TMP} +exit 0 diff --git a/file_cmds/mtree/test/test01.sh b/file_cmds/mtree/test/test01.sh new file mode 100644 index 00000000..d056b912 --- /dev/null +++ b/file_cmds/mtree/test/test01.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD: src/usr.sbin/mtree/test/test01.sh,v 1.1 2003/10/30 12:01:32 phk Exp $ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + + +ln -s "xx this=is=wrong" ${TMP}/mr/foo +mtree -c -p ${TMP}/mr > ${TMP}/_ + +if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then + true +else + echo "ERROR Mtree failed on symlink with space char" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +x=x`(cd ${TMP}/mr ; ls -l foo 2>&1) || true` +y=x`(cd ${TMP}/mt ; ls -l foo 2>&1) || true` + +if [ "$x" != "$y" ] ; then + echo "ERROR Recreation of spaced symlink failed" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 diff --git a/file_cmds/mtree/test/test02.sh b/file_cmds/mtree/test/test02.sh new file mode 100644 index 00000000..450ebb3d --- /dev/null +++ b/file_cmds/mtree/test/test02.sh @@ -0,0 +1,36 @@ +#!/bin/sh +# +# Copyright (c) 2003 Dan Nelson +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD: src/usr.sbin/mtree/test/test02.sh,v 1.1 2003/10/31 13:39:19 phk Exp $ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + +touch -t 199901020304 ${TMP}/mr/oldfile +touch ${TMP}/mt/oldfile + +mtree -c -p ${TMP}/mr > ${TMP}/_ + +mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null + +x=x`(cd ${TMP}/mr ; ls -l 2>&1) || true` +y=x`(cd ${TMP}/mt ; ls -l 2>&1) || true` + +if [ "$x" != "$y" ] ; then + echo "ERROR Update of mtime failed" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 + diff --git a/file_cmds/mtree/test/test03.sh b/file_cmds/mtree/test/test03.sh new file mode 100644 index 00000000..52e08c31 --- /dev/null +++ b/file_cmds/mtree/test/test03.sh @@ -0,0 +1,60 @@ +#!/bin/sh +# +# Copyright (c) 2003 Poul-Henning Kamp +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD: src/usr.sbin/mtree/test/test03.sh,v 1.2 2005/03/29 11:44:17 tobez Exp $ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} + +K=uid,uname,gid,gname,flags,md5digest,size,ripemd160digest,sha1digest,sha256digest,cksum + +rm -rf _FOO +mkdir _FOO +touch _FOO/_uid +touch _FOO/_size +touch _FOO/zztype + +touch _FOO/_bar +mtree -c -K $K -p .. > ${TMP}/_r +mtree -c -K $K -p .. > ${TMP}/_r2 +rm -rf _FOO/_bar + +rm -rf _FOO/zztype +mkdir _FOO/zztype + +date > _FOO/_size + +chown nobody _FOO/_uid + +touch _FOO/_foo +mtree -c -K $K -p .. > ${TMP}/_t + +rm -fr _FOO + +if mtree -f ${TMP}/_r -f ${TMP}/_r2 ; then + true +else + echo "ERROR Compare identical failed" 1>&2 + exit 1 +fi + +if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then + echo "ERROR Compare different succeeded" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 10 ] ; then + echo "ERROR wrong number of lines: `wc -l ${TMP}/_`" 1>&2 + exit 1 +fi + +exit 0 diff --git a/file_cmds/mtree/test/test04.sh b/file_cmds/mtree/test/test04.sh new file mode 100644 index 00000000..453d1466 --- /dev/null +++ b/file_cmds/mtree/test/test04.sh @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Copyright (c) 2003 Dan Nelson +# All rights reserved. +# +# Please see src/share/examples/etc/bsd-style-copyright. +# +# $FreeBSD: src/usr.sbin/mtree/test/test04.sh,v 1.1 2003/11/13 11:02:57 phk Exp $ +# + +set -e + +TMP=/tmp/mtree.$$ + +rm -rf ${TMP} +mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt + +mkdir ${TMP}/mr/a +mkdir ${TMP}/mr/b +mkdir ${TMP}/mt/a +mkdir ${TMP}/mt/b +touch ${TMP}/mt/z + +mtree -c -p ${TMP}/mr > ${TMP}/_r +mtree -c -p ${TMP}/mt > ${TMP}/_t + +if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then + echo "ERROR wrong exit on difference" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 1 ] ; then + echo "ERROR spec/spec compare generated wrong output" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +if mtree -f ${TMP}/_t -f ${TMP}/_r > ${TMP}/_ ; then + echo "ERROR wrong exit on difference" 1>&2 + exit 1 +fi + +if [ `wc -l < ${TMP}/_` -ne 1 ] ; then + echo "ERROR spec/spec compare generated wrong output" 1>&2 + rm -rf ${TMP} + exit 1 +fi + +rm -rf ${TMP} +exit 0 + diff --git a/file_cmds/mtree/verify.c b/file_cmds/mtree/verify.c new file mode 100644 index 00000000..84a37ffe --- /dev/null +++ b/file_cmds/mtree/verify.c @@ -0,0 +1,266 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include +__FBSDID("$FreeBSD: src/usr.sbin/mtree/verify.c,v 1.24 2005/08/11 15:43:55 brian Exp $"); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +static NODE *root; +static char path[MAXPATHLEN]; + +static int miss(NODE *, char *); +static int vwalk(void); + +int +mtree_verifyspec(FILE *fi) +{ + int rval, mval; + + root = mtree_readspec(fi); + rval = vwalk(); + mval = miss(root, path); + + if (rval != 0) + return rval; + else + return mval; +} + +static int +vwalk(void) +{ + FTS *t; + FTSENT *p; + NODE *ep, *level; + int specdepth, rval; + char *argv[2]; + char dot[] = "."; + + argv[0] = dot; + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "line %d: fts_open", lineno); + level = root; + specdepth = rval = 0; + while ((p = fts_read(t))) { + if (check_excludes(p->fts_name, p->fts_path)) { + fts_set(t, p, FTS_SKIP); + continue; + } + switch(p->fts_info) { + case FTS_D: + case FTS_SL: + break; + case FTS_DP: + if (level == NULL) { + errx(1 , "invalid root in vwalk"); + } + if (specdepth > p->fts_level) { + for (level = level->parent; level->prev; + level = level->prev); + --specdepth; + } + continue; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", RP(p), strerror(p->fts_errno)); + continue; + default: + if (dflag) + continue; + } + + if (specdepth != p->fts_level) + goto extra; + for (ep = level; ep; ep = ep->next) + if ((ep->flags & F_MAGIC && + !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || + !strcmp(ep->name, p->fts_name)) { + ep->flags |= F_VISIT; + if ((ep->flags & F_NOCHANGE) == 0 && + compare(ep->name, ep, p)) + rval = MISMATCHEXIT; + if (ep->flags & F_IGN) + (void)fts_set(t, p, FTS_SKIP); + else if (ep->child && ep->type == F_DIR && + p->fts_info == FTS_D) { + level = ep->child; + ++specdepth; + } + break; + } + + if (ep) + continue; +extra: + if (!eflag) { + (void)printf("%s extra", RP(p)); + if (rflag) { + if ((S_ISDIR(p->fts_statp->st_mode) + ? rmdir : unlink)(p->fts_accpath)) { + (void)printf(", not removed: %s", + strerror(errno)); + } else + (void)printf(", removed"); + } + (void)putchar('\n'); + } + (void)fts_set(t, p, FTS_SKIP); + } + (void)fts_close(t); + if (sflag) + warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total); + return (rval); +} + +static int +miss(NODE *p, char *tail) +{ + int create; + char *tp; + const char *type, *what; + int serr; + int rval = 0; + int rrval = 0; + + for (; p; p = p->next) { + if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) + continue; + (void)strcpy(tail, p->name); + if (!(p->flags & F_VISIT)) { + /* Don't print missing message if file exists as a + symbolic link and the -q flag is set. */ + struct stat statbuf; + + if (qflag && stat(path, &statbuf) == 0) { + p->flags |= F_VISIT; + } else { + (void)printf("%s missing", path); + rval = MISMATCHEXIT; + } + } + if (p->type != F_DIR && p->type != F_LINK) { + putchar('\n'); + continue; + } + + create = 0; + if (p->type == F_LINK) + type = "symlink"; + else + type = "directory"; + if (!(p->flags & F_VISIT) && uflag) { + if (!(p->flags & (F_UID | F_UNAME))) + (void)printf(" (%s not created: user not specified)", type); + else if (!(p->flags & (F_GID | F_GNAME))) + (void)printf(" (%s not created: group not specified)", type); + else if (p->type == F_LINK) { + if (symlink(p->slink, path)) + (void)printf(" (symlink not created: %s)\n", + strerror(errno)); + else + (void)printf(" (created)\n"); + if (lchown(path, p->st_uid, p->st_gid) == -1) { + serr = errno; + if (p->st_uid == (uid_t)-1) + what = "group"; + else if (lchown(path, (uid_t)-1, + p->st_gid) == -1) + what = "user & group"; + else { + what = "user"; + errno = serr; + } + (void)printf("%s: %s not modified: %s" + "\n", path, what, strerror(errno)); + } + continue; + } else if (!(p->flags & F_MODE)) + (void)printf(" (directory not created: mode not specified)"); + else if (mkdir(path, S_IRWXU)) + (void)printf(" (directory not created: %s)", + strerror(errno)); + else { + create = 1; + (void)printf(" (created)"); + } + } + if (!(p->flags & F_VISIT)) + (void)putchar('\n'); + + for (tp = tail; *tp; ++tp); + *tp = '/'; + rrval = miss(p->child, tp + 1); + if (rrval != 0) + rval = rrval; + *tp = '\0'; + + if (!create) + continue; + if (chown(path, p->st_uid, p->st_gid) == -1) { + serr = errno; + if (p->st_uid == (uid_t)-1) + what = "group"; + else if (chown(path, (uid_t)-1, p->st_gid) == -1) + what = "user & group"; + else { + what = "user"; + errno = serr; + } + (void)printf("%s: %s not modified: %s\n", + path, what, strerror(errno)); + } + if (chmod(path, p->st_mode)) + (void)printf("%s: permissions not set: %s\n", + path, strerror(errno)); + if ((p->flags & F_FLAGS) && p->st_flags && + chflags(path, (u_int)p->st_flags)) + (void)printf("%s: file flags not set: %s\n", + path, strerror(errno)); + } + return rval; +} diff --git a/file_cmds/mv/mv.1 b/file_cmds/mv/mv.1 new file mode 100644 index 00000000..569e5c58 --- /dev/null +++ b/file_cmds/mv/mv.1 @@ -0,0 +1,184 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mv.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD: src/bin/mv/mv.1,v 1.25 2002/08/26 06:16:51 keramida Exp $ +.\" +.Dd July 9, 2002 +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm mv +.Op Fl f | i | n +.Op Fl v +.Ar source target +.Nm mv +.Op Fl f | i | n +.Op Fl v +.Ar source ... directory +.Sh DESCRIPTION +In its first form, the +.Nm mv +utility renames the file named by the +.Ar source +operand to the destination path named by the +.Ar target +operand. +This form is assumed when the last operand does not name an already +existing directory. +.Pp +In its second form, +.Nm mv +moves each file named by a +.Ar source +operand to a destination file in the existing directory named by the +.Ar directory +operand. +The destination path for each operand is the pathname produced by the +concatenation of the last operand, a slash, and the final pathname +component of the named file. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl f +Do not prompt for confirmation before overwriting the destination +path. +(The +.Fl f +option overrides any previous +.Fl i +or +.Fl n +options.) +.It Fl i +Cause +.Nm mv +to write a prompt to standard error before moving a file that would +overwrite an existing file. +If the response from the standard input begins with the character +.Ql y +or +.Ql Y , +the move is attempted. +(The +.Fl i +option overrides any previous +.Fl f +or +.Fl n +options.) +.It Fl n +Do not overwrite an existing file. +(The +.Fl n +option overrides any previous +.Fl f +or +.Fl i +options.) +.It Fl v +Cause +.Nm mv +to be verbose, showing files after they are moved. +.El +.Pp +It is an error for either the +.Ar source +operand or the destination path to specify a directory unless both do. +.Pp +If the destination path does not have a mode which permits writing, +.Nm mv +prompts the user for confirmation as specified for the +.Fl i +option. +.Pp +As the +.Xr rename 2 +call does not work across file systems, +.Nm mv +uses +.Xr cp 1 +and +.Xr rm 1 +to accomplish the move. +The effect is equivalent to: +.Bd -literal -offset indent +rm -f destination_path && \e +cp -pRP source_file destination && \e +rm -rf source_file +.Ed +.Sh DIAGNOSTICS +.Ex -std +.Pp +The command "mv dir/afile dir" will abort with an error message. +.Sh LEGACY DIAGNOSTICS +In legacy mode, the command "mv dir/afile dir" will fail silently, +returning an exit code of 0. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr cp 1 , +.Xr rm 1 , +.Xr symlink 7 +.Sh COMPATIBILITY +The +.Fl n +and +.Fl v +options are non-standard and their use in scripts is not recommended. +.Pp +The +.Nm mv +utility now supports HFS+ Finder and Extended Attributes and resource forks. +The +.Nm mv +utility will no longer strip resource forks off of HFS files. +For an alternative method, +refer to +.Xr cp 1 . +.Sh STANDARDS +The +.Nm mv +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm mv +command appeared in +.At v1 . diff --git a/file_cmds/mv/mv.c b/file_cmds/mv/mv.c new file mode 100644 index 00000000..5911c379 --- /dev/null +++ b/file_cmds/mv/mv.c @@ -0,0 +1,455 @@ +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ken Smith of The State University of New York at Buffalo. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/mv/mv.c,v 1.39 2002/07/09 17:45:13 johan Exp $"); + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#endif + +#ifdef __APPLE__ +// #include +// #else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "pathnames.h" +#include "ios_error.h" + +static int fflg, iflg, nflg, vflg; + +static int do_move(char *, char *); +static int fastcopy(char *, char *, struct stat *); +static void usage(void); + +int +mv_main(int argc, char *argv[]) +{ + size_t baselen, len; + int rval; + char *p, *endp; + struct stat sb; +#ifdef __APPLE__ + struct stat fsb, tsb; +#endif /* __APPLE__ */ + int ch; + char path[PATH_MAX]; + // initialize flags + fflg = iflg = nflg = vflg = 0; + optind = 1; opterr = 1; optreset = 1; + + + while ((ch = getopt(argc, argv, "finv")) != -1) + switch (ch) { + case 'i': + iflg = 1; + fflg = nflg = 0; + break; + case 'f': + fflg = 1; + iflg = nflg = 0; + break; + case 'n': + nflg = 1; + fflg = iflg = 0; + break; + case 'v': + vflg = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); + } + +#ifdef __APPLE__ + if (argc == 2 && !lstat(argv[0], &fsb) && !lstat(argv[1], &tsb) && + fsb.st_ino == tsb.st_ino && fsb.st_dev == tsb.st_dev && + fsb.st_gen == tsb.st_gen) { + /* + * We appear to be trying to move a directory into itself, + * but it may be that the filesystem is case insensitive and + * we are trying to rename the directory to a case-variant. + * Ignoring trailing slashes, we look for any difference in + * the directory names. If there is a difference we do + * the rename, otherwise we fall-thru to the traditional + * error. Note the lstat calls above (rather than stat) + * permit the renaming of symlinks to case-variants. + */ + char *q; + + for (p = argv[0] + strlen(argv[0]); p != argv[0]; ) { + p--; + if (*p != '/') + break; + } + for (q = argv[1] + strlen(argv[1]); q != argv[1]; ) { + q--; + if (*q != '/') + break; + } + for ( ; ; p--, q--) { + if (*p != *q) + exit(do_move(argv[0], argv[1])); + if (*p == '/') + break; + if (p == argv[0]) { + if (q == argv[1] || *(q-1) == '/') + break; + exit(do_move(argv[0], argv[1])); + } + if (q == argv[1]) { + if (p == argv[0] || *(p-1) == '/') + break; + exit(do_move(argv[0], argv[1])); + } + } + } +#endif /* __APPLE__ */ + + /* It's a directory, move each file into it. */ + if (strlen(argv[argc - 1]) > sizeof(path) - 1) { + errx(1, "%s: destination pathname too long", *argv); + } + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + if (!baselen || *(endp - 1) != '/') { + *endp++ = '/'; + ++baselen; + } + for (rval = 0; --argc; ++argv) { + /* + * Find the last component of the source pathname. It + * may have trailing slashes. + */ + p = *argv + strlen(*argv); + while (p != *argv && p[-1] == '/') + --p; + while (p != *argv && p[-1] != '/') + --p; + + if ((baselen + (len = strlen(p))) >= PATH_MAX) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, (size_t)len + 1); + if (COMPAT_MODE("bin/mv", "unix2003")) { + /* + * For Unix 2003 compatibility, check if old and new are + * same file, and produce an error * (like on Sun) that + * conformance test 66 in mv.ex expects. + */ + if (!stat(*argv, &fsb) && !stat(path, &tsb) && + fsb.st_ino == tsb.st_ino && + fsb.st_dev == tsb.st_dev && + fsb.st_gen == tsb.st_gen) { + (void)fprintf(thread_stderr, "mv: %s and %s are identical\n", + *argv, path); + rval = 2; /* Like the Sun */ + } else { + if (do_move(*argv, path)) + rval = 1; + } + } else { + if (do_move(*argv, path)) + rval = 1; + } + } + } + exit(rval); +} + +int +do_move(char *from, char *to) +{ + struct stat sb; + int ask, ch, first; + char modep[15]; + + /* + * Check access. If interactive and file exists, ask user if it + * should be replaced. Otherwise if file exists but isn't writable + * make sure the user wants to clobber it. + */ + if (!fflg && !access(to, F_OK)) { + + /* prompt only if source exist */ + if (lstat(from, &sb) == -1) { + warn("%s", from); + return (1); + } + +#define YESNO "(y/n [n]) " + ask = 0; + if (nflg) { + if (vflg) + fprintf(thread_stdout, "%s not overwritten\n", to); + return (0); + } else if (iflg) { + (void)fprintf(thread_stderr, "overwrite %s? %s", to, YESNO); + ask = 1; + } else if (access(to, W_OK) && !stat(to, &sb)) { + strmode(sb.st_mode, modep); + (void)fprintf(thread_stderr, "override %s%s%s/%s for %s? %s", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to, YESNO); + ask = 1; + } + if (ask) { + fflush(thread_stderr); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (first != 'y' && first != 'Y') { + (void)fprintf(thread_stderr, "not overwritten\n"); + return (0); + } + } + } + if (!rename(from, to)) { + if (vflg) + fprintf(thread_stdout, "%s -> %s\n", from, to); + return (0); + } + + if (errno == EXDEV) { + struct statfs sfs; + char path[PATH_MAX]; + + /* Can't mv(1) a mount point. */ + if (realpath(from, path) == NULL) { + warnx("cannot resolve %s: %s", from, path); + return (1); + } + if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { + warnx("cannot rename a mount point"); + return (1); + } + } else { + warn("rename %s to %s", from, to); + return (1); + } + + /* + * If rename fails because we're trying to cross devices, and + * it's a regular file, do the copy internally; otherwise, use + * cp and rm. + */ + if (lstat(from, &sb)) { + warn("%s", from); + return (1); + } + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb): 0); // : copy(from, to)); +} + +int +fastcopy(char *from, char *to, struct stat *sbp) +{ + struct timeval tval[2]; + static u_int blen; + static char *bp; + mode_t oldmode; + ssize_t nread; + int from_fd, to_fd; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if (blen < sbp->st_blksize) { + if (bp != NULL) + free(bp); + if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) { + blen = 0; + warnx("malloc failed"); + return (1); + } + blen = sbp->st_blksize; + } + while ((to_fd = + open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { + if (errno == EEXIST && unlink(to) == 0) + continue; + warn("%s", to); + (void)close(from_fd); + return (1); + } +#ifdef __APPLE__ + { + struct statfs sfs; + + /* + * Pre-allocate blocks for the destination file if it + * resides on Xsan. + */ + if (fstatfs(to_fd, &sfs) == 0 && + strcmp(sfs.f_fstypename, "acfs") == 0) { + fstore_t fst; + + fst.fst_flags = 0; + fst.fst_posmode = F_PEOFPOSMODE; + fst.fst_offset = 0; + fst.fst_length = sbp->st_size; + + (void) fcntl(to_fd, F_PREALLOCATE, &fst); + } + } +#endif /* __APPLE__ */ + while ((nread = read(from_fd, bp, (size_t)blen)) > 0) + if (write(to_fd, bp, (size_t)nread) != nread) { + warn("%s", to); + goto err; + } + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } +#ifdef __APPLE__ + /* XATTR can fail if to_fd has mode 000 */ + if (fcopyfile(from_fd, to_fd, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) { + warn("%s: unable to move extended attributes and ACL from %s", + to, from); + } +#endif + (void)close(from_fd); + + oldmode = sbp->st_mode & ALLPERMS; + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { + warn("%s: set owner/group (was: %lu/%lu)", to, + (u_long)sbp->st_uid, (u_long)sbp->st_gid); + if (oldmode & (S_ISUID | S_ISGID)) { + warnx( + "mv: %s: owner/group changed; clearing suid/sgid (mode was 0%03o)\n", + to, oldmode); + sbp->st_mode &= ~(S_ISUID | S_ISGID); + } + } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode (was: 0%03o)", to, oldmode); + /* + * XXX + * NFS doesn't support chflags; ignore errors unless there's reason + * to believe we're losing bits. (Note, this still won't be right + * if the server supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if (fchflags(to_fd, (u_int)sbp->st_flags)) + if (errno != ENOTSUP || sbp->st_flags != 0) + warn("%s: set flags (was: 0%07o)", to, sbp->st_flags); + + tval[0].tv_sec = sbp->st_atime; + tval[1].tv_sec = sbp->st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + if (utimes(to, tval)) + warn("%s: set times", to); + + if (close(to_fd)) { + warn("%s", to); + return (1); + } + + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + if (vflg) + fprintf(thread_stdout, "%s -> %s\n", from, to); + return (0); +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "%s\n%s\n", + "usage: mv [-f | -i | -n] [-v] source target", + " mv [-f | -i | -n] [-v] source ... directory"); + exit(EX_USAGE); +} diff --git a/file_cmds/mv/pathnames.h b/file_cmds/mv/pathnames.h new file mode 100644 index 00000000..514678ff --- /dev/null +++ b/file_cmds/mv/pathnames.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD: src/bin/mv/pathnames.h,v 1.6 2002/05/17 11:38:48 jmallett Exp $ + */ + +#define _PATH_RM "/bin/rm" +#ifdef __APPLE__ +#define _PATH_CP "/bin/cp" +#endif /* __APPLE__ */ diff --git a/file_cmds/pathchk/pathchk.1 b/file_cmds/pathchk/pathchk.1 new file mode 100644 index 00000000..efe2a052 --- /dev/null +++ b/file_cmds/pathchk/pathchk.1 @@ -0,0 +1,121 @@ +.\" Copyright (c) 2001, 2002 Chuck Rouillard +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. The name of the author may not be used to endorse or promote +.\" products derived from this software without specific prior written +.\" permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/pathchk/pathchk.1,v 1.3 2002/12/12 17:26:01 ru Exp $ +.\" +.Dd May 21, 2002 +.Dt PATHCHK 1 +.Os +.Sh NAME +.Nm pathchk +.Nd check pathnames +.Sh SYNOPSIS +.Nm +.Op Fl p +.Ar pathname ... +.Sh DESCRIPTION +The +.Nm +utility checks whether each of the specified +.Ar pathname +arguments is valid or portable. +.Pp +A diagnostic message is written for each argument that: +.Bl -bullet +.It +Is longer than +.Dv PATH_MAX +bytes. +.It +Contains any component longer than +.Dv NAME_MAX +bytes. +(The value of +.Dv NAME_MAX +depends on the underlying file system.) +.It +Contains a directory component that is not searchable. +.El +.Pp +It is not considered an error if a +.Ar pathname +argument contains a nonexistent component as long as a component by that +name could be created. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl p +Perform portability checks on the specified +.Ar pathname +arguments. +Diagnostic messages will be written for each argument that: +.Bl -bullet +.It +Is longer than +.Dv _POSIX_PATH_MAX +.Pq 255 +bytes. +.It +Contains a component longer than +.Dv _POSIX_NAME_MAX +.Pq 14 +bytes. +.It +Contains any character not in the portable filename character set (that is, +alphanumeric characters, +.Ql \&. , +.Ql \&- +and +.Ql _ ) . +No component may start with the hyphen +.Pq Ql \&- +character. +.El +.El +.Sh EXAMPLES +Check whether the names of files in the current directory are portable to +other +.Tn POSIX +systems: +.Pp +.Dl "find . -print | xargs pathchk -p" +.Sh DIAGNOSTICS +.Ex -std +.Sh SEE ALSO +.Xr getconf 1 , +.Xr pathconf 2 , +.Xr stat 2 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +A +.Nm +utility appeared in +.Fx 5.0 . diff --git a/file_cmds/pathchk/pathchk.c b/file_cmds/pathchk/pathchk.c new file mode 100644 index 00000000..233a6d0d --- /dev/null +++ b/file_cmds/pathchk/pathchk.c @@ -0,0 +1,196 @@ +/*- + * Copyright (c) 2002 Tim J. Robbins. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * pathchk -- check pathnames + * + * Check whether files could be created with the names specified on the + * command line. If -p is specified, check whether the pathname is portable + * to all POSIX systems. + */ + +#include + +/* Commenting __FBSDID, as it is not needed n OSX ...... +__FBSDID("$FreeBSD: src/usr.bin/pathchk/pathchk.c,v 1.4 2002/12/15 00:40:47 tjr Exp $"); +*/ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +static int check(const char *); +static int portable(const char *); +static void usage(void); + +static int pflag; /* Perform portability checks */ + +int +main(int argc, char *argv[]) +{ + int ch, rval; + const char *arg; + + while ((ch = getopt(argc, argv, "p")) > 0) { + switch (ch) { + case 'p': + pflag = 1; + break; + default: + usage(); + /*NOTREACHED*/ + } + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + rval = 0; + while ((arg = *argv++) != NULL) + rval |= check(arg); + + exit(rval); +} + +static void +usage(void) +{ + + fprintf(stderr, "usage: pathchk [-p] pathname...\n"); + exit(1); +} + +static int +check(const char *path) +{ + struct stat sb; + long complen, namemax, pathmax, svnamemax; + int badch, last; + char *end, *p, *pathd; + + if ((pathd = strdup(path)) == NULL) + err(1, "strdup"); + + p = pathd; + + if (!pflag) { + errno = 0; + namemax = pathconf(*p == '/' ? "/" : ".", _PC_NAME_MAX); + if (namemax == -1 && errno != 0) + namemax = NAME_MAX; + } else + namemax = _POSIX_NAME_MAX; + + for (;;) { + p += strspn(p, "/"); + complen = (long)strcspn(p, "/"); + end = p + complen; + last = *end == '\0'; + *end = '\0'; + + if (namemax != -1 && complen > namemax) { + warnx("%s: %s: component too long (limit %ld)", path, + p, namemax); + goto bad; + } + + if (!pflag && stat(pathd, &sb) == -1 && errno != ENOENT) { + warn("%s: %.*s", path, (int)(strlen(pathd) - + complen - 1), pathd); + goto bad; + } + + if (pflag && (badch = portable(p)) >= 0) { + warnx("%s: %s: component contains non-portable " + "character `%c'", path, p, badch); + goto bad; + } + + if (last) + break; + + if (!pflag) { + errno = 0; + svnamemax = namemax; + namemax = pathconf(pathd, _PC_NAME_MAX); + if (namemax == -1 && errno != 0) + namemax = svnamemax; + } + + *end = '/'; + p = end + 1; + } + + if (!pflag) { + errno = 0; + pathmax = pathconf(path, _PC_PATH_MAX); + if (pathmax == -1 && errno != 0) + pathmax = PATH_MAX; + } else + pathmax = _POSIX_PATH_MAX; + if (pathmax != -1 && strlen(path) >= (size_t)pathmax) { + warnx("%s: path too long (limit %ld)", path, pathmax - 1); + goto bad; + } + + free(pathd); + return (0); + +bad: free(pathd); + return (1); +} + +/* + * Check whether a path component contains only portable characters. Return + * the first non-portable character found. + */ +static int +portable(const char *path) +{ + static const char charset[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789._-"; + long s; + + if (*path == '-') + return (*path); + + s = strspn(path, charset); + if (path[s] != '\0') + return (path[s]); + + return (-1); +} diff --git a/file_cmds/pax/ar_io.c b/file_cmds/pax/ar_io.c new file mode 100644 index 00000000..1c4b943e --- /dev/null +++ b/file_cmds/pax/ar_io.c @@ -0,0 +1,1318 @@ +/* $OpenBSD: ar_io.c,v 1.38 2008/06/11 00:49:08 pvalchev Exp $ */ +/* $NetBSD: ar_io.c,v 1.5 1996/03/26 23:54:13 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: ar_io.c,v 1.38 2008/06/11 00:49:08 pvalchev Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#endif /* !__APPLE__ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "extern.h" + +/* + * Routines which deal directly with the archive I/O device/file. + */ + +#define DMOD 0666 /* default mode of created archives */ +#define EXT_MODE O_RDONLY /* open mode for list/extract */ +#define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */ +#define APP_MODE O_RDWR /* mode for append */ +#define STDO "" /* pseudo name for stdout */ +#define STDN "" /* pseudo name for stdin */ +#define _NONE "" /* pseudo name for no files */ +static int arfd = -1; /* archive file descriptor */ +static int artyp = ISREG; /* archive type: file/FIFO/tape */ +static int arvol = 1; /* archive volume number */ +static int lstrval = -1; /* return value from last i/o */ +static int io_ok; /* i/o worked on volume after resync */ +static int did_io; /* did i/o ever occur on volume? */ +static int done; /* set via tty termination */ +static struct stat arsb; /* stat of archive device at open */ +static int invld_rec; /* tape has out of spec record size */ +static int wr_trail = 1; /* trailer was rewritten in append */ +static int can_unlnk = 0; /* do we unlink null archives? */ +const char *arcname; /* printable name of archive */ +const char *gzip_program; /* name of gzip program */ +static pid_t zpid = -1; /* pid of child process */ +int force_one_volume; /* 1 if we ignore volume changes */ + +#ifndef __APPLE__ +static int get_phys(void); +#endif /* __APPLE__ */ +extern sigset_t s_mask; +static void ar_start_gzip(int, const char *, int); + +/* + * ar_open() + * Opens the next archive volume. Determines the type of the device and + * sets up block sizes as required by the archive device and the format. + * Note: we may be called with name == NULL on the first open only. + * Return: + * -1 on failure, 0 otherwise + */ + +int +ar_open(const char *name) +{ +#ifndef __APPLE__ + struct mtget mb; +#endif /* __APPLE__ */ + if (arfd != -1) + (void)close(arfd); + arfd = -1; + can_unlnk = did_io = io_ok = invld_rec = 0; + artyp = ISREG; + flcnt = 0; + + /* + * open based on overall operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + if (name == NULL) { + arfd = STDIN_FILENO; + arcname = STDN; + } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) + syswarn(1, errno, "Failed open to read on %s", name); + if (arfd != -1 && gzip_program != NULL) + ar_start_gzip(arfd, gzip_program, 0); + break; + case ARCHIVE: + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, AR_MODE, DMOD)) < 0) + syswarn(1, errno, "Failed open to write on %s", name); + else + can_unlnk = 1; + if (arfd != -1 && gzip_program != NULL) + ar_start_gzip(arfd, gzip_program, 1); + break; + case APPND: + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, APP_MODE, DMOD)) < 0) + syswarn(1, errno, "Failed open to read/write on %s", + name); + break; + case COPY: + /* + * arfd not used in COPY mode + */ + arcname = _NONE; + lstrval = 1; + return(0); + } + if (arfd < 0) + return(-1); + + if (chdname != NULL) + if (dochdir(chdname) == -1) { + return(-1); + } + /* + * set up is based on device type + */ + if (fstat(arfd, &arsb) < 0) { + syswarn(0, errno, "Failed stat on %s", arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + if (S_ISDIR(arsb.st_mode)) { + paxwarn(0, "Cannot write an archive on top of a directory %s", + arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + +#ifndef __APPLE__ + if (S_ISCHR(arsb.st_mode)) + artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; + else +#endif /* !__APPLE__ */ + if (S_ISBLK(arsb.st_mode)) + artyp = ISBLK; + else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE)) + artyp = ISPIPE; + else + artyp = ISREG; + + /* + * make sure we beyond any doubt that we only can unlink regular files + * we created + */ + if (artyp != ISREG) + can_unlnk = 0; + /* + * if we are writing, we are done + */ + if (act == ARCHIVE) { + blksz = rdblksz = wrblksz; + lstrval = 1; + return(0); + } + + /* + * set default blksz on read. APPNDs writes rdblksz on the last volume + * On all new archive volumes, we shift to wrblksz (if the user + * specified one, otherwise we will continue to use rdblksz). We + * must set blocksize based on what kind of device the archive is + * stored. + */ + switch (artyp) { + case ISTAPE: + /* + * Tape drives come in at least two flavors. Those that support + * variable sized records and those that have fixed sized + * records. They must be treated differently. For tape drives + * that support variable sized records, we must make large + * reads to make sure we get the entire record, otherwise we + * will just get the first part of the record (up to size we + * asked). Tapes with fixed sized records may or may not return + * multiple records in a single read. We really do not care + * what the physical record size is UNLESS we are going to + * append. (We will need the physical block size to rewrite + * the trailer). Only when we are appending do we go to the + * effort to figure out the true PHYSICAL record size. + */ + blksz = rdblksz = MAXBLK; + break; + case ISPIPE: + case ISBLK: + case ISCHR: + /* + * Blocksize is not a major issue with these devices (but must + * be kept a multiple of 512). If the user specified a write + * block size, we use that to read. Under append, we must + * always keep blksz == rdblksz. Otherwise we go ahead and use + * the device optimal blocksize as (and if) returned by stat + * and if it is within pax specs. + */ + if ((act == APPND) && wrblksz) { + blksz = rdblksz = wrblksz; + break; + } + + if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && + ((arsb.st_blksize % BLKMULT) == 0)) + rdblksz = arsb.st_blksize; + else + rdblksz = DEVBLK; + /* + * For performance go for large reads when we can without harm + */ + if ((act == APPND) || (artyp == ISCHR)) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + case ISREG: + /* + * if the user specified wrblksz works, use it. Under appends + * we must always keep blksz == rdblksz + */ + if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ + blksz = rdblksz = wrblksz; + break; + } + /* + * See if we can find the blocking factor from the file size + */ + for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) + if ((arsb.st_size % rdblksz) == 0) + break; + /* + * When we cannot find a match, we may have a flawed archive. + */ + if (rdblksz <= 0) + rdblksz = FILEBLK; + /* + * for performance go for large reads when we can + */ + if (act == APPND) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + default: + /* + * should never happen, worst case, slow... + */ + blksz = rdblksz = BLKMULT; + break; + } + lstrval = 1; + return(0); +} + +/* + * ar_close() + * closes archive device, increments volume number, and prints i/o summary + */ +void +ar_close(void) +{ + int status; + + if (arfd < 0) { + did_io = io_ok = flcnt = 0; + return; + } + + /* + * Close archive file. This may take a LONG while on tapes (we may be + * forced to wait for the rewind to complete) so tell the user what is + * going on (this avoids the user hitting control-c thinking pax is + * broken). + */ + if (vflag && (artyp == ISTAPE)) { + if (vfpart) + (void)putc('\n', listf); + (void)fprintf(listf, + "%s: Waiting for tape drive close to complete...", + argv0); + (void)fflush(listf); + } + + /* + * if nothing was written to the archive (and we created it), we remove + * it + */ + if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) && + (arsb.st_size == 0)) { + (void)unlink(arcname); + can_unlnk = 0; + } + + /* + * for a quick extract/list, pax frequently exits before the child + * process is done + */ + if ((act == LIST || act == EXTRACT) && nflag && zpid > 0) + kill(zpid, SIGINT); + + (void)close(arfd); + + /* Do not exit before child to ensure data integrity */ + if (zpid > 0) + waitpid(zpid, &status, 0); + + if (vflag && (artyp == ISTAPE)) { + (void)fputs("done.\n", listf); + vfpart = 0; + (void)fflush(listf); + } + arfd = -1; + + if (!io_ok && !did_io) { + flcnt = 0; + return; + } + did_io = io_ok = 0; + + /* + * The volume number is only increased when the last device has data + * and we have already determined the archive format. + */ + if (frmt != NULL) + ++arvol; + + if (!vflag) { + flcnt = 0; + return; + } + + /* + * Print out a summary of I/O for this archive volume. + */ + if (vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + + /* + * If we have not determined the format yet, we just say how many bytes + * we have skipped over looking for a header to id. there is no way we + * could have written anything yet. + */ + if (frmt == NULL) { +# ifdef LONG_OFF_T + (void)fprintf(listf, "%s: unknown format, %lu bytes skipped.\n", +# else + (void)fprintf(listf, "%s: unknown format, %qu bytes skipped.\n", +# endif + argv0, rdcnt); + (void)fflush(listf); + flcnt = 0; + return; + } + + if (strcmp(NM_CPIO, argv0) == 0) + (void)fprintf(listf, "%qu blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120); + else if (strcmp(NM_TAR, argv0) != 0 && strcmp(NM_PAX, argv0) != 0) + (void)fprintf(listf, +# ifdef LONG_OFF_T + "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n", + argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt); +# else + "%s: %s vol %d, %lu files, %ju bytes read, %ju bytes written.\n", + argv0, frmt->name, arvol-1, flcnt, (uintmax_t)rdcnt, (uintmax_t)wrcnt); +# endif + (void)fflush(listf); + flcnt = 0; +} + +/* + * ar_drain() + * drain any archive format independent padding from an archive read + * from a socket or a pipe. This is to prevent the process on the + * other side of the pipe from getting a SIGPIPE (pax will stop + * reading an archive once a format dependent trailer is detected). + */ +void +ar_drain(void) +{ + int res; + char drbuf[MAXBLK]; + + /* + * we only drain from a pipe/socket. Other devices can be closed + * without reading up to end of file. We sure hope that pipe is closed + * on the other side so we will get an EOF. + */ + if ((artyp != ISPIPE) || (lstrval <= 0)) + return; + + /* + * keep reading until pipe is drained + */ + while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0) + ; + lstrval = res; +} + +/* + * ar_set_wr() + * Set up device right before switching from read to write in an append. + * device dependent code (if required) to do this should be added here. + * For all archive devices we are already positioned at the place we want + * to start writing when this routine is called. + * Return: + * 0 if all ready to write, -1 otherwise + */ + +int +ar_set_wr(void) +{ + off_t cpos; + + /* + * we must make sure the trailer is rewritten on append, ar_next() + * will stop us if the archive containing the trailer was not written + */ + wr_trail = 0; + + /* + * Add any device dependent code as required here + */ + if (artyp != ISREG) + return(0); + /* + * Ok we have an archive in a regular file. If we were rewriting a + * file, we must get rid of all the stuff after the current offset + * (it was not written by pax). + */ + if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) || + (ftruncate(arfd, cpos) < 0)) { + syswarn(1, errno, "Unable to truncate archive file"); + return(-1); + } + return(0); +} + +/* + * ar_app_ok() + * check if the last volume in the archive allows appends. We cannot check + * this until we are ready to write since there is no spec that says all + * volumes in a single archive have to be of the same type... + * Return: + * 0 if we can append, -1 otherwise. + */ + +int +ar_app_ok(void) +{ + if (artyp == ISPIPE) { + paxwarn(1, "Cannot append to an archive obtained from a pipe."); + return(-1); + } + + if (!invld_rec) + return(0); + paxwarn(1,"Cannot append, device record size %d does not support %s spec", + rdblksz, argv0); + return(-1); +} + +/* + * ar_read() + * read up to a specified number of bytes from the archive into the + * supplied buffer. When dealing with tapes we may not always be able to + * read what we want. + * Return: + * Number of bytes in buffer. 0 for end of file, -1 for a read error. + */ + +int +ar_read(char *buf, int cnt) +{ + int res = 0; + + /* + * if last i/o was in error, no more reads until reset or new volume + */ + if (lstrval <= 0) + return(lstrval); + + /* + * how we read must be based on device type + */ + switch (artyp) { + case ISTAPE: + if ((res = read(arfd, buf, cnt)) > 0) { + /* + * CAUTION: tape systems may not always return the same + * sized records so we leave blksz == MAXBLK. The + * physical record size that a tape drive supports is + * very hard to determine in a uniform and portable + * manner. + */ + io_ok = 1; + if (res != rdblksz) { + /* + * Record size changed. If this happens on + * any record after the first, we probably have + * a tape drive which has a fixed record size + * (we are getting multiple records in a single + * read). Watch out for record blocking that + * violates pax spec (must be a multiple of + * BLKMULT). + */ + rdblksz = res; + if (rdblksz % BLKMULT) + invld_rec = 1; + } + return(res); + } + break; + case ISREG: + case ISBLK: + case ISCHR: + case ISPIPE: + default: + /* + * Files are so easy to deal with. These other things cannot + * be trusted at all. So when we are dealing with character + * devices and pipes we just take what they have ready for us + * and return. Trying to do anything else with them runs the + * risk of failure. + */ + if ((res = read(arfd, buf, cnt)) > 0) { + io_ok = 1; + return(res); + } + break; + } + + /* + * We are in trouble at this point, something is broken... + */ + lstrval = res; + if (res < 0) + syswarn(1, errno, "Failed read on archive volume %d", arvol); + else + paxwarn(0, "End of archive volume %d reached", arvol); + return(res); +} + +/* + * ar_write() + * Write a specified number of bytes in supplied buffer to the archive + * device so it appears as a single "block". Deals with errors and tries + * to recover when faced with short writes. + * Return: + * Number of bytes written. 0 indicates end of volume reached and with no + * flaws (as best that can be detected). A -1 indicates an unrecoverable + * error in the archive occurred. + */ + +int +ar_write(char *buf, int bsz) +{ + int res; + off_t cpos; + + /* + * do not allow pax to create a "bad" archive. Once a write fails on + * an archive volume prevent further writes to it. + */ + if (lstrval <= 0) + return(lstrval); + + if ((res = write(arfd, buf, bsz)) == bsz) { + wr_trail = 1; + io_ok = 1; + return(bsz); + } else if (res < 0 && artyp == ISPIPE && errno == EPIPE) { /* ignore it */ + wr_trail = 1; + io_ok = 1; + errno = 0; + arfd = open("/dev/null", AR_MODE, DMOD); + artyp = ISREG; + return bsz; + } + + /* + * write broke, see what we can do with it. We try to send any partial + * writes that may violate pax spec to the next archive volume. + */ + if (res < 0) + lstrval = res; + else + lstrval = 0; + + switch (artyp) { + case ISREG: + if ((res > 0) && (res % BLKMULT)) { + /* + * try to fix up partial writes which are not BLKMULT + * in size by forcing the runt record to next archive + * volume + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + cpos -= (off_t)res; + if (ftruncate(arfd, cpos) < 0) + break; + res = lstrval = 0; + break; + } + if (res >= 0) + break; + /* + * if file is out of space, handle it like a return of 0 + */ + if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) + res = lstrval = 0; + break; + case ISTAPE: + case ISCHR: + case ISBLK: + if (res >= 0) + break; + if (errno == EACCES) { + paxwarn(0, "Write failed, archive is write protected."); + lstrval = 0; + return(0); + } + /* + * see if we reached the end of media, if so force a change to + * the next volume + */ + if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) + res = lstrval = 0; + break; + case ISPIPE: + default: + /* + * we cannot fix errors to these devices + */ + break; + } + + /* + * Better tell the user the bad news... + * if this is a block aligned archive format, we may have a bad archive + * if the format wants the header to start at a BLKMULT boundary.. While + * we can deal with the mis-aligned data, it violates spec and other + * archive readers will likely fail. if the format is not block + * aligned, the user may be lucky (and the archive is ok). + */ + if (res >= 0) { + if (res > 0) + wr_trail = 1; + io_ok = 1; + } + + /* + * If we were trying to rewrite the trailer and it didn't work, we + * must quit right away. + */ + if (!wr_trail && (res <= 0)) { + paxwarn(1,"Unable to append, trailer re-write failed. Quitting."); + return(res); + } + + if (res == 0) + paxwarn(0, "End of archive volume %d reached", arvol); + else if (res < 0) + syswarn(1, errno, "Failed write to archive volume: %d", arvol); + else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) + paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); + else + paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED"); + return(res); +} + +/* + * ar_rdsync() + * Try to move past a bad spot on a flawed archive as needed to continue + * I/O. Clears error flags to allow I/O to continue. + * Return: + * 0 when ok to try i/o again, -1 otherwise. + */ + +int +ar_rdsync(void) +{ + long fsbz; + off_t cpos; + off_t mpos; +#ifndef __APPLE__ + struct mtop mb; +#endif /* !__APPLE__ */ + + /* + * Fail resync attempts at user request (done) or if this is going to be + * an update/append to a existing archive. if last i/o hit media end, + * we need to go to the next volume not try a resync + */ + if ((done > 0) || (lstrval == 0)) + return(-1); + + if ((act == APPND) || (act == ARCHIVE)) { + paxwarn(1, "Cannot allow updates to an archive with flaws."); + return(-1); + } + if (io_ok) + did_io = 1; + + switch (artyp) { +#ifndef __APPLE__ + case ISTAPE: + /* + * if the last i/o was a successful data transfer, we assume + * the fault is just a bad record on the tape that we are now + * past. If we did not get any data since the last resync try + * to move the tape forward one PHYSICAL record past any + * damaged tape section. Some tape drives are stubborn and need + * to be pushed. + */ + if (io_ok) { + io_ok = 0; + lstrval = 1; + break; + } + mb.mt_op = MTFSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) + break; + lstrval = 1; + break; +#endif /* !__APPLE__ */ + case ISREG: + case ISCHR: + case ISBLK: + /* + * try to step over the bad part of the device. + */ + io_ok = 0; + if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG)) + fsbz = BLKMULT; + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + mpos = fsbz - (cpos % (off_t)fsbz); + if (lseek(arfd, mpos, SEEK_CUR) < 0) + break; + lstrval = 1; + break; + case ISPIPE: + default: + /* + * cannot recover on these archive device types + */ + io_ok = 0; + break; + } + if (lstrval <= 0) { + paxwarn(1, "Unable to recover from an archive read failure."); + return(-1); + } + paxwarn(0, "Attempting to recover from an archive read failure."); + return(0); +} + +/* + * ar_fow() + * Move the I/O position within the archive forward the specified number of + * bytes as supported by the device. If we cannot move the requested + * number of bytes, return the actual number of bytes moved in skipped. + * Return: + * 0 if moved the requested distance, -1 on complete failure, 1 on + * partial move (the amount moved is in skipped) + */ + +int +ar_fow(off_t sksz, off_t *skipped) +{ + off_t cpos; + off_t mpos; + + *skipped = 0; + if (sksz <= 0) + return(0); + + /* + * we cannot move forward at EOF or error + */ + if (lstrval <= 0) + return(lstrval); + + /* + * Safer to read forward on devices where it is hard to find the end of + * the media without reading to it. With tapes we cannot be sure of the + * number of physical blocks to skip (we do not know physical block + * size at this point), so we must only read forward on tapes! + */ + if (artyp != ISREG) + return(0); + + /* + * figure out where we are in the archive + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) { + /* + * we can be asked to move farther than there are bytes in this + * volume, if so, just go to file end and let normal buf_fill() + * deal with the end of file (it will go to next volume by + * itself) + */ + if ((mpos = cpos + sksz) > arsb.st_size) { + *skipped = arsb.st_size - cpos; + mpos = arsb.st_size; + } else + *skipped = sksz; + if (lseek(arfd, mpos, SEEK_SET) >= 0) + return(0); + } + syswarn(1, errno, "Forward positioning operation on archive failed"); + lstrval = -1; + return(-1); +} + +/* + * ar_rev() + * move the i/o position within the archive backwards the specified byte + * count as supported by the device. With tapes drives we RESET rdblksz to + * the PHYSICAL blocksize. + * NOTE: We should only be called to move backwards so we can rewrite the + * last records (the trailer) of an archive (APPEND). + * Return: + * 0 if moved the requested distance, -1 on complete failure + */ + +int +ar_rev(off_t sksz) +{ + off_t cpos; +#ifndef __APPLE__ + struct mtop mb; + int phyblk; +#endif /* __APPLE__ */ + + /* + * make sure we do not have try to reverse on a flawed archive + */ + if (lstrval < 0) + return(lstrval); + + switch (artyp) { + case ISPIPE: + if (sksz <= 0) + break; + /* + * cannot go backwards on these critters + */ + paxwarn(1, "Reverse positioning on pipes is not supported."); + lstrval = -1; + return(-1); + case ISREG: + case ISBLK: + case ISCHR: + default: + if (sksz <= 0) + break; + + /* + * For things other than files, backwards movement has a very + * high probability of failure as we really do not know the + * true attributes of the device we are talking to (the device + * may not even have the ability to lseek() in any direction). + * First we figure out where we are in the archive. + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) { + syswarn(1, errno, + "Unable to obtain current archive byte offset"); + lstrval = -1; + return(-1); + } + + /* + * we may try to go backwards past the start when the archive + * is only a single record. If this happens and we are on a + * multi-volume archive, we need to go to the end of the + * previous volume and continue our movement backwards from + * there. + */ + if ((cpos -= sksz) < (off_t)0L) { + if (arvol > 1) { + /* + * this should never happen + */ + paxwarn(1,"Reverse position on previous volume."); + lstrval = -1; + return(-1); + } + cpos = (off_t)0L; + } + if (lseek(arfd, cpos, SEEK_SET) < 0) { + syswarn(1, errno, "Unable to seek archive backwards"); + lstrval = -1; + return(-1); + } + break; +#ifndef __APPLE__ + case ISTAPE: + /* + * Calculate and move the proper number of PHYSICAL tape + * blocks. If the sksz is not an even multiple of the physical + * tape size, we cannot do the move (this should never happen). + * (We also cannot handle trailers spread over two vols.) + * get_phys() also makes sure we are in front of the filemark. + */ + if ((phyblk = get_phys()) <= 0) { + lstrval = -1; + return(-1); + } + + /* + * make sure future tape reads only go by physical tape block + * size (set rdblksz to the real size). + */ + rdblksz = phyblk; + + /* + * if no movement is required, just return (we must be after + * get_phys() so the physical blocksize is properly set) + */ + if (sksz <= 0) + break; + + /* + * ok we have to move. Make sure the tape drive can do it. + */ + if (sksz % phyblk) { + paxwarn(1, + "Tape drive unable to backspace requested amount"); + lstrval = -1; + return(-1); + } + + /* + * move backwards the requested number of bytes + */ + mb.mt_op = MTBSR; + mb.mt_count = sksz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno, "Unable to backspace tape %d blocks.", + mb.mt_count); + lstrval = -1; + return(-1); + } + break; +#endif /* !__APPLE__ */ + } + lstrval = 1; + return(0); +} +#ifndef __APPLE__ +/* + * get_phys() + * Determine the physical block size on a tape drive. We need the physical + * block size so we know how many bytes we skip over when we move with + * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when + * return. + * This is one really SLOW routine... + * Return: + * physical block size if ok (ok > 0), -1 otherwise + */ + +static int +get_phys(void) +{ + int padsz = 0; + int res; + int phyblk; + struct mtop mb; + char scbuf[MAXBLK]; + + /* + * move to the file mark, and then back up one record and read it. + * this should tell us the physical record size the tape is using. + */ + if (lstrval == 1) { + /* + * we know we are at file mark when we get back a 0 from + * read() + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + padsz += res; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + } + + /* + * move backwards over the file mark so we are at the end of the + * last record. + */ + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * move backwards so we are in front of the last record and read it to + * get physical tape blocksize. + */ + mb.mt_op = MTBSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over last tape block."); + return(-1); + } + if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { + syswarn(1, errno, "Cannot determine archive tape blocksize."); + return(-1); + } + + /* + * read forward to the file mark, then back up in front of the filemark + * (this is a bit paranoid, but should be safe to do). + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + ; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * set lstrval so we know that the filemark has not been seen + */ + lstrval = 1; + + /* + * return if there was no padding + */ + if (padsz == 0) + return(phyblk); + + /* + * make sure we can move backwards over the padding. (this should + * never fail). + */ + if (padsz % phyblk) { + paxwarn(1, "Tape drive unable to backspace requested amount"); + return(-1); + } + + /* + * move backwards over the padding so the head is where it was when + * we were first called (if required). + */ + mb.mt_op = MTBSR; + mb.mt_count = padsz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno,"Unable to backspace tape over %d pad blocks", + mb.mt_count); + return(-1); + } + return(phyblk); +} +#endif /* !__APPLE__ */ +/* + * ar_next() + * prompts the user for the next volume in this archive. For some devices + * we may allow the media to be changed. Otherwise a new archive is + * prompted for. By pax spec, if there is no controlling tty or an eof is + * read on tty input, we must quit pax. + * Return: + * 0 when ready to continue, -1 when all done + */ + +int +ar_next(void) +{ + char buf[PAXPATHLEN+2]; + static int freeit = 0; + sigset_t o_mask; + + /* + * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so + * things like writing EOF etc will be done) (Watch out ar_close() can + * also be called via a signal handler, so we must prevent a race. + */ + if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) + syswarn(0, errno, "Unable to set signal mask"); + ar_close(); + if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) + syswarn(0, errno, "Unable to restore signal mask"); + + if (frmt == NULL || done || !wr_trail || force_one_volume || strcmp(NM_TAR, argv0) == 0 || + strcmp(NM_PAX, argv0) == 0) + return(-1); + + tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); + + /* + * if i/o is on stdin or stdout, we cannot reopen it (we do not know + * the name), the user will be forced to type it in. + */ + if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG) + && (artyp != ISPIPE)) { + if (artyp == ISTAPE) { + tty_prnt("%s ready for archive tape volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT TAPE on the tape drive"); + } else { + tty_prnt("%s ready for archive volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); + } + + if ((act == ARCHIVE) || (act == APPND)) + tty_prnt(" and make sure it is WRITE ENABLED.\n"); + else + tty_prnt("\n"); + + for (;;) { + tty_prnt("Type \"y\" to continue, \".\" to quit %s,", + argv0); + tty_prnt(" or \"s\" to switch to new device.\nIf you"); + tty_prnt(" cannot change storage media, type \"s\"\n"); + tty_prnt("Is the device ready and online? > "); + + if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){ + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + + if ((buf[0] == '\0') || (buf[1] != '\0')) { + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + + switch (buf[0]) { + case 'y': + case 'Y': + /* + * we are to continue with the same device + */ + if (ar_open(arcname) >= 0) + return(0); + tty_prnt("Cannot re-open %s, try again\n", + arcname); + continue; + case 's': + case 'S': + /* + * user wants to open a different device + */ + tty_prnt("Switching to a different archive\n"); + break; + default: + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + break; + } + } else + tty_prnt("Ready for archive volume: %d\n", arvol); + + /* + * have to go to a different archive + */ + for (;;) { + tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); + tty_prnt("Archive name > "); + + if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) { + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + if (buf[0] == '\0') { + tty_prnt("Empty file name, try again\n"); + continue; + } + if (!strcmp(buf, "..")) { + tty_prnt("Illegal file name: .. try again\n"); + continue; + } + if (strlen(buf) > PAXPATHLEN) { + tty_prnt("File name too long, try again\n"); + continue; + } + + /* + * try to open new archive + */ + if (ar_open(buf) >= 0) { + if (freeit) { + (void)free((char *)arcname); + freeit = 0; + } + if ((arcname = strdup(buf)) == NULL) { + done = 1; + lstrval = -1; + paxwarn(0, "Cannot save archive name."); + return(-1); + } + freeit = 1; + break; + } + tty_prnt("Cannot open %s, try again\n", buf); + continue; + } + return(0); +} + +/* + * ar_start_gzip() + * starts the gzip compression/decompression process as a child, using magic + * to keep the fd the same in the calling function (parent). + */ +void +ar_start_gzip(int fd, const char *gzip_program, int wr) +{ + int fds[2]; + const char *gzip_flags = NULL; + + if (pipe(fds) < 0) + err(1, "could not pipe"); + zpid = fork(); + if (zpid < 0) + err(1, "could not fork"); + + /* parent */ + if (zpid) { + if (wr) + dup2(fds[1], fd); + else + dup2(fds[0], fd); + close(fds[0]); + close(fds[1]); + } else { + if (wr) { + dup2(fds[0], STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + gzip_flags = "-c"; + } else { + dup2(fds[1], STDOUT_FILENO); + dup2(fd, STDIN_FILENO); + gzip_flags = "-dc"; + } + close(fds[0]); + close(fds[1]); + if (execlp(gzip_program, gzip_program, gzip_flags, (char *)NULL) < 0) + err(1, "could not exec %s", gzip_program); + /* NOTREACHED */ + } +} diff --git a/file_cmds/pax/ar_subs.c b/file_cmds/pax/ar_subs.c new file mode 100644 index 00000000..b4896eab --- /dev/null +++ b/file_cmds/pax/ar_subs.c @@ -0,0 +1,1517 @@ +/* $OpenBSD: ar_subs.c,v 1.32 2008/05/06 06:54:28 henning Exp $ */ +/* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: ar_subs.c,v 1.32 2008/05/06 06:54:28 henning Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef __APPLE__ +#include +#include +#include +#include +#endif +#include "pax.h" +#include "options.h" +#include "extern.h" + +static int path_check(ARCHD *, int); +static void wr_archive(ARCHD *, int is_app); +static int get_arc(void); +static int next_head(ARCHD *); +extern sigset_t s_mask; + +/* + * Routines which control the overall operation modes of pax as specified by + * the user: list, append, read ... + */ + +static char hdbuf[BLKMULT]; /* space for archive header on read */ +u_long flcnt; /* number of files processed */ + +static char cwdpath[MAXPATHLEN]; /* current working directory path */ +static size_t cwdpathlen; /* current working directory path len */ + +int +updatepath(void) +{ + if (getcwd(cwdpath, sizeof(cwdpath)) == NULL) { + syswarn(1, errno, "Cannot get working directory"); + return -1; + } + cwdpathlen = strlen(cwdpath); + return 0; +} + +int +fdochdir(int fcwd) +{ + if (fchdir(fcwd) == -1) { + syswarn(1, errno, "Cannot chdir to `.'"); + return -1; + } + return updatepath(); +} + +int +dochdir(const char *name) +{ + if (chdir(name) == -1) + syswarn(1, errno, "Cannot chdir to `%s'", name); + return updatepath(); +} + +static int +path_check(ARCHD *arcn, int level) +{ + char buf[MAXPATHLEN]; + char *p; + + if ((p = strrchr(arcn->name, '/')) == NULL) + return 0; + *p = '\0'; + + if (realpath(arcn->name, buf) == NULL) { + int error; + error = path_check(arcn, level + 1); + *p = '/'; + if (error == 0) + return 0; + if (level == 0) + syswarn(1, 0, "Cannot resolve `%s'", arcn->name); + return -1; + } + if (cwdpathlen == 1) { /* We're in the root */ + *p = '/'; + return 0; + } + if ((strncmp(buf, cwdpath, cwdpathlen) != 0) || (buf[cwdpathlen] != '\0' && buf[cwdpathlen] != '/')) { + *p = '/'; + syswarn(1, 0, "Attempt to write file `%s' that resolves into " + "`%s/%s' outside current working directory `%s' ignored", + arcn->name, buf, p + 1, cwdpath); + return -1; + } + *p = '/'; + return 0; +} + +/* + * list() + * list the contents of an archive which match user supplied pattern(s) + * (no pattern matches all). + */ + +void +list(void) +{ + ARCHD *arcn; + int res; + ARCHD archd; + time_t now; + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine. We + * also save current time for ls_list() so we do not make a system + * call for each file we need to print. If verbose (vflag) start up + * the name and group caches. + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0)) + return; + + if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0))) + return; + + now = time(NULL); + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { + /* + * we need to read, to get the real filename + */ + off_t cnt; + if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF + ? -1 : -2, &cnt)) + (void)rd_skip(cnt + arcn->pad); + continue; + } + + /* + * check for pattern, and user specified options match. + * When all patterns are matched we are done. + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res == 0) && (sel_chk(arcn) == 0)) { + /* + * pattern resulted in a selected file + */ + if (pat_sel(arcn) < 0) + break; + + /* + * modify the name as requested by the user if name + * survives modification, do a listing of the file + */ + if ((res = mod_name(arcn)) < 0) + break; + if (res == 0) + ls_list(arcn, now, stdout); + } + + /* + * skip to next archive format header using values calculated + * by the format header read routine + */ + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + } + + /* + * all done, let format have a chance to cleanup, and make sure that + * the patterns supplied by the user were all matched + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + pat_chk(); +} + +/* + * extract() + * extract the member(s) of an archive as specified by user supplied + * pattern(s) (no patterns extracts all members) + */ + +void +extract(void) +{ + ARCHD *arcn; + int res; + off_t cnt; + ARCHD archd; + struct stat sb; + int fd; + time_t now; + +#ifdef __APPLE__ + int copyfile_disable = (getenv(COPYFILE_DISABLE_VAR) != NULL); + LIST_HEAD(copyfile_list_t, copyfile_list_entry_t) copyfile_list; + struct copyfile_list_entry_t { + char *src; + char *dst; + LIST_ENTRY(copyfile_list_entry_t) link; + } *cle; + + LIST_INIT(©file_list); +#endif + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine; + * start up the directory modification time and access mode database + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + now = time(NULL); + + /* + * step through each entry on the archive until the format read routine + * says it is done + */ + while (next_head(arcn) == 0) { + if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { + /* + * we need to read, to get the real filename + */ + if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF + ? -1 : -2, &cnt)) + (void)rd_skip(cnt + arcn->pad); + continue; + } + + /* + * check for pattern, and user specified options match. When + * all the patterns are matched we are done + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res > 0) || (sel_chk(arcn) != 0)) { + /* + * file is not selected. skip past any file data and + * padding and go back for the next archive member + */ + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * with -u or -D only extract when the archive member is newer + * than the file with the same name in the file system (no + * test of being the same type is required). + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this operation can be confusing to the + * user who might expect the test to be done on an existing + * file AFTER the name mod. In honesty the pax spec is probably + * flawed in this respect. + */ + if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + /* + * this archive member is now been selected. modify the name. + */ + if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if (res > 0) { + /* + * a bad name mod, skip and purge name from link table + */ + purg_lnk(arcn); + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * Non standard -Y and -Z flag. When the existing file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + if (vflag) { + if (vflag > 1) + ls_list(arcn, now, listf); + else { + (void)safe_print(arcn->name, listf); + vfpart = 1; + } + } + + /* + * if required, chdir around. + */ + if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) + dochdir(arcn->pat->chdname); + + if (secure && path_check(arcn, 0) != 0) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * all ok, extract this member based on type + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * process archive members that are not regular files. + * throw out padding and any data that might follow the + * header (as determined by the format). + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + + (void)rd_skip(arcn->skip + arcn->pad); + if (res < 0) + purg_lnk(arcn); + + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + continue; + } + /* + * we have a file with data here. If we can not create it, skip + * over the data and purge the name from hard link table + */ + if ((fd = file_creat(arcn)) < 0) { + (void)rd_skip(arcn->skip + arcn->pad); + purg_lnk(arcn); + continue; + } + /* + * extract the file from the archive and skip over padding and + * any unprocessed data + */ + res = (*frmt->rd_data)(arcn, fd, &cnt); + file_close(arcn, fd); + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + if (!res) + (void)rd_skip(cnt + arcn->pad); + +#ifdef __APPLE__ + if (!strncmp(basename(arcn->name), "._", 2)) { + cle = alloca(sizeof(struct copyfile_list_entry_t)); + cle->src = strdup(arcn->name); + + if (asprintf(&cle->dst, "%s/%s", + dirname(arcn->name), basename(arcn->name) + 2) != -1) { + LIST_INSERT_HEAD(©file_list, cle, link); + } else { + free(cle->src); + } + } +#endif + /* + * if required, chdir around. + */ + if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) + fdochdir(cwdfd); + } +#ifdef __APPLE__ + LIST_FOREACH(cle, ©file_list, link) + { + if(copyfile_disable || copyfile(cle->src, cle->dst, NULL, + COPYFILE_UNPACK | COPYFILE_XATTR | COPYFILE_ACL)) { + if (!copyfile_disable) { + syswarn(1, errno, "Unable to set metadata on %s", cle->dst); + } + } else { + unlink(cle->src); + } + free(cle->dst); + free(cle->src); + } +#endif + + /* + * all done, restore directory modes and times as required; make sure + * all patterns supplied by the user were matched; block off signals + * to avoid chance for multiple entry into the cleanup code. + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + proc_dir(); + pat_chk(); +} + +/* + * wr_archive() + * Write an archive. used in both creating a new archive and appends on + * previously written archive. + */ + +static void +wr_archive(ARCHD *arcn, int is_app) +{ + int res; + int hlk; + int wr_one; + off_t cnt; + int (*wrf)(ARCHD *); + int fd = -1; + time_t now; + +#ifdef __APPLE__ + int metadata = 0; + char *md_fname = NULL; + ARCHD arcn_copy; + char arcn_copy_name[PAXPATHLEN+1]; +#endif + + /* + * if this format supports hard link storage, start up the database + * that detects them. + */ + if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0)) + return; + + if (hlk && want_linkdata) hlk=0; /* Treat hard links as individual files */ + + /* + * start up the file traversal code and format specific write + */ + if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0)) + return; + wrf = frmt->wr; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * if this is not append, and there are no files, we do not write a + * trailer + */ + wr_one = is_app; + + now = time(NULL); + + /* + * while there are files to archive, process them one at at time + */ + while (next_file(arcn) == 0) { + /* + * check if this file meets user specified options match. + */ + if (sel_chk(arcn) != 0) { + ftree_notsel(); + continue; + } + fd = -1; + if (uflag) { + /* + * only archive if this file is newer than a file with + * the same name that is already stored on the archive + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) + continue; + } + +#ifdef __APPLE__ + /* + * synthesize ._ files for each node we encounter + */ + if (getenv(COPYFILE_DISABLE_VAR) == NULL + && copyfile(arcn->name, NULL, NULL, + COPYFILE_CHECK | COPYFILE_XATTR | COPYFILE_ACL) + && arcn->nlen + 2 < sizeof(arcn->name)) { + char *tmpdir = P_tmpdir, *TMPDIR; + int fd_src, fd_dst; + + if (!issetugid() && (TMPDIR = getenv("TMPDIR"))) { + tmpdir = TMPDIR; + } + asprintf(&md_fname, "%s%s", tmpdir, "/pax-md-XXXXXX"); + if (!md_fname) { + syswarn(1, errno, "Unable to create temporary file name"); + return; + } + memcpy(&arcn_copy, arcn, sizeof(ARCHD)); + strncpy(arcn_copy_name, arcn->name, PAXPATHLEN+1); + + arcn->skip = 0; + arcn->pad = 0; + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->type = PAX_REG; + fd_dst = mkstemp(md_fname); + if (fd_dst >= 0) { + fd_src = open(arcn->name, O_RDONLY, 0); + if (fd_src < 0) { + syswarn(1, errno, "Unable to open %s for reading", arcn->name); + close(fd_dst); + unlink(md_fname); + free(md_fname); + md_fname = NULL; + goto next; + } + if(fcopyfile(fd_src, fd_dst, NULL, + COPYFILE_PACK | COPYFILE_XATTR | COPYFILE_ACL) < 0) { + syswarn(1, errno, + "Unable to preserve metadata on %s", arcn->name); + close(fd_src); + close(fd_dst); + unlink(md_fname); + free(md_fname); + md_fname = NULL; + goto next; + } + close(fd_src); + fstat(fd_dst, &arcn->sb); + close(fd_dst); + } else { + syswarn(1, errno, "Unable to create temporary file %s", md_fname); + free(md_fname); + goto next; + } + arcn->skip = arcn->sb.st_size; + + if (!strncmp(dirname(arcn->name), ".", 2)) { + snprintf(arcn->name, sizeof(arcn->name), + "._%s", basename(arcn->name)); + } else { + snprintf(arcn->name, sizeof(arcn->name), + "%s/._%s", + dirname(arcn->name), basename(arcn->name)); + } + arcn->nlen = strlen(arcn->name); + arcn->org_name = arcn->name; + metadata = 1; + } else if (metadata) { +next: + metadata = 0; + memcpy(arcn, &arcn_copy, sizeof(ARCHD)); + strncpy(arcn->name, arcn_copy_name, PAXPATHLEN+1); + } +#endif /* __APPLE__ */ + + fd = -1; + + /* + * this file is considered selected now. see if this is a hard + * link to a file already stored + */ + ftree_sel(arcn); + if (hlk && (chk_lnk(arcn) < 0)) { + if (md_fname) { + unlink(md_fname); + free(md_fname); + md_fname = NULL; + } + break; + } + + if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) || + (arcn->type == PAX_CTG)) { + /* + * we will have to read this file. by opening it now we + * can avoid writing a header to the archive for a file + * we were later unable to read (we also purge it from + * the link table). + */ +#ifdef __APPLE__ + if (metadata) { + fd = open(md_fname, O_RDONLY, 0); + unlink(md_fname); + free(md_fname); + md_fname = NULL; + } else + fd = open(arcn->org_name, O_RDONLY, 0); + if (fd < 0) { +#else /* !__APPLE__ */ + if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) { +#endif /* __APPLE__ */ + syswarn(1,errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + } + + /* + * Now modify the name as requested by the user + */ + if ((res = mod_name(arcn)) < 0) { + /* + * name modification says to skip this file, close the + * file and purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + break; + } + + if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) { + /* + * unable to obtain the crc we need, close the file, + * purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + continue; + } + + if (vflag) { + if (vflag > 1) + ls_list(arcn, now, listf); + else { + (void)safe_print(arcn->name, listf); + vfpart = 1; + } + } + ++flcnt; + + /* + * looks safe to store the file, have the format specific + * routine write routine store the file header on the archive + */ + if ((res = (*wrf)(arcn)) < 0) { + rdfile_close(arcn, &fd); + break; + } + wr_one = 1; + if (res > 0) { + /* + * format write says no file data needs to be stored + * so we are done messing with this file + */ + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + rdfile_close(arcn, &fd); + continue; + } + + /* + * Add file data to the archive, quit on write error. if we + * cannot write the entire file contents to the archive we + * must pad the archive to replace the missing file data + * (otherwise during an extract the file header for the file + * which FOLLOWS this one will not be where we expect it to + * be). + */ + res = (*frmt->wr_data)(arcn, fd, &cnt); + rdfile_close(arcn, &fd); + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + if (res < 0) + break; + + /* + * pad as required, cnt is number of bytes not written + */ + if (((cnt > 0) && (wr_skip(cnt) < 0)) || + ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) + break; +#ifdef __APPLE__ + if (metadata) + goto next; +#endif /* __APPLE__ */ + } + + /* + * tell format to write trailer; pad to block boundary; reset directory + * mode/access times, and check if all patterns supplied by the user + * were matched. block off signals to avoid chance for multiple entry + * into the cleanup code + */ + if (wr_one) { + (*frmt->end_wr)(); + wr_fin(); + } + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + if (tflag) + proc_dir(); + ftree_chk(); +} + +/* + * append() + * Add file to previously written archive. Archive format specified by the + * user must agree with archive. The archive is read first to collect + * modification times (if -u) and locate the archive trailer. The archive + * is positioned in front of the record with the trailer and wr_archive() + * is called to add the new members. + * PAX IMPLEMENTATION DETAIL NOTE: + * -u is implemented by adding the new members to the end of the archive. + * Care is taken so that these do not end up as links to the older + * version of the same file already stored in the archive. It is expected + * when extraction occurs these newer versions will over-write the older + * ones stored "earlier" in the archive (this may be a bad assumption as + * it depends on the implementation of the program doing the extraction). + * It is really difficult to splice in members without either re-writing + * the entire archive (from the point were the old version was), or having + * assistance of the format specification in terms of a special update + * header that invalidates a previous archive record. The POSIX spec left + * the method used to implement -u unspecified. This pax is able to + * over write existing files that it creates. + */ + +void +append(void) +{ + ARCHD *arcn; + int res; + ARCHD archd; + const FSUB *orgfrmt; + int udev; + off_t tlen; + + arcn = &archd; + orgfrmt = frmt; + + /* + * Do not allow an append operation if the actual archive is of a + * different format than the user specified format. + */ + if (get_arc() < 0) + return; + if ((orgfrmt != NULL) && (orgfrmt != frmt)) { + paxwarn(1, "Cannot mix current archive format %s with %s", + frmt->name, orgfrmt->name); + return; + } + + /* + * pass the format any options and start up format + */ + if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) + return; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files we see. + */ + if (uflag && (ftime_start() < 0)) + return; + + /* + * some archive formats encode hard links by recording the device and + * file serial number (inode) but copy the file anyway (multiple times) + * to the archive. When we append, we run the risk that newly added + * files may have the same device and inode numbers as those recorded + * on the archive but during a previous run. If this happens, when the + * archive is extracted we get INCORRECT hard links. We avoid this by + * remapping the device numbers so that newly added files will never + * use the same device number as one found on the archive. remapping + * allows new members to safely have links among themselves. remapping + * also avoids problems with file inode (serial number) truncations + * when the inode number is larger than storage space in the archive + * header. See the remap routines for more details. + */ + if ((udev = frmt->udev) && (dev_start() < 0)) + return; + + /* + * reading the archive may take a long time. If verbose tell the user + */ + if (vflag) { + (void)fprintf(listf, + "%s: Reading archive to position at the end...", argv0); + vfpart = 1; + } + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + /* + * check if this file meets user specified options. + */ + if (sel_chk(arcn) != 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + + if (uflag) { + /* + * see if this is the newest version of this file has + * already been seen, if so skip. + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + } + + /* + * Store this device number. Device numbers seen during the + * read phase of append will cause newly appended files with a + * device number seen in the old part of the archive to be + * remapped to an unused device number. + */ + if ((udev && (add_dev(arcn) < 0)) || + (rd_skip(arcn->skip + arcn->pad) == 1)) + break; + } + + /* + * done, finish up read and get the number of bytes to back up so we + * can add new members. The format might have used the hard link table, + * purge it. + */ + tlen = (*frmt->end_rd)(); + lnk_end(); + + /* + * try to position for write, if this fails quit. if any error occurs, + * we will refuse to write + */ + if (appnd_start(tlen) < 0) + return; + + /* + * tell the user we are done reading. + */ + if (vflag && vfpart) { + (void)fputs("done.\n", listf); + vfpart = 0; + } + + /* + * go to the writing phase to add the new members + */ + wr_archive(arcn, 1); +} + +/* + * archive() + * write a new archive + */ + +void +archive(void) +{ + ARCHD archd; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files; set up for writing; pass the format any + * options write the archive + */ + if ((uflag && (ftime_start() < 0)) || (wr_start() < 0)) + return; + if ((*frmt->options)() < 0) + return; + + wr_archive(&archd, 0); +} + +/* + * copy() + * copy files from one part of the file system to another. this does not + * use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an + * archive was written and then extracted in the destination directory + * (except the files are forced to be under the destination directory). + */ + +void +copy(void) +{ + ARCHD *arcn; + int res; + int fddest; + char *dest_pt; + int dlen; + int drem; + int fdsrc = -1; + struct stat sb; + ARCHD archd; + char dirbuf[PAXPATHLEN+1]; + + arcn = &archd; + if (frmt && strcmp(frmt->name, NM_PAX)==0) { + /* Copy using pax format: must check if any -o options */ + if ((*frmt->options)() < 0) + return; + if (pax_invalid_action==0) + pax_invalid_action = PAX_INVALID_ACTION_BYPASS; + } + /* + * set up the destination dir path and make sure it is a directory. We + * make sure we have a trailing / on the destination + */ + dlen = strlcpy(dirbuf, dirptr, sizeof(dirbuf)); + if (dlen >= sizeof(dirbuf) || + (dlen == sizeof(dirbuf) - 1 && dirbuf[dlen - 1] != '/')) { + paxwarn(1, "directory name is too long %s", dirptr); + return; + } + dest_pt = dirbuf + dlen; + if (*(dest_pt-1) != '/') { + *dest_pt++ = '/'; + *dest_pt = '\0'; + ++dlen; + } + drem = PAXPATHLEN - dlen; + + if (stat(dirptr, &sb) < 0) { + syswarn(1, errno, "Cannot access destination directory %s", + dirptr); + return; + } + if (!S_ISDIR(sb.st_mode)) { + paxwarn(1, "Destination is not a directory %s", dirptr); + return; + } + + /* + * start up the hard link table; file traversal routines and the + * modification time and access mode database + */ + if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * set up to cp file trees + */ + cp_start(); + + /* + * while there are files to archive, process them + */ + while (next_file(arcn) == 0) { + fdsrc = -1; + + /* + * Fill in arcn from any pax options + */ + adjust_copy_for_pax_options(arcn); + + /* + * check if this file meets user specified options + */ + if (sel_chk(arcn) != 0) { + ftree_notsel(); + continue; + } + + /* + * if there is already a file in the destination directory with + * the same name and it is newer, skip the one stored on the + * archive. + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this can be confusing to the user who + * might expect the test to be done on an existing file AFTER + * the name mod. In honesty the pax spec is probably flawed in + * this respect + */ + if (uflag || Dflag) { + /* + * create the destination name + */ + if (strlcpy(dest_pt, arcn->name + (*arcn->name == '/'), + drem + 1) > drem) { + paxwarn(1, "Destination pathname too long %s", + arcn->name); + continue; + } + + /* + * if existing file is same age or newer skip + */ + res = lstat(dirbuf, &sb); + *dest_pt = '\0'; + + if (res == 0) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime<=sb.st_mtime) && + (arcn->sb.st_ctime<=sb.st_ctime)) + continue; + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + } + + /* + * this file is considered selected. See if this is a hard link + * to a previous file; modify the name as requested by the + * user; set the final destination. + */ + ftree_sel(arcn); + if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) { + /* + * skip file, purge from link table + */ + purg_lnk(arcn); + continue; + } + + /* + * Non standard -Y and -Z flag. When the existing file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) + continue; + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + + if (vflag) { + (void)safe_print(arcn->name, listf); + vfpart = 1; + } + ++flcnt; + + /* + * try to create a hard link to the src file if requested + * but make sure we are not trying to overwrite ourselves. + */ + if (lflag) + res = cross_lnk(arcn); + else + res = chk_same(arcn); + if (res <= 0) { + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + continue; + } + + /* + * have to create a new file + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * create a link or special file + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + if (res < 0) + purg_lnk(arcn); +#ifdef __APPLE__ + if (res >= 0 && + arcn->type == PAX_DIR && + copyfile(arcn->org_name, arcn->name, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) + paxwarn(1, "Directory %s had metadata that could not be copied: %s", arcn->org_name, strerror(errno)); +#endif /* __APPLE__ */ + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + continue; + } + + /* + * have to copy a regular file to the destination directory. + * first open source file and then create the destination file + */ + if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) { + syswarn(1, errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + if ((fddest = file_creat(arcn)) < 0) { + rdfile_close(arcn, &fdsrc); + purg_lnk(arcn); + continue; + } + + /* + * copy source file data to the destination file + */ + cp_file(arcn, fdsrc, fddest); +#ifdef __APPLE__ + /* do this before file close so that mtimes are correct regardless */ + if (getenv(COPYFILE_DISABLE_VAR) == NULL) { + if (fcopyfile(fdsrc, fddest, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) + paxwarn(1, "File %s had metadata that could not be copied: %s", arcn->org_name, + strerror(errno)); + } +#endif + file_close(arcn, fddest); + rdfile_close(arcn, &fdsrc); + + if (vflag && vfpart) { + (void)putc('\n', listf); + vfpart = 0; + } + } + + /* + * restore directory modes and times as required; make sure all + * patterns were selected block off signals to avoid chance for + * multiple entry into the cleanup code. + */ + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + proc_dir(); + ftree_chk(); +} + +/* + * next_head() + * try to find a valid header in the archive. Uses format specific + * routines to extract the header and id the trailer. Trailers may be + * located within a valid header or in an invalid header (the location + * is format specific. The inhead field from the option table tells us + * where to look for the trailer). + * We keep reading (and resyncing) until we get enough contiguous data + * to check for a header. If we cannot find one, we shift by a byte + * add a new byte from the archive to the end of the buffer and try again. + * If we get a read error, we throw out what we have (as we must have + * contiguous data) and start over again. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if we got a header, -1 if we are unable to ever find another one + * (we reached the end of input, or we reached the limit on retries. see + * the specs for rd_wrbuf() for more details) + */ + +static int +next_head(ARCHD *arcn) +{ + int ret; + char *hdend; + int res; + int shftsz; + int hsz; + int in_resync = 0; /* set when we are in resync mode */ + int cnt = 0; /* counter for trailer function */ + int first = 1; /* on 1st read, EOF isn't premature. */ + + /* + * set up initial conditions, we want a whole frmt->hsz block as we + * have no data yet. + */ + res = hsz = frmt->hsz; + hdend = hdbuf; + shftsz = hsz - 1; + for (;;) { + /* + * keep looping until we get a contiguous FULL buffer + * (frmt->hsz is the proper size) + */ + for (;;) { + if ((ret = rd_wrbuf(hdend, res)) == res) + break; + + /* + * If we read 0 bytes (EOF) from an archive when we + * expect to find a header, we have stepped upon + * an archive without the customary block of zeroes + * end marker. It's just stupid to error out on + * them, so exit gracefully. + */ + if (first && ret == 0) + return(-1); + first = 0; + + /* + * some kind of archive read problem, try to resync the + * storage device, better give the user the bad news. + */ + if ((ret == 0) || (rd_sync() < 0)) { + paxwarn(1,"Premature end of file on archive read"); + return(-1); + } + if (!in_resync) { + if (act == APPND) { + paxwarn(1, + "Archive I/O error, cannot continue"); + return(-1); + } + paxwarn(1,"Archive I/O error. Trying to recover."); + ++in_resync; + } + + /* + * oh well, throw it all out and start over + */ + res = hsz; + hdend = hdbuf; + } + + /* + * ok we have a contiguous buffer of the right size. Call the + * format read routine. If this was not a valid header and this + * format stores trailers outside of the header, call the + * format specific trailer routine to check for a trailer. We + * have to watch out that we do not mis-identify file data or + * block padding as a header or trailer. Format specific + * trailer functions must NOT check for the trailer while we + * are running in resync mode. Some trailer functions may tell + * us that this block cannot contain a valid header either, so + * we then throw out the entire block and start over. + */ + if ((*frmt->rd)(arcn, hdbuf) == 0) + break; + + if (!frmt->inhead) { + /* + * this format has trailers outside of valid headers + */ + if ((ret = (*frmt->trail)(arcn,hdbuf,in_resync,&cnt)) == 0){ + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + if (ret == 1) { + /* + * we are in resync and we were told to throw + * the whole block out because none of the + * bytes in this block can be used to form a + * valid header + */ + res = hsz; + hdend = hdbuf; + continue; + } + } + + /* + * Brute force section. + * not a valid header. We may be able to find a header yet. So + * we shift over by one byte, and set up to read one byte at a + * time from the archive and place it at the end of the buffer. + * We will keep moving byte at a time until we find a header or + * get a read error and have to start over. + */ + if (!in_resync) { + if (act == APPND) { + paxwarn(1,"Unable to append, archive header flaw"); + return(-1); + } + paxwarn(1,"Invalid header, starting valid header search."); + ++in_resync; + } + memmove(hdbuf, hdbuf+1, shftsz); + res = 1; + hdend = hdbuf + shftsz; + } + + /* + * ok got a valid header, check for trailer if format encodes it in the + * the header. NOTE: the parameters are different than trailer routines + * which encode trailers outside of the header! + */ + if (frmt->inhead && ((*frmt->trail)(arcn,NULL,0,NULL) == 0)) { + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + ++flcnt; + return(0); +} + +/* + * get_arc() + * Figure out what format an archive is. Handles archive with flaws by + * brute force searches for a legal header in any supported format. The + * format id routines have to be careful to NOT mis-identify a format. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if archive found -1 otherwise + */ + +static int +get_arc(void) +{ + int i; + int hdsz = 0; + int res; + int minhd = BLKMULT; + char *hdend; + int notice = 0; + + /* + * find the smallest header size in all archive formats and then set up + * to read the archive. + */ + for (i = 0; ford[i] >= 0; ++i) { + if (fsub[ford[i]].hsz < minhd) + minhd = fsub[ford[i]].hsz; + } + if (rd_start() < 0) + return(-1); + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + for (;;) { + for (;;) { + /* + * fill the buffer with at least the smallest header + */ + i = rd_wrbuf(hdend, res); + if (i > 0) + hdsz += i; + if (hdsz >= minhd) + break; + + /* + * if we cannot recover from a read error quit + */ + if ((i == 0) || (rd_sync() < 0)) + goto out; + + /* + * when we get an error none of the data we already + * have can be used to create a legal header (we just + * got an error in the middle), so we throw it all out + * and refill the buffer with fresh data. + */ + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + if (!notice) { + if (act == APPND) + return(-1); + paxwarn(1,"Cannot identify format. Searching..."); + ++notice; + } + } + + /* + * we have at least the size of the smallest header in any + * archive format. Look to see if we have a match. The array + * ford[] is used to specify the header id order to reduce the + * chance of incorrectly id'ing a valid header (some formats + * may be subsets of each other and the order would then be + * important). + */ + for (i = 0; ford[i] >= 0; ++i) { + if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0) + continue; + frmt = &(fsub[ford[i]]); + /* + * yuck, to avoid slow special case code in the extract + * routines, just push this header back as if it was + * not seen. We have left extra space at start of the + * buffer for this purpose. This is a bit ugly, but + * adding all the special case code is far worse. + */ + pback(hdbuf, hdsz); + return(0); + } + + /* + * We have a flawed archive, no match. we start searching, but + * we never allow additions to flawed archives + */ + if (!notice) { + if (act == APPND) + return(-1); + paxwarn(1, "Cannot identify format. Searching..."); + ++notice; + } + + /* + * brute force search for a header that we can id. + * we shift through byte at a time. this is slow, but we cannot + * determine the nature of the flaw in the archive in a + * portable manner + */ + if (--hdsz > 0) { + memmove(hdbuf, hdbuf+1, hdsz); + res = BLKMULT - hdsz; + hdend = hdbuf + hdsz; + } else { + res = BLKMULT; + hdend = hdbuf; + hdsz = 0; + } + } + + out: + /* + * we cannot find a header, bow, apologize and quit + */ + paxwarn(1, "Sorry, unable to determine archive format."); + return(-1); +} diff --git a/file_cmds/pax/buf_subs.c b/file_cmds/pax/buf_subs.c new file mode 100644 index 00000000..e56e8226 --- /dev/null +++ b/file_cmds/pax/buf_subs.c @@ -0,0 +1,994 @@ +/* $OpenBSD: buf_subs.c,v 1.21 2005/11/09 19:59:06 otto Exp $ */ +/* $NetBSD: buf_subs.c,v 1.5 1995/03/21 09:07:08 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: buf_subs.c,v 1.21 2005/11/09 19:59:06 otto Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +/* + * routines which implement archive and file buffering + */ + +#define MINFBSZ 512 /* default block size for hole detect */ +#define MAXFLT 10 /* default media read error limit */ + +/* + * Need to change bufmem to dynamic allocation when the upper + * limit on blocking size is removed (though that will violate pax spec) + * MAXBLK define and tests will also need to be updated. + */ +static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */ +static char *buf; /* normal start of i/o buffer */ +static char *bufend; /* end or last char in i/o buffer */ +static char *bufpt; /* read/write point in i/o buffer */ +int blksz = MAXBLK; /* block input/output size in bytes */ +int wrblksz; /* user spec output size in bytes */ +int maxflt = MAXFLT; /* MAX consecutive media errors */ +int rdblksz; /* first read blksize (tapes only) */ +off_t wrlimit; /* # of bytes written per archive vol */ +off_t wrcnt; /* # of bytes written on current vol */ +off_t rdcnt; /* # of bytes read on current vol */ + +/* + * wr_start() + * set up the buffering system to operate in a write mode + * Return: + * 0 if ok, -1 if the user specified write block size violates pax spec + */ + +int +wr_start(void) +{ + buf = &(bufmem[BLKMULT]); + /* + * Check to make sure the write block size meets pax specs. If the user + * does not specify a blocksize, we use the format default blocksize. + * We must be picky on writes, so we do not allow the user to create an + * archive that might be hard to read elsewhere. If all ok, we then + * open the first archive volume + */ + if (!wrblksz) + wrblksz = frmt->bsz; + if (wrblksz > MAXBLK) { + paxwarn(1, "Write block size of %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + paxwarn(1, "Write block size of %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + if (wrblksz > MAXBLK_POSIX) { + paxwarn(0, "Write block size of %d larger than POSIX max %d, archive may not be portable", + wrblksz, MAXBLK_POSIX); + return(-1); + } + + /* + * we only allow wrblksz to be used with all archive operations + */ + blksz = rdblksz = wrblksz; + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + wrcnt = 0; + bufend = buf + wrblksz; + bufpt = buf; + return(0); +} + +/* + * rd_start() + * set up buffering system to read an archive + * Return: + * 0 if ok, -1 otherwise + */ + +int +rd_start(void) +{ + /* + * leave space for the header pushback (see get_arc()). If we are + * going to append and user specified a write block size, check it + * right away + */ + buf = &(bufmem[BLKMULT]); + if ((act == APPND) && wrblksz) { + if (wrblksz > MAXBLK) { + paxwarn(1,"Write block size %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + paxwarn(1, "Write block size %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + } + + /* + * open the archive + */ + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + bufend = buf + rdblksz; + bufpt = bufend; + rdcnt = 0; + return(0); +} + +/* + * cp_start() + * set up buffer system for copying within the file system + */ + +void +cp_start(void) +{ + buf = &(bufmem[BLKMULT]); + rdblksz = blksz = MAXBLK; +} + +/* + * appnd_start() + * Set up the buffering system to append new members to an archive that + * was just read. The last block(s) of an archive may contain a format + * specific trailer. To append a new member, this trailer has to be + * removed from the archive. The first byte of the trailer is replaced by + * the start of the header of the first file added to the archive. The + * format specific end read function tells us how many bytes to move + * backwards in the archive to be positioned BEFORE the trailer. Two + * different position have to be adjusted, the O.S. file offset (e.g. the + * position of the tape head) and the write point within the data we have + * stored in the read (soon to become write) buffer. We may have to move + * back several records (the number depends on the size of the archive + * record and the size of the format trailer) to read up the record where + * the first byte of the trailer is recorded. Trailers may span (and + * overlap) record boundaries. + * We first calculate which record has the first byte of the trailer. We + * move the OS file offset back to the start of this record and read it + * up. We set the buffer write pointer to be at this byte (the byte where + * the trailer starts). We then move the OS file pointer back to the + * start of this record so a flush of this buffer will replace the record + * in the archive. + * A major problem is rewriting this last record. For archives stored + * on disk files, this is trivial. However, many devices are really picky + * about the conditions under which they will allow a write to occur. + * Often devices restrict the conditions where writes can be made, + * so it may not be feasible to append archives stored on all types of + * devices. + * Return: + * 0 for success, -1 for failure + */ + +int +appnd_start(off_t skcnt) +{ + int res; + off_t cnt; + + if (exit_val != 0) { + paxwarn(0, "Cannot append to an archive that may have flaws."); + return(-1); + } + /* + * if the user did not specify a write blocksize, inherit the size used + * in the last archive volume read. (If a is set we still use rdblksz + * until next volume, cannot shift sizes within a single volume). + */ + if (!wrblksz) + wrblksz = blksz = rdblksz; + else + blksz = rdblksz; + + /* + * make sure that this volume allows appends + */ + if (ar_app_ok() < 0) + return(-1); + + /* + * Calculate bytes to move back and move in front of record where we + * need to start writing from. Remember we have to add in any padding + * that might be in the buffer after the trailer in the last block. We + * travel skcnt + padding ROUNDED UP to blksize. + */ + skcnt += bufend - bufpt; + if ((cnt = (skcnt/blksz) * blksz) < skcnt) + cnt += blksz; + if (ar_rev((off_t)cnt) < 0) + goto out; + + /* + * We may have gone too far if there is valid data in the block we are + * now in front of, read up the block and position the pointer after + * the valid data. + */ + if ((cnt -= skcnt) > 0) { + /* + * watch out for stupid tape drives. ar_rev() will set rdblksz + * to be real physical blocksize so we must loop until we get + * the old rdblksz (now in blksz). If ar_rev() fouls up the + * determination of the physical block size, we will fail. + */ + bufpt = buf; + bufend = buf + blksz; + while (bufpt < bufend) { + if ((res = ar_read(bufpt, rdblksz)) <= 0) + goto out; + bufpt += res; + } + if (ar_rev((off_t)(bufpt - buf)) < 0) + goto out; + bufpt = buf + cnt; + bufend = buf + blksz; + } else { + /* + * buffer is empty + */ + bufend = buf + blksz; + bufpt = buf; + } + rdblksz = blksz; + rdcnt -= skcnt; + wrcnt = 0; + + /* + * At this point we are ready to write. If the device requires special + * handling to write at a point were previously recorded data resides, + * that is handled in ar_set_wr(). From now on we operate under normal + * ARCHIVE mode (write) conditions + */ + if (ar_set_wr() < 0) + return(-1); + act = ARCHIVE; + return(0); + + out: + paxwarn(1, "Unable to rewrite archive trailer, cannot append."); + return(-1); +} + +/* + * rd_sync() + * A read error occurred on this archive volume. Resync the buffer and + * try to reset the device (if possible) so we can continue to read. Keep + * trying to do this until we get a valid read, or we reach the limit on + * consecutive read faults (at which point we give up). The user can + * adjust the read error limit through a command line option. + * Returns: + * 0 on success, and -1 on failure + */ + +int +rd_sync(void) +{ + int errcnt = 0; + int res; + + /* + * if the user says bail out on first fault, we are out of here... + */ + if (maxflt == 0) + return(-1); + if (act == APPND) { + paxwarn(1, "Unable to append when there are archive read errors."); + return(-1); + } + + /* + * poke at device and try to get past media error + */ + if (ar_rdsync() < 0) { + if (ar_next() < 0) + return(-1); + else + rdcnt = 0; + } + + for (;;) { + if ((res = ar_read(buf, blksz)) > 0) { + /* + * All right! got some data, fill that buffer + */ + bufpt = buf; + bufend = buf + res; + rdcnt += res; + return(0); + } + + /* + * Oh well, yet another failed read... + * if error limit reached, ditch. o.w. poke device to move past + * bad media and try again. if media is badly damaged, we ask + * the poor (and upset user at this point) for the next archive + * volume. remember the goal on reads is to get the most we + * can extract out of the archive. + */ + if ((maxflt > 0) && (++errcnt > maxflt)) + paxwarn(0,"Archive read error limit (%d) reached",maxflt); + else if (ar_rdsync() == 0) + continue; + if (ar_next() < 0) + break; + rdcnt = 0; + errcnt = 0; + } + return(-1); +} + +/* + * pback() + * push the data used during the archive id phase back into the I/O + * buffer. This is required as we cannot be sure that the header does NOT + * overlap a block boundary (as in the case we are trying to recover a + * flawed archived). This was not designed to be used for any other + * purpose. (What software engineering, HA!) + * WARNING: do not even THINK of pback greater than BLKMULT, unless the + * pback space is increased. + */ + +void +pback(char *pt, int cnt) +{ + bufpt -= cnt; + memcpy(bufpt, pt, cnt); + return; +} + +/* + * rd_skip() + * skip forward in the archive during a archive read. Used to get quickly + * past file data and padding for files the user did NOT select. + * Return: + * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected. + */ + +int +rd_skip(off_t skcnt) +{ + off_t res; + off_t cnt; + off_t skipped = 0; + + /* + * consume what data we have in the buffer. If we have to move forward + * whole records, we call the low level skip function to see if we can + * move within the archive without doing the expensive reads on data we + * do not want. + */ + if (skcnt == 0) + return(0); + res = MIN((bufend - bufpt), skcnt); + bufpt += res; + skcnt -= res; + + /* + * if skcnt is now 0, then no additional i/o is needed + */ + if (skcnt == 0) + return(0); + + /* + * We have to read more, calculate complete and partial record reads + * based on rdblksz. we skip over "cnt" complete records + */ + res = skcnt%rdblksz; + cnt = (skcnt/rdblksz) * rdblksz; + + /* + * if the skip fails, we will have to resync. ar_fow will tell us + * how much it can skip over. We will have to read the rest. + */ + if (ar_fow(cnt, &skipped) < 0) + return(-1); + res += cnt - skipped; + rdcnt += skipped; + + /* + * what is left we have to read (which may be the whole thing if + * ar_fow() told us the device can only read to skip records); + */ + while (res > 0L) { + cnt = bufend - bufpt; + /* + * if the read fails, we will have to resync + */ + if ((cnt <= 0) && ((cnt = buf_fill()) < 0)) + return(-1); + if (cnt == 0) + return(1); + cnt = MIN(cnt, res); + bufpt += cnt; + res -= cnt; + } + return(0); +} + +/* + * wr_fin() + * flush out any data (and pad if required) the last block. We always pad + * with zero (even though we do not have to). Padding with 0 makes it a + * lot easier to recover if the archive is damaged. zero padding SHOULD + * BE a requirement.... + */ + +void +wr_fin(void) +{ + if (bufpt > buf) { + memset(bufpt, 0, bufend - bufpt); + bufpt = bufend; + (void)buf_flush(blksz); + } +} + +/* + * wr_rdbuf() + * fill the write buffer from data passed to it in a buffer (usually used + * by format specific write routines to pass a file header). On failure we + * punt. We do not allow the user to continue to write flawed archives. + * We assume these headers are not very large (the memory copy we use is + * a bit expensive). + * Return: + * 0 if buffer was filled ok, -1 o.w. (buffer flush failure) + */ + +int +wr_rdbuf(char *out, int outcnt) +{ + int cnt; + + /* + * while there is data to copy copy into the write buffer. when the + * write buffer fills, flush it to the archive and continue + */ + while (outcnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + /* + * only move what we have space for + */ + cnt = MIN(cnt, outcnt); + memcpy(bufpt, out, cnt); + bufpt += cnt; + out += cnt; + outcnt -= cnt; + } + return(0); +} + +/* + * rd_wrbuf() + * copy from the read buffer into a supplied buffer a specified number of + * bytes. If the read buffer is empty fill it and continue to copy. + * usually used to obtain a file header for processing by a format + * specific read routine. + * Return + * number of bytes copied to the buffer, 0 indicates EOF on archive volume, + * -1 is a read error + */ + +int +rd_wrbuf(char *in, int cpcnt) +{ + int res; + int cnt; + int incnt = cpcnt; + + /* + * loop until we fill the buffer with the requested number of bytes + */ + while (incnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) { + /* + * read error, return what we got (or the error if + * no data was copied). The caller must know that an + * error occurred and has the best knowledge what to + * do with it + */ + if ((res = cpcnt - incnt) > 0) + return(res); + return(cnt); + } + + /* + * calculate how much data to copy based on whats left and + * state of buffer + */ + cnt = MIN(cnt, incnt); + memcpy(in, bufpt, cnt); + bufpt += cnt; + incnt -= cnt; + in += cnt; + } + return(cpcnt); +} + +/* + * wr_skip() + * skip forward during a write. In other words add padding to the file. + * we add zero filled padding as it makes flawed archives much easier to + * recover from. the caller tells us how many bytes of padding to add + * This routine was not designed to add HUGE amount of padding, just small + * amounts (a few 512 byte blocks at most) + * Return: + * 0 if ok, -1 if there was a buf_flush failure + */ + +int +wr_skip(off_t skcnt) +{ + int cnt; + + /* + * loop while there is more padding to add + */ + while (skcnt > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + cnt = MIN(cnt, skcnt); + memset(bufpt, 0, cnt); + bufpt += cnt; + skcnt -= cnt; + } + return(0); +} + +/* + * wr_rdfile() + * fill write buffer with the contents of a file. We are passed an open + * file descriptor to the file an the archive structure that describes the + * file we are storing. The variable "left" is modified to contain the + * number of bytes of the file we were NOT able to write to the archive. + * it is important that we always write EXACTLY the number of bytes that + * the format specific write routine told us to. The file can also get + * bigger, so reading to the end of file would create an improper archive, + * we just detect this case and warn the user. We never create a bad + * archive if we can avoid it. Of course trying to archive files that are + * active is asking for trouble. It we fail, we pass back how much we + * could NOT copy and let the caller deal with it. + * Return: + * 0 ok, -1 if archive write failure. a short read of the file returns a + * 0, but "left" is set to be greater than zero. + */ + +int +wr_rdfile(ARCHD *arcn, int ifd, off_t *left) +{ + int cnt; + int res = 0; + off_t size = arcn->sb.st_size; + struct stat sb; + + /* + * while there are more bytes to write + */ + while (size > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) { + *left = size; + return(-1); + } + cnt = MIN(cnt, size); + if ((res = read(ifd, bufpt, cnt)) <= 0) + break; + size -= res; + bufpt += res; + } + + /* + * better check the file did not change during this operation + * or the file read failed. + */ + if (res < 0) + syswarn(1, errno, "Read fault on %s", arcn->org_name); + else if (size != 0L) + paxwarn(1, "File changed size during read %s", arcn->org_name); + else if (fstat(ifd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during copy to archive", + arcn->org_name); + *left = size; + return(0); +} + +/* + * rd_wrfile() + * extract the contents of a file from the archive. If we are unable to + * extract the entire file (due to failure to write the file) we return + * the numbers of bytes we did NOT process. This way the caller knows how + * many bytes to skip past to find the next archive header. If the failure + * was due to an archive read, we will catch that when we try to skip. If + * the format supplies a file data crc value, we calculate the actual crc + * so that it can be compared to the value stored in the header + * NOTE: + * We call a special function to write the file. This function attempts to + * restore file holes (blocks of zeros) into the file. When files are + * sparse this saves space, and is a LOT faster. For non sparse files + * the performance hit is small. As of this writing, no archive supports + * information on where the file holes are. + * Return: + * 0 ok, -1 if archive read failure. if we cannot write the entire file, + * we return a 0 but "left" is set to be the amount unwritten + */ + +int +rd_wrfile(ARCHD *arcn, int ofd, off_t *left) +{ + int cnt = 0; + off_t size = arcn->sb.st_size; + int res = 0; + char *fnm = arcn->name; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + u_int32_t crc = 0; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (ofd < 0) + sz = PAXPATHLEN + 1; /* GNU tar long link/file */ + else if (fstat(ofd, &sb) == 0) { + if (sb.st_blksize > 0) + sz = (int)sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + *left = 0L; + + /* + * Copy the archive to the file the number of bytes specified. We have + * to assume that we want to recover file holes as none of the archive + * formats can record the location of file holes. + */ + while (size > 0L) { + cnt = bufend - bufpt; + /* + * if we get a read error, we do not want to skip, as we may + * miss a header, so we do not set left, but if we get a write + * error, we do want to skip over the unprocessed data. + */ + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) + break; + cnt = MIN(cnt, size); + if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) { + *left = size; + break; + } + + if (docrc) { + /* + * update the actual crc value + */ + cnt = res; + while (--cnt >= 0) + crc += *bufpt++ & 0xff; + } else + bufpt += res; + size -= res; + } + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved forward may not put + * a hole at the end of the file. + */ + if (isem && (arcn->sb.st_size > 0L)) + file_flush(ofd, fnm, isem); + + /* + * if we failed from archive read, we do not want to skip + */ + if ((size > 0L) && (*left == 0L)) + return(-1); + + /* + * some formats record a crc on file data. If so, then we compare the + * calculated crc to the crc stored in the archive + */ + if (docrc && (size == 0L) && (arcn->crc != crc)) + paxwarn(1,"Actual crc does not match expected crc %s",arcn->name); + return(0); +} + +/* + * cp_file() + * copy the contents of one file to another. used during -rw phase of pax + * just as in rd_wrfile() we use a special write function to write the + * destination file so we can properly copy files with holes. + */ + +void +cp_file(ARCHD *arcn, int fd1, int fd2) +{ + int cnt; + off_t cpcnt = 0L; + int res = 0; + char *fnm = arcn->name; + int no_hole = 0; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + + /* + * check for holes in the source file. If none, we will use regular + * write instead of file write. + */ + if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size) + ++no_hole; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (fstat(fd2, &sb) == 0) { + if (sb.st_blksize > 0) + sz = sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + + /* + * read the source file and copy to destination file until EOF + */ + for (;;) { + if ((cnt = read(fd1, buf, blksz)) <= 0) + break; + if (no_hole) + res = write(fd2, buf, cnt); + else + res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm); + if (res != cnt) + break; + cpcnt += cnt; + } + + /* + * check to make sure the copy is valid. + */ + if (res < 0) + syswarn(1, errno, "Failed write during copy of %s to %s", + arcn->org_name, arcn->name); + else if (cpcnt != arcn->sb.st_size) + paxwarn(1, "File %s changed size during copy to %s", + arcn->org_name, arcn->name); + else if (fstat(fd1, &sb) < 0) + syswarn(1, errno, "Failed stat of %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during copy to %s", + arcn->org_name, arcn->name); + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved forward may not put + * a hole at the end of the file. + */ + if (!no_hole && isem && (arcn->sb.st_size > 0L)) + file_flush(fd2, fnm, isem); + return; +} + +/* + * buf_fill() + * fill the read buffer with the next record (or what we can get) from + * the archive volume. + * Return: + * Number of bytes of data in the read buffer, -1 for read error, and + * 0 when finished (user specified termination in ar_next()). + */ + +int +buf_fill(void) +{ + int cnt; + static int fini = 0; + + if (fini) + return(0); + + for (;;) { + /* + * try to fill the buffer. on error the next archive volume is + * opened and we try again. + */ + if ((cnt = ar_read(buf, blksz)) > 0) { + bufpt = buf; + bufend = buf + cnt; + rdcnt += cnt; + return(cnt); + } + + /* + * errors require resync, EOF goes to next archive + */ + if (cnt < 0) + break; + if (ar_next() < 0) { + fini = 1; + return(0); + } + rdcnt = 0; + } + exit_val = 1; + return(-1); +} + +/* + * buf_flush() + * force the write buffer to the archive. We are passed the number of + * bytes in the buffer at the point of the flush. When we change archives + * the record size might change. (either larger or smaller). + * Return: + * 0 if all is ok, -1 when a write error occurs. + */ + +int +buf_flush(int bufcnt) +{ + int cnt; + int push = 0; + int totcnt = 0; + + /* + * if we have reached the user specified byte count for each archive + * volume, prompt for the next volume. (The non-standard -R flag). + * NOTE: If the wrlimit is smaller than wrcnt, we will always write + * at least one record. We always round limit UP to next blocksize. + */ + if ((wrlimit > 0) && (wrcnt > wrlimit)) { + paxwarn(0, "User specified archive volume byte limit reached."); + if (ar_next() < 0) { + wrcnt = 0; + exit_val = 1; + return(-1); + } + wrcnt = 0; + + /* + * The new archive volume might have changed the size of the + * write blocksize. if so we figure out if we need to write + * (one or more times), or if there is now free space left in + * the buffer (it is no longer full). bufcnt has the number of + * bytes in the buffer, (the blocksize, at the point we were + * CALLED). Push has the amount of "extra" data in the buffer + * if the block size has shrunk from a volume change. + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * We have enough data to write at least one archive block + */ + for (;;) { + /* + * write a block and check if it all went out ok + */ + cnt = ar_write(buf, blksz); + if (cnt == blksz) { + /* + * the write went ok + */ + wrcnt += cnt; + totcnt += cnt; + if (push > 0) { + /* we have extra data to push to the front. + * check for more than 1 block of push, and if + * so we loop back to write again + */ + memcpy(buf, bufend, push); + bufpt = buf + push; + if (push >= blksz) { + push -= blksz; + continue; + } + } else + bufpt = buf; + return(totcnt); + } else if (cnt > 0) { + /* + * Oh drat we got a partial write! + * if format doesnt care about alignment let it go, + * we warned the user in ar_write().... but this means + * the last record on this volume violates pax spec.... + */ + totcnt += cnt; + wrcnt += cnt; + bufpt = buf + cnt; + cnt = bufcnt - cnt; + memcpy(buf, bufpt, cnt); + bufpt = buf + cnt; + if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0)) + return(totcnt); + break; + } + + /* + * All done, go to next archive + */ + wrcnt = 0; + if (ar_next() < 0) + break; + + /* + * The new archive volume might also have changed the block + * size. if so, figure out if we have too much or too little + * data for using the new block size + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * write failed, stop pax. we must not create a bad archive! + */ + exit_val = 1; + return(-1); +} diff --git a/file_cmds/pax/cache.c b/file_cmds/pax/cache.c new file mode 100644 index 00000000..e5c49960 --- /dev/null +++ b/file_cmds/pax/cache.c @@ -0,0 +1,426 @@ +/* $OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $ */ +/* $NetBSD: cache.c,v 1.4 1995/03/21 09:07:10 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: cache.c,v 1.17 2004/03/16 03:28:34 tedu Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "cache.h" +#include "extern.h" + +/* + * routines that control user, group, uid and gid caches (for the archive + * member print routine). + * IMPORTANT: + * these routines cache BOTH hits and misses, a major performance improvement + */ + +static int pwopn = 0; /* is password file open */ +static int gropn = 0; /* is group file open */ +static UIDC **uidtb = NULL; /* uid to name cache */ +static GIDC **gidtb = NULL; /* gid to name cache */ +static UIDC **usrtb = NULL; /* user name to uid cache */ +static GIDC **grptb = NULL; /* group name to gid cache */ + +/* + * uidtb_start + * creates an empty uidtb + * Return: + * 0 if ok, -1 otherwise + */ + +int +uidtb_start(void) +{ + static int fail = 0; + + if (uidtb != NULL) + return(0); + if (fail) + return(-1); + if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for user id cache table"); + return(-1); + } + return(0); +} + +/* + * gidtb_start + * creates an empty gidtb + * Return: + * 0 if ok, -1 otherwise + */ + +int +gidtb_start(void) +{ + static int fail = 0; + + if (gidtb != NULL) + return(0); + if (fail) + return(-1); + if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for group id cache table"); + return(-1); + } + return(0); +} + +/* + * usrtb_start + * creates an empty usrtb + * Return: + * 0 if ok, -1 otherwise + */ + +int +usrtb_start(void) +{ + static int fail = 0; + + if (usrtb != NULL) + return(0); + if (fail) + return(-1); + if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for user name cache table"); + return(-1); + } + return(0); +} + +/* + * grptb_start + * creates an empty grptb + * Return: + * 0 if ok, -1 otherwise + */ + +int +grptb_start(void) +{ + static int fail = 0; + + if (grptb != NULL) + return(0); + if (fail) + return(-1); + if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + paxwarn(1,"Unable to allocate memory for group name cache table"); + return(-1); + } + return(0); +} + +/* + * name_uid() + * caches the name (if any) for the uid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +char * +name_uid(uid_t uid, int frc) +{ + struct passwd *pw; + UIDC *ptr; + + if ((uidtb == NULL) && (uidtb_start() < 0)) + return(""); + + /* + * see if we have this uid cached + */ + ptr = uidtb[uid % UID_SZ]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this uid, we will add it + */ + if (!pwopn) { + setpassent(1); + ++pwopn; + } + if (ptr == NULL) + ptr = uidtb[uid % UID_SZ] = malloc(sizeof(UIDC)); + + if ((pw = getpwuid(uid)) == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numeric format + */ + if (ptr == NULL) + return(""); + ptr->uid = uid; + ptr->valid = INVALID; + (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", + (unsigned long)uid); + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return(pw->pw_name); + ptr->uid = uid; + (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name)); + ptr->valid = VALID; + } + return(ptr->name); +} + +/* + * name_gid() + * caches the name (if any) for the gid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +char * +name_gid(gid_t gid, int frc) +{ + struct group *gr; + GIDC *ptr; + + if ((gidtb == NULL) && (gidtb_start() < 0)) + return(""); + + /* + * see if we have this gid cached + */ + ptr = gidtb[gid % GID_SZ]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this gid, we will add it + */ + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = gidtb[gid % GID_SZ] = malloc(sizeof(GIDC)); + + if ((gr = getgrgid(gid)) == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numberic format + */ + if (ptr == NULL) + return(""); + ptr->gid = gid; + ptr->valid = INVALID; + (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", + (unsigned long)gid); + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return(gr->gr_name); + ptr->gid = gid; + (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name)); + ptr->valid = VALID; + } + return(ptr->name); +} + +/* + * uid_name() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ + +int +uid_name(char *name, uid_t *uid) +{ + struct passwd *pw; + UIDC *ptr; + int namelen; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((usrtb == NULL) && (usrtb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + ptr = usrtb[st_hash(name, namelen, UNM_SZ)]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *uid = ptr->uid; + return(0); + } + + if (!pwopn) { + setpassent(1); + ++pwopn; + } + + if (ptr == NULL) + ptr = usrtb[st_hash(name, namelen, UNM_SZ)] = + (UIDC *)malloc(sizeof(UIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + if (ptr == NULL) { + if ((pw = getpwnam(name)) == NULL) + return(-1); + *uid = pw->pw_uid; + return(0); + } + (void)strlcpy(ptr->name, name, sizeof(ptr->name)); + if ((pw = getpwnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return(0); +} + +/* + * gid_name() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ + +int +gid_name(char *name, gid_t *gid) +{ + struct group *gr; + GIDC *ptr; + int namelen; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((grptb == NULL) && (grptb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + ptr = grptb[st_hash(name, namelen, GID_SZ)]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *gid = ptr->gid; + return(0); + } + + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = grptb[st_hash(name, namelen, GID_SZ)] = + (GIDC *)malloc(sizeof(GIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + if (ptr == NULL) { + if ((gr = getgrnam(name)) == NULL) + return(-1); + *gid = gr->gr_gid; + return(0); + } + + (void)strlcpy(ptr->name, name, sizeof(ptr->name)); + if ((gr = getgrnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return(0); +} diff --git a/file_cmds/pax/cache.h b/file_cmds/pax/cache.h new file mode 100644 index 00000000..5c7b9b8b --- /dev/null +++ b/file_cmds/pax/cache.h @@ -0,0 +1,73 @@ +/* $OpenBSD: cache.h,v 1.4 2003/10/20 06:22:27 jmc Exp $ */ +/* $NetBSD: cache.h,v 1.3 1995/03/21 09:07:12 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cache.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Constants and data structures used to implement group and password file + * caches. Traditional passwd/group cache routines perform quite poorly with + * archives. The chances of hitting a valid lookup with an archive is quite a + * bit worse than with files already resident on the file system. These misses + * create a MAJOR performance cost. To address this problem, these routines + * cache both hits and misses. + * + * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and + * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME + */ +#define UNMLEN 32 /* >= user name found in any protocol */ +#define GNMLEN 32 /* >= group name found in any protocol */ +#define UID_SZ 317 /* size of user_name/uid cache */ +#define UNM_SZ 317 /* size of user_name/uid cache */ +#define GID_SZ 251 /* size of gid cache */ +#define GNM_SZ 317 /* size of group name cache */ +#define VALID 1 /* entry and name are valid */ +#define INVALID 2 /* entry valid, name NOT valid */ + +/* + * Node structures used in the user, group, uid, and gid caches. + */ + +typedef struct uidc { + int valid; /* is this a valid or a miss entry */ + char name[UNMLEN]; /* uid name */ + uid_t uid; /* cached uid */ +} UIDC; + +typedef struct gidc { + int valid; /* is this a valid or a miss entry */ + char name[GNMLEN]; /* gid name */ + gid_t gid; /* cached gid */ +} GIDC; diff --git a/file_cmds/pax/cpio.1 b/file_cmds/pax/cpio.1 new file mode 100644 index 00000000..b518a022 --- /dev/null +++ b/file_cmds/pax/cpio.1 @@ -0,0 +1,304 @@ +.\"- +.\" Copyright (c) 1997 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: cpio.1,v 1.16 2001/05/01 17:58:01 aaron Exp $ +.\" $FreeBSD: src/bin/pax/cpio.1,v 1.6 2005/02/09 18:02:29 ru Exp $ +.\" +.Dd February 16, 1997 +.Dt CPIO 1 +.Os +.Sh NAME +.Nm cpio +.Nd copy file archives in and out +.Sh SYNOPSIS +.Nm cpio +.Fl o +.Op Fl aABcLvzZ +.Op Fl C Ar bytes +.Op Fl F Ar archive +.Op Fl H Ar format +.Op Fl O Ar archive +.Ar "< name-list" +.Op Ar "> archive" +.Nm cpio +.Fl i +.Op Fl bBcdfmrsStuvzZ6 +.Op Fl C Ar bytes +.Op Fl E Ar file +.Op Fl F Ar archive +.Op Fl H Ar format +.Op Fl I Ar archive +.Op Ar "pattern ..." +.Op Ar "< archive" +.Nm cpio +.Fl p +.Op Fl adlLmuv +.Ar destination-directory +.No < Ar name-list +.Sh DESCRIPTION +The +.Nm cpio +command copies files to and from a +.Nm cpio +archive. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl o +Create an archive. +Reads the list of files to store in the +archive from standard input, and writes the archive on standard +output. +.Bl -tag -width Ds +.It Fl A +Append to the specified archive. +.It Fl a +Reset the access times on files that have been copied to the +archive. +.It Fl B +Set block size of output to 5120 bytes. +.It Fl C Ar bytes +Set the block size of output to +.Ar bytes . +.It Fl c +Use ASCII format for +.Nm cpio +header for portability. +.It Fl F Ar archive +Use the specified file name as the archive to write to. +.It Fl H Ar format +Write the archive in the specified format. +Recognized formats are: +.Pp +.Bl -tag -width sv4cpio -compact +.It Ar bcpio +Old binary +.Nm cpio +format. +.It Ar cpio +Old octal character +.Nm cpio +format. +.It Ar sv4cpio +SVR4 hex +.Nm cpio +format. +.It Ar tar +Old tar format. +.It Cm ustar +.Tn POSIX +ustar format. +.El +.It Fl L +Follow symbolic links. +.It Fl O Ar archive +Use the specified file name as the archive to write to. +.It Fl v +Be verbose about operations. +List filenames as they are written to the archive. +.It Fl Z +Compress archive using +.Xr compress 1 +format. +.It Fl z +Compress archive using +.Xr gzip 1 +format. +.El +.It Fl i +Restore files from an archive. +Reads the archive file from +standard input and extracts files matching the +.Ar patterns +that were specified on the command line. +.Bl -tag -width Ds +.It Fl 6 +Process old-style +.Nm cpio +format archives. +.It Fl B +Set the block size of the archive being read to 5120 bytes. +.It Fl b +Do byte and word swapping after reading in data from the +archive, for restoring archives created on systems with +a different byte order. +.It Fl C Ar bytes +Read archive written with a block size of +.Ar bytes . +.It Fl c +Expect the archive headers to be in ASCII format. +.It Fl d +Create any intermediate directories as needed during +restore. +.It Fl E Ar file +Read list of file name patterns to extract or list from +.Ar file . +.It Fl F Ar archive +Use the specified file as the input for the archive. +.It Fl f +Restore all files except those matching the +.Ar patterns +given on the command line. +.It Fl H Ar format +Read an archive of the specified format. +Recognized formats are: +.Pp +.Bl -tag -width sv4cpio -compact +.It Ar bcpio +Old binary +.Nm cpio +format. +.It Ar cpio +Old octal character +.Nm cpio +format. +.It Ar sv4cpio +SVR4 hex +.Nm cpio +format. +.It Ar tar +Old tar format. +.It Cm ustar +.Tn POSIX +ustar format. +.El +.It Fl I Ar archive +Use the specified file as the input for the archive. +.It Fl m +Restore modification times on files. +.It Fl r +Rename restored files interactively. +.It Fl S +Swap words after reading data from the archive. +.It Fl s +Swap bytes after reading data from the archive. +.It Fl t +Only list the contents of the archive, no files or +directories will be created. +.It Fl u +Overwrite files even when the file in the archive is +older than the one that will be overwritten. +.It Fl v +Be verbose about operations. +List filenames as they are copied in from the archive. +.It Fl Z +Uncompress archive using +.Xr compress 1 +format. +.It Fl z +Uncompress archive using +.Xr gzip 1 +format. +.It Fl Z +Uncompress archive using +.Xr compress 1 +format. +.It Fl 6 +Process old-style +.Nm +format archives. +.El +.It Fl p +Copy files from one location to another in a single pass. +The list of files to copy are read from standard input and +written out to a directory relative to the specified +.Ar directory +argument. +By default, an older file will not replace a newer file with the same name. +.Bl -tag -width Ds +.It Fl a +Reset the access times on files that have been copied. +.It Fl d +Create any intermediate directories as needed to write +the files at the new location. +.It Fl L +Follow symbolic links. +.It Fl l +When possible, link files rather than creating an +extra copy. +.It Fl m +Restore modification times on files. +.It Fl u +Overwrite files even when the original file being copied is +older than the one that will be overwritten. +.It Fl v +Be verbose about operations. +List filenames as they are copied. +.El +.El +.Sh ENVIRONMENT +.Bl -tag -width Fl +.It Ev TMPDIR +Path in which to store temporary files. +.El +.Sh ERRORS +.Nm cpio +will exit with one of the following values: +.Bl -tag -width 2n +.It 0 +All files were processed successfully. +.It 1 +An error occurred. +.El +.Pp +Whenever +.Nm cpio +cannot create a file or a link when extracting an archive or cannot +find a file while writing an archive, or cannot preserve the user +ID, group ID, file mode, or access and modification times when the +.Fl p +option is specified, a diagnostic message is written to standard +error and a non-zero exit value will be returned, but processing +will continue. +In the case where +.Nm cpio +cannot create a link to a file, +.Nm cpio +will not create a second copy of the file. +.Pp +If the extraction of a file from an archive is prematurely terminated +by a signal or error, +.Nm cpio +may have only partially extracted the file the user wanted. +Additionally, the file modes of extracted files and directories may +have incorrect file bits, and the modification and access times may +be wrong. +.Pp +If the creation of an archive is prematurely terminated by a signal +or error, +.Nm cpio +may have only partially created the archive, which may violate the +specific archive format specification. +.Sh SEE ALSO +.Xr pax 1 , +.Xr tar 1 +.Sh AUTHORS +.An Keith Muller +at the University of California, San Diego. +.Sh BUGS +The +.Fl s +and +.Fl S +options are currently not implemented. diff --git a/file_cmds/pax/cpio.c b/file_cmds/pax/cpio.c new file mode 100644 index 00000000..cb9f969d --- /dev/null +++ b/file_cmds/pax/cpio.c @@ -0,0 +1,1149 @@ +/* $OpenBSD: cpio.c,v 1.18 2008/01/01 16:22:44 tobias Exp $ */ +/* $NetBSD: cpio.c,v 1.5 1995/03/21 09:07:13 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: cpio.c,v 1.18 2008/01/01 16:22:44 tobias Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "cpio.h" +#include "extern.h" + +static int rd_nm(ARCHD *, int); +static int rd_ln_nm(ARCHD *); +static int com_rd(ARCHD *); + +/* + * Routines which support the different cpio versions + */ + +static int swp_head; /* binary cpio header byte swap */ + +/* + * Routines common to all versions of cpio + */ + +/* + * cpio_strd() + * Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +int +cpio_strd(void) +{ + return(lnk_start()); +} + +/* + * cpio_trail() + * Called to determine if a header block is a valid trailer. We are + * passed the block, the in_sync flag (which tells us we are in resync + * mode; looking for a valid header), and cnt (which starts at zero) + * which is used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, + */ + +int +cpio_trail(ARCHD *arcn, char *notused, int notused2, int *notused3) +{ + /* + * look for trailer id in file we are about to process + */ + if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0)) + return(0); + return(-1); +} + +/* + * com_rd() + * operations common to all cpio read functions. + * Return: + * 0 + */ + +static int +com_rd(ARCHD *arcn) +{ + arcn->skip = 0; + arcn->pat = NULL; + arcn->org_name = arcn->name; + switch (arcn->sb.st_mode & C_IFMT) { + case C_ISFIFO: + arcn->type = PAX_FIF; + break; + case C_ISDIR: + arcn->type = PAX_DIR; + break; + case C_ISBLK: + arcn->type = PAX_BLK; + break; + case C_ISCHR: + arcn->type = PAX_CHR; + break; + case C_ISLNK: + arcn->type = PAX_SLK; + break; + case C_ISOCK: + arcn->type = PAX_SCK; + break; + case C_ISCTG: + case C_ISREG: + default: + /* + * we have file data, set up skip (pad is set in the format + * specific sections) + */ + arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG; + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + } + if (chk_lnk(arcn) < 0) + return(-1); + return(0); +} + +/* + * cpio_endwr() + * write the special file with the name trailer in the proper format + * Return: + * result of the write of the trailer from the cpio specific write func + */ + +int +cpio_endwr(void) +{ + ARCHD last; + + /* + * create a trailer request and call the proper format write function + */ + memset(&last, 0, sizeof(last)); + last.nlen = sizeof(TRAILER) - 1; + last.type = PAX_REG; + last.sb.st_nlink = 1; + (void)strlcpy(last.name, TRAILER, sizeof(last.name)); + return((*frmt->wr)(&last)); +} + +/* + * rd_nam() + * read in the file name which follows the cpio header + * Return: + * 0 if ok, -1 otherwise + */ + +static int +rd_nm(ARCHD *arcn, int nsz) +{ + /* + * do not even try bogus values + */ + if ((nsz == 0) || (nsz > sizeof(arcn->name))) { + paxwarn(1, "Cpio file name length %d is out of range", nsz); + return(-1); + } + + /* + * read the name and make sure it is not empty and is \0 terminated + */ + if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || + (arcn->name[0] == '\0')) { + paxwarn(1, "Cpio file name in header is corrupted"); + return(-1); + } + return(0); +} + +/* + * rd_ln_nm() + * read in the link name for a file with links. The link name is stored + * like file data (and is NOT \0 terminated!) + * Return: + * 0 if ok, -1 otherwise + */ + +static int +rd_ln_nm(ARCHD *arcn) +{ + /* + * check the length specified for bogus values + */ + if ((arcn->sb.st_size == 0) || + (arcn->sb.st_size >= sizeof(arcn->ln_name))) { +# ifdef LONG_OFF_T + paxwarn(1, "Cpio link name length is invalid: %lu", + arcn->sb.st_size); +# else + paxwarn(1, "Cpio link name length is invalid: %qu", + arcn->sb.st_size); +# endif + return(-1); + } + + /* + * read in the link name and \0 terminate it + */ + if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != + (int)arcn->sb.st_size) { + paxwarn(1, "Cpio link name read error"); + return(-1); + } + arcn->ln_nlen = (int)arcn->sb.st_size; + arcn->ln_name[arcn->ln_nlen] = '\0'; + + /* + * watch out for those empty link names + */ + if (arcn->ln_name[0] == '\0') { + paxwarn(1, "Cpio link name is corrupt"); + return(-1); + } + return(0); +} + +/* + * Routines common to the extended byte oriented cpio format + */ + +/* + * cpio_id() + * determine if a block given to us is a valid extended byte oriented + * cpio header + * Return: + * 0 if a valid header, -1 otherwise + */ + +int +cpio_id(char *blk, int size) +{ + if ((size < sizeof(HD_CPIO)) || + (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * cpio_rd() + * determine if a buffer is a byte oriented extended cpio archive entry. + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +int +cpio_rd(ARCHD *arcn, char *buf) +{ + int nsz; + HD_CPIO *hd; + + /* + * check that this is a valid header, if not return -1 + */ + if (cpio_id(buf, sizeof(HD_CPIO)) < 0) + return(-1); + hd = (HD_CPIO *)buf; + + /* + * byte oriented cpio (posix) does not have padding! extract the octal + * ascii fields from the header + */ + arcn->pad = 0L; + arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT); + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT); + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + OCT); + arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime), + OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef LONG_OFF_T + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# endif + + /* + * check name size and if valid, read in the name of this entry (name + * follows header in the archive) + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * no link name to read for this file + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + return(com_rd(arcn)); + } + + /* + * check link name size and read in the link name. Link names are + * stored like file data. + */ + if (rd_ln_nm(arcn) < 0) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * cpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +off_t +cpio_endrd(void) +{ + return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER))); +} + +/* + * cpio_stwr() + * start up the device mapping table + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +int +cpio_stwr(void) +{ + return(dev_start()); +} + +/* + * cpio_wr() + * copy the data in the ARCHD to buffer in extended byte oriented cpio + * format. + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +cpio_wr(ARCHD *arcn) +{ + HD_CPIO *hd; + int nsz; + char hdblk[sizeof(HD_CPIO)]; + + /* + * check and repair truncated device and inode fields in the header + */ + if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0) + return(-1); + + arcn->pad = 0L; + nsz = arcn->nlen + 1; + hd = (HD_CPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + switch (arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * set data size for file data + */ +# ifdef LONG_OFF_T + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# endif + paxwarn(1,"File is too large for cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * set data size to hold link name + */ + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) + goto out; + break; + default: + /* + * all other file types have no file data + */ + if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize), + OCT)) + goto out; + break; + } + + /* + * copy the values to the header using octal ascii + */ + if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || + ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev), + OCT) || + ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + OCT) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + OCT) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + OCT) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + OCT) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + OCT) || + ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), + OCT) || + ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime), + OCT) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) + goto out; + + /* + * write the file name to the archive + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0)) { + paxwarn(1, "Unable to write cpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if this file has data, we are done. The caller will write the file + * data, if we are link tell caller we are done, go to next file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name to the archive, tell the caller to go to the + * next file as we are done. + */ + if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { + paxwarn(1,"Unable to write cpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Cpio header field is too small to store file %s", + arcn->org_name); + return(1); +} + +/* + * Routines common to the system VR4 version of cpio (with/without file CRC) + */ + +/* + * vcpio_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header + * uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +int +vcpio_id(char *blk, int size) +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +int +crc_id(char *blk, int size) +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_strd() + w set file data CRC calculations. Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +int +crc_strd(void) +{ + docrc = 1; + return(lnk_start()); +} + +/* + * vcpio_rd() + * determine if a buffer is a system VR4 archive entry. (with/without CRC) + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +int +vcpio_rd(ARCHD *arcn, char *buf) +{ + HD_VCPIO *hd; + dev_t devminor; + dev_t devmajor; + int nsz; + + /* + * during the id phase it was determined if we were using CRC, use the + * proper id routine. + */ + if (docrc) { + if (crc_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } else { + if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } + + hd = (HD_VCPIO *)buf; + arcn->pad = 0L; + + /* + * extract the hex ascii fields from the header + */ + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef LONG_OFF_T + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# endif + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + HEX); + devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX); + arcn->sb.st_dev = TODEV(devmajor, devminor); + devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX); + + /* + * check the length of the file name, if ok read it in, return -1 if + * bogus + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * skip padding. header + filename is aligned to 4 byte boundaries + */ + if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = VCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + /* + * read in the link name and skip over the padding + */ + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * vcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +off_t +vcpio_endrd(void) +{ + return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) + + (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER))))); +} + +/* + * crc_stwr() + * start up the device mapping table, enable crc file calculation + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +int +crc_stwr(void) +{ + docrc = 1; + return(dev_start()); +} + +/* + * vcpio_wr() + * copy the data in the ARCHD to buffer in system VR4 cpio + * (with/without crc) format. + * Return + * 0 if file has data to be written after the header, 1 if file has + * NO data to write after the header, -1 if archive write failed + */ + +int +vcpio_wr(ARCHD *arcn) +{ + HD_VCPIO *hd; + unsigned int nsz; + char hdblk[sizeof(HD_VCPIO)]; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0) + return(-1); + nsz = arcn->nlen + 1; + hd = (HD_VCPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + /* + * add the proper magic value depending whether we were asked for + * file data crc's, and the crc if needed. + */ + if (docrc) { + if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), + HEX)) + goto out; + } else { + if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX)) + goto out; + } + + switch (arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = VCPIO_PAD(arcn->sb.st_size); +# ifdef LONG_OFF_T + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# endif + paxwarn(1,"File is too large for sv4cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize), + HEX)) + goto out; + break; + } + + /* + * set the other fields in the header + */ + if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + HEX) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + HEX) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + HEX) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + HEX) || + ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime), + HEX) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min), + HEX) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) || + (wr_rdbuf(arcn->name, (int)nsz) < 0) || + (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) { + paxwarn(1,"Could not write sv4cpio header for %s",arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done, copy the file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) { + paxwarn(1,"Could not write sv4cpio link name for %s", + arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1,"Sv4cpio header field is too small for file %s",arcn->org_name); + return(1); +} + +/* + * Routines common to the old binary header cpio + */ + +/* + * bcpio_id() + * determine if a block given to us is a old binary cpio header + * (with/without header byte swapping) + * Return: + * 0 if a valid header, -1 otherwise + */ + +int +bcpio_id(char *blk, int size) +{ + if (size < sizeof(HD_BCPIO)) + return(-1); + + /* + * check both normal and byte swapped magic cookies + */ + if (((u_short)SHRT_EXT(blk)) == MAGIC) + return(0); + if (((u_short)RSHRT_EXT(blk)) == MAGIC) { + if (!swp_head) + ++swp_head; + return(0); + } + return(-1); +} + +/* + * bcpio_rd() + * determine if a buffer is a old binary archive entry. (it may have byte + * swapped header) convert and store the values in the ARCHD parameter. + * This is a very old header format and should not really be used. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +int +bcpio_rd(ARCHD *arcn, char *buf) +{ + HD_BCPIO *hd; + int nsz; + + /* + * check the header + */ + if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) + return(-1); + + arcn->pad = 0L; + hd = (HD_BCPIO *)buf; + if (swp_head) { + /* + * header has swapped bytes on 16 bit boundaries + */ + arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(RSHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(RSHRT_EXT(hd->h_filesize_2))); + nsz = (int)(RSHRT_EXT(hd->h_namesize)); + } else { + arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(SHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(SHRT_EXT(hd->h_filesize_2))); + nsz = (int)(SHRT_EXT(hd->h_namesize)); + } + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * check the file name size, if bogus give up. otherwise read the file + * name + */ + if (nsz < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * header + file name are aligned to 2 byte boundaries, skip if needed + */ + if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * bcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +off_t +bcpio_endrd(void) +{ + return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) + + (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER))))); +} + +/* + * bcpio_wr() + * copy the data in the ARCHD to buffer in old binary cpio format + * There is a real chance of field overflow with this critter. So we + * always check the conversion is ok. nobody in their right mind + * should write an archive in this format... + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +bcpio_wr(ARCHD *arcn) +{ + HD_BCPIO *hd; + int nsz; + char hdblk[sizeof(HD_BCPIO)]; + off_t t_offt; + int t_int; + time_t t_timet; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0) + return(-1); + + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + hd = (HD_BCPIO *)hdblk; + + switch (arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); + hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); + hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); + hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); + t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); + t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->sb.st_size != t_offt) { + paxwarn(1,"File is too large for bcpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); + hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); + hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); + hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); + t_int = (int)(SHRT_EXT(hd->h_filesize_1)); + t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->ln_nlen != t_int) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = (char)0; + hd->h_filesize_1[1] = (char)0; + hd->h_filesize_2[0] = (char)0; + hd->h_filesize_2[1] = (char)0; + break; + } + + /* + * build up the rest of the fields + */ + hd->h_magic[0] = CHR_WR_2(MAGIC); + hd->h_magic[1] = CHR_WR_3(MAGIC); + hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); + hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); + if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) + goto out; + hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); + hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); + if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) + goto out; + hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); + hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); + if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) + goto out; + hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); + hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); + if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) + goto out; + hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); + hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); + if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) + goto out; + hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); + hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); + if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) + goto out; + hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); + hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); + if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) + goto out; + hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); + hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); + hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); + hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); + t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1)); + t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); + if (arcn->sb.st_mtime != t_timet) + goto out; + nsz = arcn->nlen + 1; + hd->h_namesize[0] = CHR_WR_2(nsz); + hd->h_namesize[1] = CHR_WR_3(nsz); + if (nsz != (int)(SHRT_EXT(hd->h_namesize))) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0) || + (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) { + paxwarn(1, "Could not write bcpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) { + paxwarn(1,"Could not write bcpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1,"Bcpio header field is too small for file %s", arcn->org_name); + return(1); +} diff --git a/file_cmds/pax/cpio.h b/file_cmds/pax/cpio.h new file mode 100644 index 00000000..dfbd03f2 --- /dev/null +++ b/file_cmds/pax/cpio.h @@ -0,0 +1,150 @@ +/* $OpenBSD: cpio.h,v 1.4 2003/06/02 23:32:08 millert Exp $ */ +/* $NetBSD: cpio.h,v 1.3 1995/03/21 09:07:15 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cpio.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Defines common to all versions of cpio + */ +#define TRAILER "TRAILER!!!" /* name in last archive record */ + +/* + * Header encoding of the different file types + */ +#define C_ISDIR 040000 /* Directory */ +#define C_ISFIFO 010000 /* FIFO */ +#define C_ISREG 0100000 /* Regular file */ +#define C_ISBLK 060000 /* Block special file */ +#define C_ISCHR 020000 /* Character special file */ +#define C_ISCTG 0110000 /* Reserved for contiguous files */ +#define C_ISLNK 0120000 /* Reserved for symbolic links */ +#define C_ISOCK 0140000 /* Reserved for sockets */ +#define C_IFMT 0170000 /* type of file */ + +/* + * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990 + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_dev[6]; /* device number */ + char c_ino[6]; /* inode number */ + char c_mode[6]; /* file type/access */ + char c_uid[6]; /* owners uid */ + char c_gid[6]; /* owners gid */ + char c_nlink[6]; /* # of links at archive creation */ + char c_rdev[6]; /* block/char major/minor # */ + char c_mtime[11]; /* modification time */ + char c_namesize[6]; /* length of pathname */ + char c_filesize[11]; /* length of file in bytes */ +} HD_CPIO; + +#define MAGIC 070707 /* transportable archive id */ + +#ifdef _PAX_ +#define AMAGIC "070707" /* ascii equivalent string of MAGIC */ +#define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */ + /* used for dev/inode remaps */ +#endif /* _PAX_ */ + +/* + * Binary cpio header structure + * + * CAUTION! CAUTION! CAUTION! + * Each field really represents a 16 bit short (NOT ASCII). Described as + * an array of chars in an attempt to improve portability!! + */ +typedef struct { + u_char h_magic[2]; + u_char h_dev[2]; + u_char h_ino[2]; + u_char h_mode[2]; + u_char h_uid[2]; + u_char h_gid[2]; + u_char h_nlink[2]; + u_char h_rdev[2]; + u_char h_mtime_1[2]; + u_char h_mtime_2[2]; + u_char h_namesize[2]; + u_char h_filesize_1[2]; + u_char h_filesize_2[2]; +} HD_BCPIO; + +#ifdef _PAX_ +/* + * extraction and creation macros for binary cpio + */ +#define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff)) +#define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff)) +#define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff)) +#define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff)) +#define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff)) +#define CHR_WR_3(val) ((char)((val) & 0xff)) + +/* + * binary cpio masks and pads + */ +#define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */ +#define BCPIO_MASK 0xffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ + +/* + * System VR4 cpio header structure (with/without file data crc) + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_ino[8]; /* inode number */ + char c_mode[8]; /* file type/access */ + char c_uid[8]; /* owners uid */ + char c_gid[8]; /* owners gid */ + char c_nlink[8]; /* # of links at archive creation */ + char c_mtime[8]; /* modification time */ + char c_filesize[8]; /* length of file in bytes */ + char c_maj[8]; /* block/char major # */ + char c_min[8]; /* block/char minor # */ + char c_rmaj[8]; /* special file major # */ + char c_rmin[8]; /* special file minor # */ + char c_namesize[8]; /* length of pathname */ + char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */ +} HD_VCPIO; + +#define VMAGIC 070701 /* sVr4 new portable archive id */ +#define VCMAGIC 070702 /* sVr4 new portable archive id CRC */ +#ifdef _PAX_ +#define AVMAGIC "070701" /* ascii string of above */ +#define AVCMAGIC "070702" /* ascii string of above */ +#define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */ +#define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ diff --git a/file_cmds/pax/extern.h b/file_cmds/pax/extern.h new file mode 100644 index 00000000..5dad6408 --- /dev/null +++ b/file_cmds/pax/extern.h @@ -0,0 +1,342 @@ +/* $OpenBSD: extern.h,v 1.33 2008/05/06 06:54:28 henning Exp $ */ +/* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * External references from each source file + */ + +#include + +/* + * ar_io.c + */ +extern const char *arcname; +extern const char *gzip_program; +extern int force_one_volume; +int ar_open(const char *); +void ar_close(void); +void ar_drain(void); +int ar_set_wr(void); +int ar_app_ok(void); +int ar_read(char *, int); +int ar_write(char *, int); +int ar_rdsync(void); +int ar_fow(off_t, off_t *); +int ar_rev(off_t ); +int ar_next(void); + +/* + * ar_subs.c + */ +extern u_long flcnt; +int updatepath(void); +int dochdir(const char *); +int fdochdir(int); +void list(void); +void extract(void); +void append(void); +void archive(void); +void copy(void); + +/* + * buf_subs.c + */ +extern int blksz; +extern int wrblksz; +extern int maxflt; +extern int rdblksz; +extern off_t wrlimit; +extern off_t rdcnt; +extern off_t wrcnt; +int wr_start(void); +int rd_start(void); +void cp_start(void); +int appnd_start(off_t); +int rd_sync(void); +void pback(char *, int); +int rd_skip(off_t); +void wr_fin(void); +int wr_rdbuf(char *, int); +int rd_wrbuf(char *, int); +int wr_skip(off_t); +int wr_rdfile(ARCHD *, int, off_t *); +int rd_wrfile(ARCHD *, int, off_t *); +void cp_file(ARCHD *, int, int); +int buf_fill(void); +int buf_flush(int); + +/* + * cache.c + */ +int uidtb_start(void); +int gidtb_start(void); +int usrtb_start(void); +int grptb_start(void); +char * name_uid(uid_t, int); +char * name_gid(gid_t, int); +int uid_name(char *, uid_t *); +int gid_name(char *, gid_t *); + +/* + * cpio.c + */ +int cpio_strd(void); +int cpio_trail(ARCHD *, char *, int, int *); +int cpio_endwr(void); +int cpio_id(char *, int); +int cpio_rd(ARCHD *, char *); +off_t cpio_endrd(void); +int cpio_stwr(void); +int cpio_wr(ARCHD *); +int vcpio_id(char *, int); +int crc_id(char *, int); +int crc_strd(void); +int vcpio_rd(ARCHD *, char *); +off_t vcpio_endrd(void); +int crc_stwr(void); +int vcpio_wr(ARCHD *); +int bcpio_id(char *, int); +int bcpio_rd(ARCHD *, char *); +off_t bcpio_endrd(void); +int bcpio_wr(ARCHD *); + +/* + * file_subs.c + */ +extern char *gnu_name_string, *gnu_link_string; +int file_creat(ARCHD *); +void file_close(ARCHD *, int); +int lnk_creat(ARCHD *); +int cross_lnk(ARCHD *); +int chk_same(ARCHD *); +int node_creat(ARCHD *); +int unlnk_exist(char *, int); +int chk_path(char *, uid_t, gid_t, char **); +void set_ftime(char *fnm, time_t mtime, time_t atime, int frc); +void fset_ftime(char *fnm, int, time_t mtime, time_t atime, int frc); +int set_ids(char *, uid_t, gid_t); +int fset_ids(char *, int, uid_t, gid_t); +int set_lids(char *, uid_t, gid_t); +void set_pmode(char *, mode_t); +void fset_pmode(char *, int, mode_t); +int file_write(int, char *, int, int *, int *, int, char *); +void file_flush(int, char *, int); +void rdfile_close(ARCHD *, int *); +int set_crc(ARCHD *, int); + +/* + * ftree.c + */ +int ftree_start(void); +int ftree_add(char *, int); +void ftree_sel(ARCHD *); +void ftree_notsel(void); +void ftree_skipped_newer(ARCHD *); +void ftree_chk(void); +int next_file(ARCHD *); + +/* + * gen_subs.c + */ +void ls_list(ARCHD *, time_t, FILE *); +void ls_tty(ARCHD *); +void safe_print(const char *, FILE *); +u_long asc_ul(char *, int, int); +int ul_asc(u_long, char *, int, int); +#ifndef LONG_OFF_T +u_quad_t asc_uqd(char *, int, int); +int uqd_asc(u_quad_t, char *, int, int); +#endif +size_t fieldcpy(char *, size_t, const char *, size_t); + +/* + * getoldopt.c + */ +int getoldopt(int, char **, const char *); + +/* + * options.c + */ +extern const FSUB fsub[]; +extern int ford[]; +void options(int, char **); +OPLIST * opt_next(void); +int opt_add(const char *); +int bad_opt(void); +int pax_format_opt_add(char *); +int pax_opt(void); +char *chdname; + +/* + * pat_rep.c + */ +int rep_add(char *); +int pat_add(char *, char *); +void pat_chk(void); +int pat_sel(ARCHD *); +int pat_match(ARCHD *); +int mod_name(ARCHD *); +int set_dest(ARCHD *, char *, int); + +/* + * pax.c + */ +extern int act; +extern const FSUB *frmt; +extern int cflag; +extern int cwdfd; +extern int dflag; +extern int iflag; +extern int kflag; +extern int lflag; +extern int nflag; +extern int tflag; +extern int uflag; +extern int vflag; +extern int Dflag; +extern int Hflag; +extern int Lflag; +extern int Xflag; +extern int Yflag; +extern int Zflag; +extern int zeroflag; +extern int vfpart; +extern int patime; +extern int pmtime; +extern int nodirs; +extern int pmode; +extern int pids; +extern int rmleadslash; +extern int secure; +extern int exit_val; +extern int docrc; +extern char *dirptr; +extern char *ltmfrmt; +extern char *argv0; +extern FILE *listf; +extern char *tempfile; +extern char *tempbase; +extern int havechd; + +int main(int, char **); +void sig_cleanup(int); + +/* + * sel_subs.c + */ +int sel_chk(ARCHD *); +int grp_add(char *); +int usr_add(char *); +int trng_add(char *); + +/* + * tables.c + */ +int lnk_start(void); +int chk_lnk(ARCHD *); +void purg_lnk(ARCHD *); +void lnk_end(void); +int ftime_start(void); +int chk_ftime(ARCHD *); +int name_start(void); +int add_name(char *, int, char *); +void sub_name(char *, int *, size_t); +int dev_start(void); +int add_dev(ARCHD *); +int map_dev(ARCHD *, u_long, u_long); +int atdir_start(void); +void atdir_end(void); +void add_atdir(char *, dev_t, ino_t, time_t, time_t); +int get_atdir(dev_t, ino_t, time_t *, time_t *); +int dir_start(void); +void add_dir(char *, size_t, struct stat *, int); +void proc_dir(void); +u_int st_hash(char *, int, int); + +/* + * tar.c + */ +extern char *gnu_hack_string; +int tar_endwr(void); +off_t tar_endrd(void); +int tar_trail(ARCHD *, char *, int, int *); +int tar_id(char *, int); +int tar_opt(void); +int tar_rd(ARCHD *, char *); +int tar_wr(ARCHD *); +int ustar_strd(void); +int ustar_stwr(void); +int ustar_id(char *, int); +int ustar_rd(ARCHD *, char *); +int ustar_wr(ARCHD *); + +/* + * pax_format.c + */ +extern char *header_name_g; +extern int pax_read_or_list_mode; +#define PAX_INVALID_ACTION_BYPASS 1 +#define PAX_INVALID_ACTION_RENAME 2 +#define PAX_INVALID_ACTION_UTF8 3 +#define PAX_INVALID_ACTION_WRITE 4 +extern int want_linkdata; +extern int pax_invalid_action; +extern char * pax_list_opt_format; +extern char * pax_invalid_action_write_path; +extern char * pax_invalid_action_write_cwd; +void pax_format_list_output(ARCHD *, time_t, FILE *, int); +void cleanup_pax_invalid_action(void); +void record_pax_invalid_action_results(ARCHD *, char *); +int perform_pax_invalid_action(ARCHD *, int); +void adjust_copy_for_pax_options(ARCHD *); +/* +int pax_strd(void); +int pax_stwr(void); +*/ +int pax_id(char *, int); +int pax_rd(ARCHD *, char *); +int pax_wr(ARCHD *); + +/* + * tty_subs.c + */ +int tty_init(void); +void tty_prnt(const char *, ...); +int tty_read(char *, int); +void paxwarn(int, const char *, ...); +void syswarn(int, int, const char *, ...); diff --git a/file_cmds/pax/file_subs.c b/file_cmds/pax/file_subs.c new file mode 100644 index 00000000..e78ba2b0 --- /dev/null +++ b/file_cmds/pax/file_subs.c @@ -0,0 +1,1180 @@ +/* $OpenBSD: file_subs.c,v 1.30 2005/11/09 19:59:06 otto Exp $ */ +/* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: file_subs.c,v 1.30 2005/11/09 19:59:06 otto Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "extern.h" + +static int +mk_link(char *, struct stat *, char *, int); + +/* + * routines that deal with file operations such as: creating, removing; + * and setting access modes, uid/gid and times of files + */ + +#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) +#define SETBITS (S_ISUID | S_ISGID) +#define ABITS (FILEBITS | SETBITS) + +/* + * file_creat() + * Create and open a file. + * Return: + * file descriptor or -1 for failure + */ + +int +file_creat(ARCHD *arcn) +{ + int fd = -1; + mode_t file_mode; + int oerrno; + int rc = 0; + char *path_to_open; + char *new_path; + char *cwd; + char cwd_buff[MAXPATHLEN]; + + /* + * Assume file doesn't exist, so just try to create it, most times this + * works. We have to take special handling when the file does exist. To + * detect this, we use O_EXCL. For example when trying to create a + * file and a character device or fifo exists with the same name, we + * can accidently open the device by mistake (or block waiting to open). + * If we find that the open has failed, then spend the effort to + * figure out why. This strategy was found to have better average + * performance in common use than checking the file (and the path) + * first with lstat. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, + file_mode)) >= 0) + return(fd); + + /* + * the file seems to exist. First we try to get rid of it (found to be + * the second most common failure when traced). If this fails, only + * then we go to the expense to check and create the path to the file + */ + if (unlnk_exist(arcn->name, arcn->type) != 0) + return(-1); + + path_to_open = arcn->name; + new_path = arcn->name; + cwd = getcwd(cwd_buff,sizeof(cwd_buff)); + if (cwd==NULL) return -1; + for (;;) { + /* + * try to open it again, if this fails, check all the nodes in + * the path and give it a final try. if chk_path() finds that + * it cannot fix anything, we will skip the last attempt + */ + if ((fd = open(path_to_open, O_WRONLY | O_CREAT | O_TRUNC, + file_mode)) >= 0) { + /* clean up the invalid_action */ + if (pax_invalid_action>0) { + record_pax_invalid_action_results(arcn, path_to_open); + } + break; + } + oerrno = errno; + if (pax_invalid_action>0) { + rc = perform_pax_invalid_action(arcn, oerrno); + if (rc == 0) continue; + if (rc == 1) { + fd = -1; + break; + } + } + /* rc == 2 reserved for -o invalid_action=write */ + if (nodirs || chk_path(path_to_open,arcn->sb.st_uid,arcn->sb.st_gid, + (rc==2) ? &new_path: NULL) < 0) { + syswarn((pax_invalid_action==0), oerrno, "Unable to create %s", arcn->name); + fd = -1; + break; + } + if (new_path) path_to_open = new_path; /* try again */ + } + if (new_path && strcmp(new_path, arcn->name)!=0) { + dochdir(cwd); /* go back to original directory */ + } + return(fd); +} + +/* + * file_close() + * Close file descriptor to a file just created by pax. Sets modes, + * ownership and times as required. + * Return: + * 0 for success, -1 for failure + */ + +void +file_close(ARCHD *arcn, int fd) +{ + int res = 0; + + if (fd < 0) + return; + + if (close(fd) < 0) + syswarn(0, errno, "Unable to close file descriptor on %s", + arcn->name); + + /* + * set owner/groups first as this may strip off mode bits we want + * then set file permission modes. Then set file access and + * modification times. + */ + if (pids) + res = set_ids(arcn->name, arcn->sb.st_uid, + arcn->sb.st_gid); + else + res = 1; /* without pids, pax should NOT set s bits */ + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(arcn->name, arcn->sb.st_mode); + if (patime || pmtime) + set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); +} + +/* + * lnk_creat() + * Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name + * must exist; + * Return: + * 0 if ok, -1 otherwise + */ + +int +lnk_creat(ARCHD *arcn) +{ + struct stat sb; + + /* + * we may be running as root, so we have to be sure that link target + * is not a directory, so we lstat and check + */ + if (lstat(arcn->ln_name, &sb) < 0) { + syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name, + arcn->name); + return(-1); + } + + if (S_ISDIR(sb.st_mode)) { + paxwarn(1, "A hard link to the directory %s is not allowed", + arcn->ln_name); + return(-1); + } + + if (S_ISLNK(sb.st_mode)) { + int res; + char buff[PATH_MAX+1]; + /* + * Conformance: cannot make hard link to symlink - just make a + * symlink to the target of the symlink + */ + if ((res = readlink(arcn->ln_name, buff, sizeof(buff)-1)) < 0) { + syswarn(1,errno,"Unable to symlink to %s from %s", arcn->ln_name, + arcn->name); + return(-1); + } + buff[res] = 0; + res = symlink(buff, arcn->name); + return res; + } + + return(mk_link(arcn->ln_name, &sb, arcn->name, 0)); +} + +/* + * cross_lnk() + * Create a hard link to arcn->org_name from arcn->name. Only used in copy + * with the -l flag. No warning or error if this does not succeed (we will + * then just create the file) + * Return: + * 1 if copy() should try to create this file node + * 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self). + */ + +int +cross_lnk(ARCHD *arcn) +{ + /* + * try to make a link to original file (-l flag in copy mode). make + * sure we do not try to link to directories in case we are running as + * root (and it might succeed). + */ + if (arcn->type == PAX_DIR) + return(1); + if (arcn->type == PAX_SLK) { /* for Unix 03 conformance tests 202,203 */ + if (!Lflag) + return(1); + } + return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1)); +} + +/* + * chk_same() + * In copy mode if we are not trying to make hard links between the src + * and destinations, make sure we are not going to overwrite ourselves by + * accident. This slows things down a little, but we have to protect all + * those people who make typing errors. + * Return: + * 1 the target does not exist, go ahead and copy + * 0 skip it file exists (-k) or may be the same as source file + */ + +int +chk_same(ARCHD *arcn) +{ + struct stat sb; + + /* + * if file does not exist, return. if file exists and -k, skip it + * quietly + */ + if (lstat(arcn->name, &sb) < 0) + return(1); + if (kflag) + return(0); + + /* + * better make sure the user does not have src == dest by mistake + */ + if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) { + paxwarn(1, "Unable to copy %s, file would overwrite itself", + arcn->name); + return(0); + } + return(1); +} + +/* + * mk_link() + * try to make a hard link between two files. if ign set, we do not + * complain. + * Return: + * 0 if successful (or we are done with this file but no error, such as + * finding the from file exists and the user has set -k). + * 1 when ign was set to indicates we could not make the link but we + * should try to copy/extract the file as that might work (and is an + * allowed option). -1 an error occurred. + */ + +static int +mk_link(char *to, struct stat *to_sb, char *from, int ign) +{ + struct stat sb; + int oerrno; + + /* + * if from file exists, it has to be unlinked to make the link. If the + * file exists and -k is set, skip it quietly + */ + if (lstat(from, &sb) == 0) { + if (kflag) + return(0); + + /* + * make sure it is not the same file, protect the user + */ + if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) { + paxwarn(1, "Unable to link file %s to itself", to); + return(-1); + } + + /* + * try to get rid of the file, based on the type + */ + if (S_ISDIR(sb.st_mode)) { + if (rmdir(from) < 0) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + } else if (unlink(from) < 0) { + if (!ign) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + return(1); + } + } + + /* + * from file is gone (or did not exist), try to make the hard link. + * if it fails, check the path and try it again (if chk_path() says to + * try again) + */ + for (;;) { + if (link(to, from) == 0) + break; + oerrno = errno; + if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid, NULL) == 0) + continue; + if (!ign) { + syswarn(1, oerrno, "Could not link to %s from %s", to, + from); + return(-1); + } + return(1); + } + + /* + * all right the link was made + */ + return(0); +} + +/* + * node_creat() + * create an entry in the file system (other than a file or hard link). + * If successful, sets uid/gid modes and times as required. + * Return: + * 0 if ok, -1 otherwise + */ + +int +node_creat(ARCHD *arcn) +{ + int res; + int ign = 0; + int oerrno; + int pass = 0; + mode_t file_mode; + struct stat sb; + char target[MAXPATHLEN]; + char *nm = arcn->name; + int nmlen = arcn->nlen; + int len; + + /* + * create node based on type, if that fails try to unlink the node and + * try again. finally check the path and try again. As noted in the + * file and link creation routines, this method seems to exhibit the + * best performance in general use workloads. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + + for (;;) { + switch (arcn->type) { + case PAX_DIR: + /* + * If -h (or -L) was given in tar-mode, follow the + * potential symlink chain before trying to create the + * directory. + */ + if (strcmp(NM_TAR, argv0) == 0 && Lflag) { + while (lstat(nm, &sb) == 0 && + S_ISLNK(sb.st_mode)) { + len = readlink(nm, target, + sizeof target - 1); + if (len == -1) { + syswarn(0, errno, + "cannot follow symlink %s in chain for %s", + nm, arcn->name); + res = -1; + goto badlink; + } + target[len] = '\0'; + nm = target; + nmlen = len; + } + } + res = mkdir(nm, file_mode); + +badlink: + if (ign) + res = 0; + break; + case PAX_CHR: + file_mode |= S_IFCHR; + res = mknod(nm, file_mode, arcn->sb.st_rdev); + break; + case PAX_BLK: + file_mode |= S_IFBLK; + res = mknod(nm, file_mode, arcn->sb.st_rdev); + break; + case PAX_FIF: + res = mkfifo(nm, file_mode); + break; + case PAX_SCK: + /* + * Skip sockets, operation has no meaning under BSD + */ + paxwarn(0, + "%s skipped. Sockets cannot be copied or extracted", + nm); + return(-1); + case PAX_SLK: + res = symlink(arcn->ln_name, nm); + break; + case PAX_CTG: + case PAX_HLK: + case PAX_HRG: + case PAX_REG: + default: + /* + * we should never get here + */ + paxwarn(0, "%s has an unknown file type, skipping", + nm); + return(-1); + } + + /* + * if we were able to create the node break out of the loop, + * otherwise try to unlink the node and try again. if that + * fails check the full path and try a final time. + */ + if (res == 0) + break; + + /* + * we failed to make the node + */ + oerrno = errno; + if ((ign = unlnk_exist(nm, arcn->type)) < 0) + return(-1); + + if (++pass <= 1) + continue; + + if (nodirs || chk_path(nm,arcn->sb.st_uid,arcn->sb.st_gid, NULL) < 0) { + syswarn(1, oerrno, "Could not create: %s", nm); + return(-1); + } + } + + /* + * we were able to create the node. set uid/gid, modes and times + */ + if (pids) + res = ((arcn->type == PAX_SLK) ? +#if defined(__APPLE__) + /* Mac OS X doesn't have lchown, so don't bother */ + 0 : +#else + set_lids(nm, arcn->sb.st_uid, arcn->sb.st_gid) : +#endif + set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid)); + else + res = 1; /* without pids, pax should NOT set s bits */ + + /* + * symlinks are done now. + */ + if (arcn->type == PAX_SLK) + return(0); + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT any + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(nm, arcn->sb.st_mode); + + if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) { + /* + * Dirs must be processed again at end of extract to set times + * and modes to agree with those stored in the archive. However + * to allow extract to continue, we may have to also set owner + * rights. This allows nodes in the archive that are children + * of this directory to be extracted without failure. Both time + * and modes will be fixed after the entire archive is read and + * before pax exits. + */ + if (access(nm, R_OK | W_OK | X_OK) < 0) { + if (lstat(nm, &sb) < 0) { + syswarn(0, errno,"Could not access %s (stat)", + arcn->name); + set_pmode(nm,file_mode | S_IRWXU); + } else { + /* + * We have to add rights to the dir, so we make + * sure to restore the mode. The mode must be + * restored AS CREATED and not as stored if + * pmode is not set. + */ + set_pmode(nm, + ((sb.st_mode & FILEBITS) | S_IRWXU)); + if (!pmode) + arcn->sb.st_mode = sb.st_mode; + } + + /* + * we have to force the mode to what was set here, + * since we changed it from the default as created. + */ + add_dir(nm, nmlen, &(arcn->sb), 1); + } else if (pmode || patime || pmtime) + add_dir(nm, nmlen, &(arcn->sb), 0); + } + + if (patime || pmtime) + set_ftime(nm, arcn->sb.st_mtime, arcn->sb.st_atime, 0); + return(0); +} + +/* + * unlnk_exist() + * Remove node from file system with the specified name. We pass the type + * of the node that is going to replace it. When we try to create a + * directory and find that it already exists, we allow processing to + * continue as proper modes etc will always be set for it later on. + * Return: + * 0 is ok to proceed, no file with the specified name exists + * -1 we were unable to remove the node, or we should not remove it (-k) + * 1 we found a directory and we were going to create a directory. + */ + +int +unlnk_exist(char *name, int type) +{ + struct stat sb; + + /* + * the file does not exist, or -k we are done + */ + if (lstat(name, &sb) < 0) + return(0); + if (kflag) + return(-1); + + if(strstr(name, "._") != NULL) /* remove when stat works properly */ + return(0); + + if (S_ISDIR(sb.st_mode)) { + /* + * try to remove a directory, if it fails and we were going to + * create a directory anyway, tell the caller (return a 1) + */ + if (rmdir(name) < 0) { + if (type == PAX_DIR) + return(1); + syswarn(1,errno,"Unable to remove directory %s", name); + return(-1); + } + return(0); + } + + /* + * try to get rid of all non-directory type nodes + */ + if (unlink(name) < 0) { + syswarn(1, errno, "Could not unlink %s", name); + return(-1); + } + return(0); +} + +/* + * chk_path() + * We were trying to create some kind of node in the file system and it + * failed. chk_path() makes sure the path up to the node exists and is + * writeable. When we have to create a directory that is missing along the + * path somewhere, the directory we create will be set to the same + * uid/gid as the file has (when uid and gid are being preserved). + * NOTE: this routine is a real performance loss. It is only used as a + * last resort when trying to create entries in the file system. + * Return: + * -1 when it could find nothing it is allowed to fix. + * 0 otherwise + */ + +int +chk_path(char *name, uid_t st_uid, gid_t st_gid, char ** new_name) +{ + char *spt = name; + int namelen = strlen(name); + struct stat sb; + int retval = -1; + + /* + * watch out for paths with nodes stored directly in / (e.g. /bozo) + */ + if (*spt == '/') + ++spt; + + for (;;) { + /* + * work forward from the first / and check each part of the path + */ + spt = strchr(spt, '/'); + if (spt == NULL) + break; + *spt = '\0'; + + /* + * if it exists we assume it is a directory, it is not within + * the spec (at least it seems to read that way) to alter the + * file system for nodes NOT EXPLICITLY stored on the archive. + * If that assumption is changed, you would test the node here + * and figure out how to get rid of it (probably like some + * recursive unlink()) or fix up the directory permissions if + * required (do an access()). + */ + if (lstat(name, &sb) == 0) { + *(spt++) = '/'; + if (new_name==NULL) continue; + retval = 0; /* accept it one directory at a time */ + break; + } + + /* + * the path fails at this point, see if we can create the + * needed directory and continue on + */ + if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + *spt = '/'; + retval = -1; + break; + } + + /* + * we were able to create the directory. We will tell the + * caller that we found something to fix, and it is ok to try + * and create the node again. + */ + retval = 0; + if (pids) + (void)set_ids(name, st_uid, st_gid); + + /* + * make sure the user doesn't have some strange umask that + * causes this newly created directory to be unusable. We fix + * the modes and restore them back to the creation default at + * the end of pax + */ + if ((access(name, R_OK | W_OK | X_OK) < 0) && + (lstat(name, &sb) == 0)) { + set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU)); + add_dir(name, namelen, &sb, 1); + } + *(spt++) = '/'; + if (new_name==NULL) continue; + break; + } + if ((new_name != NULL) && retval==0) { + /* save the new path */ + *(--spt) = '\0'; + /* + printf ("chdir to %s\n", name); + */ + if(0==chdir(name)) { + *spt++ = '/'; + /* + printf ("remaining path: %s\n",spt); + */ + *new_name = spt; + } else + *spt = '/'; + } + return(retval); +} + +/* + * set_ftime() + * Set the access time and modification time for a named file. If frc + * is non-zero we force these times to be set even if the user did not + * request access and/or modification time preservation (this is also + * used by -t to reset access times). + * When frc is zero, only those times the user has asked for are set, the + * other ones are left alone. We do not assume the un-documented feature + * of many utimes() implementations that consider a 0 time value as a do + * not set request. + */ + +void +set_ftime(char *fnm, time_t mtime, time_t atime, int frc) +{ + static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; + struct stat sb; + + tv[0].tv_sec = (long)atime; + tv[1].tv_sec = (long)mtime; + if (!frc && (!patime || !pmtime)) { + /* + * if we are not forcing, only set those times the user wants + * set. We get the current values of the times if we need them. + */ + if (lstat(fnm, &sb) == 0) { + if (!patime) + tv[0].tv_sec = (long)sb.st_atime; + if (!pmtime) + tv[1].tv_sec = (long)sb.st_mtime; + } else + syswarn(0,errno,"Unable to obtain file stats %s", fnm); + } + + /* + * set the times + */ + if (pax_invalid_action_write_cwd) { + char cwd_buff[MAXPATHLEN]; + char * cwd; + cwd = getcwd(&cwd_buff[0],MAXPATHLEN); + chdir(pax_invalid_action_write_cwd); + if (utimes(pax_invalid_action_write_path, tv) < 0) + syswarn(1, errno, "Access/modification time set failed on: %s", + pax_invalid_action_write_path); + chdir(cwd); + cleanup_pax_invalid_action(); + } else { + if (utimes(fnm, tv) < 0) + syswarn(1, errno, "Access/modification time set failed on: %s", + fnm); + } + return; +} + +void +fset_ftime(char *fnm, int fd, time_t mtime, time_t atime, int frc) +{ + static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; + struct stat sb; + + tv[0].tv_sec = (long)atime; + tv[1].tv_sec = (long)mtime; + if (!frc && (!patime || !pmtime)) { + /* + * if we are not forcing, only set those times the user wants + * set. We get the current values of the times if we need them. + */ + if (fstat(fd, &sb) == 0) { + if (!patime) + tv[0].tv_sec = (long)sb.st_atime; + if (!pmtime) + tv[1].tv_sec = (long)sb.st_mtime; + } else + syswarn(0,errno,"Unable to obtain file stats %s", fnm); + } + /* + * set the times + */ + if (futimes(fd, tv) < 0) + syswarn(1, errno, "Access/modification time set failed on: %s", + fnm); + return; +} + +/* + * set_ids() + * set the uid and gid of a file system node + * Return: + * 0 when set, -1 on failure + */ + +int +set_ids(char *fnm, uid_t uid, gid_t gid) +{ + if (chown(fnm, uid, gid) < 0) { + /* + * ignore EPERM unless in verbose mode or being run by root. + * if running as pax, POSIX requires a warning. + */ + if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag || + geteuid() == 0) + syswarn(1, errno, "Unable to set file uid/gid of %s", + fnm); + return(-1); + } + return(0); +} + +int +fset_ids(char *fnm, int fd, uid_t uid, gid_t gid) +{ + if (fchown(fd, uid, gid) < 0) { + /* + * ignore EPERM unless in verbose mode or being run by root. + * if running as pax, POSIX requires a warning. + */ + if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag || + geteuid() == 0) + syswarn(1, errno, "Unable to set file uid/gid of %s", + fnm); + return(-1); + } + return(0); +} + +#if !defined(__APPLE__) +/* + * set_lids() + * set the uid and gid of a file system node + * Return: + * 0 when set, -1 on failure + */ + +int +set_lids(char *fnm, uid_t uid, gid_t gid) +{ + if (lchown(fnm, uid, gid) < 0) { + /* + * ignore EPERM unless in verbose mode or being run by root. + * if running as pax, POSIX requires a warning. + */ + if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag || + geteuid() == 0) + syswarn(1, errno, "Unable to set file uid/gid of %s", + fnm); + return(-1); + } + return(0); +} +#endif /* !__APPLE__ */ + +/* + * set_pmode() + * Set file access mode + */ + +void +set_pmode(char *fnm, mode_t mode) +{ + mode &= ABITS; + if (chmod(fnm, mode) < 0) + syswarn(1, errno, "Could not set permissions on %s", fnm); + return; +} + +void +fset_pmode(char *fnm, int fd, mode_t mode) +{ + mode &= ABITS; + if (fchmod(fd, mode) < 0) + syswarn(1, errno, "Could not set permissions on %s", fnm); + return; +} + +/* + * file_write() + * Write/copy a file (during copy or archive extract). This routine knows + * how to copy files with lseek holes in it. (Which are read as file + * blocks containing all 0's but do not have any file blocks associated + * with the data). Typical examples of these are files created by dbm + * variants (.pag files). While the file size of these files are huge, the + * actual storage is quite small (the files are sparse). The problem is + * the holes read as all zeros so are probably stored on the archive that + * way (there is no way to determine if the file block is really a hole, + * we only know that a file block of all zero's can be a hole). + * At this writing, no major archive format knows how to archive files + * with holes. However, on extraction (or during copy, -rw) we have to + * deal with these files. Without detecting the holes, the files can + * consume a lot of file space if just written to disk. This replacement + * for write when passed the basic allocation size of a file system block, + * uses lseek whenever it detects the input data is all 0 within that + * file block. In more detail, the strategy is as follows: + * While the input is all zero keep doing an lseek. Keep track of when we + * pass over file block boundaries. Only write when we hit a non zero + * input. once we have written a file block, we continue to write it to + * the end (we stop looking at the input). When we reach the start of the + * next file block, start checking for zero blocks again. Working on file + * block boundaries significantly reduces the overhead when copying files + * that are NOT very sparse. This overhead (when compared to a write) is + * almost below the measurement resolution on many systems. Without it, + * files with holes cannot be safely copied. It does has a side effect as + * it can put holes into files that did not have them before, but that is + * not a problem since the file contents are unchanged (in fact it saves + * file space). (Except on paging files for diskless clients. But since we + * cannot determine one of those file from here, we ignore them). If this + * ever ends up on a system where CTG files are supported and the holes + * are not desired, just do a conditional test in those routines that + * call file_write() and have it call write() instead. BEFORE CLOSING THE + * FILE, make sure to call file_flush() when the last write finishes with + * an empty block. A lot of file systems will not create an lseek hole at + * the end. In this case we drop a single 0 at the end to force the + * trailing 0's in the file. + * ---Parameters--- + * rem: how many bytes left in this file system block + * isempt: have we written to the file block yet (is it empty) + * sz: basic file block allocation size + * cnt: number of bytes on this write + * str: buffer to write + * Return: + * number of bytes written, -1 on write (or lseek) error. + */ + +int +file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz, + char *name) +{ + char *pt; + char *end; + int wcnt; + char *st = str; + char **strp; + + /* + * while we have data to process + */ + while (cnt) { + if (!*rem) { + /* + * We are now at the start of file system block again + * (or what we think one is...). start looking for + * empty blocks again + */ + *isempt = 1; + *rem = sz; + } + + /* + * only examine up to the end of the current file block or + * remaining characters to write, whatever is smaller + */ + wcnt = MIN(cnt, *rem); + cnt -= wcnt; + *rem -= wcnt; + if (*isempt) { + /* + * have not written to this block yet, so we keep + * looking for zero's + */ + pt = st; + end = st + wcnt; + + /* + * look for a zero filled buffer + */ + while ((pt < end) && (*pt == '\0')) + ++pt; + + if (pt == end) { + /* + * skip, buf is empty so far + */ + if (fd > -1 && + lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { + syswarn(1,errno,"File seek on %s", + name); + return(-1); + } + st = pt; + continue; + } + /* + * drat, the buf is not zero filled + */ + *isempt = 0; + } + + /* + * have non-zero data in this file system block, have to write + */ + switch (fd) { + case -1: + strp = &gnu_name_string; + break; + case -2: + strp = &gnu_link_string; + break; + default: + strp = NULL; + break; + } + if (strp) { + if (*strp) + err(1, "WARNING! Major Internal Error! GNU hack Failing!"); + *strp = malloc(wcnt + 1); + if (*strp == NULL) { + paxwarn(1, "Out of memory"); + return(-1); + } + memcpy(*strp, st, wcnt); + (*strp)[wcnt] = '\0'; + break; + } else if (write(fd, st, wcnt) != wcnt) { + syswarn(1, errno, "Failed write to file %s", name); + return(-1); + } + st += wcnt; + } + return(st - str); +} + +/* + * file_flush() + * when the last file block in a file is zero, many file systems will not + * let us create a hole at the end. To get the last block with zeros, we + * write the last BYTE with a zero (back up one byte and write a zero). + */ + +void +file_flush(int fd, char *fname, int isempt) +{ + static char blnk[] = "\0"; + + /* + * silly test, but make sure we are only called when the last block is + * filled with all zeros. + */ + if (!isempt) + return; + + /* + * move back one byte and write a zero + */ + if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { + syswarn(1, errno, "Failed seek on file %s", fname); + return; + } + + if (write(fd, blnk, 1) < 0) + syswarn(1, errno, "Failed write to file %s", fname); + return; +} + +/* + * rdfile_close() + * close a file we have beed reading (to copy or archive). If we have to + * reset access time (tflag) do so (the times are stored in arcn). + */ + +void +rdfile_close(ARCHD *arcn, int *fd) +{ + /* + * make sure the file is open + */ + if (*fd < 0) + return; + + (void)close(*fd); + *fd = -1; + if (!tflag) + return; + + /* + * user wants last access time reset + */ + set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1); + return; +} + +/* + * set_crc() + * read a file to calculate its crc. This is a real drag. Archive formats + * that have this, end up reading the file twice (we have to write the + * header WITH the crc before writing the file contents. Oh well... + * Return: + * 0 if was able to calculate the crc, -1 otherwise + */ + +int +set_crc(ARCHD *arcn, int fd) +{ + int i; + int res; + off_t cpcnt = 0L; + u_long size; + u_int32_t crc = 0; + char tbuf[FILEBLK]; + struct stat sb; + + if (fd < 0) { + /* + * hmm, no fd, should never happen. well no crc then. + */ + arcn->crc = 0L; + return(0); + } + + if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf)) + size = (u_long)sizeof(tbuf); + + /* + * read all the bytes we think that there are in the file. If the user + * is trying to archive an active file, forget this file. + */ + for (;;) { + if ((res = read(fd, tbuf, size)) <= 0) + break; + cpcnt += res; + for (i = 0; i < res; ++i) + crc += (tbuf[i] & 0xff); + } + + /* + * safety check. we want to avoid archiving files that are active as + * they can create inconsistent archive copies. + */ + if (cpcnt != arcn->sb.st_size) + paxwarn(1, "File changed size %s", arcn->org_name); + else if (fstat(fd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during read", arcn->org_name); + else if (lseek(fd, (off_t)0L, SEEK_SET) < 0) + syswarn(1, errno, "File rewind failed on: %s", arcn->org_name); + else { + arcn->crc = crc; + return(0); + } + return(-1); +} diff --git a/file_cmds/pax/ftree.c b/file_cmds/pax/ftree.c new file mode 100644 index 00000000..f2742417 --- /dev/null +++ b/file_cmds/pax/ftree.c @@ -0,0 +1,585 @@ +/* $OpenBSD: ftree.c,v 1.28 2008/05/06 06:54:28 henning Exp $ */ +/* $NetBSD: ftree.c,v 1.4 1995/03/21 09:07:21 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: ftree.c,v 1.28 2008/05/06 06:54:28 henning Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "ftree.h" +#include "extern.h" + +/* + * routines to interface with the fts library function. + * + * file args supplied to pax are stored on a single linked list (of type FTREE) + * and given to fts to be processed one at a time. pax "selects" files from + * the expansion of each arg into the corresponding file tree (if the arg is a + * directory, otherwise the node itself is just passed to pax). The selection + * is modified by the -n and -u flags. The user is informed when a specific + * file arg does not generate any selected files. -n keeps expanding the file + * tree arg until one of its files is selected, then skips to the next file + * arg. when the user does not supply the file trees as command line args to + * pax, they are read from stdin + */ + +static FTS *ftsp = NULL; /* current FTS handle */ +static int ftsopts; /* options to be used on fts_open */ +static char *farray[2]; /* array for passing each arg to fts */ +static FTREE *fthead = NULL; /* head of linked list of file args */ +static FTREE *fttail = NULL; /* tail of linked list of file args */ +static FTREE *ftcur = NULL; /* current file arg being processed */ +static FTSENT *ftent = NULL; /* current file tree entry */ +static int ftree_skip; /* when set skip to next file arg */ + +static int ftree_arg(void); +static char *getpathname(char *, int); + +/* + * ftree_start() + * initialize the options passed to fts_open() during this run of pax + * options are based on the selection of pax options by the user + * fts_start() also calls fts_arg() to open the first valid file arg. We + * also attempt to reset directory access times when -t (tflag) is set. + * Return: + * 0 if there is at least one valid file arg to process, -1 otherwise + */ + +int +ftree_start(void) +{ + /* + * set up the operation mode of fts, open the first file arg. We must + * use FTS_NOCHDIR, as the user may have to open multiple archives and + * if fts did a chdir off into the boondocks, we may create an archive + * volume in an place where the user did not expect to. + */ + ftsopts = FTS_NOCHDIR; + + /* + * optional user flags that effect file traversal + * -H command line symlink follow only (half follow) + * -L follow sylinks (logical) + * -P do not follow sylinks (physical). This is the default. + * -X do not cross over mount points + * -t preserve access times on files read. + * -n select only the first member of a file tree when a match is found + * -d do not extract subtrees rooted at a directory arg. + */ + if (Lflag) + ftsopts |= FTS_LOGICAL; + else + ftsopts |= FTS_PHYSICAL; + if (Hflag) + ftsopts |= FTS_COMFOLLOW; + if (Xflag) + ftsopts |= FTS_XDEV; + + if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { + paxwarn(1, "Unable to allocate memory for file name buffer"); + return(-1); + } + + if (ftree_arg() < 0) + return(-1); + if (tflag && (atdir_start() < 0)) + return(-1); + return(0); +} + +/* + * ftree_add() + * add the arg to the linked list of files to process. Each will be + * processed by fts one at a time + * Return: + * 0 if added to the linked list, -1 if failed + */ + +int +ftree_add(char *str, int chflg) +{ + FTREE *ft; + int len; + + /* + * simple check for bad args + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(0, "Invalid file name argument"); + return(-1); + } + + /* + * allocate FTREE node and add to the end of the linked list (args are + * processed in the same order they were passed to pax). Get rid of any + * trailing / the user may pass us. (watch out for / by itself). + */ + if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { + paxwarn(0, "Unable to allocate memory for filename"); + return(-1); + } + + if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) + str[len] = '\0'; + ft->fname = str; + ft->refcnt = 0; + ft->newercnt = 0; + ft->chflg = chflg; + ft->fow = NULL; + if (fthead == NULL) { + fttail = fthead = ft; + return(0); + } + fttail->fow = ft; + fttail = ft; + return(0); +} + +/* + * ftree_sel() + * this entry has been selected by pax. bump up reference count and handle + * -n and -d processing. + */ + +void +ftree_sel(ARCHD *arcn) +{ + /* + * set reference bit for this pattern. This linked list is only used + * when file trees are supplied pax as args. The list is not used when + * the trees are read from stdin. + */ + if (ftcur != NULL) + ftcur->refcnt = 1; + + /* + * if -n we are done with this arg, force a skip to the next arg when + * pax asks for the next file in next_file(). + * if -d we tell fts only to match the directory (if the arg is a dir) + * and not the entire file tree rooted at that point. + */ + if (nflag) + ftree_skip = 1; + + if (!dflag || (arcn->type != PAX_DIR)) + return; + + if (ftent != NULL) + (void)fts_set(ftsp, ftent, FTS_SKIP); +} + +/* + * ftree_notsel() + * this entry has not been selected by pax. + */ + +void +ftree_notsel() +{ + if (ftent != NULL) + (void)fts_set(ftsp, ftent, FTS_SKIP); +} + +/* + * ftree_skipped_newer() + * file has been skipped because a newer file exists and -u/-D given + */ + +void +ftree_skipped_newer(ARCHD *arcn) +{ + /* skipped due to -u/-D, mark accordingly */ + if (ftcur != NULL) + ftcur->newercnt = 1; +} + +/* + * ftree_chk() + * called at end on pax execution. Prints all those file args that did not + * have a selected member (reference count still 0) + */ + +void +ftree_chk(void) +{ + FTREE *ft; + int wban = 0; + + /* + * make sure all dir access times were reset. + */ + if (tflag) + atdir_end(); + + /* + * walk down list and check reference count. Print out those members + * that never had a match + */ + for (ft = fthead; ft != NULL; ft = ft->fow) { + if ((ft->refcnt > 0) || ft->newercnt > 0 || ft->chflg) + continue; + if (wban == 0) { + paxwarn(1,"WARNING! These file names were not selected:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", ft->fname); + } +} + +/* + * ftree_arg() + * Get the next file arg for fts to process. Can be from either the linked + * list or read from stdin when the user did not them as args to pax. Each + * arg is processed until the first successful fts_open(). + * Return: + * 0 when the next arg is ready to go, -1 if out of file args (or EOF on + * stdin). + */ + +static int +ftree_arg(void) +{ + + /* + * close off the current file tree + */ + if (ftsp != NULL) { + (void)fts_close(ftsp); + ftsp = NULL; + } + + /* + * keep looping until we get a valid file tree to process. Stop when we + * reach the end of the list (or get an eof on stdin) + */ + for (;;) { + if (fthead == NULL) { + /* + * the user didn't supply any args, get the file trees + * to process from stdin; + */ + if (getpathname(farray[0], PAXPATHLEN+1) == NULL) + return(-1); + } else { + /* + * the user supplied the file args as arguments to pax + */ + if (ftcur == NULL) + ftcur = fthead; + else if ((ftcur = ftcur->fow) == NULL) + return(-1); + if (ftcur->chflg) { + /* First fchdir() back... */ + if (fdochdir(cwdfd) < 0) { + syswarn(1, errno, + "Can't fchdir to starting directory"); + return(-1); + } + if (dochdir(ftcur->fname) < 0) { + syswarn(1, errno, "Can't chdir to %s", + ftcur->fname); + return(-1); + } + continue; + } else + farray[0] = ftcur->fname; + } + + /* + * watch it, fts wants the file arg stored in a array of char + * ptrs, with the last one a null. we use a two element array + * and set farray[0] to point at the buffer with the file name + * in it. We cannot pass all the file args to fts at one shot + * as we need to keep a handle on which file arg generates what + * files (the -n and -d flags need this). If the open is + * successful, return a 0. + */ + if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) + break; + } + return(0); +} + +/* + * next_file() + * supplies the next file to process in the supplied archd structure. + * Return: + * 0 when contents of arcn have been set with the next file, -1 when done. + */ + +int +next_file(ARCHD *arcn) +{ + int cnt; + time_t atime; + time_t mtime; + + /* + * ftree_sel() might have set the ftree_skip flag if the user has the + * -n option and a file was selected from this file arg tree. (-n says + * only one member is matched for each pattern) ftree_skip being 1 + * forces us to go to the next arg now. + */ + if (ftree_skip) { + /* + * clear and go to next arg + */ + ftree_skip = 0; + if (ftree_arg() < 0) + return(-1); + } + + /* + * loop until we get a valid file to process + */ + for (;;) { + if ((ftent = fts_read(ftsp)) == NULL) { + if (errno) + syswarn(1, errno, "next_file"); + /* + * out of files in this tree, go to next arg, if none + * we are done + */ + if (ftree_arg() < 0) + return(-1); + continue; + } + + /* + * handle each type of fts_read() flag + */ + switch (ftent->fts_info) { + case FTS_D: + case FTS_DEFAULT: + case FTS_F: + case FTS_SL: + /* + * these are all ok + */ + break; + case FTS_SLNONE: /* was same as above cases except Unix + conformance requires this error check */ + if (Hflag || Lflag) { /* -H or -L was specified */ + if (ftent->fts_errno) + paxwarn(1, "%s: %s", + ftent->fts_name, strerror(ftent->fts_errno)); + } + break; + case FTS_DP: + /* + * already saw this directory. If the user wants file + * access times reset, we use this to restore the + * access time for this directory since this is the + * last time we will see it in this file subtree + * remember to force the time (this is -t on a read + * directory, not a created directory). + */ + if (!tflag || (get_atdir(ftent->fts_statp->st_dev, + ftent->fts_statp->st_ino, &mtime, &atime) < 0)) + continue; + set_ftime(ftent->fts_path, mtime, atime, 1); + continue; + case FTS_DC: + /* + * fts claims a file system cycle + */ + paxwarn(1,"File system cycle found at %s",ftent->fts_path); + continue; + case FTS_DNR: + syswarn(1, ftent->fts_errno, + "Unable to read directory %s", ftent->fts_path); + continue; + case FTS_ERR: + syswarn(1, ftent->fts_errno, + "File system traversal error"); + continue; + case FTS_NS: + case FTS_NSOK: + syswarn(1, ftent->fts_errno, + "Unable to access %s", ftent->fts_path); + continue; + } + + /* + * ok got a file tree node to process. copy info into arcn + * structure (initialize as required) + */ + arcn->skip = 0; + arcn->pad = 0; + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + memcpy(&arcn->sb, ftent->fts_statp, sizeof(arcn->sb)); + + /* + * file type based set up and copy into the arcn struct + * SIDE NOTE: + * we try to reset the access time on all files and directories + * we may read when the -t flag is specified. files are reset + * when we close them after copying. we reset the directories + * when we are done with their file tree (we also clean up at + * end in case we cut short a file tree traversal). However + * there is no way to reset access times on symlinks. + */ + switch (S_IFMT & arcn->sb.st_mode) { + case S_IFDIR: + arcn->type = PAX_DIR; + if (!tflag) + break; + add_atdir(ftent->fts_path, arcn->sb.st_dev, + arcn->sb.st_ino, arcn->sb.st_mtime, + arcn->sb.st_atime); + break; + case S_IFCHR: + arcn->type = PAX_CHR; + break; + case S_IFBLK: + arcn->type = PAX_BLK; + break; + case S_IFREG: + /* + * only regular files with have data to store on the + * archive. all others will store a zero length skip. + * the skip field is used by pax for actual data it has + * to read (or skip over). + */ + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + case S_IFLNK: + arcn->type = PAX_SLK; + /* + * have to read the symlink path from the file + */ + if ((cnt = readlink(ftent->fts_path, arcn->ln_name, + PAXPATHLEN)) < 0) { + syswarn(1, errno, "Unable to read symlink %s", + ftent->fts_path); + continue; + } + /* + * set link name length, watch out readlink does not + * always NUL terminate the link path + */ + arcn->ln_name[cnt] = '\0'; + arcn->ln_nlen = cnt; + break; + case S_IFSOCK: + /* + * under BSD storing a socket is senseless but we will + * let the format specific write function make the + * decision of what to do with it. + */ + arcn->type = PAX_SCK; + break; + case S_IFIFO: + arcn->type = PAX_FIF; + break; + } + break; + } + + /* + * copy file name, set file name length + */ + arcn->nlen = strlcpy(arcn->name, ftent->fts_path, sizeof(arcn->name)); + if (arcn->nlen >= sizeof(arcn->name)) + arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */ + arcn->org_name = ftent->fts_path; + return(0); +} + +/* + * getpathname() + * Reads a pathname from stdin, handling NUL- or newline-termination. + * Return: + * NULL at end of file, otherwise the NUL-terminated buffer. + */ + +static char * +getpathname(char *buf, int buflen) +{ + char *bp, *ep; + int ch, term; + + if (zeroflag) { + /* + * Read a NUL-terminated pathname, being especially + * paranoid about proper termination and pathname length. + */ + for (bp = buf, ep = buf + buflen; bp < ep; bp++) { + if ((ch = getchar()) == EOF) { + if (bp != buf) + paxwarn(1, "Ignoring unterminated " + "pathname at EOF"); + return(NULL); + } + if ((*bp = ch) == '\0') + return(buf); + } + /* Too long - skip this path */ + *--bp = '\0'; + term = '\0'; + } else { + if (fgets(buf, buflen, stdin) == NULL) + return(NULL); + if ((bp = strchr(buf, '\n')) != NULL || feof(stdin)) { + if (bp != NULL) + *bp = '\0'; + return(buf); + } + /* Too long - skip this path */ + term = '\n'; + } + while ((ch = getchar()) != term && ch != EOF) + ; + paxwarn(1, "Ignoring too-long pathname: %s", buf); + return(NULL); +} diff --git a/file_cmds/pax/ftree.h b/file_cmds/pax/ftree.h new file mode 100644 index 00000000..9b4cffcc --- /dev/null +++ b/file_cmds/pax/ftree.h @@ -0,0 +1,51 @@ +/* $OpenBSD: ftree.h,v 1.5 2008/05/06 06:54:28 henning Exp $ */ +/* $NetBSD: ftree.h,v 1.3 1995/03/21 09:07:23 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ftree.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Data structure used by the ftree.c routines to store the file args to be + * handed to fts(). It keeps a reference count of which args generated a + * "selected" member + */ + +typedef struct ftree { + char *fname; /* file tree name */ + int refcnt; /* has tree had a selected file? */ + int newercnt; /* skipped due to -u/-D */ + int chflg; /* change directory flag */ + struct ftree *fow; /* pointer to next entry on list */ +} FTREE; diff --git a/file_cmds/pax/gen_subs.c b/file_cmds/pax/gen_subs.c new file mode 100644 index 00000000..d3b4c167 --- /dev/null +++ b/file_cmds/pax/gen_subs.c @@ -0,0 +1,439 @@ +/* $OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $ */ +/* $NetBSD: gen_subs.c,v 1.5 1995/03/21 09:07:26 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: gen_subs.c,v 1.19 2007/04/04 21:55:10 millert Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +/* + * a collection of general purpose subroutines used by pax + */ + +/* + * constants used by ls_list() when printing out archive members + */ +#define MODELEN 20 +#define DATELEN 64 +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) +#define CURFRMTM "%b %e %H:%M" +#define OLDFRMTM "%b %e %Y" +#define CURFRMTD "%e %b %H:%M" +#define OLDFRMTD "%e %b %Y" +#define NAME_WIDTH 8 + +static int d_first = -1; + +/* + * ls_list() + * list the members of an archive in ls format + */ + +void +ls_list(ARCHD *arcn, time_t now, FILE *fp) +{ + struct stat *sbp; + char f_mode[MODELEN]; + char f_date[DATELEN]; + const char *timefrmt; + int term; + + term = zeroflag ? '\0' : '\n'; /* path termination character */ + + /* + * if not verbose, just print the file name + */ + if (!vflag) { + if (zeroflag) + (void)fputs(arcn->name, fp); + else + safe_print(arcn->name, fp); + (void)putc(term, fp); + (void)fflush(fp); + return; + } + + if (pax_list_opt_format) { + pax_format_list_output(arcn, now, fp, term); + return; + } + + if (d_first < 0) + d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + /* + * user wants long mode + */ + sbp = &(arcn->sb); + strmode(sbp->st_mode, f_mode); + + /* + * time format based on age compared to the time pax was started. + */ + if ((sbp->st_mtime + SIXMONTHS) <= now || + sbp->st_mtime > now) + timefrmt = d_first ? OLDFRMTD : OLDFRMTM; + else + timefrmt = d_first ? CURFRMTD : CURFRMTM; + + /* + * print file mode, link count, uid, gid and time + */ + if (strftime(f_date,DATELEN,timefrmt,localtime(&(sbp->st_mtime))) == 0) + f_date[0] = '\0'; +#define UT_NAMESIZE 8 + (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, sbp->st_nlink, + NAME_WIDTH, UT_NAMESIZE, name_uid(sbp->st_uid, 1), + NAME_WIDTH, UT_NAMESIZE, name_gid(sbp->st_gid, 1)); + + /* + * print device id's for devices, or sizes for other nodes + */ + if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK)) +# ifdef LONG_OFF_T + (void)fprintf(fp, "%4u,%4u ", MAJOR(sbp->st_rdev), +# else + (void)fprintf(fp, "%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev), +# endif + (unsigned long)MINOR(sbp->st_rdev)); + else { +# ifdef LONG_OFF_T + (void)fprintf(fp, "%9lu ", sbp->st_size); +# else + (void)fprintf(fp, "%9qu ", sbp->st_size); +# endif + } + + /* + * print name and link info for hard and soft links + */ + (void)fputs(f_date, fp); + (void)putc(' ', fp); + safe_print(arcn->name, fp); + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) { + fputs(" == ", fp); + safe_print(arcn->ln_name, fp); + } else if (arcn->type == PAX_SLK) { + fputs(" -> ", fp); + safe_print(arcn->ln_name, fp); + } + (void)putc(term, fp); + (void)fflush(fp); + return; +} + +/* + * tty_ls() + * print a short summary of file to tty. + */ + +void +ls_tty(ARCHD *arcn) +{ + char f_date[DATELEN]; + char f_mode[MODELEN]; + const char *timefrmt; + + if (d_first < 0) + d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + + if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL)) + timefrmt = d_first ? OLDFRMTD : OLDFRMTM; + else + timefrmt = d_first ? CURFRMTD : CURFRMTM; + + /* + * convert time to string, and print + */ + if (strftime(f_date, DATELEN, timefrmt, + localtime(&(arcn->sb.st_mtime))) == 0) + f_date[0] = '\0'; + strmode(arcn->sb.st_mode, f_mode); + tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name); + return; +} + +void +safe_print(const char *str, FILE *fp) +{ + char visbuf[5]; + const char *cp; + + /* + * if printing to a tty, use vis(3) to print special characters. + */ + if (isatty(fileno(fp))) { + for (cp = str; *cp; cp++) { + (void)vis(visbuf, cp[0], VIS_CSTYLE, cp[1]); + (void)fputs(visbuf, fp); + } + } else { + (void)fputs(str, fp); + } +} + +/* + * asc_ul() + * convert hex/octal character string into a u_long. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * unsigned long value + */ + +u_long +asc_ul(char *str, int len, int base) +{ + char *stop; + u_long tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * ul_asc() + * convert an unsigned long into an hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +int +ul_asc(u_long val, char *str, int len, int base) +{ + char *pt; + u_long digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_long)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_long)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef LONG_OFF_T +/* + * asc_uqd() + * convert hex/octal character string into a u_quad_t. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * u_quad_t value + */ + +u_quad_t +asc_uqd(char *str, int len, int base) +{ + char *stop; + u_quad_t tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * uqd_asc() + * convert an u_quad_t into a hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +int +uqd_asc(u_quad_t val, char *str, int len, int base) +{ + char *pt; + u_quad_t digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_quad_t)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_quad_t)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif + +/* + * Copy at max min(bufz, fieldsz) chars from field to buf, stopping + * at the first NUL char. NUL terminate buf if there is room left. + */ +size_t +fieldcpy(char *buf, size_t bufsz, const char *field, size_t fieldsz) +{ + char *p = buf; + const char *q = field; + size_t i = 0; + + if (fieldsz > bufsz) + fieldsz = bufsz; + while (i < fieldsz && *q != '\0') { + *p++ = *q++; + i++; + } + if (i < bufsz) + *p = '\0'; + return(i); +} diff --git a/file_cmds/pax/getoldopt.c b/file_cmds/pax/getoldopt.c new file mode 100644 index 00000000..3133f62b --- /dev/null +++ b/file_cmds/pax/getoldopt.c @@ -0,0 +1,74 @@ +/* $OpenBSD: getoldopt.c,v 1.8 2003/07/02 21:19:33 deraadt Exp $ */ +/* $NetBSD: getoldopt.c,v 1.3 1995/03/21 09:07:28 cgd Exp $ */ + +/* + * Plug-compatible replacement for getopt() for parsing tar-like + * arguments. If the first argument begins with "-", it uses getopt; + * otherwise, it uses the old rules used by tar, dump, and ps. + * + * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed + * in the Public Domain for your edification and enjoyment. + */ + +#include +#ifndef lint +__used static const char rcsid[] = "$OpenBSD: getoldopt.c,v 1.8 2003/07/02 21:19:33 deraadt Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +int +getoldopt(int argc, char **argv, const char *optstring) +{ + static char *key; /* Points to next keyletter */ + static char use_getopt; /* !=0 if argv[1][0] was '-' */ + char c; + char *place; + + optarg = NULL; + + if (key == NULL) { /* First time */ + if (argc < 2) + return (-1); + key = argv[1]; + if (*key == '-') + use_getopt++; + else + optind = 2; + } + + if (use_getopt) + return (getopt(argc, argv, optstring)); + + c = *key++; + if (c == '\0') { + key--; + return (-1); + } + place = strchr(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option %c\n", argv[0], c); + return ('?'); + } + + place++; + if (*place == ':') { + if (optind < argc) { + optarg = argv[optind]; + optind++; + } else { + fprintf(stderr, "%s: %c argument missing\n", + argv[0], c); + return ('?'); + } + } + + return (c); +} diff --git a/file_cmds/pax/options.c b/file_cmds/pax/options.c new file mode 100644 index 00000000..0544e5ef --- /dev/null +++ b/file_cmds/pax/options.c @@ -0,0 +1,1747 @@ +/* $OpenBSD: options.c,v 1.70 2008/06/11 00:49:08 pvalchev Exp $ */ +/* $NetBSD: options.c,v 1.6 1996/03/26 23:54:18 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: options.c,v 1.70 2008/06/11 00:49:08 pvalchev Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#ifndef __APPLE__ +#include +#endif /* __APPLE__ */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "cpio.h" +#include "tar.h" +#include "extern.h" + +/* + * Routines which handle command line options + */ + +static char flgch[] = FLGCH; /* list of all possible flags */ +static OPLIST *ophead = NULL; /* head for format specific options -x */ +static OPLIST *optail = NULL; /* option tail */ + +static int no_op(void); +static void printflg(unsigned int); +static int c_frmt(const void *, const void *); +static off_t str_offt(char *); +static char *pax_getline(FILE *fp); +static void pax_options(int, char **); +void pax_usage(void); +static void tar_options(int, char **); +static void tar_usage(void); +static void cpio_options(int, char **); +static void cpio_usage(void); + +/* errors from getline */ +#define GETLINE_FILE_CORRUPT 1 +#define GETLINE_OUT_OF_MEM 2 +static int getline_error; + + +#define GZIP_CMD "gzip" /* command to run as gzip */ +#define COMPRESS_CMD "compress" /* command to run as compress */ +#define BZIP2_CMD "bzip2" /* command to run as bzip2 */ + +/* + * Format specific routine table - MUST BE IN SORTED ORDER BY NAME + * (see pax.h for description of each function) + * + * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read, + * read, end_read, st_write, write, end_write, trail, + * rd_data, wr_data, options + */ + +const FSUB fsub[] = { +/* OLD BINARY CPIO */ + {"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd, + bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* OLD OCTAL CHARACTER CPIO */ + {"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd, + cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* POSIX 3 PAX */ + {"pax", 5120, BLKMULT, 0, 1, BLKMULT, 0, pax_id, ustar_strd, + pax_rd, tar_endrd, ustar_stwr, pax_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, pax_opt}, + +/* SVR4 HEX CPIO */ + {"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd, + vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* SVR4 HEX CPIO WITH CRC */ + {"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd, + vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* OLD TAR */ + {"tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op, + tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, tar_opt}, + +/* POSIX USTAR */ + {"ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd, + ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, bad_opt}, +}; +#define F_OCPIO 0 /* format when called as cpio -6 */ +#define F_ACPIO 1 /* format when called as cpio -c */ +#define F_PAX 2 /* -x pax */ +#define F_SCPIO 3 /* -x sv4cpio */ +#define F_CPIO 4 /* format when called as cpio */ +#define F_OTAR 5 /* format when called as tar -o */ +#define F_TAR 6 /* format when called as tar */ +#define DEFLT F_TAR /* default write format from list above */ + +/* + * ford is the archive search order used by get_arc() to determine what kind + * of archive we are dealing with. This helps to properly id archive formats + * some formats may be subsets of others.... + */ +int ford[] = {F_PAX, F_TAR, F_OTAR, F_CPIO, F_SCPIO, F_ACPIO, F_OCPIO, -1 }; + +/* + * Do we have -C anywhere? + */ +int havechd = 0; + +/* + * options() + * figure out if we are pax, tar or cpio. Call the appropriate options + * parser + */ + +void +options(int argc, char **argv) +{ + + /* + * Are we acting like pax, tar or cpio (based on argv[0]) + */ + if ((argv0 = strrchr(argv[0], '/')) != NULL) + argv0++; + else + argv0 = argv[0]; + + if (strcmp(NM_TAR, argv0) == 0) { + tar_options(argc, argv); + return; + } else if (strcmp(NM_CPIO, argv0) == 0) { + cpio_options(argc, argv); + return; + } + /* + * assume pax as the default + */ + argv0 = NM_PAX; + pax_options(argc, argv); +} + +#define OPT_INSECURE 1 +struct option pax_longopts[] = { + { "insecure", no_argument, 0, OPT_INSECURE }, + { 0, 0, 0, 0 }, +}; + +/* + * pax_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +static void +pax_options(int argc, char **argv) +{ + int c; + size_t i; + unsigned int flg = 0; + unsigned int bflg = 0; + char *pt; + FSUB tmp; + size_t n_fsub; + char * tmp_name; + + listf = stderr; + /* + * process option flags + */ + while ((c=getopt_long(argc,argv,"0ab:cdf:ijklno:p:rs:tuvwx:zB:DE:G:HLOPT:U:XYZ", pax_longopts, NULL)) != -1) { + switch (c) { + case '0': + /* + * Use \0 as pathname terminator. + * (For use with the -print0 option of find(1).) + */ + zeroflag = 1; + flg |= C0F; + break; + case 'a': + /* + * append + */ + flg |= AF; + break; + case 'b': + /* + * specify blocksize + */ + flg |= BF; + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid block size %s", optarg); + pax_usage(); + } + break; + case 'c': + /* + * inverse match on patterns + */ + cflag = 1; + flg |= CF; + break; + case 'd': + /* + * match only dir on extract, not the subtree at dir + */ + dflag = 1; + flg |= DF; + break; + case 'f': + /* + * filename where the archive is stored + */ + if ((optarg[0] == '-') && (optarg[1]== '\0')) { + /* + * treat a - as stdin (like tar) + */ + arcname = NULL; + break; + } + arcname = optarg; + flg |= FF; + break; + case 'i': + /* + * interactive file rename + */ + iflag = 1; + flg |= IF; + break; + case 'j': + /* + * use bzip2. Non standard option. + */ + gzip_program = BZIP2_CMD; + break; + case 'k': + /* + * do not clobber files that exist + */ + kflag = 1; + flg |= KF; + break; + case 'l': + /* + * try to link src to dest with copy (-rw) + */ + lflag = 1; + flg |= LF; + break; + case 'n': + /* + * select first match for a pattern only + */ + nflag = 1; + flg |= NF; + break; + case 'o': + /* + * pass format specific options + */ + flg |= OF; + if (pax_format_opt_add(optarg) < 0) + pax_usage(); + break; + case 'p': + /* + * specify file characteristic options + */ + for (pt = optarg; *pt != '\0'; ++pt) { + switch (*pt) { + case 'a': + /* + * do not preserve access time + */ + patime = 0; + break; + case 'e': + /* + * preserve user id, group id, file + * mode, access/modification times + */ + pids = 1; + pmode = 1; + patime = 1; + pmtime = 1; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'o': + /* + * preserve uid/gid + */ + pids = 1; + break; + case 'p': + /* + * preserve file mode bits + */ + pmode = 1; + break; + default: + paxwarn(1, "Invalid -p string: %c", *pt); + pax_usage(); + break; + } + } + flg |= PF; + break; + case 'r': + /* + * read the archive + */ + pax_read_or_list_mode=1; + flg |= RF; + break; + case 's': + /* + * file name substitution name pattern + */ + if (rep_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= SF; + break; + case 't': + /* + * preserve access time on filesystem nodes we read + */ + tflag = 1; + flg |= TF; + break; + case 'u': + /* + * ignore those older files + */ + uflag = 1; + flg |= UF; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + flg |= VF; + break; + case 'w': + /* + * write an archive + */ + flg |= WF; + break; + case 'x': + /* + * specify an archive format on write + */ + tmp.name = optarg; + n_fsub = sizeof(fsub)/sizeof(FSUB); + if ((frmt = (FSUB *)bsearch(&tmp, fsub, n_fsub, sizeof(FSUB), + c_frmt)) != NULL) { + flg |= XF; + break; + } + paxwarn(1, "Unknown -x format: %s", optarg); + (void)fputs("pax: Known -x formats are:", stderr); + for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) + (void)fprintf(stderr, " %s", fsub[i].name); + (void)fputs("\n\n", stderr); + pax_usage(); + break; + case 'z': + /* + * use gzip. Non standard option. + */ + gzip_program = GZIP_CMD; + break; + case 'B': + /* + * non-standard option on number of bytes written on a + * single archive volume. + */ + if ((wrlimit = str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid write limit %s", optarg); + pax_usage(); + } + if (wrlimit % BLKMULT) { + paxwarn(1, "Write limit is not a %d byte multiple", + BLKMULT); + pax_usage(); + } + flg |= CBF; + break; + case 'D': + /* + * On extraction check file inode change time before the + * modification of the file name. Non standard option. + */ + Dflag = 1; + flg |= CDF; + break; + case 'E': + /* + * non-standard limit on read faults + * 0 indicates stop after first error, values + * indicate a limit, "NONE" try forever + */ + flg |= CEF; + if (strcmp(NONE, optarg) == 0) + maxflt = -1; + else if ((maxflt = atoi(optarg)) < 0) { + paxwarn(1, "Error count value must be positive"); + pax_usage(); + } + break; + case 'G': + /* + * non-standard option for selecting files within an + * archive by group (gid or name) + */ + if (grp_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CGF; + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + flg |= CHF; + Lflag = 0; /* -H and -L are mutually exclusive */ + flg &= ~CLF; /* only use the last one seen */ + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + flg |= CLF; + Hflag = 0; /* -H and -L are mutually exclusive */ + flg &= ~CHF; /* only use the last one seen */ + break; + case 'O': + /* + * Force one volume. Non standard option. + */ + force_one_volume = 1; + break; + case 'P': + /* + * do NOT follow symlinks (default) + */ + Lflag = 0; + flg |= CPF; + break; + case 'T': + /* + * non-standard option for selecting files within an + * archive by modification time range (lower,upper) + */ + if (trng_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CTF; + break; + case 'U': + /* + * non-standard option for selecting files within an + * archive by user (uid or name) + */ + if (usr_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CUF; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + flg |= CXF; + break; + case 'Y': + /* + * On extraction check file inode change time after the + * modification of the file name. Non standard option. + */ + Yflag = 1; + flg |= CYF; + break; + case 'Z': + /* + * On extraction check modification time after the + * modification of the file name. Non standard option. + */ + Zflag = 1; + flg |= CZF; + break; + case OPT_INSECURE: + secure = 0; + break; + default: + pax_usage(); + break; + } + } + + /* + * Fix for POSIX.cmd/pax/pax.ex test 132: force -wu options to look + * like -wua options were specified. + */ + if (uflag && (flg & WF) && !(flg & RF)) { /* -w but not -r -w */ + flg |= AF; + } + + /* + * figure out the operation mode of pax read,write,extract,copy,append + * or list. check that we have not been given a bogus set of flags + * for the operation mode. + */ + if (ISLIST(flg)) { + act = LIST; + pax_read_or_list_mode=1; + listf = stdout; + bflg = flg & BDLIST; + } else if (ISEXTRACT(flg)) { + act = EXTRACT; + bflg = flg & BDEXTR; + } else if (ISARCHIVE(flg)) { + act = ARCHIVE; + bflg = flg & BDARCH; + } else if (ISAPPND(flg)) { + act = APPND; + bflg = flg & BDARCH; + } else if (ISCOPY(flg)) { + act = COPY; + bflg = flg & BDCOPY; + } else + pax_usage(); + if (bflg) { + printflg(flg); + pax_usage(); + } + + /* + * if we are writing (ARCHIVE) we use the default format if the user + * did not specify a format. when we write during an APPEND, we will + * adopt the format of the existing archive if none was supplied. + */ + if (!(flg & XF) && (act == ARCHIVE)) + frmt = &(fsub[DEFLT]); + + /* + * if copying (-r and -w) and there is no -x specified, we act as + * if -x pax was specified. + */ + if (!(flg & XF) && (act == COPY)) + frmt = &(fsub[F_PAX]); + + /* + * Initialize the global extended header template. + */ + tmp_name = getenv("TMPDIR"); + if (tmp_name) { + asprintf(&header_name_g, "%s%s", tmp_name, "/GlobalHead.%p.%n"); + } else { + header_name_g = "/tmp/GlobalHead.%p.%n"; + } + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + for (; optind < argc; optind++) + if (pat_add(argv[optind], NULL) < 0) + pax_usage(); + break; + case COPY: + if (optind >= argc) { + paxwarn(0, "Destination directory was not supplied"); + pax_usage(); + } + --argc; + dirptr = argv[argc]; + /* FALL THROUGH */ + case ARCHIVE: + case APPND: + for (; optind < argc; optind++) + if (ftree_add(argv[optind], 0) < 0) + pax_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } +} + + +/* + * tar_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +static void +tar_options(int argc, char **argv) +{ + int c; + int fstdin = 0; + int Oflag = 0; + int nincfiles = 0; + int incfiles_max = 0; + struct incfile { + char *file; + char *dir; + }; + struct incfile *incfiles = NULL; + + /* + * Set default values. + */ + rmleadslash = 1; + + /* + * process option flags + */ + while ((c = getoldopt(argc, argv, + "b:cef:hjmopqruts:vwxzBC:HI:LOPXZ014578")) != -1) { + switch (c) { + case 'b': + /* + * specify blocksize in 512-byte blocks + */ + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid block size %s", optarg); + tar_usage(); + } + wrblksz *= 512; /* XXX - check for int oflow */ + break; + case 'c': + /* + * create an archive + */ + act = ARCHIVE; + break; + case 'e': + /* + * stop after first error + */ + maxflt = 0; + break; + case 'f': + /* + * filename where the archive is stored + */ + if ((optarg[0] == '-') && (optarg[1]== '\0')) { + /* + * treat a - as stdin + */ + fstdin = 1; + arcname = NULL; + break; + } + fstdin = 0; + arcname = optarg; + break; + case 'h': + /* + * follow symlinks + */ + Lflag = 1; + break; + case 'j': + /* + * use bzip2. Non standard option. + */ + gzip_program = BZIP2_CMD; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'O': + Oflag = 1; + break; + case 'o': + Oflag = 2; + break; + case 'p': + /* + * preserve uid/gid and file mode, regardless of umask + */ + pmode = 1; + pids = 1; + break; + case 'q': + /* + * select first match for a pattern only + */ + nflag = 1; + break; + case 'r': + case 'u': + /* + * append to the archive + */ + act = APPND; + break; + case 's': + /* + * file name substitution name pattern + */ + if (rep_add(optarg) < 0) { + tar_usage(); + break; + } + break; + case 't': + /* + * list contents of the tape + */ + act = LIST; + break; + case 'v': + /* + * verbose operation mode + */ + vflag++; + break; + case 'w': + /* + * interactive file rename + */ + iflag = 1; + break; + case 'x': + /* + * extract an archive, preserving mode, + * and mtime if possible. + */ + act = EXTRACT; + pmtime = 1; + break; + case 'z': + /* + * use gzip. Non standard option. + */ + gzip_program = GZIP_CMD; + break; + case 'B': + /* + * Nothing to do here, this is pax default + */ + break; + case 'C': + havechd++; + chdname = optarg; + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + break; + case 'I': + if (++nincfiles > incfiles_max) { + incfiles_max = nincfiles + 3; + incfiles = realloc(incfiles, + sizeof(*incfiles) * incfiles_max); + if (incfiles == NULL) { + paxwarn(0, "Unable to allocate space " + "for option list"); + exit(1); + } + } + incfiles[nincfiles - 1].file = optarg; + incfiles[nincfiles - 1].dir = chdname; + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + break; + case 'P': + /* + * do not remove leading '/' from pathnames + */ + rmleadslash = 0; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + break; + case 'Z': + /* + * use compress. + */ + gzip_program = COMPRESS_CMD; + break; + case '0': + arcname = DEV_0; + break; + case '1': + arcname = DEV_1; + break; + case '4': + arcname = DEV_4; + break; + case '5': + arcname = DEV_5; + break; + case '7': + arcname = DEV_7; + break; + case '8': + arcname = DEV_8; + break; + default: + tar_usage(); + break; + } + } + argc -= optind; + argv += optind; + + /* Traditional tar behaviour (pax uses stderr unless in list mode) */ + if (fstdin == 1 && act == ARCHIVE) + listf = stderr; + else + listf = stdout; + + /* Traditional tar behaviour (pax wants to read file list from stdin) */ + if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0) + exit(0); + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + default: + { + int sawpat = 0; + char *file, *dir = NULL; + + while (nincfiles || *argv != NULL) { + /* + * If we queued up any include files, + * pull them in now. Otherwise, check + * for -I and -C positional flags. + * Anything else must be a file to + * extract. + */ + if (nincfiles) { + file = incfiles->file; + dir = incfiles->dir; + incfiles++; + nincfiles--; + } else if (strcmp(*argv, "-I") == 0) { + if (*++argv == NULL) + break; + file = *argv++; + dir = chdname; + } else + file = NULL; + if (file != NULL) { + FILE *fp; + char *str; + + if (strcmp(file, "-") == 0) + fp = stdin; + else if ((fp = fopen(file, "r")) == NULL) { + paxwarn(1, "Unable to open file '%s' for read", file); + tar_usage(); + } + while ((str = pax_getline(fp)) != NULL) { + if (pat_add(str, dir) < 0) + tar_usage(); + sawpat = 1; + } + if (strcmp(file, "-") != 0) + fclose(fp); + if (getline_error) { + paxwarn(1, "Problem with file '%s'", file); + tar_usage(); + } + } else if (strcmp(*argv, "-C") == 0) { + if (*++argv == NULL) + break; + chdname = *argv++; + havechd++; + } else if (pat_add(*argv++, chdname) < 0) + tar_usage(); + else + sawpat = 1; + } + /* + * if patterns were added, we are doing chdir() + * on a file-by-file basis, else, just one + * global chdir (if any) after opening input. + */ + if (sawpat > 0) + chdname = NULL; + } + break; + case ARCHIVE: + case APPND: + frmt = &(fsub[Oflag ? F_OTAR : F_TAR]); + + if (Oflag == 2 && opt_add("write_opt=nodir") < 0) + tar_usage(); + + if (chdname != NULL) { /* initial chdir() */ + if (ftree_add(chdname, 1) < 0) + tar_usage(); + } + + while (nincfiles || *argv != NULL) { + char *file, *dir = NULL; + + /* + * If we queued up any include files, pull them in + * now. Otherwise, check for -I and -C positional + * flags. Anything else must be a file to include + * in the archive. + */ + if (nincfiles) { + file = incfiles->file; + dir = incfiles->dir; + incfiles++; + nincfiles--; + } else if (strcmp(*argv, "-I") == 0) { + if (*++argv == NULL) + break; + file = *argv++; + dir = NULL; + } else + file = NULL; + if (file != NULL) { + FILE *fp; + char *str; + + /* Set directory if needed */ + if (dir) { + if (ftree_add(dir, 1) < 0) + tar_usage(); + } + + if (strcmp(file, "-") == 0) + fp = stdin; + else if ((fp = fopen(file, "r")) == NULL) { + paxwarn(1, "Unable to open file '%s' for read", file); + tar_usage(); + } + while ((str = pax_getline(fp)) != NULL) { + if (ftree_add(str, 0) < 0) + tar_usage(); + } + if (strcmp(file, "-") != 0) + fclose(fp); + if (getline_error) { + paxwarn(1, "Problem with file '%s'", + file); + tar_usage(); + } + } else if (strcmp(*argv, "-C") == 0) { + if (*++argv == NULL) + break; + if (ftree_add(*argv++, 1) < 0) + tar_usage(); + havechd++; + } else if (ftree_add(*argv++, 0) < 0) + tar_usage(); + } + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } + if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) { + arcname = getenv("TAPE"); + if ((arcname == NULL) || (*arcname == '\0')) + arcname = _PATH_DEFTAPE; + } +} + +int mkpath(char *); + +int +mkpath(path) + char *path; +{ + struct stat sb; + char *slash; + int done = 0; + + slash = path; + + while (!done) { + slash += strspn(slash, "/"); + slash += strcspn(slash, "/"); + + done = (*slash == '\0'); + *slash = '\0'; + + if (stat(path, &sb)) { + if (errno != ENOENT || mkdir(path, 0777)) { + paxwarn(1, "%s", path); + return (-1); + } + } else if (!S_ISDIR(sb.st_mode)) { + syswarn(1, ENOTDIR, "%s", path); + return (-1); + } + + if (!done) + *slash = '/'; + } + + return (0); +} +/* + * cpio_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +static void +cpio_options(int argc, char **argv) +{ + int c, i; + char *str; + FSUB tmp; + FILE *fp; + size_t n_fsub; + + kflag = 1; + pids = 1; + pmode = 1; + pmtime = 0; + arcname = NULL; + dflag = 1; + act = -1; + nodirs = 1; + while ((c=getopt(argc,argv,"abcdfijklmoprstuvzABC:E:F:H:I:LO:SZ6")) != -1) + switch (c) { + case 'a': + /* + * preserve access time on files read + */ + tflag = 1; + break; + case 'b': + /* + * swap bytes and half-words when reading data + */ + break; + case 'c': + /* + * ASCII cpio header + */ + frmt = &(fsub[F_ACPIO]); + break; + case 'd': + /* + * create directories as needed + */ + nodirs = 0; + break; + case 'f': + /* + * invert meaning of pattern list + */ + cflag = 1; + break; + case 'i': + /* + * restore an archive + */ + act = EXTRACT; + break; + case 'j': + /* + * use bzip2. Non standard option. + */ + gzip_program = BZIP2_CMD; + break; + case 'k': + break; + case 'l': + /* + * use links instead of copies when possible + */ + lflag = 1; + break; + case 'm': + /* + * preserve modification time + */ + pmtime = 1; + break; + case 'o': + /* + * create an archive + */ + act = ARCHIVE; + frmt = &(fsub[F_CPIO]); + break; + case 'p': + /* + * copy-pass mode + */ + act = COPY; + break; + case 'r': + /* + * interactively rename files + */ + iflag = 1; + break; + case 's': + /* + * swap bytes after reading data + */ + break; + case 't': + /* + * list contents of archive + */ + act = LIST; + listf = stdout; + break; + case 'u': + /* + * replace newer files + */ + kflag = 0; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + break; + case 'z': + /* + * use gzip. Non standard option. + */ + gzip_program = GZIP_CMD; + break; + case 'A': + /* + * append mode + */ + act = APPND; + break; + case 'B': + /* + * Use 5120 byte block size + */ + wrblksz = 5120; + break; + case 'C': + /* + * set block size in bytes + */ + wrblksz = atoi(optarg); + break; + case 'E': + /* + * file with patterns to extract or list + */ + if ((fp = fopen(optarg, "r")) == NULL) { + paxwarn(1, "Unable to open file '%s' for read", optarg); + cpio_usage(); + } + while ((str = pax_getline(fp)) != NULL) { + pat_add(str, NULL); + } + fclose(fp); + if (getline_error) { + paxwarn(1, "Problem with file '%s'", optarg); + cpio_usage(); + } + break; + case 'F': + case 'I': + case 'O': + /* + * filename where the archive is stored + */ + if ((optarg[0] == '-') && (optarg[1]== '\0')) { + /* + * treat a - as stdin + */ + arcname = NULL; + break; + } + arcname = optarg; + break; + case 'H': + /* + * specify an archive format on write + */ + tmp.name = optarg; + n_fsub = sizeof(fsub)/sizeof(FSUB); + if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, + n_fsub, sizeof(FSUB), c_frmt)) != NULL) + break; + paxwarn(1, "Unknown -H format: %s", optarg); + (void)fputs("cpio: Known -H formats are:", stderr); + for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) + (void)fprintf(stderr, " %s", fsub[i].name); + (void)fputs("\n\n", stderr); + cpio_usage(); + break; + case 'L': + /* + * follow symbolic links + */ + Lflag = 1; + break; + case 'S': + /* + * swap halfwords after reading data + */ + break; + case 'Z': + /* + * use compress. Non standard option. + */ + gzip_program = COMPRESS_CMD; + break; + case '6': + /* + * process Version 6 cpio format + */ + frmt = &(fsub[F_OCPIO]); + break; + case '?': + default: + cpio_usage(); + break; + } + argc -= optind; + argv += optind; + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + while (*argv != NULL) + if (pat_add(*argv++, NULL) < 0) + cpio_usage(); + break; + case COPY: + if (*argv == NULL) { + paxwarn(0, "Destination directory was not supplied"); + cpio_usage(); + } + dirptr = *argv; + if (mkpath(dirptr) < 0) + cpio_usage(); + --argc; + ++argv; + /* FALL THROUGH */ + case ARCHIVE: + case APPND: + if (*argv != NULL) + cpio_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + while ((str = pax_getline(stdin)) != NULL) { + ftree_add(str, 0); + } + if (getline_error) { + paxwarn(1, "Problem while reading stdin"); + cpio_usage(); + } + break; + default: + cpio_usage(); + break; + } +} + +/* + * printflg() + * print out those invalid flag sets found to the user + */ + +static void +printflg(unsigned int flg) +{ + int nxt; + int pos = 0; + + (void)fprintf(stderr,"%s: Invalid combination of options:", argv0); + while ((nxt = ffs(flg)) != 0) { + flg = flg >> nxt; + pos += nxt; + (void)fprintf(stderr, " -%c", flgch[pos-1]); + } + (void)putc('\n', stderr); +} + +/* + * c_frmt() + * comparison routine used by bsearch to find the format specified + * by the user + */ + +static int +c_frmt(const void *a, const void *b) +{ + return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name)); +} + +/* + * opt_next() + * called by format specific options routines to get each format specific + * flag and value specified with -o + * Return: + * pointer to next OPLIST entry or NULL (end of list). + */ + +OPLIST * +opt_next(void) +{ + OPLIST *opt; + + if ((opt = ophead) != NULL) + ophead = ophead->fow; + return(opt); +} + +/* + * bad_opt() + * generic routine used to complain about a format specific options + * when the format does not support options. + */ + +int +bad_opt(void) +{ + OPLIST *opt; + + if (ophead == NULL) + return(0); + /* + * print all we were given + */ + paxwarn(1,"These format options are not supported"); + while ((opt = opt_next()) != NULL) { + if (opt->separator == SEP_EQ) { + (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); + } else if (opt->separator == SEP_COLONEQ ) { + (void)fprintf(stderr, "\t%s := %s\n", opt->name, opt->value); + } else { /* SEP_NONE */ + (void)fprintf(stderr, "\t%s\n", opt->name); + } + } + pax_usage(); + return(0); +} + +/* + * opt_add() + * breaks the value supplied to -o into an option name and value. Options + * are given to -o in the form -o name-value,name=value + * multiple -o may be specified. + * Return: + * 0 if format in name=value format, -1 if -o is passed junk. + */ + +int +opt_add(const char *str) +{ + OPLIST *opt; + char *frpt; + char *pt; + char *dstr; + char *endpt; + + if ((str == NULL) || (*str == '\0')) { + paxwarn(0, "Invalid option name"); + return(-1); + } + if ((dstr = strdup(str)) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + return(-1); + } + frpt = dstr; + + /* + * break into name and values pieces and stuff each one into a + * OPLIST structure. When we know the format, the format specific + * option function will go through this list + */ + while ((frpt != NULL) && (*frpt != '\0')) { + if ((endpt = strchr(frpt, ',')) != NULL) + *endpt = '\0'; + if ((pt = strchr(frpt, '=')) == NULL) { + paxwarn(0, "Invalid options format"); + free(dstr); + return(-1); + } + if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + free(dstr); + return(-1); + } + *pt++ = '\0'; + opt->name = frpt; + opt->value = pt; + opt->separator = SEP_EQ; + opt->fow = NULL; + if (endpt != NULL) + frpt = endpt + 1; + else + frpt = NULL; + if (ophead == NULL) { + optail = ophead = opt; + continue; + } + optail->fow = opt; + optail = opt; + } + return(0); +} + +/* + * pax_format_opt_add() + * breaks the value supplied to -o into a option name and value. options + * are given to -o in the form -o name-value,name=value + * multiple -o may be specified. + * Return: + * 0 if format in name=value format, -1 if -o is passed junk + */ + +int +pax_format_opt_add(register char *str) +{ + register OPLIST *opt; + register char *frpt; + register char *pt; + register char *endpt; + register int separator; + + if ((str == NULL) || (*str == '\0')) { + paxwarn(0, "Invalid option name"); + return(-1); + } + if ((str = strdup(str)) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + return(-1); + } + frpt = str; + + /* + * break into name and values pieces and stuff each one into a + * OPLIST structure. When we know the format, the format specific + * option function will go through this list + */ + while ((frpt != NULL) && (*frpt != '\0')) { + if ((endpt = strchr(frpt, ',')) != NULL) + *endpt = '\0'; + if ((pt = strstr(frpt, ":=")) != NULL) { + *pt++ = '\0'; + pt++; /* beyond the := */ + separator = SEP_COLONEQ; + } else if ((pt = strchr(frpt, '=')) != NULL) { + *pt++ = '\0'; + separator = SEP_EQ; + } else { + /* keyword with no value */ + separator = SEP_NONE; + } + if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + free(str); + return(-1); + } + opt->name = frpt; + opt->value = pt; + opt->separator = separator; + opt->fow = NULL; + if (endpt != NULL) + frpt = endpt + 1; + else + frpt = NULL; + if (ophead == NULL) { + optail = ophead = opt; + continue; + } + optail->fow = opt; + optail = opt; + } + return(0); +} + +/* + * str_offt() + * Convert an expression of the following forms to an off_t > 0. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * separated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + * Return: + * 0 for an error, a positive value o.w. + */ + +static off_t +str_offt(char *val) +{ + char *expr; + off_t num, t; + +# ifdef LONG_OFF_T + num = strtol(val, &expr, 0); + if ((num == LONG_MAX) || (num <= 0) || (expr == val)) +# else + num = strtoq(val, &expr, 0); + if ((num == QUAD_MAX) || (num <= 0) || (expr == val)) +# endif + return(0); + + switch (*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + return(0); + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + return(0); + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + return(0); + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + return(0); + ++expr; + break; + } + + switch (*expr) { + case '\0': + break; + case '*': + case 'x': + t = num; + num *= str_offt(expr + 1); + if (t > num) + return(0); + break; + default: + return(0); + } + return(num); +} + +char * +pax_getline(FILE *f) +{ + char *name, *temp; + size_t len; + + name = fgetln(f, &len); + if (!name) { + getline_error = ferror(f) ? GETLINE_FILE_CORRUPT : 0; + return(0); + } + if (name[len-1] != '\n') + len++; + temp = malloc(len); + if (!temp) { + getline_error = GETLINE_OUT_OF_MEM; + return(0); + } + memcpy(temp, name, len-1); + temp[len-1] = 0; + return(temp); +} + +/* + * no_op() + * for those option functions where the archive format has nothing to do. + * Return: + * 0 + */ + +static int +no_op(void) +{ + return(0); +} + +/* + * pax_usage() + * print the usage summary to the user + */ + +void +pax_usage(void) +{ + (void)fputs( + "usage: pax [-0cdjnOvz] [-E limit] [-f archive] [-G group] [-s replstr]\n" + " [-T range] [-U user] [--insecure] [pattern ...]\n" + " pax -r [-0cDdijknOuvYZz] [-E limit] [-f archive] [-G group] [-o options]\n" + " [-p string] [-s replstr] [-T range] [-U user] [--insecure] [pattern ...]\n" + " pax -w [-0adHijLOPtuvXz] [-B bytes] [-b blocksize] [-f archive]\n" + " [-G group] [-o options] [-s replstr] [-T range] [-U user]\n" + " [-x format] [--insecure] [file ...]\n" + " pax -rw [-0DdHikLlnOPtuvXYZ] [-G group] [-p string] [-s replstr]\n" + " [-T range] [-U user] [--insecure] [file ...] directory\n", + stderr); + exit(1); +} + +/* + * tar_usage() + * print the usage summary to the user + */ + +void +tar_usage(void) +{ + (void)fputs( + "usage: tar {crtux}[014578befHhjLmOoPpqsvwXZz]\n" + " [blocking-factor | archive | replstr] [-C directory] [-I file]\n" + " [file ...]\n" + " tar {-crtux} [-014578eHhjLmOoPpqvwXZz] [-b blocking-factor]\n" + " [-C directory] [-f archive] [-I file] [-s replstr] [file ...]\n", + stderr); + exit(1); +} + +/* + * cpio_usage() + * print the usage summary to the user + */ + +void +cpio_usage(void) +{ + (void)fputs( + "usage: cpio -o [-AaBcjLvZz] [-C bytes] [-F archive] [-H format]\n" + " [-O archive] < name-list [> archive]\n" + " cpio -i [-6BbcdfjmrSstuvZz] [-C bytes] [-E file] [-F archive] [-H format]\n" + " [-I archive] [pattern ...] [< archive]\n" + " cpio -p [-adLlmuv] destination-directory < name-list\n", + stderr); + exit(1); +} diff --git a/file_cmds/pax/options.h b/file_cmds/pax/options.h new file mode 100644 index 00000000..873d8d55 --- /dev/null +++ b/file_cmds/pax/options.h @@ -0,0 +1,113 @@ +/* $OpenBSD: options.h,v 1.4 2003/06/13 17:51:14 millert Exp $ */ +/* $NetBSD: options.h,v 1.3 1995/03/21 09:07:32 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * argv[0] names. Used for tar and cpio emulation + */ + +#define NM_TAR "tar" +#define NM_CPIO "cpio" +#define NM_PAX "pax" + +/* + * Constants used to specify the legal sets of flags in pax. For each major + * operation mode of pax, a set of illegal flags is defined. If any one of + * those illegal flags are found set, we scream and exit + */ +#define NONE "none" + +/* + * flags (one for each option). + */ +#define AF 0x00000001 +#define BF 0x00000002 +#define CF 0x00000004 +#define DF 0x00000008 +#define FF 0x00000010 +#define IF 0x00000020 +#define KF 0x00000040 +#define LF 0x00000080 +#define NF 0x00000100 +#define OF 0x00000200 +#define PF 0x00000400 +#define RF 0x00000800 +#define SF 0x00001000 +#define TF 0x00002000 +#define UF 0x00004000 +#define VF 0x00008000 +#define WF 0x00010000 +#define XF 0x00020000 +#define CBF 0x00040000 /* nonstandard extension */ +#define CDF 0x00080000 /* nonstandard extension */ +#define CEF 0x00100000 /* nonstandard extension */ +#define CGF 0x00200000 /* nonstandard extension */ +#define CHF 0x00400000 /* nonstandard extension */ +#define CLF 0x00800000 /* nonstandard extension */ +#define CPF 0x01000000 /* nonstandard extension */ +#define CTF 0x02000000 /* nonstandard extension */ +#define CUF 0x04000000 /* nonstandard extension */ +#define CXF 0x08000000 +#define CYF 0x10000000 /* nonstandard extension */ +#define CZF 0x20000000 /* nonstandard extension */ +#define C0F 0x40000000 /* nonstandard extension */ + +/* + * ascii string indexed by bit position above (alter the above and you must + * alter this string) used to tell the user what flags caused us to complain + */ +#define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ0" + +/* + * legal pax operation bit patterns + */ + +#define ISLIST(x) (((x) & (RF|WF)) == 0) +#define ISEXTRACT(x) (((x) & (RF|WF)) == RF) +#define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF) +#define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF)) +#define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF)) +#define ISWRITE(x) (((x) & (RF|WF)) == WF) + +/* + * Illegal option flag subsets based on pax operation + */ + +#define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF) +#define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF) +#define BDCOPY (AF|BF|FF|CBF|CEF) +#define BDLIST (AF|BF|IF|KF|LF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF) diff --git a/file_cmds/pax/pat_rep.c b/file_cmds/pax/pat_rep.c new file mode 100644 index 00000000..ff2819e2 --- /dev/null +++ b/file_cmds/pax/pat_rep.c @@ -0,0 +1,1062 @@ +/* $OpenBSD: pat_rep.c,v 1.30 2005/08/05 08:30:10 djm Exp $ */ +/* $NetBSD: pat_rep.c,v 1.4 1995/03/21 09:07:33 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: pat_rep.c,v 1.30 2005/08/05 08:30:10 djm Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "pat_rep.h" +#include "extern.h" + +/* + * routines to handle pattern matching, name modification (regular expression + * substitution and interactive renames), and destination name modification for + * copy (-rw). Both file name and link names are adjusted as required in these + * routines. + */ + +#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ +static PATTERN *pathead = NULL; /* file pattern match list head */ +static PATTERN *pattail = NULL; /* file pattern match list tail */ +static REPLACE *rephead = NULL; /* replacement string list head */ +static REPLACE *reptail = NULL; /* replacement string list tail */ + +static int rep_name(char *, size_t, int *, int); +int tty_rename(ARCHD *); +static int fix_path(char *, int *, char *, int); +static int fn_match(char *, char *, char **); +static char * range_match(char *, int); +static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); + +/* + * rep_add() + * parses the -s replacement string; compiles the regular expression + * and stores the compiled value and it's replacement string together in + * replacement string list. Input to this function is of the form: + * /old/new/pg + * The first char in the string specifies the delimiter used by this + * replacement string. "Old" is a regular expression in "ed" format which + * is compiled by regcomp() and is applied to filenames. "new" is the + * substitution string; p and g are options flags for printing and global + * replacement (over the single filename) + * Return: + * 0 if a proper replacement string and regular expression was added to + * the list of replacement patterns; -1 otherwise. + */ + +int +rep_add(char *str) +{ + char *pt1; + char *pt2; + REPLACE *rep; + int res; + char rebuf[BUFSIZ]; + + /* + * throw out the bad parameters + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty replacement string"); + return(-1); + } + + /* + * first character in the string specifies what the delimiter is for + * this expression + */ + for (pt1 = str+1; *pt1; pt1++) { + if (*pt1 == '\\') { + pt1++; + continue; + } + if (*pt1 == *str) + break; + } + if (*pt1 == '\0') { + paxwarn(1, "Invalid replacement string %s", str); + return(-1); + } + + /* + * allocate space for the node that handles this replacement pattern + * and split out the regular expression and try to compile it + */ + if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { + paxwarn(1, "Unable to allocate memory for replacement string"); + return(-1); + } + + *pt1 = '\0'; + if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { + regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); + paxwarn(1, "%s while compiling regular expression %s", rebuf, str); + (void)free((char *)rep); + return(-1); + } + + /* + * put the delimiter back in case we need an error message and + * locate the delimiter at the end of the replacement string + * we then point the node at the new substitution string + */ + *pt1++ = *str; + for (pt2 = pt1; *pt2; pt2++) { + if (*pt2 == '\\') { + pt2++; + continue; + } + if (*pt2 == *str) + break; + } + if (*pt2 == '\0') { + regfree(&(rep->rcmp)); + (void)free((char *)rep); + paxwarn(1, "Invalid replacement string %s", str); + return(-1); + } + + *pt2 = '\0'; + rep->nstr = pt1; + pt1 = pt2++; + rep->flgs = 0; + + /* + * set the options if any + */ + while (*pt2 != '\0') { + switch (*pt2) { + case 'g': + case 'G': + rep->flgs |= GLOB; + break; + case 'p': + case 'P': + rep->flgs |= PRNT; + break; + default: + regfree(&(rep->rcmp)); + (void)free((char *)rep); + *pt1 = *str; + paxwarn(1, "Invalid replacement string option %s", str); + return(-1); + } + ++pt2; + } + + /* + * all done, link it in at the end + */ + rep->fow = NULL; + if (rephead == NULL) { + reptail = rephead = rep; + return(0); + } + reptail->fow = rep; + reptail = rep; + return(0); +} + +/* + * pat_add() + * add a pattern match to the pattern match list. Pattern matches are used + * to select which archive members are extracted. (They appear as + * arguments to pax in the list and read modes). If no patterns are + * supplied to pax, all members in the archive will be selected (and the + * pattern match list is empty). + * Return: + * 0 if the pattern was added to the list, -1 otherwise + */ + +int +pat_add(char *str, char *chdname) +{ + PATTERN *pt; + + /* + * throw out the junk + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty pattern string"); + return(-1); + } + + /* + * allocate space for the pattern and store the pattern. the pattern is + * part of argv so do not bother to copy it, just point at it. Add the + * node to the end of the pattern list + */ + if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { + paxwarn(1, "Unable to allocate memory for pattern string"); + return(-1); + } + + pt->pstr = str; + pt->pend = NULL; + pt->plen = strlen(str); + pt->fow = NULL; + pt->flgs = 0; + pt->chdname = chdname; + + if (pathead == NULL) { + pattail = pathead = pt; + return(0); + } + pattail->fow = pt; + pattail = pt; + return(0); +} + +/* + * pat_chk() + * complain if any the user supplied pattern did not result in a match to + * a selected archive member. + */ + +void +pat_chk(void) +{ + PATTERN *pt; + int wban = 0; + + /* + * walk down the list checking the flags to make sure MTCH was set, + * if not complain + */ + for (pt = pathead; pt != NULL; pt = pt->fow) { + if (pt->flgs & MTCH) + continue; + if (!wban) { + paxwarn(1, "WARNING! These patterns were not matched:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", pt->pstr); + } +} + +/* + * pat_sel() + * the archive member which matches a pattern was selected. Mark the + * pattern as having selected an archive member. arcn->pat points at the + * pattern that was matched. arcn->pat is set in pat_match() + * + * NOTE: When the -c option is used, we are called when there was no match + * by pat_match() (that means we did match before the inverted sense of + * the logic). Now this seems really strange at first, but with -c we + * need to keep track of those patterns that cause an archive member to NOT + * be selected (it found an archive member with a specified pattern) + * Return: + * 0 if the pattern pointed at by arcn->pat was tagged as creating a + * match, -1 otherwise. + */ + +int +pat_sel(ARCHD *arcn) +{ + PATTERN *pt; + PATTERN **ppt; + int len; + + /* + * if no patterns just return + */ + if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) + return(0); + + /* + * when we are NOT limited to a single match per pattern mark the + * pattern and return + */ + if (!nflag) { + pt->flgs |= MTCH; + return(0); + } + + /* + * we reach this point only when we allow a single selected match per + * pattern, if the pattern matches a directory and we do not have -d + * (dflag) we are done with this pattern. We may also be handed a file + * in the subtree of a directory. in that case when we are operating + * with -d, this pattern was already selected and we are done + */ + if (pt->flgs & DIR_MTCH) + return(0); + + if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { + /* + * ok we matched a directory and we are allowing + * subtree matches but because of the -n only its children will + * match. This is tagged as a DIR_MTCH type. + * WATCH IT, the code assumes that pt->pend points + * into arcn->name and arcn->name has not been modified. + * If not we will have a big mess. Yup this is another kludge + */ + + /* + * if this was a prefix match, remove trailing part of path + * so we can copy it. Future matches will be exact prefix match + */ + if (pt->pend != NULL) + *pt->pend = '\0'; + + if ((pt->pstr = strdup(arcn->name)) == NULL) { + paxwarn(1, "Pattern select out of memory"); + if (pt->pend != NULL) + *pt->pend = '/'; + pt->pend = NULL; + return(-1); + } + + /* + * put the trailing / back in the source string + */ + if (pt->pend != NULL) { + *pt->pend = '/'; + pt->pend = NULL; + } + pt->plen = strlen(pt->pstr); + + /* + * strip off any trailing /, this should really never happen + */ + len = pt->plen - 1; + if (*(pt->pstr + len) == '/') { + *(pt->pstr + len) = '\0'; + pt->plen = len; + } + pt->flgs = DIR_MTCH | MTCH; + arcn->pat = pt; + return(0); + } + + /* + * we are then done with this pattern, so we delete it from the list + * because it can never be used for another match. + * Seems kind of strange to do for a -c, but the pax spec is really + * vague on the interaction of -c, -n and -d. We assume that when -c + * and the pattern rejects a member (i.e. it matched it) it is done. + * In effect we place the order of the flags as having -c last. + */ + pt = pathead; + ppt = &pathead; + while ((pt != NULL) && (pt != arcn->pat)) { + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt == NULL) { + /* + * should never happen.... + */ + paxwarn(1, "Pattern list inconsistent"); + return(-1); + } + *ppt = pt->fow; + (void)free((char *)pt); + arcn->pat = NULL; + return(0); +} + +/* + * pat_match() + * see if this archive member matches any supplied pattern, if a match + * is found, arcn->pat is set to point at the potential pattern. Later if + * this archive member is "selected" we process and mark the pattern as + * one which matched a selected archive member (see pat_sel()) + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + */ + +int +pat_match(ARCHD *arcn) +{ + PATTERN *pt; + + arcn->pat = NULL; + + /* + * if there are no more patterns and we have -n (and not -c) we are + * done. otherwise with no patterns to match, matches all + */ + if (pathead == NULL) { + if (nflag && !cflag) + return(-1); + return(0); + } + + /* + * have to search down the list one at a time looking for a match. + */ + pt = pathead; + while (pt != NULL) { + /* + * check for a file name match unless we have DIR_MTCH set in + * this pattern then we want a prefix match + */ + if (pt->flgs & DIR_MTCH) { + /* + * this pattern was matched before to a directory + * as we must have -n set for this (but not -d). We can + * only match CHILDREN of that directory so we must use + * an exact prefix match (no wildcards). + */ + if ((arcn->name[pt->plen] == '/') && + (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) + break; + } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) + break; + pt = pt->fow; + } + + /* + * return the result, remember that cflag (-c) inverts the sense of a + * match + */ + if (pt == NULL) + return(cflag ? 0 : 1); + + /* + * we had a match, now when we invert the sense (-c) we reject this + * member. However we have to tag the pattern a being successful, (in a + * match, not in selecting an archive member) so we call pat_sel() here. + */ + arcn->pat = pt; + if (!cflag) + return(0); + + if (pat_sel(arcn) < 0) + return(-1); + arcn->pat = NULL; + return(1); +} + +/* + * fn_match() + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + * Note: *pend may be changed to show where the prefix ends. + */ + +static int +fn_match(char *pattern, char *string, char **pend) +{ + char c; + char test; + + *pend = NULL; + for (;;) { + switch (c = *pattern++) { + case '\0': + /* + * Ok we found an exact match + */ + if (*string == '\0') + return(0); + + /* + * Check if it is a prefix match + */ + if ((dflag == 1) || (*string != '/')) + return(-1); + + /* + * It is a prefix match, remember where the trailing + * / is located + */ + *pend = string; + return(0); + case '?': + if ((*string++) == '\0') + return (-1); + break; + case '*': + c = *pattern; + /* + * Collapse multiple *'s. + */ + while (c == '*') + c = *++pattern; + + /* + * Optimized hack for pattern with a * at the end + */ + if (c == '\0') + return (0); + + /* + * General case, use recursion. + */ + while ((*string) != '\0') { + if (!fn_match(pattern, string, pend)) + return (0); + ++string; + } + return (-1); + case '[': + /* + * range match + */ + if (((test = *string++) == '\0') || + ((pattern = range_match(pattern, test)) == NULL)) + return (-1); + break; + case '\\': + default: + if (c != *string++) + return (-1); + break; + } + } + /* NOTREACHED */ +} + +static char * +range_match(char *pattern, int test) +{ + char c; + char c2; + int negate; + int ok = 0; + + if ((negate = (*pattern == '!')) != 0) + ++pattern; + + while ((c = *pattern++) != ']') { + /* + * Illegal pattern + */ + if (c == '\0') + return (NULL); + + if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && + (c2 != ']')) { + if ((c <= test) && (test <= c2)) + ok = 1; + pattern += 2; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} + +/* + * mod_name() + * modify a selected file name. first attempt to apply replacement string + * expressions, then apply interactive file rename. We apply replacement + * string expressions to both filenames and file links (if we didn't the + * links would point to the wrong place, and we could never be able to + * move an archive that has a file link in it). When we rename files + * interactively, we store that mapping (old name to user input name) so + * if we spot any file links to the old file name in the future, we will + * know exactly how to fix the file link. + * Return: + * 0 continue to process file, 1 skip this file, -1 pax is finished + */ + +int +mod_name(ARCHD *arcn) +{ + int res = 0; + + /* + * Strip off leading '/' if appropriate. + * Currently, this option is only set for the tar format. + */ + while (rmleadslash && arcn->name[0] == '/') { + if (arcn->name[1] == '\0') { + arcn->name[0] = '.'; + } else { + (void)memmove(arcn->name, &arcn->name[1], + strlen(arcn->name)); + arcn->nlen--; + } + if (rmleadslash < 2) { + rmleadslash = 2; + paxwarn(0, "Removing leading / from absolute path names in the archive"); + } + } + while (rmleadslash && arcn->ln_name[0] == '/' && + (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { + if (arcn->ln_name[1] == '\0') { + arcn->ln_name[0] = '.'; + } else { + (void)memmove(arcn->ln_name, &arcn->ln_name[1], + strlen(arcn->ln_name)); + arcn->ln_nlen--; + } + if (rmleadslash < 2) { + rmleadslash = 2; + paxwarn(0, "Removing leading / from absolute path names in the archive"); + } + } + + /* + * IMPORTANT: We have a problem. what do we do with symlinks? + * Modifying a hard link name makes sense, as we know the file it + * points at should have been seen already in the archive (and if it + * wasn't seen because of a read error or a bad archive, we lose + * anyway). But there are no such requirements for symlinks. On one + * hand the symlink that refers to a file in the archive will have to + * be modified to so it will still work at its new location in the + * file system. On the other hand a symlink that points elsewhere (and + * should continue to do so) should not be modified. There is clearly + * no perfect solution here. So we handle them like hardlinks. Clearly + * a replacement made by the interactive rename mapping is very likely + * to be correct since it applies to a single file and is an exact + * match. The regular expression replacements are a little harder to + * justify though. We claim that the symlink name is only likely + * to be replaced when it points within the file tree being moved and + * in that case it should be modified. what we really need to do is to + * call an oracle here. :) + */ + if (rephead != NULL) { + /* + * we have replacement strings, modify the name and the link + * name if any. + */ + if ((res = rep_name(arcn->name, sizeof(arcn->name), &(arcn->nlen), 1)) != 0) + return(res); + + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && + ((res = rep_name(arcn->ln_name, sizeof(arcn->ln_name), &(arcn->ln_nlen), 0)) != 0)) + return(res); + } + + if (iflag) { + /* + * perform interactive file rename, then map the link if any + */ + if ((res = tty_rename(arcn)) != 0) + return(res); + if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) + sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); + } + return(res); +} + +/* + * tty_rename() + * Prompt the user for a replacement file name. A "." keeps the old name, + * a empty line skips the file, and an EOF on reading the tty, will cause + * pax to stop processing and exit. Otherwise the file name input, replaces + * the old one. + * Return: + * 0 process this file, 1 skip this file, -1 we need to exit pax + */ + +int +tty_rename(ARCHD *arcn) +{ + char tmpname[PAXPATHLEN+2]; + int res; + + /* + * prompt user for the replacement name for a file, keep trying until + * we get some reasonable input. Archives may have more than one file + * on them with the same name (from updates etc). We print verbose info + * on the file so the user knows what is up. + */ + tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); + + for (;;) { + ls_tty(arcn); + tty_prnt("Input new name, or a \".\" to keep the old name, "); + tty_prnt("or a \"return\" to skip this file.\n"); + tty_prnt("Input > "); + if (tty_read(tmpname, sizeof(tmpname)) < 0) + return(-1); + if (strcmp(tmpname, "..") == 0) { + tty_prnt("Try again, illegal file name: ..\n"); + continue; + } + if (strlen(tmpname) > PAXPATHLEN) { + tty_prnt("Try again, file name too long\n"); + continue; + } + break; + } + + /* + * empty file name, skips this file. a "." leaves it alone + */ + if (tmpname[0] == '\0') { + tty_prnt("Skipping file.\n"); + return(1); + } + if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { + tty_prnt("Processing continues, name unchanged.\n"); + return(0); + } + + /* + * ok the name changed. We may run into links that point at this + * file later. we have to remember where the user sent the file + * in order to repair any links. + */ + tty_prnt("Processing continues, name changed to: %s\n", tmpname); + res = add_name(arcn->name, arcn->nlen, tmpname); + arcn->nlen = strlcpy(arcn->name, tmpname, sizeof(arcn->name)); + if (arcn->nlen >= sizeof(arcn->name)) + arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */ + if (res < 0) + return(-1); + return(0); +} + +/* + * set_dest() + * fix up the file name and the link name (if any) so this file will land + * in the destination directory (used during copy() -rw). + * Return: + * 0 if ok, -1 if failure (name too long) + */ + +int +set_dest(ARCHD *arcn, char *dest_dir, int dir_len) +{ + if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) + return(-1); + + /* + * It is really hard to deal with symlinks here, we cannot be sure + * if the name they point was moved (or will be moved). It is best to + * leave them alone. + */ + if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) + return(0); + + if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) + return(-1); + return(0); +} + +/* + * fix_path + * concatenate dir_name and or_name and store the result in or_name (if + * it fits). This is one ugly function. + * Return: + * 0 if ok, -1 if the final name is too long + */ + +static int +fix_path(char *or_name, int *or_len, char *dir_name, int dir_len) +{ + char *src; + char *dest; + char *start; + int len; + + /* + * we shift the or_name to the right enough to tack in the dir_name + * at the front. We make sure we have enough space for it all before + * we start. since dest always ends in a slash, we skip of or_name + * if it also starts with one. + */ + start = or_name; + src = start + *or_len; + dest = src + dir_len; + if (*start == '/') { + ++start; + --dest; + } + if ((len = dest - or_name) > PAXPATHLEN) { + paxwarn(1, "File name %s/%s, too long", dir_name, start); + return(-1); + } + *or_len = len; + + /* + * enough space, shift + */ + while (src >= start) + *dest-- = *src--; + src = dir_name + dir_len - 1; + + /* + * splice in the destination directory name + */ + while (src >= dir_name) + *dest-- = *src--; + + *(or_name + len) = '\0'; + return(0); +} + +/* + * rep_name() + * walk down the list of replacement strings applying each one in order. + * when we find one with a successful substitution, we modify the name + * as specified. if required, we print the results. if the resulting name + * is empty, we will skip this archive member. We use the regexp(3) + * routines (regexp() ought to win a prize as having the most cryptic + * library function manual page). + * --Parameters-- + * name is the file name we are going to apply the regular expressions to + * (and may be modified) + * nsize is the size of the name buffer. + * nlen is the length of this name (and is modified to hold the length of + * the final string). + * prnt is a flag that says whether to print the final result. + * Return: + * 0 if substitution was successful, 1 if we are to skip the file (the name + * ended up empty) + */ + +static int +rep_name(char *name, size_t nsize, int *nlen, int prnt) +{ + REPLACE *pt; + char *inpt; + char *outpt; + char *endpt; + char *rpt; + int found = 0; + int res; + regmatch_t pm[MAXSUBEXP]; + char nname[PAXPATHLEN+1]; /* final result of all replacements */ + char buf1[PAXPATHLEN+1]; /* where we work on the name */ + + /* + * copy the name into buf1, where we will work on it. We need to keep + * the orig string around so we can print out the result of the final + * replacement. We build up the final result in nname. inpt points at + * the string we apply the regular expression to. prnt is used to + * suppress printing when we handle replacements on the link field + * (the user already saw that substitution go by) + */ + pt = rephead; + (void)strlcpy(buf1, name, sizeof(buf1)); + inpt = buf1; + outpt = nname; + endpt = outpt + PAXPATHLEN; + + /* + * try each replacement string in order + */ + while (pt != NULL) { + do { + char *oinpt = inpt; + /* + * check for a successful substitution, if not go to + * the next pattern, or cleanup if we were global + */ + if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) + break; + + /* + * ok we found one. We have three parts, the prefix + * which did not match, the section that did and the + * tail (that also did not match). Copy the prefix to + * the final output buffer (watching to make sure we + * do not create a string too long). + */ + found = 1; + rpt = inpt + pm[0].rm_so; + + while ((inpt < rpt) && (outpt < endpt)) + *outpt++ = *inpt++; + if (outpt == endpt) + break; + + /* + * for the second part (which matched the regular + * expression) apply the substitution using the + * replacement string and place it the prefix in the + * final output. If we have problems, skip it. + */ + if ((res = resub(&(pt->rcmp),pm,pt->nstr,oinpt,outpt,endpt)) + < 0) { + if (prnt) + paxwarn(1, "Replacement name error %s", + name); + return(1); + } + outpt += res; + + /* + * we set up to look again starting at the first + * character in the tail (of the input string right + * after the last character matched by the regular + * expression (inpt always points at the first char in + * the string to process). If we are not doing a global + * substitution, we will use inpt to copy the tail to + * the final result. Make sure we do not overrun the + * output buffer + */ + inpt += pm[0].rm_eo - pm[0].rm_so; + + if ((outpt == endpt) || (*inpt == '\0')) + break; + + /* + * if the user wants global we keep trying to + * substitute until it fails, then we are done. + */ + } while (pt->flgs & GLOB); + + if (found) + break; + + /* + * a successful substitution did NOT occur, try the next one + */ + pt = pt->fow; + } + + if (found) { + /* + * we had a substitution, copy the last tail piece (if there is + * room) to the final result + */ + while ((outpt < endpt) && (*inpt != '\0')) + *outpt++ = *inpt++; + + *outpt = '\0'; + if ((outpt == endpt) && (*inpt != '\0')) { + if (prnt) + paxwarn(1,"Replacement name too long %s >> %s", + name, nname); + return(1); + } + + /* + * inform the user of the result if wanted + */ + if (prnt && (pt->flgs & PRNT)) { + if (*nname == '\0') + (void)fprintf(stderr,"%s >> \n", + name); + else + (void)fprintf(stderr,"%s >> %s\n", name, nname); + } + + /* + * if empty inform the caller this file is to be skipped + * otherwise copy the new name over the orig name and return + */ + if (*nname == '\0') + return(1); + *nlen = strlcpy(name, nname, nsize); + } + return(0); +} + +/* + * resub() + * apply the replacement to the matched expression. expand out the old + * style ed(1) subexpression expansion. + * Return: + * -1 if error, or the number of characters added to the destination. + */ + +static int +resub(regex_t *rp, regmatch_t *pm, char *src, char *inpt, char *dest, + char *destend) +{ + char *spt; + char *dpt; + char c; + regmatch_t *pmpt; + int len; + int subexcnt; + + spt = src; + dpt = dest; + subexcnt = rp->re_nsub; + while ((dpt < destend) && ((c = *spt++) != '\0')) { + /* + * see if we just have an ordinary replacement character + * or we refer to a subexpression. + */ + if (c == '&') { + pmpt = pm; + } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { + /* + * make sure there is a subexpression as specified + */ + if ((len = *spt++ - '0') > subexcnt) + return(-1); + pmpt = pm + len; + } else { + /* + * Ordinary character, just copy it + */ + if ((c == '\\') && (*spt != '\0')) + c = *spt++; + *dpt++ = c; + continue; + } + + /* + * continue if the subexpression is bogus + */ + if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || + ((len = (int)(pmpt->rm_eo - pmpt->rm_so)) <= 0)) + continue; + + /* + * copy the subexpression to the destination. + * fail if we run out of space or the match string is damaged + */ + if (len > (destend - dpt)) + len = destend - dpt; + strncpy(dpt, inpt + pmpt->rm_so, len); + dpt += len; + } + return(dpt - dest); +} diff --git a/file_cmds/pax/pat_rep.h b/file_cmds/pax/pat_rep.h new file mode 100644 index 00000000..9138d22f --- /dev/null +++ b/file_cmds/pax/pat_rep.h @@ -0,0 +1,51 @@ +/* $OpenBSD: pat_rep.h,v 1.4 2003/06/02 23:32:08 millert Exp $ */ +/* $NetBSD: pat_rep.h,v 1.3 1995/03/21 09:07:35 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pat_rep.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing user supplied replacement strings (-s) + */ +typedef struct replace { + char *nstr; /* the new string we will substitute with */ + regex_t rcmp; /* compiled regular expression used to match */ + int flgs; /* print conversions? global in operation? */ +#define PRNT 0x1 +#define GLOB 0x2 + struct replace *fow; /* pointer to next pattern */ +} REPLACE; + +int tty_rename(ARCHD *); /* used for -o invalid=rename recovery */ diff --git a/file_cmds/pax/pax.1 b/file_cmds/pax/pax.1 new file mode 100644 index 00000000..e79cc4ba --- /dev/null +++ b/file_cmds/pax/pax.1 @@ -0,0 +1,1185 @@ +.\" $OpenBSD: pax.1,v 1.54 2008/06/11 07:42:50 jmc Exp $ +.\" $NetBSD: pax.1,v 1.3 1995/03/21 09:07:37 cgd Exp $ +.\" +.\" Copyright (c) 1992 Keith Muller. +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pax.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd $Mdocdate: June 11 2008 $ +.Dt PAX 1 +.Os +.Sh NAME +.Nm pax +.Nd read and write file archives and copy directory hierarchies +.Sh SYNOPSIS +.Bk -words +.Nm pax +.Op Fl 0cdjnOvz +.Op Fl E Ar limit +.Op Fl f Ar archive +.Op Fl G Ar group +.Op Fl s Ar replstr +.Op Fl T Ar range +.Op Fl U Ar user +.Op Ar pattern ... +.Nm pax +.Fl r +.Op Fl 0cDdijknOuvYZz +.Op Fl E Ar limit +.Op Fl f Ar archive +.Op Fl G Ar group +.Op Fl o Ar options +.Op Fl p Ar string +.Op Fl s Ar replstr +.Op Fl T Ar range +.Op Fl U Ar user +.Op Ar pattern ... +.Nm pax +.Fl w +.Op Fl 0adHijLOPtuvXz +.Op Fl B Ar bytes +.Op Fl b Ar blocksize +.Op Fl f Ar archive +.Op Fl G Ar group +.Op Fl o Ar options +.Op Fl s Ar replstr +.Op Fl T Ar range +.Op Fl U Ar user +.Op Fl x Ar format +.Op Ar file ... +.Nm pax +.Fl rw +.Op Fl 0DdHijkLlnOPtuvXYZ +.Op Fl G Ar group +.Op Fl p Ar string +.Op Fl s Ar replstr +.Op Fl T Ar range +.Op Fl U Ar user +.Op Ar file ... +.Ar directory +.Ek +.Sh DESCRIPTION +.Nm +will read, write, and list the members of an archive file +and will copy directory hierarchies. +.Nm +operation is independent of the specific archive format +and supports a wide variety of different archive formats. +A list of supported archive formats can be found under the description of the +.Fl x +option. +.Pp +The presence of the +.Fl r +and the +.Fl w +options specifies which of the following functional modes +.Nm +will operate under: +.Em list , read , write , +and +.Em copy . +.Bl -tag -width 6n +.It \*(Ltnone\*(Gt +.Em List . +.Nm +will write to standard output +a table of contents of the members of the archive file read from +standard input, whose pathnames match the specified +.Ar pattern +arguments. +The table of contents contains one filename per line +and is written using single line buffering. +.It Fl r +.Em Read . +.Nm +extracts the members of the archive file read from the standard input, +with pathnames matching the specified +.Ar pattern +arguments. +The archive format and blocking is automatically determined on input. +When an extracted file is a directory, the entire file hierarchy +rooted at that directory is extracted. +Extracted files are created either at absolute paths (those that begin +with a / character) or relative to the current file hierarchy unless the +.Fl s +option is used to remove leading slashes or add a relative path prefix. +Files being extracted to absolute paths may overwrite +files outside of the current working directory, +so care should be taken when extracting untrusted archives. +The setting of ownership, access and modification times, and file mode of +the extracted files are discussed in more detail under the +.Fl p +option. +.It Fl w +.Em Write . +.Nm +writes an archive containing the +.Ar file +operands to standard output +using the specified archive format. +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +standard input. +When a +.Ar file +operand is also a directory, the entire file hierarchy rooted +at that directory will be included. +.It Fl rw +.Em Copy . +.Nm +copies the +.Ar file +operands to the destination +.Ar directory . +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +the standard input. +When a +.Ar file +operand is also a directory the entire file +hierarchy rooted at that directory will be included. +The effect of the +.Em copy +is as if the copied files were written to an archive file and then +subsequently extracted, except that there may be hard links between +the original and the copied files (see the +.Fl l +option below). +.Pp +.Sy Warning : +The destination +.Ar directory +must not be one of the +.Ar file +operands or a member of a file hierarchy rooted at one of the +.Ar file +operands. +The result of a +.Em copy +under these conditions is unpredictable. +.El +.Pp +While processing a damaged archive during a +.Em read +or +.Em list +operation, +.Nm +will attempt to recover from media defects and will search through the archive +to locate and process the largest number of archive members possible (see the +.Fl E +option for more details on error handling). +.Pp +The +.Ar directory +operand specifies a destination directory pathname. +If the +.Ar directory +operand does not exist, or it is not writable by the user, +or it is not of type directory, +.Nm +will exit with a non-zero exit status. +.Pp +The +.Ar pattern +operand is used to select one or more pathnames of archive members. +Archive members are selected using the pattern matching notation described +by +.Xr glob 3 . +When the +.Ar pattern +operand is not supplied, all members of the archive will be selected. +When a +.Ar pattern +matches a directory, the entire file hierarchy rooted at that directory will +be selected. +When a +.Ar pattern +operand does not select at least one archive member, +.Nm +will write these +.Ar pattern +operands in a diagnostic message to standard error +and then exit with a non-zero exit status. +.Pp +The +.Ar file +operand specifies the pathname of a file to be copied or archived. +When a +.Ar file +operand does not select at least one archive member, +.Nm +will write these +.Ar file +operand pathnames in a diagnostic message to standard error +and then exit with a non-zero exit status. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl 0 +Use the NUL +.Pq Ql \e0 +character as a pathname terminator, instead of newline +.Pq Ql \en . +This applies only to the pathnames read from standard input in +the write and copy modes, +and to the pathnames written to standard output in list mode. +This option is expected to be used in concert with the +.Fl print0 +function in +.Xr find 1 +or the +.Fl 0 +flag in +.Xr xargs 1 . +.It Fl a +Append the given +.Ar file +operands +to the end of an archive that was previously written. +If an archive format is not specified with a +.Fl x +option, the format currently being used in the archive will be selected. +Any attempt to append to an archive in a format different from the +format already used in the archive will cause +.Nm +to exit immediately +with a non-zero exit status. +The blocking size used in the archive volume where writing starts +will continue to be used for the remainder of that archive volume. +.Pp +.Sy Warning : +Many storage devices are not able to support the operations necessary +to perform an append operation. +Any attempt to append to an archive stored on such a device may damage the +archive or have other unpredictable results. +Tape drives in particular are more likely to not support an append operation. +An archive stored in a regular file system file or on a disk device will +usually support an append operation. +.It Fl B Ar bytes +Limit the number of bytes written to a single archive volume to +.Ar bytes . +The +.Ar bytes +limit can end with +.Sq Li m , +.Sq Li k , +or +.Sq Li b +to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively. +A pair of +.Ar bytes +limits can be separated by +.Sq Li x +to indicate a product. +.Pp +.Em Warning : +Only use this option when writing an archive to a device which supports +an end of file read condition based on last (or largest) write offset +(such as a regular file or a tape drive). +The use of this option with a floppy or hard disk is not recommended. +.It Fl b Ar blocksize +When +.Em writing +an archive, +block the output at a positive decimal integer number of +bytes per write to the archive file. +The +.Ar blocksize +must be a multiple of 512 bytes with a maximum of 64512 bytes. +Archive block sizes larger than 32256 bytes violate the +.Tn POSIX +standard and will not be portable to all systems. +A +.Ar blocksize +can end with +.Sq Li k +or +.Sq Li b +to specify multiplication by 1024 (1K) or 512, respectively. +A pair of +.Ar blocksizes +can be separated by +.Sq Li x +to indicate a product. +A specific archive device may impose additional restrictions on the size +of blocking it will support. +When blocking is not specified, the default +.Ar blocksize +is dependent on the specific archive format being used (see the +.Fl x +option). +.It Fl c +Match all file or archive members +.Em except +those specified by the +.Ar pattern +and +.Ar file +operands. +.It Fl D +This option is the same as the +.Fl u +option, except that the file inode change time is checked instead of the +file modification time. +The file inode change time can be used to select files whose inode information +(e.g., UID, GID, etc.) is newer than a copy of the file in the destination +.Ar directory . +.It Fl d +Cause files of type directory being copied or archived, or archive members of +type directory being extracted, to match only the directory file or archive +member and not the file hierarchy rooted at the directory. +.It Fl E Ar limit +Limit the number of consecutive read faults while trying to read a flawed +archive to +.Ar limit . +With a positive +.Ar limit , +.Nm +will attempt to recover from an archive read error and will +continue processing starting with the next file stored in the archive. +A +.Ar limit +of 0 will cause +.Nm +to stop operation after the first read error is detected on an archive volume. +A +.Ar limit +of +.Li NONE +will cause +.Nm +to attempt to recover from read errors forever. +The default +.Ar limit +is a small positive number of retries. +.Pp +.Em Warning : +Using this option with +.Li NONE +should be used with extreme caution as +.Nm +may get stuck in an infinite loop on a very badly flawed archive. +.It Fl f Ar archive +Specify +.Ar archive +as the pathname of the input or output archive, overriding the default +standard input (for +.Em list +and +.Em read ) +or standard output +(for +.Em write ) . +A single archive may span multiple files and different archive devices. +When required, +.Nm +will prompt for the pathname of the file or device of the next volume in the +archive. +.It Fl G Ar group +Select a file based on its +.Ar group +name, or when starting with a +.Cm # , +a numeric GID. +A +.Ql \e +can be used to escape the +.Cm # . +Multiple +.Fl G +options may be supplied and checking stops with the first match. +.It Fl H +Follow only command-line symbolic links while performing a physical file +system traversal. +.It Fl i +Interactively rename files or archive members. +For each archive member matching a +.Ar pattern +operand or each file matching a +.Ar file +operand, +.Nm +will prompt to +.Pa /dev/tty +giving the name of the file, its file mode, and its modification time. +.Nm +will then read a line from +.Pa /dev/tty . +If this line is blank, the file or archive member is skipped. +If this line consists of a single period, the +file or archive member is processed with no modification to its name. +Otherwise, its name is replaced with the contents of the line. +.Nm +will immediately exit with a non-zero exit status if +.Dv EOF +is encountered when reading a response or if +.Pa /dev/tty +cannot be opened for reading and writing. +.It Fl j +Use bzip2 to compress (decompress) the archive while writing (reading). +The bzip2 utility must be installed separately. +Incompatible with +.Fl a . +.It Fl k +Do not overwrite existing files. +.It Fl L +Follow all symbolic links to perform a logical file system traversal. +.It Fl l +(The lowercase letter +.Dq ell . ) +Link files. +In the +.Em copy +mode +.Pq Fl r Fl w , +hard links are made between the source and destination file hierarchies +whenever possible. +.It Fl n +Select the first archive member that matches each +.Ar pattern +operand. +No more than one archive member is matched for each +.Ar pattern . +When members of type directory are matched, the file hierarchy rooted at that +directory is also matched (unless +.Fl d +is also specified). +.It Fl O +Force the archive to be one volume. +If a volume ends prematurely, +.Nm +will not prompt for a new volume. +This option can be useful for +automated tasks where error recovery cannot be performed by a human. +.It Fl o Ar options +Information to modify the algorithm for extracting or writing archive files +which is specific to the archive format specified by +.Fl x . +In general, +.Ar options +take the form: +.Ar name Ns = Ns Ar value . +.Pp +The following options are available for the old +.Bx +.Em tar +format: +.Pp +.Bl -tag -width Ds -compact +.It Cm nodir +.It Cm write_opt=nodir +When writing archives, omit the storage of directories. +.El +.It Fl P +Do not follow symbolic links, perform a physical file system traversal. +This is the default mode. +.It Fl p Ar string +Specify one or more file characteristic options (privileges). +The +.Ar string +option-argument is a string specifying file characteristics to be retained or +discarded on extraction. +The string consists of the specification characters +.Cm a , e , m , o , +and +.Cm p . +Multiple characteristics can be concatenated within the same string +and multiple +.Fl p +options can be specified. +The meanings of the specification characters are as follows: +.Bl -tag -width 2n +.It Cm a +Do not preserve file access times. +By default, file access times are preserved whenever possible. +.It Cm e +.Dq Preserve everything , +the user ID, group ID, file mode bits, +file access time, and file modification time. +This is intended to be used by +.Em root , +someone with all the appropriate privileges, in order to preserve all +aspects of the files as they are recorded in the archive. +The +.Cm e +flag is the sum of the +.Cm o +and +.Cm p +flags. +.It Cm m +Do not preserve file modification times. +By default, file modification times are preserved whenever possible. +.It Cm o +Preserve the user ID and group ID. +.It Cm p +.Dq Preserve +the file mode bits. +This is intended to be used by a +.Em user +with regular privileges who wants to preserve all aspects of the file other +than the ownership. +The file times are preserved by default, but two other flags are offered to +disable this and use the time of extraction instead. +.El +.Pp +In the preceding list, +.Sq preserve +indicates that an attribute stored in the archive is given to the +extracted file, subject to the permissions of the invoking +process. +Otherwise the attribute of the extracted file is determined as +part of the normal file creation action. +If neither the +.Cm e +nor the +.Cm o +specification character is specified, or the user ID and group ID are not +preserved for any reason, +.Nm +will not set the +.Dv S_ISUID +.Em ( setuid ) +and +.Dv S_ISGID +.Em ( setgid ) +bits of the file mode. +If the preservation of any of these items fails for any reason, +.Nm +will write a diagnostic message to standard error. +Failure to preserve these items will affect the final exit status, +but will not cause the extracted file to be deleted. +If the file characteristic letters in any of the string option-arguments are +duplicated or conflict with each other, the one(s) given last will take +precedence. +For example, if +.Fl p Ar eme +is specified, file modification times are still preserved. +.It Fl r +Read an archive file from standard input +and extract the specified +.Ar file +operands. +If any intermediate directories are needed in order to extract an archive +member, these directories will be created as if +.Xr mkdir 2 +was called with the bitwise inclusive +.Tn OR +of +.Dv S_IRWXU , S_IRWXG , +and +.Dv S_IRWXO +as the mode argument. +When the selected archive format supports the specification of linked +files and these files cannot be linked while the archive is being extracted, +.Nm +will write a diagnostic message to standard error +and exit with a non-zero exit status at the completion of operation. +.It Fl s Ar replstr +Modify the archive member names according to the substitution expression +.Ar replstr , +using the syntax of the +.Xr ed 1 +utility regular expressions. +.Ar file +or +.Ar pattern +arguments may be given to restrict the list of archive members to those +specified. +.Pp +The format of these regular expressions is: +.Pp +.Dl /old/new/[gp] +.Pp +As in +.Xr ed 1 , +.Ar old +is a basic regular expression (see +.Xr re_format 7 ) +and +.Ar new +can contain an ampersand +.Pq Ql & , +.Ql \e Ns Em n +(where +.Em n +is a digit) back-references, +or subexpression matching. +The +.Ar old +string may also contain newline characters. +Any non-null character can be used as a delimiter +.Po +.Ql / +is shown here +.Pc . +Multiple +.Fl s +expressions can be specified. +The expressions are applied in the order they are specified on the +command line, terminating with the first successful substitution. +.Pp +The optional trailing +.Cm g +continues to apply the substitution expression to the pathname substring, +which starts with the first character following the end of the last successful +substitution. +The first unsuccessful substitution stops the operation of the +.Cm g +option. +The optional trailing +.Cm p +will cause the final result of a successful substitution to be written to +standard error in the following format: +.Pp +.D1 Em original-pathname No \*(Gt\*(Gt Em new-pathname +.Pp +File or archive member names that substitute to the empty string +are not selected and will be skipped. +.It Fl T Ar range +Allow files to be selected based on a file modification or inode change +time falling within the specified time range. +The range has the format: +.Sm off +.Bd -filled -offset indent +.Oo Ar from_date Oc Oo , +.Ar to_date Oc Oo / +.Oo Cm c Oc Op Cm m Oc +.Ed +.Sm on +.Pp +The dates specified by +.Ar from_date +to +.Ar to_date +are inclusive. +If only a +.Ar from_date +is supplied, all files with a modification or inode change time +equal to or younger are selected. +If only a +.Ar to_date +is supplied, all files with a modification or inode change time +equal to or older will be selected. +When the +.Ar from_date +is equal to the +.Ar to_date , +only files with a modification or inode change time of exactly that +time will be selected. +.Pp +When +.Nm +is in the +.Em write +or +.Em copy +mode, the optional trailing field +.Oo Cm c Oc Op Cm m +can be used to determine which file time (inode change, file modification or +both) are used in the comparison. +If neither is specified, the default is to use file modification time only. +The +.Cm m +specifies the comparison of file modification time (the time when +the file was last written). +The +.Cm c +specifies the comparison of inode change time (the time when the file +inode was last changed; e.g., a change of owner, group, mode, etc). +When +.Cm c +and +.Cm m +are both specified, then the modification and inode change times are +both compared. +.Pp +The inode change time comparison is useful in selecting files whose +attributes were recently changed or selecting files which were recently +created and had their modification time reset to an older time (as what +happens when a file is extracted from an archive and the modification time +is preserved). +Time comparisons using both file times is useful when +.Nm +is used to create a time based incremental archive (only files that were +changed during a specified time range will be archived). +.Pp +A time range is made up of six different fields and each field must contain two +digits. +The format is: +.Pp +.Dl [[[[[cc]yy]mm]dd]HH]MM[.SS] +.Pp +Where +.Ar cc +is the first two digits of the year (the century), +.Ar yy +is the last two digits of the year, +the first +.Ar mm +is the month (from 01 to 12), +.Ar dd +is the day of the month (from 01 to 31), +.Ar HH +is the hour of the day (from 00 to 23), +.Ar MM +is the minute (from 00 to 59), +and +.Ar SS +is the seconds (from 00 to 59). +The minute field +.Ar MM +is required, while the other fields are optional and must be added in the +following order: +.Ar HH , dd , mm , +.Ar yy , cc . +.Pp +The +.Ar SS +field may be added independently of the other fields. +Time ranges are relative to the current time, so +.Ic -T 1234/cm +would select all files with a modification or inode change time +of 12:34 PM today or later. +Multiple +.Fl T +time range can be supplied and checking stops with the first match. +.It Fl t +Reset the access times of any file or directory read or accessed by +.Nm +to be the same as they were before being read or accessed by +.Nm pax . +.It Fl U Ar user +Select a file based on its +.Ar user +name, or when starting with a +.Cm # , +a numeric UID. +A +.Ql \e +can be used to escape the +.Cm # . +Multiple +.Fl U +options may be supplied and checking stops with the first match. +.It Fl u +Ignore files that are older (having a less recent file modification time) +than a pre-existing file or archive member with the same name. +During +.Em read , +an archive member with the same name as a file in the file system will be +extracted if the archive member is newer than the file. +During +.Em write , +a file system member with the same name as an archive member will be +written to the archive if it is newer than the archive member. +During +.Em copy , +the file in the destination hierarchy is replaced by the file in the source +hierarchy or by a link to the file in the source hierarchy if the file in +the source hierarchy is newer. +.It Fl v +During a +.Em list +operation, produce a verbose table of contents using the format of the +.Xr ls 1 +utility with the +.Fl l +option. +For pathnames representing a hard link to a previous member of the archive, +the output has the format: +.Pp +.Dl Em ls -l listing Li == Em link-name +.Pp +For pathnames representing a symbolic link, the output has the format: +.Pp +.Dl Em ls -l listing Li =\*(Gt Em link-name +.Pp +Where +.Em ls -l listing +is the output format specified by the +.Xr ls 1 +utility when used with the +.Fl l +option. +Otherwise for all the other operational modes +.Po Em read , write , No and Em copy +.Pc , +pathnames are written and flushed to standard error +without a trailing newline +as soon as processing begins on that file or +archive member. +The trailing newline +is not buffered and is written only after the file has been read or written. +.It Fl w +Write files to the standard output +in the specified archive format. +When no +.Ar file +operands are specified, standard input +is read for a list of pathnames with one per line without any leading or +trailing +.Aq blanks . +.It Fl X +When traversing the file hierarchy specified by a pathname, +do not descend into directories that have a different device ID. +See the +.Li st_dev +field as described in +.Xr stat 2 +for more information about device IDs. +.It Fl x Ar format +Specify the output archive format, with the default format being +.Cm ustar . +.Nm +currently supports the following formats: +.Bl -tag -width "sv4cpio" +.It Cm bcpio +The old binary cpio format. +The default blocksize for this format is 5120 bytes. +This format is not very portable and should not be used when other formats +are available. +Inode and device information about a file (used for detecting file hard links +by this format), which may be truncated by this format, is detected by +.Nm +and is repaired. +.It Cm cpio +The extended cpio interchange format specified in the +.St -p1003.2 +standard. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format), which may be truncated by this format, is detected by +.Nm +and is repaired. +.It Cm sv4cpio +The System V release 4 cpio. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format), which may be truncated by this format, is detected by +.Nm +and is repaired. +.It Cm sv4crc +The System V release 4 cpio with file CRC checksums. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format), which may be truncated by this format, is detected by +.Nm +and is repaired. +.It Cm tar +The old +.Bx +tar format as found in +.Bx 4.3 . +The default blocksize for this format is 10240 bytes. +Pathnames stored by this format must be 100 characters or less in length. +Only +.Em regular +files, +.Em hard links , soft links , +and +.Em directories +will be archived (other file system types are not supported). +For backwards compatibility with even older tar formats, a +.Fl o +option can be used when writing an archive to omit the storage of directories. +This option takes the form: +.Pp +.Dl Fl o Cm write_opt=nodir +.It Cm ustar +The extended tar interchange format specified in the +.St -p1003.2 +standard. +The default blocksize for this format is 10240 bytes. +Filenames stored by this format must be 100 characters or less in length; +the total pathname must be 255 characters or less. +.El +.Pp +.Nm +will detect and report any file that it is unable to store or extract +as the result of any specific archive format restrictions. +The individual archive formats may impose additional restrictions on use. +Typical archive format restrictions include (but are not limited to): +file pathname length, file size, link pathname length, and the type of the +file. +.It Fl Y +This option is the same as the +.Fl D +option, except that the inode change time is checked using the +pathname created after all the file name modifications have completed. +.It Fl Z +This option is the same as the +.Fl u +option, except that the modification time is checked using the +pathname created after all the file name modifications have completed. +.It Fl z +Use +.Xr gzip 1 +to compress (decompress) the archive while writing (reading). +Incompatible with +.Fl a . +.It Fl -insecure +Normally +.Nm +ignores filenames or symbolic links that contain +.Dq .. +as a path component. +With this option, +files that contain +.Dq .. +can be processed. +.El +.Pp +The options that operate on the names of files or archive members +.Po Fl c , +.Fl i , +.Fl j , +.Fl n , +.Fl s , +.Fl u , +.Fl v , +.Fl D , +.Fl G , +.Fl T , +.Fl U , +.Fl Y , +and +.Fl Z +.Pc +interact as follows. +.Pp +When extracting files during a +.Em read +operation, archive members are +.Sq selected , +based only on the user specified pattern operands as modified by the +.Fl c , +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +.Fl U +options. +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then the +.Fl Y +and +.Fl Z +options will be applied based on the final pathname. +Finally, the +.Fl v +option will write the names resulting from these modifications. +.Pp +When archiving files during a +.Em write +operation, or copying files during a +.Em copy +operation, archive members are +.Sq selected , +based only on the user specified pathnames as modified by the +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +and +.Fl U +options (the +.Fl D +option only applies during a copy operation). +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then during a +.Em copy +operation the +.Fl Y +and the +.Fl Z +options will be applied based on the final pathname. +Finally, the +.Fl v +option will write the names resulting from these modifications. +.Pp +When one or both of the +.Fl u +or +.Fl D +options are specified along with the +.Fl n +option, a file is not considered selected unless it is newer +than the file to which it is compared. +.Sh ENVIRONMENT +.Bl -tag -width Fl +.It Ev TMPDIR +Path in which to store temporary files. +.El +.Sh EXAMPLES +Copy the contents of the current directory to the device +.Pa /dev/rst0 : +.Pp +.Dl $ pax -w -f /dev/rst0 \&. +.Pp +Give the verbose table of contents for an archive stored in +.Pa filename : +.Pp +.Dl $ pax -v -f filename +.Pp +This sequence of commands will copy the entire +.Pa olddir +directory hierarchy to +.Pa newdir : +.Bd -literal -offset indent +$ mkdir newdir +$ cd olddir +$ pax -rw . ../newdir +.Ed +.Pp +Extract files from the archive +.Pa a.pax . +Files rooted in +.Pa /usr +are extracted relative to the current working directory; +all other files are extracted to their unmodified path. +.Pp +.Dl $ pax -r -s ',^/usr/,,' -f a.pax +.Pp +This can be used to interactively select the files to copy from the +current directory to +.Pa dest_dir : +.Pp +.Dl $ pax -rw -i \&. dest_dir +.Pp +Extract all files from the archive +.Pa a.pax +which are owned by +.Em root +with group +.Em bin +and preserve all file permissions: +.Pp +.Dl "$ pax -r -pe -U root -G bin -f a.pax" +.Pp +Update (and list) only those files in the destination directory +.Pa /backup +which are older (less recent inode change or file modification times) than +files with the same name found in the source file tree +.Pa home : +.Pp +.Dl "$ pax -r -w -v -Y -Z home /backup" +.Sh DIAGNOSTICS +.Nm +will exit with one of the following values: +.Bl -tag -width 2n -offset indent +.It 0 +All files were processed successfully. +.It 1 +An error occurred. +.El +.Pp +Whenever +.Nm +cannot create a file or a link when reading an archive or cannot +find a file when writing an archive, or cannot preserve the user ID, +group ID, or file mode when the +.Fl p +option is specified, a diagnostic message is written to standard error +and a non-zero exit status will be returned, but processing will continue. +In the case where +.Nm +cannot create a link to a file, +.Nm +will not create a second copy of the file. +.Pp +If the extraction of a file from an archive is prematurely terminated by +a signal or error, +.Nm +may have only partially extracted a file the user wanted. +Additionally, the file modes of extracted files and directories +may have incorrect file bits, and the modification and access times may be +wrong. +.Pp +If the creation of an archive is prematurely terminated by a signal or error, +.Nm +may have only partially created the archive, which may violate the specific +archive format specification. +.Pp +If while doing a +.Em copy , +.Nm +detects a file is about to overwrite itself, the file is not copied, +a diagnostic message is written to standard error +and when +.Nm +completes it will exit with a non-zero exit status. +.Sh SEE ALSO +.Xr cpio 1 , +.Xr tar 1 +.Pp +"Archiving with Pax", Dru Lavigne, ONLamp.com BSD DevCenter, +http://www.onlamp.com/pub/a/bsd/2002/08/22/FreeBSD_Basics.html +.Pp +pax(1) manual page, +http://heirloom.sourceforge.net/man/pax.1.html +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2004 +specification. +.Pp +The flags +.Op Fl 0BDEGHjLOPTUYZz , +the archive formats +.Em bcpio , +.Em sv4cpio , +.Em sv4crc , +.Em tar , +and the flawed archive handling during +.Em list +and +.Em read +operations +are extensions to that specification. +.Sh AUTHORS +Keith Muller at the University of California, San Diego. diff --git a/file_cmds/pax/pax.c b/file_cmds/pax/pax.c new file mode 100644 index 00000000..7c278f04 --- /dev/null +++ b/file_cmds/pax/pax.c @@ -0,0 +1,441 @@ +/* $OpenBSD: pax.c,v 1.28 2005/08/04 10:02:44 mpf Exp $ */ +/* $NetBSD: pax.c,v 1.5 1996/03/26 23:54:20 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: pax.c,v 1.28 2005/08/04 10:02:44 mpf Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +static int gen_init(void); + +/* + * PAX main routines, general globals and some simple start up routines + */ + +/* + * Variables that can be accessed by any routine within pax + */ +int act = DEFOP; /* read/write/append/copy */ +const FSUB *frmt = NULL; /* archive format type */ +int cflag; /* match all EXCEPT pattern/file */ +int cwdfd; /* starting cwd */ +int dflag; /* directory member match only */ +int iflag; /* interactive file/archive rename */ +int kflag; /* do not overwrite existing files */ +int lflag; /* use hard links when possible */ +int nflag; /* select first archive member match */ +int tflag; /* restore access time after read */ +int uflag; /* ignore older modification time files */ +int vflag; /* produce verbose output */ +int Dflag; /* same as uflag except inode change time */ +int Hflag; /* follow command line symlinks (write only) */ +int Lflag; /* follow symlinks when writing */ +int Xflag; /* archive files with same device id only */ +int Yflag; /* same as Dflag except after name mode */ +int Zflag; /* same as uflag except after name mode */ +int zeroflag; /* use \0 as pathname terminator */ +int vfpart; /* is partial verbose output in progress */ +int patime = 1; /* preserve file access time */ +int pmtime = 1; /* preserve file modification times */ +int nodirs; /* do not create directories as needed */ +int pmode; /* preserve file mode bits */ +int pids; /* preserve file uid/gid */ +int rmleadslash = 0; /* remove leading '/' from pathnames */ +int secure = 1; /* don't extract names that contain .. */ +int exit_val; /* exit value */ +int docrc; /* check/create file crc */ +char *dirptr; /* destination dir in a copy */ +char *ltmfrmt; /* -v locale time format (if any) */ +char *argv0; /* root of argv[0] */ +sigset_t s_mask; /* signal mask for cleanup critical sect */ +FILE *listf; /* file pointer to print file list to */ +char *tempfile; /* tempfile to use for mkstemp(3) */ +char *tempbase; /* basename of tempfile to use for mkstemp(3) */ + +/* + * PAX - Portable Archive Interchange + * + * A utility to read, write, and write lists of the members of archive + * files and copy directory hierarchies. A variety of archive formats + * are supported (some are described in POSIX 1003.1 10.1): + * + * ustar - 10.1.1 extended tar interchange format + * cpio - 10.1.2 extended cpio interchange format + * tar - old BSD 4.3 tar format + * binary cpio - old cpio with binary header format + * sysVR4 cpio - with and without CRC + * + * This version is a superset of IEEE Std 1003.2b-d3 + * + * Summary of Extensions to the IEEE Standard: + * + * 1 READ ENHANCEMENTS + * 1.1 Operations which read archives will continue to operate even when + * processing archives which may be damaged, truncated, or fail to meet + * format specs in several different ways. Damaged sections of archives + * are detected and avoided if possible. Attempts will be made to resync + * archive read operations even with badly damaged media. + * 1.2 Blocksize requirements are not strictly enforced on archive read. + * Tapes which have variable sized records can be read without errors. + * 1.3 The user can specify via the non-standard option flag -E if error + * resync operation should stop on a media error, try a specified number + * of times to correct, or try to correct forever. + * 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks + * of all zeros will be restored with holes appropriate for the target + * filesystem + * 1.5 The user is notified whenever something is found during archive + * read operations which violates spec (but the read will continue). + * 1.6 Multiple archive volumes can be read and may span over different + * archive devices + * 1.7 Rigidly restores all file attributes exactly as they are stored on the + * archive. + * 1.8 Modification change time ranges can be specified via multiple -T + * options. These allow a user to select files whose modification time + * lies within a specific time range. + * 1.9 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 1.10 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 1.11 File modification time can be checked against existing file after + * name modification (-Z) + * + * 2 WRITE ENHANCEMENTS + * 2.1 Write operation will stop instead of allowing a user to create a flawed + * flawed archive (due to any problem). + * 2.2 Archives written by pax are forced to strictly conform to both the + * archive and pax the specific format specifications. + * 2.3 Blocking size and format is rigidly enforced on writes. + * 2.4 Formats which may exhibit header overflow problems (they have fields + * too small for large file systems, such as inode number storage), use + * routines designed to repair this problem. These techniques still + * conform to both pax and format specifications, but no longer truncate + * these fields. This removes any restrictions on using these archive + * formats on large file systems. + * 2.5 Multiple archive volumes can be written and may span over different + * archive devices + * 2.6 A archive volume record limit allows the user to specify the number + * of bytes stored on an archive volume. When reached the user is + * prompted for the next archive volume. This is specified with the + * non-standard -B flag. The limit is rounded up to the next blocksize. + * 2.7 All archive padding during write use zero filled sections. This makes + * it much easier to pull data out of flawed archive during read + * operations. + * 2.8 Access time reset with the -t applies to all file nodes (including + * directories). + * 2.9 Symbolic links can be followed with -L (optional in the spec). + * 2.10 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 2.11 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 2.12 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 2.13 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * + * 3 COPY ENHANCEMENTS + * 3.1 Sparse files (lseek holes) can be copied without expanding the holes + * into zero filled blocks. The file copy is created with holes which are + * appropriate for the target filesystem + * 3.2 Access time as well as modification time on copied file trees can be + * preserved with the appropriate -p options. + * 3.3 Access time reset with the -t applies to all file nodes (including + * directories). + * 3.4 Symbolic links can be followed with -L (optional in the spec). + * 3.5 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 3.6 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 3.7 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 3.8 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * 3.9 File inode change time can be checked against existing file before + * name modification (-D) + * 3.10 File inode change time can be checked against existing file after + * name modification (-Y) + * 3.11 File modification time can be checked against existing file after + * name modification (-Z) + * + * 4 GENERAL ENHANCEMENTS + * 4.1 Internal structure is designed to isolate format dependent and + * independent functions. Formats are selected via a format driver table. + * This encourages the addition of new archive formats by only having to + * write those routines which id, read and write the archive header. + */ + +/* + * main() + * parse options, set up and operate as specified by the user. + * any operational flaw will set exit_val to non-zero + * Return: 0 if ok, 1 otherwise + */ + +int +main(int argc, char **argv) +{ + char *tmpdir; + size_t tdlen; + + listf = stderr; + /* + * Keep a reference to cwd, so we can always come back home. + */ + cwdfd = open(".", O_RDONLY); + if (cwdfd < 0) { + syswarn(1, errno, "Can't open current working directory."); + return(exit_val); + } + + if (updatepath() == -1) + return exit_val; + /* + * Where should we put temporary files? + */ + if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') + tmpdir = _PATH_TMP; + tdlen = strlen(tmpdir); + while (tdlen > 0 && tmpdir[tdlen - 1] == '/') + tdlen--; + tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE)); + if (tempfile == NULL) { + paxwarn(1, "Cannot allocate memory for temp file name."); + return(exit_val); + } + if (tdlen) + memcpy(tempfile, tmpdir, tdlen); + tempbase = tempfile + tdlen; + *tempbase++ = '/'; + + /* + * parse options, determine operational mode, general init + */ + options(argc, argv); + if ((gen_init() < 0) || (tty_init() < 0)) + return(exit_val); + + /* + * select a primary operation mode + */ + switch (act) { + case EXTRACT: + extract(); + break; + case ARCHIVE: + archive(); + break; + case APPND: + if (gzip_program != NULL) + errx(1, "can not gzip while appending"); + append(); + break; + case COPY: + copy(); + break; + default: + case LIST: + list(); + break; + } + return(exit_val); +} + +/* + * sig_cleanup() + * when interrupted we try to do whatever delayed processing we can. + * This is not critical, but we really ought to limit our damage when we + * are aborted by the user. + * Return: + * never.... + */ + +void +sig_cleanup(int which_sig) +{ + /* XXX signal races */ + + /* + * restore modes and times for any dirs we may have created + * or any dirs we may have read. Set vflag and vfpart so the user + * will clearly see the message on a line by itself. + */ + vflag = vfpart = 1; + if (which_sig == SIGXCPU) + paxwarn(0, "Cpu time limit reached, cleaning up."); + else + paxwarn(0, "Signal caught, cleaning up."); + + ar_close(); + proc_dir(); + if (tflag) + atdir_end(); + exit(1); +} + +/* + * gen_init() + * general setup routines. Not all are required, but they really help + * when dealing with a medium to large sized archives. + */ + +static int +gen_init(void) +{ + struct rlimit reslimit; + struct sigaction n_hand; + struct sigaction o_hand; + + /* + * Really needed to handle large archives. We can run out of memory for + * internal tables really fast when we have a whole lot of files... + */ + if (getrlimit(RLIMIT_DATA , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_DATA , &reslimit); + } + + /* + * should file size limits be waived? if the os limits us, this is + * needed if we want to write a large archive + */ + if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_FSIZE , &reslimit); + } + + /* + * increase the size the stack can grow to + */ + if (getrlimit(RLIMIT_STACK , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_STACK , &reslimit); + } + + /* + * not really needed, but doesn't hurt + */ + if (getrlimit(RLIMIT_RSS , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_RSS , &reslimit); + } + + /* + * Handle posix locale + * + * set user defines time printing format for -v option + */ + ltmfrmt = getenv("LC_TIME"); + + /* + * signal handling to reset stored directory times and modes. Since + * we deal with broken pipes via failed writes we ignore it. We also + * deal with any file size limit through failed writes. Cpu time + * limits are caught and a cleanup is forced. + */ + if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) || + (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) || + (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) || + (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) { + paxwarn(1, "Unable to set up signal mask"); + return(-1); + } + memset(&n_hand, 0, sizeof n_hand); + n_hand.sa_mask = s_mask; + n_hand.sa_flags = 0; + n_hand.sa_handler = sig_cleanup; + + if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGHUP, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGTERM, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGINT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGQUIT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGXCPU, &o_hand, &o_hand) < 0)) + goto out; + + n_hand.sa_handler = SIG_IGN; + if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0) || + (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0)) + goto out; + return(0); + + out: + syswarn(1, errno, "Unable to set up signal handler"); + return(-1); +} diff --git a/file_cmds/pax/pax.h b/file_cmds/pax/pax.h new file mode 100644 index 00000000..4d74aa00 --- /dev/null +++ b/file_cmds/pax/pax.h @@ -0,0 +1,249 @@ +/* $OpenBSD: pax.h,v 1.17 2005/11/09 19:59:06 otto Exp $ */ +/* $NetBSD: pax.h,v 1.3 1995/03/21 09:07:41 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pax.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * BSD PAX global data structures and constants. + */ + +#define MAXBLK 64512 /* MAX blocksize supported (posix SPEC) */ + /* WARNING: increasing MAXBLK past 32256 */ + /* will violate posix spec. */ +#define MAXBLK_POSIX 32256 /* MAX blocksize supported as per POSIX */ +#define BLKMULT 512 /* blocksize must be even mult of 512 bytes */ + /* Don't even think of changing this */ +#define DEVBLK 8192 /* default read blksize for devices */ +#define FILEBLK 10240 /* default read blksize for files */ +#define PAXPATHLEN 3072 /* maximum path length for pax. MUST be */ + /* longer than the system MAXPATHLEN */ + +/* + * Pax modes of operation + */ +#define LIST 0 /* List the file in an archive */ +#define EXTRACT 1 /* extract the files in an archive */ +#define ARCHIVE 2 /* write a new archive */ +#define APPND 3 /* append to the end of an archive */ +#define COPY 4 /* copy files to destination dir */ +#define DEFOP LIST /* if no flags default is to LIST */ + +/* + * Device type of the current archive volume + */ +#define ISREG 0 /* regular file */ +#define ISCHR 1 /* character device */ +#define ISBLK 2 /* block device */ +#define ISTAPE 3 /* tape drive */ +#define ISPIPE 4 /* pipe/socket */ + +/* + * Pattern matching structure + * + * Used to store command line patterns + */ +typedef struct pattern { + char *pstr; /* pattern to match, user supplied */ + char *pend; /* end of a prefix match */ + char *chdname; /* the dir to change to if not NULL. */ + int plen; /* length of pstr */ + int flgs; /* processing/state flags */ +#define MTCH 0x1 /* pattern has been matched */ +#define DIR_MTCH 0x2 /* pattern matched a directory */ + struct pattern *fow; /* next pattern */ +} PATTERN; + +/* + * General Archive Structure (used internal to pax) + * + * This structure is used to pass information about archive members between + * the format independent routines and the format specific routines. When + * new archive formats are added, they must accept requests and supply info + * encoded in a structure of this type. The name fields are declared statically + * here, as there is only ONE of these floating around, size is not a major + * consideration. Eventually converting the name fields to a dynamic length + * may be required if and when the supporting operating system removes all + * restrictions on the length of pathnames it will resolve. + */ +typedef struct { + int nlen; /* file name length */ + char name[PAXPATHLEN+1]; /* file name */ + int ln_nlen; /* link name length */ + char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */ + char *org_name; /* orig name in file system */ + PATTERN *pat; /* ptr to pattern match (if any) */ + struct stat sb; /* stat buffer see stat(2) */ + off_t pad; /* bytes of padding after file xfer */ + off_t skip; /* bytes of real data after header */ + /* IMPORTANT. The st_size field does */ + /* not always indicate the amount of */ + /* data following the header. */ + u_int32_t crc; /* file crc */ + int type; /* type of file node */ +#define PAX_DIR 1 /* directory */ +#define PAX_CHR 2 /* character device */ +#define PAX_BLK 3 /* block device */ +#define PAX_REG 4 /* regular file */ +#define PAX_SLK 5 /* symbolic link */ +#define PAX_SCK 6 /* socket */ +#define PAX_FIF 7 /* fifo */ +#define PAX_HLK 8 /* hard link */ +#define PAX_HRG 9 /* hard link to a regular file */ +#define PAX_CTG 10 /* high performance file */ +#define PAX_GLL 11 /* GNU long symlink */ +#define PAX_GLF 12 /* GNU long file */ +} ARCHD; + +/* + * Format Specific Routine Table + * + * The format specific routine table allows new archive formats to be quickly + * added. Overall pax operation is independent of the actual format used to + * form the archive. Only those routines which deal directly with the archive + * are tailored to the oddities of the specific format. All other routines are + * independent of the archive format. Data flow in and out of the format + * dependent routines pass pointers to ARCHD structure (described below). + */ +typedef struct { + char *name; /* name of format, this is the name the user */ + /* gives to -x option to select it. */ + int bsz; /* default block size. used when the user */ + /* does not specify a blocksize for writing */ + /* Appends continue to with the blocksize */ + /* the archive is currently using. */ + int hsz; /* Header size in bytes. this is the size of */ + /* the smallest header this format supports. */ + /* Headers are assumed to fit in a BLKMULT. */ + /* If they are bigger, get_head() and */ + /* get_arc() must be adjusted */ + int udev; /* does append require unique dev/ino? some */ + /* formats use the device and inode fields */ + /* to specify hard links. when members in */ + /* the archive have the same inode/dev they */ + /* are assumed to be hard links. During */ + /* append we may have to generate unique ids */ + /* to avoid creating incorrect hard links */ + int hlk; /* does archive store hard links info? if */ + /* not, we do not bother to look for them */ + /* during archive write operations */ + int blkalgn; /* writes must be aligned to blkalgn boundary */ + int inhead; /* is the trailer encoded in a valid header? */ + /* if not, trailers are assumed to be found */ + /* in invalid headers (i.e like tar) */ + int (*id)(char *, /* checks if a buffer is a valid header */ + int); /* returns 1 if it is, o.w. returns a 0 */ + int (*st_rd)(void); /* initialize routine for read. so format */ + /* can set up tables etc before it starts */ + /* reading an archive */ + int (*rd)(ARCHD *, /* read header routine. passed a pointer to */ + char *); /* ARCHD. It must extract the info from the */ + /* format and store it in the ARCHD struct. */ + /* This routine is expected to fill all the */ + /* fields in the ARCHD (including stat buf) */ + /* 0 is returned when a valid header is */ + /* found. -1 when not valid. This routine */ + /* set the skip and pad fields so the format */ + /* independent routines know the amount of */ + /* padding and the number of bytes of data */ + /* which follow the header. This info is */ + /* used skip to the next file header */ + off_t (*end_rd)(void); /* read cleanup. Allows format to clean up */ + /* and MUST RETURN THE LENGTH OF THE TRAILER */ + /* RECORD (so append knows how many bytes */ + /* to move back to rewrite the trailer) */ + int (*st_wr)(void); /* initialize routine for write operations */ + int (*wr)(ARCHD *); /* write archive header. Passed an ARCHD */ + /* filled with the specs on the next file to */ + /* archived. Returns a 1 if no file data is */ + /* is to be stored; 0 if file data is to be */ + /* added. A -1 is returned if a write */ + /* operation to the archive failed. this */ + /* function sets the skip and pad fields so */ + /* the proper padding can be added after */ + /* file data. This routine must NEVER write */ + /* a flawed archive header. */ + int (*end_wr)(void); /* end write. write the trailer and do any */ + /* other format specific functions needed */ + /* at the end of an archive write */ + int (*trail)(ARCHD *, /* returns 0 if a valid trailer, -1 if not */ + char *, int, /* For formats which encode the trailer */ + int *); /* outside of a valid header, a return value */ + /* of 1 indicates that the block passed to */ + /* it can never contain a valid header (skip */ + /* this block, no point in looking at it) */ + /* CAUTION: parameters to this function are */ + /* different for trailers inside or outside */ + /* of headers. See get_head() for details */ + int (*rd_data)(ARCHD *, /* read/process file data from the archive */ + int, off_t *); + int (*wr_data)(ARCHD *, /* write/process file data to the archive */ + int, off_t *); + int (*options)(void); /* process format specific options (-o) */ +} FSUB; + +/* + * Format Specific Options List + * + * Used to pass format options to the format options handler + */ +typedef struct oplist { + char *name; /* option variable name e.g. name= */ + char *value; /* value for option variable */ + struct oplist *fow; /* next option */ + int separator; /* 2 means := separator; 1 means = separator + 0 means no separator */ +} OPLIST; +#define SEP_COLONEQ 2 +#define SEP_EQ 1 +#define SEP_NONE 0 + +/* + * General Macros + */ +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#define MAJOR(x) major(x) +#define MINOR(x) minor(x) +#define TODEV(x, y) makedev((x), (y)) + +/* + * General Defines + */ +#define HEX 16 +#define OCT 8 +#define _PAX_ 1 +#define _TFILE_BASE "paxXXXXXXXXXX" diff --git a/file_cmds/pax/pax_format.c b/file_cmds/pax/pax_format.c new file mode 100644 index 00000000..3ac97ed4 --- /dev/null +++ b/file_cmds/pax/pax_format.c @@ -0,0 +1,1604 @@ +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; +#else +static const char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tar.c,v 1.34 2004/10/23 19:34:14 otto Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#include "tar.h" +#include +#include +#include "pat_rep.h" +#include + +/* + * This file implements the -x pax format support; it is incomplete. + * Known missing features include: + * many -o options for "copy" mode are not implemented (only path=) + * many format specifiers for -o listopt are not implemented + * -o listopt option should work for all archive formats, not just -x pax + * This file was originally derived from the file tar.c. You should + * 'diff' it to that file to see how much of the -x pax format has been implemented. + */ + +char pax_eh_datablk[4*1024]; +int pax_read_or_list_mode = 0; +int want_a_m_time_headers = 0; +int want_linkdata = 0; + +int pax_invalid_action = 0; +char * pax_invalid_action_write_path = NULL; +char * pax_invalid_action_write_cwd = NULL; + +char + *path_g, *path_x, *path_g_current, *path_x_current, + *uname_g, *uname_x, *uname_g_current, *uname_x_current, + *gname_g, *gname_x, *gname_g_current, *gname_x_current, + *comment_g, *comment_x, *comment_g_current, *comment_x_current, + *charset_g, *charset_x, *charset_g_current, *charset_x_current, + *atime_g, *atime_x, *atime_g_current, *atime_x_current, + *gid_g, *gid_x, *gid_g_current, *gid_x_current, + *linkpath_g, *linkpath_x, *linkpath_g_current, *linkpath_x_current, + *mtime_g, *mtime_x, *mtime_g_current, *mtime_x_current, + *size_g, *size_x, *size_g_current, *size_x_current, + *uid_g, *uid_x, *uid_g_current, *uid_x_current; + +char *header_name_g_requested = NULL, + *header_name_x_requested = NULL; + +char *header_name_g = "/tmp/GlobalHead.%p.%n", + *header_name_x = "%d/PaxHeaders.%p/%f"; + +int nglobal_headers = 0; +char *pax_list_opt_format; + +#define O_OPTION_ACTION_NOTIMPL 0 +#define O_OPTION_ACTION_INVALID 1 +#define O_OPTION_ACTION_DELETE 2 +#define O_OPTION_ACTION_STORE_HEADER 3 +#define O_OPTION_ACTION_TIMES 4 +#define O_OPTION_ACTION_HEADER_NAME 5 +#define O_OPTION_ACTION_LISTOPT 6 +#define O_OPTION_ACTION_LINKDATA 7 + +#define O_OPTION_ACTION_IGNORE 8 +#define O_OPTION_ACTION_ERROR 9 +#define O_OPTION_ACTION_STORE_HEADER2 10 + +#define ATTRSRC_FROM_NOWHERE 0 +#define ATTRSRC_FROM_X_O_OPTION 1 +#define ATTRSRC_FROM_G_O_OPTION 2 +#define ATTRSRC_FROM_X_HEADER 3 +#define ATTRSRC_FROM_G_HEADER 4 + +#define KW_PATH_CASE 0 +#define KW_SKIP_CASE -1 +#define KW_ATIME_CASE -2 + +typedef struct { + char * name; + int len; + int active; /* 1 means active, 0 means deleted via -o delete= */ + int cmdline_action; + int header_action; + /* next 2 entries only used by store_header actions */ + char ** g_value; /* -o keyword= value */ + char ** x_value; /* -o keyword:= value */ + char ** g_value_current; /* keyword= value found in Global extended header */ + char ** x_value_current; /* keyword= value found in extended header */ + int header_inx; /* starting index of header field this keyword represents */ + int header_len; /* length of header field this keyword represents */ + /* If negative, special cases line path= */ +} O_OPTION_TYPE; + +O_OPTION_TYPE o_option_table[] = { + { "atime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER, + &atime_g, &atime_x, &atime_g_current, &atime_x_current, 0, KW_ATIME_CASE }, + { "charset", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE, + &charset_g, &charset_x, &charset_g_current, &charset_x_current, 0, KW_SKIP_CASE }, + { "comment", 7, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_IGNORE, + &comment_g, &comment_x, &comment_g_current, &comment_x_current, 0, KW_SKIP_CASE }, + { "gid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2, + &gid_g, &gid_x, &gid_g_current, &gid_x_current , 116, 8 }, + { "gname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2, + &gname_g, &gname_x, &gname_g_current, &gname_x_current, 297, 32 }, + { "linkpath", 8, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER, + &linkpath_g, &linkpath_x, &linkpath_g_current, &linkpath_x_current, 0, KW_SKIP_CASE }, + { "mtime", 5, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER, + &mtime_g, &mtime_x, &mtime_g_current, &mtime_x_current, 136, KW_SKIP_CASE }, + { "path", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER, + &path_g, &path_x, &path_g_current, &path_x_current, 0, KW_PATH_CASE }, + { "size", 4, 1, O_OPTION_ACTION_STORE_HEADER, O_OPTION_ACTION_STORE_HEADER, + &size_g, &size_x, &size_g_current, &size_x_current, 124, KW_SKIP_CASE }, + { "uid", 3, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2, + &uid_g, &uid_x, &uid_g_current, &uid_x_current, 108, 8 }, + { "uname", 5, 1, O_OPTION_ACTION_STORE_HEADER2, O_OPTION_ACTION_STORE_HEADER2, + &uname_g, &uname_x, &uname_g_current, &uname_x_current, 265, 32 }, + + { "exthdr.name", 11, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR, + &header_name_x, &header_name_x_requested, NULL, NULL, 0, KW_SKIP_CASE }, + { "globexthdr.name", 15, 1, O_OPTION_ACTION_HEADER_NAME, O_OPTION_ACTION_ERROR, + &header_name_g, &header_name_g_requested, NULL, NULL, 0, KW_SKIP_CASE }, + + { "delete", 6, 1, O_OPTION_ACTION_DELETE, O_OPTION_ACTION_ERROR, + NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE }, + { "invalid", 7, 1, O_OPTION_ACTION_INVALID, O_OPTION_ACTION_ERROR, + NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE }, + { "linkdata", 8, 1, O_OPTION_ACTION_LINKDATA, O_OPTION_ACTION_ERROR, + NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE }, /* Test 241 */ + { "listopt", 7, 1, O_OPTION_ACTION_LISTOPT, O_OPTION_ACTION_ERROR, + &pax_list_opt_format, NULL, NULL, NULL, 0, KW_SKIP_CASE }, /* Test 242 */ + /* Note: listopt is supposed to apply for all formats, not just -x pax only */ + { "times", 5, 1, O_OPTION_ACTION_TIMES, O_OPTION_ACTION_ERROR, + NULL, NULL, NULL, NULL, 0, KW_SKIP_CASE }, +}; + +int ext_header_inx, + global_ext_header_inx; + +/* Make these tables big enough to handle lots of -o options, not just one per table entry */ +int ext_header_entry [4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)], + global_ext_header_entry[4*sizeof(o_option_table)/sizeof(O_OPTION_TYPE)]; + +/* + * Routines for reading, writing and header identify of various versions of pax + */ + +static size_t expandname(char *, size_t, char **, const char *, size_t); +static u_long pax_chksm(char *, int); +static char *name_split(char *, int); +static int ul_oct(u_long, char *, int, int); +#ifndef LONG_OFF_T +static int uqd_oct(u_quad_t, char *, int, int); +#endif + +static uid_t uid_nobody; +static uid_t uid_warn; +static gid_t gid_nobody; +static gid_t gid_warn; + +/* + * Routines common to all versions of pax + */ + +/* + * ul_oct() + * convert an unsigned long to an octal string. many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +ul_oct(u_long val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch (term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == (u_long)0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef LONG_OFF_T +/* + * uqd_oct() + * convert an u_quad_t to an octal string. one of many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +uqd_oct(u_quad_t val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch (term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == 0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif + +/* + * pax_chksm() + * calculate the checksum for a pax block counting the checksum field as + * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks). + * NOTE: we use len to short circuit summing 0's on write since we ALWAYS + * pad headers with 0. + * Return: + * unsigned long checksum + */ + +static u_long +pax_chksm(char *blk, int len) +{ + char *stop; + char *pt; + u_long chksm = BLNKSUM; /* initial value is checksum field sum */ + + /* + * add the part of the block before the checksum field + */ + pt = blk; + stop = blk + CHK_OFFSET; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + /* + * move past the checksum field and keep going, spec counts the + * checksum field as the sum of 8 blanks (which is pre-computed as + * BLNKSUM). + * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding + * starts, no point in summing zero's) + */ + pt += CHK_LEN; + stop = blk + len; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + return(chksm); +} + +void +pax_format_list_output(ARCHD *arcn, time_t now, FILE *fp, int term) +{ + /* parse specified listopt format */ + char *nextpercent, *nextchar; + char buf[4*1024]; + int pos, cpylen; + char *fname; + + nextpercent = strchr(pax_list_opt_format,'%'); + if (nextpercent==NULL) { + /* Strange case: no specifiers? */ + safe_print(pax_list_opt_format, fp); + (void)putc(term, fp); + (void)fflush(fp); + return; + } + pos = nextpercent-pax_list_opt_format; + memcpy(buf,pax_list_opt_format, pos); + while (nextpercent++) { + switch (*nextpercent) { + case 'F': + fname = arcn->name; + cpylen = strlen(fname); + memcpy(&buf[pos],fname,cpylen); + pos+= cpylen; + break; + case 'D': + case 'T': + case 'M': + case 'L': + default: + paxwarn(1, "Unimplemented listopt format: %c",*nextpercent); + break; + } + nextpercent++; + if (*nextpercent=='\0') { + break; + } + nextchar = nextpercent; + nextpercent = strchr(nextpercent,'%'); + if (nextpercent==NULL) { + cpylen = strlen(nextchar); + } else { + cpylen = nextpercent - nextchar; + } + memcpy(&buf[pos],nextchar, cpylen); + pos += cpylen; + } + buf[pos]='\0'; + safe_print(&buf[0], fp); + (void)putc(term, fp); + (void)fflush(fp); + return; +} + +void +cleanup_pax_invalid_action() +{ + switch (pax_invalid_action) { + case PAX_INVALID_ACTION_BYPASS: + case PAX_INVALID_ACTION_RENAME: + break; + case PAX_INVALID_ACTION_WRITE: + pax_invalid_action_write_path = NULL; + if (pax_invalid_action_write_cwd) { + free(pax_invalid_action_write_cwd); + pax_invalid_action_write_cwd = NULL; + } + break; + case PAX_INVALID_ACTION_UTF8: + default: + paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action); + } +} + +void +record_pax_invalid_action_results(ARCHD * arcn, char * fixed_path) +{ + switch (pax_invalid_action) { + case PAX_INVALID_ACTION_BYPASS: + case PAX_INVALID_ACTION_RENAME: + break; + case PAX_INVALID_ACTION_WRITE: + pax_invalid_action_write_path = fixed_path; + pax_invalid_action_write_cwd = strdup(arcn->name); + pax_invalid_action_write_cwd[fixed_path-arcn->name-1] = '\0'; + break; + case PAX_INVALID_ACTION_UTF8: + default: + paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action); + } +} + +int +perform_pax_invalid_action(ARCHD * arcn, int err) +{ + int rc = 0; + switch (pax_invalid_action) { + case PAX_INVALID_ACTION_BYPASS: + rc = -1; + break; + case PAX_INVALID_ACTION_RENAME: + rc = tty_rename(arcn); + break; + case PAX_INVALID_ACTION_WRITE: + pax_invalid_action_write_path = NULL; + pax_invalid_action_write_cwd = NULL; + rc = 2; + break; + case PAX_INVALID_ACTION_UTF8: + default: + paxwarn(1, "pax_invalid_action not implemented:%d", pax_invalid_action); + rc = -1; /* do nothing? */ + } + return rc; +} + +static void +delete_keywords(char * pattern) +{ + int i; + /* loop over all keywords, marking any matched as deleted */ + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + if (fnmatch(pattern, o_option_table[i].name, 0) == 0) { + /* Found option: mark deleted */ + o_option_table[i].active = 0; + } + } +} + +/* + * pax_opt() + * handle pax format specific -o options + * Return: + * 0 if ok -1 otherwise + */ + +int +pax_opt(void) +{ + OPLIST *opt; + int got_option = 0; + + while ((opt = opt_next()) != NULL) { + int i; + got_option = -1; + pax_invalid_action = PAX_INVALID_ACTION_BYPASS; /* Default for pax format */ + /* look up opt->name */ + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + if (strncasecmp(opt->name, o_option_table[i].name, o_option_table[i].len) == 0) { + /* Found option: see if already set */ + /* Save it away */ + got_option = 1; + switch (o_option_table[i].cmdline_action) { + case O_OPTION_ACTION_INVALID: + if (opt->separator != SEP_EQ) { + paxwarn(1,"-o %s= option requires '=' separator: option ignored", + opt->name); + break; + } + if (opt->value) { + if (strncasecmp(opt->value,"bypass",6) == 0) { + pax_invalid_action = PAX_INVALID_ACTION_BYPASS; + } else if (strncasecmp(opt->value,"rename",6) == 0) { + pax_invalid_action = PAX_INVALID_ACTION_RENAME; + } else if (strncasecmp(opt->value,"UTF-8",5) == 0) { + pax_invalid_action = PAX_INVALID_ACTION_UTF8; + } else if (strncasecmp(opt->value,"write",5) == 0) { + pax_invalid_action = PAX_INVALID_ACTION_WRITE; + } else { + paxwarn(1,"Invalid action %s not recognized: option ignored", + opt->value); + } + } else { + paxwarn(1,"Invalid action RHS not specified: option ignored"); + } + break; + case O_OPTION_ACTION_DELETE: + if (opt->separator != SEP_EQ) { + paxwarn(1,"-o %s= option requires '=' separator: option ignored", + opt->name); + break; + } + /* Mark all matches as deleted */ + /* can have multiple -o delete= patterns */ + delete_keywords(opt->value); + break; + case O_OPTION_ACTION_STORE_HEADER2: + if(pax_read_or_list_mode) pids = 1; /* Force -p o for these options */ + case O_OPTION_ACTION_STORE_HEADER: + if (o_option_table[i].g_value == NULL || + o_option_table[i].x_value == NULL ) { + paxwarn(1,"-o option not implemented: %s=%s", + opt->name, opt->value); + } else { + if (opt->separator == SEP_EQ) { + *(o_option_table[i].g_value) = opt->value; + global_ext_header_entry[global_ext_header_inx++] = i; + } else if (opt->separator == SEP_COLONEQ ) { + *(o_option_table[i].x_value) = opt->value; + ext_header_entry [ext_header_inx++] = i; + } else { /* SEP_NONE */ + paxwarn(1,"-o %s option is missing value", opt->name); + } + } + break; + case O_OPTION_ACTION_TIMES: + if (opt->separator != SEP_NONE) { + paxwarn(1,"-o %s option takes no value: option ignored", opt->name); + break; + } + want_a_m_time_headers = 1; + break; + case O_OPTION_ACTION_LINKDATA: + if (opt->separator != SEP_NONE) { + paxwarn(1,"-o %s option takes no value: option ignored", opt->name); + break; + } + want_linkdata = 1; + break; + case O_OPTION_ACTION_HEADER_NAME: + if (opt->separator != SEP_EQ) { + paxwarn(1,"-o %s= option requires '=' separator: option ignored", + opt->name); + break; + } + *(o_option_table[i].g_value) = opt->value; + *(o_option_table[i].x_value) = "YES"; + break; + case O_OPTION_ACTION_LISTOPT: + if (opt->separator != SEP_EQ) { + paxwarn(1,"-o %s= option requires '=' separator: option ignored", + opt->name); + break; + } + *(o_option_table[i].g_value) = opt->value; + break; + case O_OPTION_ACTION_NOTIMPL: + default: + paxwarn(1,"pax format -o option not yet implemented: %s=%s", + opt->name, opt->value); + break; + } + break; + } + } + if (got_option == -1) { + paxwarn(1,"pax format -o option not recognized: %s=%s", + opt->name, opt->value); + } + } + return(0); +} + +static int +expand_extended_headers(ARCHD *arcn, HD_USTAR *hd) +{ + char mybuf[BLKMULT]; + HD_USTAR *myhd; + char * current_value; + int path_replaced = 0; + int i, len; + + myhd = hd; + while (myhd->typeflag == PAXGTYPE || myhd->typeflag == PAXXTYPE) { + char *name, *str; + int size, nbytes, inx; + size = asc_ul(myhd->size, sizeof(myhd->size), OCT); + if (size > sizeof(mybuf)) { + paxwarn(1,"extended header buffer overflow"); + exit(1); + } + nbytes = rd_wrbuf(mybuf, size); + if (nbytes != size) { + paxwarn(1,"extended header data read failure: nbytes=%d, size=%d\n", + nbytes, size); + exit(1); + } + /* + printf("Read 1 extended header: type=%c, size=%d\n", + myhd->typeflag, size); + */ + inx=0; + /* loop over buffer collecting attributes */ + while (nbytes > 0) { + int got_option = -1; + int nentries = sscanf(&mybuf[inx],"%d ", &len); + if (nentries != 1) { + paxwarn(1,"Extended header failure: length"); + exit(1); + } + if (len < 0 || (inx+len-1 >= sizeof(mybuf))) { + paxwarn(1, "Extended header failure: invalid length (%d)", len); + exit(1); + } + if (mybuf[inx+len-1] != '\n') { + paxwarn(1,"Extended header failure: missed newline"); + exit(1); + } else + mybuf[inx+len-1] = '\0'; + name = strchr(&mybuf[inx],' '); + if (name) name++; + else { + paxwarn(1,"Extended header failure: missing space"); + exit(1); + } + str = strchr(name,'='); + if (str) { + *str++='\0'; /* end of name */ + } else { + paxwarn(1,"Extended header failure: missing RHS string"); + exit(1); + } + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + if (strncasecmp(name, o_option_table[i].name, o_option_table[i].len) == 0) { + /* Found option: see if already set TBD */ + /* Save it away */ + got_option = i; + break; + } + } + if (got_option == -1) { + paxwarn(1,"Unrecognized header keyword: %s",name); + } else { + /* Determine precedence of -o and header attributes */ + int found_value = ATTRSRC_FROM_NOWHERE; + current_value = NULL; + if (myhd->typeflag == PAXXTYPE) { + if (*o_option_table[got_option].x_value) { + current_value = *o_option_table[got_option].x_value; + found_value = ATTRSRC_FROM_X_O_OPTION; + } else { + current_value = str; + found_value = ATTRSRC_FROM_X_HEADER; + } + } else if (myhd->typeflag == PAXGTYPE) { + if (*o_option_table[got_option].g_value) { + current_value = *o_option_table[got_option].g_value; + found_value = ATTRSRC_FROM_G_O_OPTION; + } else { + current_value = str; + found_value = ATTRSRC_FROM_G_HEADER; + } + } else { + paxwarn(1,"Unsupported header type:%c",myhd->typeflag); + } + if (current_value) { + /* Save this attribute value for use later */ + switch (o_option_table[got_option].header_action) { + case O_OPTION_ACTION_IGNORE: + paxwarn(1,"ignoring header keyword: %s",name); + break; + case O_OPTION_ACTION_STORE_HEADER2: + case O_OPTION_ACTION_STORE_HEADER: + switch (found_value) { + case ATTRSRC_FROM_NOWHERE: /* shouldn't happen */ + paxwarn(1, "internal error: value from nowhere"); + break; + case ATTRSRC_FROM_X_O_OPTION: + case ATTRSRC_FROM_G_O_OPTION: + break; + case ATTRSRC_FROM_X_HEADER: + current_value = strdup(current_value); + if(*o_option_table[got_option].x_value_current) + free(*o_option_table[got_option].x_value_current); + *o_option_table[got_option].x_value_current = current_value; + break; + case ATTRSRC_FROM_G_HEADER: + current_value = strdup(current_value); + if(*o_option_table[got_option].g_value_current) + free(*o_option_table[got_option].g_value_current); + *o_option_table[got_option].g_value_current = current_value; + break; + } + break; + case O_OPTION_ACTION_ERROR: + default: + paxwarn(1,"Unsupported extended header attribute: %s=%s", + name, str); + } + } + } + inx+=len; + nbytes -= len; + } + + /* position file at next header */ + (void)rd_skip(TAR_PAD(size)); + + /* read next header */ + nbytes = rd_wrbuf(mybuf, frmt->hsz); + if (nbytes != frmt->hsz) { + paxwarn(1,"extended header read failure: nbytes=%d, size=%d\n", + nbytes, frmt->hsz); + } + myhd = ((HD_USTAR *)mybuf); + /* repeat until no more extended headers */ + } + + /* The header about to be returned must now be updated using all the extended + header values collected and any command line options */ + /* Acceleration: check during command option processing. If there are no -o + options, and no changes from any header, do not need to run through this loop. */ + + for (i = 0; i < sizeof(o_option_table)/sizeof(O_OPTION_TYPE); i++) { + int header_len, free_it; + if (!o_option_table[i].active) { + continue; /* deleted keywords */ + } + header_len = o_option_table[i].header_len; + if (header_len == KW_SKIP_CASE) { + continue; + } + free_it = 0; + /* Calculate values for all non-skip keywords */ + current_value = NULL; + if (o_option_table[i].x_value) { + current_value = *o_option_table[i].x_value; + } + if (!current_value) { /* No -o := */ + if (o_option_table[i].x_value_current) { + current_value = *o_option_table[i].x_value_current; + } + if (current_value) { + /* Must remove it: x header values not valid beyond this header */ + *o_option_table[i].x_value_current = NULL; + free_it = 1; + } else { /* No x values, try globals */ + current_value = *o_option_table[i].g_value; + if (!current_value) { + current_value = *o_option_table[i].g_value_current; + } + } + } + if (current_value) { + /* Update current header with this value */ + /* + printf ("Found current_value:%s for %s, pids=%d\n", + current_value, o_option_table[i].name, pids); + */ + len = strlen(current_value); + if (header_len == KW_ATIME_CASE) { + time_t asecs = strtoul(current_value, NULL, 10); + arcn->sb.st_atimespec.tv_sec = asecs; + } else if (header_len == KW_PATH_CASE) { /* Special case for path keyword */ + path_replaced = 1; + arcn->nlen = len; + strlcpy(arcn->name,current_value,sizeof(arcn->name)); + } else if (header_len >= 0) { // Skip negative values + if (len > header_len) { + paxwarn(1," length of string from extended header bigger than header field:" + " THAT won't work!\n"); + } else { + char * p = (char *) myhd; + memcpy(&p[o_option_table[i].header_inx], + current_value, len); + if (len != header_len) { + /* pad with ? */ + p[o_option_table[i].header_inx+len] = '\0'; + } + } + } + if (free_it) { + free(current_value); + } + } + } + + if (myhd==hd) return(path_replaced); + + /* must put new header into memory of original */ + memcpy(hd, myhd, sizeof(HD_USTAR)); + + return(path_replaced); +} + +/* + * pax_id() + * determine if a block given to us is a valid pax header. We have to + * be on the lookout for those pesky blocks of all zero's + * Return: + * 0 if a ustar header, -1 otherwise + */ + +int +pax_id(char *blk, int size) +{ + HD_USTAR *hd; + + if (size < BLKMULT) + return(-1); + hd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test then check + * ustar magic cookie. We should use TMAGLEN, but some USTAR archive + * programs are fouled up and create archives missing the \0. Last we + * check the checksum. If ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != pax_chksm(blk,BLKMULT)) + return(-1); + if ((hd->typeflag != PAXXTYPE) && (hd->typeflag != PAXGTYPE)) { + /* Not explicitly pax format, but at least ustar */ + if (act==LIST || act==EXTRACT) { + /* Although insufficient evidence, call it pax format */ + return(0); + } + return(-1); + } + pax_invalid_action = PAX_INVALID_ACTION_BYPASS; /* Default for pax format */ + return(0); +} + +/* + * pax_rd() + * extract the values out of block already determined to be a pax header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +int +pax_rd(ARCHD *arcn, char *buf) +{ + HD_USTAR *hd; + int cnt = 0; + int check_path; + dev_t devmajor; + dev_t devminor; + + /* + * we only get proper sized buffers + */ + if (pax_id(buf, BLKMULT) < 0) + return(-1); + + memset(arcn, 0, sizeof(*arcn)); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + hd = (HD_USTAR *)buf; + + check_path = expand_extended_headers(arcn, hd); + + if (check_path) { + /* + * pathname derived from extended head or -o option; + * full name is in one string, but length may exceed + * max path so be careful. + */ + if (arcn->nlen > sizeof(arcn->name)) { + paxwarn(1,"pathname from extended header info doesn't fit! (len=%d)\n", + arcn->nlen); + } + } else { + /* + * see if the filename is split into two parts. if so, join the parts. + * we copy the prefix first and add a / between the prefix and name. + */ + char *dest = arcn->name; + if (*(hd->prefix) != '\0') { + cnt = strlcpy(dest, hd->prefix, sizeof(arcn->name) - 1); + dest += cnt; + *dest++ = '/'; + cnt++; + } else { + cnt = 0; + } + + if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { + arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt, + &gnu_name_string, hd->name, sizeof(hd->name)); + arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), + &gnu_link_string, hd->linkname, sizeof(hd->linkname)); + } + } + + /* + * follow the spec to the letter. we should only have mode bits, strip + * off all other crud we may be passed. + */ + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & + 0xfff); +#ifdef LONG_OFF_T + arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT); +#else + arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT); +#endif + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + if (arcn->sb.st_atimespec.tv_sec == 0) { // Can be set from header + arcn->sb.st_atime = arcn->sb.st_mtime; + } + arcn->sb.st_ctime = arcn->sb.st_mtime; + + /* + * If we can find the ascii names for gname and uname in the password + * and group files we will use the uid's and gid they bind. Otherwise + * we use the uid and gid values stored in the header. (This is what + * the posix spec wants). + */ + hd->gname[sizeof(hd->gname) - 1] = '\0'; + if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + hd->uname[sizeof(hd->uname) - 1] = '\0'; + if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + + /* + * set the defaults, these may be changed depending on the file type + */ + arcn->pad = 0; + arcn->skip = 0; + arcn->sb.st_rdev = (dev_t)0; + + /* + * set the mode and PAX type according to the typeflag in the header + */ + switch (hd->typeflag) { + case FIFOTYPE: + arcn->type = PAX_FIF; + arcn->sb.st_mode |= S_IFIFO; + break; + case DIRTYPE: + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + + /* + * Some programs that create pax archives append a '/' + * to the pathname for directories. This clearly violates + * pax specs, but we will silently strip it off anyway. + */ + if (arcn->name[arcn->nlen - 1] == '/') + arcn->name[--arcn->nlen] = '\0'; + break; + case BLKTYPE: + case CHRTYPE: + /* + * this type requires the rdev field to be set. + */ + if (hd->typeflag == BLKTYPE) { + arcn->type = PAX_BLK; + arcn->sb.st_mode |= S_IFBLK; + } else { + arcn->type = PAX_CHR; + arcn->sb.st_mode |= S_IFCHR; + } + devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); + devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + break; + case SYMTYPE: + case LNKTYPE: + if (hd->typeflag == SYMTYPE) { + arcn->type = PAX_SLK; + arcn->sb.st_mode |= S_IFLNK; + } else { + arcn->type = PAX_HLK; + /* + * so printing looks better + */ + arcn->sb.st_mode |= S_IFREG; + arcn->sb.st_nlink = 2; + } + break; + case LONGLINKTYPE: + case LONGNAMETYPE: + /* + * GNU long link/file; we tag these here and let the + * pax internals deal with it -- too ugly otherwise. + */ + arcn->type = + hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + break; + case CONTTYPE: + case AREGTYPE: + case REGTYPE: + default: + /* + * these types have file data that follows. Set the skip and + * pad fields. + */ + arcn->type = PAX_REG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + arcn->sb.st_mode |= S_IFREG; + break; + } + return(0); +} + +void +adjust_copy_for_pax_options(ARCHD * arcn) +{ + /* Because ext_header options take precedence over global_header options, apply + global options first, then override with any extended header options */ + int i; + if (global_ext_header_inx) { + for (i=0; i < global_ext_header_inx; i++) { + if (!o_option_table[global_ext_header_entry[i]].active) continue; /* deleted keywords */ + if (strcmp(o_option_table[global_ext_header_entry[i]].name, "path")==0) { + strlcpy(arcn->name,*(o_option_table[global_ext_header_entry[i]].g_value), + sizeof(arcn->name)); + arcn->nlen = strlen(*(o_option_table[global_ext_header_entry[i]].g_value)); + } else { /* only handle path for now: others TBD */ + paxwarn(1, "adjust arcn for global extended header options not implemented:%d", i); + } + } + } + if (ext_header_inx) { + for (i=0; i < ext_header_inx; i++) { + if (!o_option_table[ext_header_entry[i]].active) continue; /* deleted keywords */ + if (strcmp(o_option_table[ext_header_entry[i]].name, "path")==0) { + strlcpy(arcn->name,*(o_option_table[ext_header_entry[i]].x_value), + sizeof(arcn->name)); + arcn->nlen = strlen(*(o_option_table[ext_header_entry[i]].x_value)); + } else { /* only handle path for now: others TBD */ + paxwarn(1, "adjust arcn for extended header options not implemented:%d", i); + } + } + } + if (want_a_m_time_headers) { + /* TBD */ + } +} + +static int +emit_extended_header_record(int len, int total_len, int head_type, + char * name, char * value) +{ + if (total_len + len > sizeof(pax_eh_datablk)) { + paxwarn(1,"extended header buffer overflow for header type '%c': %d", + head_type, total_len+len); + } else { + sprintf(&pax_eh_datablk[total_len],"%d %s=%s\n", len, name, value); + total_len += len; + } + return (total_len); +} + +__attribute__((__malloc__)) +static char * +substitute_percent(char * header, char * filename) +{ + char *nextpercent, *nextchar; + char buf[4*1024]; + int pos, cpylen; + char *dname, *fname; + + nextpercent = strchr(header,'%'); + if (nextpercent==NULL) return strdup(header); + pos = nextpercent-header; + memcpy(buf,header, pos); + while (nextpercent++) { + switch (*nextpercent) { + case '%': + buf[pos++]='%'; /* just skip it */ + break; + case 'd': + dname = strrchr(filename,'/'); + if (dname==NULL) { + cpylen = 1; + dname = "."; + } else { + cpylen = dname-filename; + dname = filename; + } + memcpy(&buf[pos],dname,cpylen); + pos+= cpylen; + break; + case 'f': + fname = strrchr(filename,'/'); + if (fname==NULL) { + fname = filename; + } else { + fname++; + } + cpylen = strlen(fname); + memcpy(&buf[pos],fname,cpylen); + pos+= cpylen; + break; + case 'n': + pos += sprintf (&buf[pos],"%d",nglobal_headers); + break; + case 'p': + pos += sprintf (&buf[pos],"%d",getpid()); + break; + default: + paxwarn(1,"header format substitution failed: '%c'", *nextpercent); + return strdup(header); + } + nextpercent++; + if (*nextpercent=='\0') { + break; + } + nextchar = nextpercent; + nextpercent = strchr(nextpercent,'%'); + if (nextpercent==NULL) { + cpylen = strlen(nextchar); + } else { + cpylen = nextpercent - nextchar; + } + memcpy(&buf[pos],nextchar, cpylen); + pos += cpylen; + } + buf[pos]='\0'; + return (strdup(&buf[0])); +} + +static int +generate_pax_ext_header_and_data(ARCHD *arcn, int nfields, int *table, + char header_type, char * header_name, char * header_name_requested) +{ + HD_USTAR *hd; + char hdblk[sizeof(HD_USTAR)]; + u_long records_size; + int term_char, i, len, total_len; + char * str, *name; + + if (nfields == 0 && (header_name_requested == NULL)) { + if (header_type==PAXXTYPE) { + if (!want_a_m_time_headers) return (0); + } else + return (0); + } + + /* There might be no fields but a header with a specific name or + times might be wanted */ + + term_char = 1; + memset(hdblk, 0, sizeof(hdblk)); + hd = (HD_USTAR *)hdblk; + memset(pax_eh_datablk, 0, sizeof(pax_eh_datablk)); + + /* generate header */ + hd->typeflag = header_type; + + /* These fields appear to be necessary to be able to treat extended headers + like files in older versions of pax */ + ul_oct((u_long)0444, hd->mode, sizeof(hd->mode), term_char); + strncpy(hd->magic, TMAGIC, TMAGLEN); + strncpy(hd->version, TVERSION, TVERSLEN); + ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char); + + /* compute size of data */ + total_len = 0; + for (i=0; i < nfields; i++) { + if (!o_option_table[table[i]].active) continue; /* deleted keywords */ + name = o_option_table[table[i]].name; + if (header_type == PAXXTYPE) { + str = *(o_option_table[table[i]].x_value); + } else { + str = *(o_option_table[table[i]].g_value); + } + if (str==NULL) { + paxwarn(1,"Missing option value for %s", name); + continue; + } + len = strlen(str) + o_option_table[table[i]].len + 3; + if (len < 9) len++; + else if (len < 98) len = len + 2; + else if (len < 997) len = len + 3; + else if (len < 9996) len = len + 4; + else { + paxwarn(1,"extended header data too long for header type '%c': %d", + header_type, len); + } + total_len = emit_extended_header_record(len, total_len, + header_type, name, str); + } + + if ((header_type == PAXXTYPE) && want_a_m_time_headers) { + char time_buffer[12]; + memset(time_buffer,0,sizeof(time_buffer)); + sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_atime); + /* 3 chars + strlen("atime") + time + # chars in len */ + len = 3 + 5 + strlen(&time_buffer[0]) + 2; + total_len = emit_extended_header_record(len, total_len, + header_type, "atime", &time_buffer[0]); + memset(time_buffer,0,sizeof(time_buffer)); + sprintf(&time_buffer[0],"%d",(int)arcn->sb.st_mtime); + /* 3 chars + strlen("mtime") + time + # chars in len */ + len = 3 + 5 + strlen(&time_buffer[0]) + 2; + total_len = emit_extended_header_record(len, total_len, + header_type, "mtime", &time_buffer[0]); + } + + /* Check if all fields were deleted: might not need to generate anything */ + if ((total_len==0) && (header_name_requested == NULL)) return (0); + + if (header_type == PAXGTYPE) nglobal_headers++; + /* substitution of fields in header_name */ + header_name = substitute_percent(header_name, arcn->name); + if (strlen(header_name) == sizeof(hd->name)) { /* must account for name just fits in buffer */ + strncpy(hd->name, header_name, sizeof(hd->name)); + } else { + strlcpy(hd->name, header_name, sizeof(hd->name)); + } + + free(header_name); + header_name = NULL; + records_size = (u_long)total_len; + if (ul_oct(records_size, hd->size, sizeof(hd->size), term_char)) { + paxwarn(1,"extended header data too long for header type '%c'", header_type); + return(1); + } + + if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, sizeof(hd->chksum), term_char)) { + paxwarn(1,"extended header data checksum failed: header type '%c'", header_type); + return(1); + } + + /* write out header */ + if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + /* write out header data */ + if (total_len > 0) { + if (wr_rdbuf(pax_eh_datablk, total_len) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - total_len)) < 0) + return(-1); + /* + printf("data written:\n%s",&pax_eh_datablk[0]); + */ + } + + /* + paxwarn(0,"extended header and data written: header type '%c', #items: %d, %d characters", + header_type, nfields, records_size); + */ + return (0); +} + +/* + * pax_wr() + * write a pax header for the file specified in the ARCHD to the archive + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, we only use + * '\0' for the termination character (this is different than picky tar) + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +pax_wr(ARCHD *arcn) +{ + HD_USTAR *hd; + char *pt; + char hdblk[sizeof(HD_USTAR)]; + mode_t mode12only; + int term_char=3; /* orignal setting */ + term_char=1; /* To pass conformance tests 274, 301 */ + + /* + * check for those file system types pax cannot store + */ + if (arcn->type == PAX_SCK) { + paxwarn(1, "Pax cannot archive a socket %s", arcn->org_name); + return(1); + } + + /* + * check the length of the linkname + */ + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){ + paxwarn(1, "Link name too long for pax %s", arcn->ln_name); + /* + * Conformance: test pax:285 wants error code to be non-zero, and + * test tar:12 wants error code from pax to be 0 + */ + return(1); + } + + /* + * split the path name into prefix and name fields (if needed). if + * pt != arcn->name, the name has to be split + */ + if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { + paxwarn(1, "File name too long for pax %s", arcn->name); + return(1); + } + + generate_pax_ext_header_and_data(arcn, global_ext_header_inx, &global_ext_header_entry[0], + PAXGTYPE, header_name_g, header_name_g_requested); + generate_pax_ext_header_and_data(arcn, ext_header_inx, &ext_header_entry[0], + PAXXTYPE, header_name_x, header_name_x_requested); + + /* + * zero out the header so we don't have to worry about zero fill below + */ + memset(hdblk, 0, sizeof(hdblk)); + hd = (HD_USTAR *)hdblk; + arcn->pad = 0L; + /* To pass conformance tests 274/301, always set these fields to "zero" */ + ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char); + ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char); + + /* + * split the name, or zero out the prefix + */ + if (pt != arcn->name) { + /* + * name was split, pt points at the / where the split is to + * occur, we remove the / and copy the first part to the prefix + */ + *pt = '\0'; + strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix)); + *pt++ = '/'; + } + + /* + * copy the name part. this may be the whole path or the part after + * the prefix + */ + if (strlen(pt) == sizeof(hd->name)) { /* must account for name just fits in buffer */ + strncpy(hd->name, pt, sizeof(hd->name)); + } else { + strlcpy(hd->name, pt, sizeof(hd->name)); + } + + /* + * set the fields in the header that are type dependent + */ + switch (arcn->type) { + case PAX_DIR: + hd->typeflag = DIRTYPE; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_CHR: + case PAX_BLK: + if (arcn->type == PAX_CHR) + hd->typeflag = CHRTYPE; + else + hd->typeflag = BLKTYPE; + if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, + sizeof(hd->devmajor), term_char) || + ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, + sizeof(hd->devminor), term_char) || + ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_FIF: + hd->typeflag = FIFOTYPE; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->type == PAX_SLK) + hd->typeflag = SYMTYPE; + else + hd->typeflag = LNKTYPE; + if (strlen(arcn->ln_name) == sizeof(hd->linkname)) { /* must account for name just fits in buffer */ + strncpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + } else { + strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + } + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_REG: + case PAX_CTG: + default: + /* + * file data with this type, set the padding + */ + if (arcn->type == PAX_CTG) + hd->typeflag = CONTTYPE; + else + hd->typeflag = REGTYPE; + arcn->pad = TAR_PAD(arcn->sb.st_size); +# ifdef LONG_OFF_T + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), term_char)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), term_char)) { +# endif + paxwarn(1,"File is too long for pax %s",arcn->org_name); + return(1); + } + break; + } + + strncpy(hd->magic, TMAGIC, TMAGLEN); + strncpy(hd->version, TVERSION, TVERSLEN); + + /* + * set the remaining fields. Some versions want all 16 bits of mode + * we better humor them (they really do not meet spec though).... + */ + if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), term_char)) { + if (uid_nobody == 0) { + if (uid_name("nobody", &uid_nobody) == -1) + goto out; + } + if (uid_warn != arcn->sb.st_uid) { + uid_warn = arcn->sb.st_uid; + paxwarn(1, + "Pax header field is too small for uid %lu, " + "using nobody", (u_long)arcn->sb.st_uid); + } + if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), term_char)) + goto out; + } + if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), term_char)) { + if (gid_nobody == 0) { + if (gid_name("nobody", &gid_nobody) == -1) + goto out; + } + if (gid_warn != arcn->sb.st_gid) { + gid_warn = arcn->sb.st_gid; + paxwarn(1, + "Pax header field is too small for gid %lu, " + "using nobody", (u_long)arcn->sb.st_gid); + } + if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), term_char)) + goto out; + } + /* However, Unix conformance tests do not like MORE than 12 mode bits: + remove all beyond (see definition of stat.st_mode structure) */ + mode12only = ((u_long)arcn->sb.st_mode) & 0x00000fff; + if (ul_oct((u_long)mode12only, hd->mode, sizeof(hd->mode), term_char) || + ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char)) + goto out; + strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname)); + strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname)); + + /* + * calculate and store the checksum write the header to the archive + * return 0 tells the caller to now write the file data, 1 says no data + * needs to be written + */ + if (ul_oct(pax_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, + sizeof(hd->chksum), term_char)) + goto out; + if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Pax header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * name_split() + * see if the name has to be split for storage in a ustar header. We try + * to fit the entire name in the name field without splitting if we can. + * The split point is always at a / + * Return + * character pointer to split point (always the / that is to be removed + * if the split is not needed, the points is set to the start of the file + * name (it would violate the spec to split there). A NULL is returned if + * the file name is too long + */ + +static char * +name_split(char *name, int len) +{ + char *start; + + /* + * check to see if the file name is small enough to fit in the name + * field. if so just return a pointer to the name. + */ + if (len <= TNMSZ) + return(name); + if (len > (TPFSZ + TNMSZ)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk forward looking for a slash to split at. The idea is + * to find the biggest piece to fit in the name field (or the smallest + * prefix we can find) + */ + start = name + len - TNMSZ -1; + if ((*start == '/') && (start == name)) + ++start; /* 101 byte paths with leading '/' are dinged otherwise */ + while ((*start != '\0') && (*start != '/')) + ++start; + + /* + * if we hit the end of the string, this name cannot be split, so we + * cannot store this file. + */ + if (*start == '\0') + return(NULL); + len = start - name; + + /* + * NOTE: /str where the length of str == TNMSZ can not be stored under + * the p1003.1-1990 spec for ustar. We could force a prefix of / and + * the file would then expand on extract to //str. The len == 0 below + * makes this special case follow the spec to the letter. + */ + if ((len >= TPFSZ) || (len == 0)) + return(NULL); + + /* + * ok have a split point, return it to the caller + */ + return(start); +} + +static size_t +expandname(char *buf, size_t len, char **gnu_name, const char *name, size_t name_len) +{ + size_t nlen; + + if (*gnu_name) { + if ((nlen = strlcpy(buf, *gnu_name, len)) >= len) + nlen = len - 1; + free(*gnu_name); + *gnu_name = NULL; + } else { + if (name_len < len) { + /* name may not be null terminated: it might be as big as the + field, so copy is limited to the max size of the header field */ + if ((nlen = strlcpy(buf, name, name_len+1)) >= name_len+1) + nlen = name_len; + } else { + if ((nlen = strlcpy(buf, name, len)) >= len) + nlen = len - 1; + } + } + return(nlen); +} diff --git a/file_cmds/pax/pax_format.h b/file_cmds/pax/pax_format.h new file mode 100644 index 00000000..506ce581 --- /dev/null +++ b/file_cmds/pax/pax_format.h @@ -0,0 +1,153 @@ +/* $OpenBSD: tar.h,v 1.7 2003/06/02 23:32:09 millert Exp $ */ +/* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tar.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * defines and data structures common to all tar formats + */ +#define CHK_LEN 8 /* length of checksum field */ +#define TNMSZ 100 /* size of name field */ +#ifdef _PAX_ +#define NULLCNT 2 /* number of null blocks in trailer */ +#define CHK_OFFSET 148 /* start of chksum field */ +#define BLNKSUM 256L /* sum of checksum field using ' ' */ +#endif /* _PAX_ */ + +/* + * Values used in typeflag field in all tar formats + * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers) + */ +#define REGTYPE '0' /* Regular File */ +#define AREGTYPE '\0' /* Regular File */ +#define LNKTYPE '1' /* Link */ +#define SYMTYPE '2' /* Symlink */ +#define CHRTYPE '3' /* Character Special File */ +#define BLKTYPE '4' /* Block Special File */ +#define DIRTYPE '5' /* Directory */ +#define FIFOTYPE '6' /* FIFO */ +#define CONTTYPE '7' /* high perf file */ + +/* + * GNU tar compatibility; + */ +#define LONGLINKTYPE 'K' /* Long Symlink */ +#define LONGNAMETYPE 'L' /* Long File */ + +/* + * Mode field encoding of the different file types - values in octal + */ +#define TSUID 04000 /* Set UID on execution */ +#define TSGID 02000 /* Set GID on execution */ +#define TSVTX 01000 /* Reserved */ +#define TUREAD 00400 /* Read by owner */ +#define TUWRITE 00200 /* Write by owner */ +#define TUEXEC 00100 /* Execute/Search by owner */ +#define TGREAD 00040 /* Read by group */ +#define TGWRITE 00020 /* Write by group */ +#define TGEXEC 00010 /* Execute/Search by group */ +#define TOREAD 00004 /* Read by other */ +#define TOWRITE 00002 /* Write by other */ +#define TOEXEC 00001 /* Execute/Search by other */ + +#ifdef _PAX_ +/* + * Pad with a bit mask, much faster than doing a mod but only works on powers + * of 2. Macro below is for block of 512 bytes. + */ +#define TAR_PAD(x) ((512 - ((x) & 511)) & 511) +#endif /* _PAX_ */ + +/* + * structure of an old tar header as it appeared in BSD releases + */ +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char linkflag; /* norm, hard, or sym. */ + char linkname[TNMSZ]; /* linked to name */ +} HD_TAR; + +#ifdef _PAX_ +/* + * -o options for BSD tar to not write directories to the archive + */ +#define TAR_NODIR "nodir" +#define TAR_OPTION "write_opt" + +/* + * default device names + */ +#define DEV_0 "/dev/rst0" +#define DEV_1 "/dev/rst1" +#define DEV_4 "/dev/rst4" +#define DEV_5 "/dev/rst5" +#define DEV_7 "/dev/rst7" +#define DEV_8 "/dev/rst8" +#endif /* _PAX_ */ + +/* + * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990 + */ +#define TPFSZ 155 +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGLEN 6 +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char typeflag; /* type of file. */ + char linkname[TNMSZ]; /* linked to name */ + char magic[TMAGLEN]; /* magic cookie */ + char version[TVERSLEN]; /* version */ + char uname[32]; /* ascii owner name */ + char gname[32]; /* ascii group name */ + char devmajor[8]; /* major device number */ + char devminor[8]; /* minor device number */ + char prefix[TPFSZ]; /* linked to name */ +} HD_USTAR; diff --git a/file_cmds/pax/sel_subs.c b/file_cmds/pax/sel_subs.c new file mode 100644 index 00000000..008479ba --- /dev/null +++ b/file_cmds/pax/sel_subs.c @@ -0,0 +1,614 @@ +/* $OpenBSD: sel_subs.c,v 1.18 2004/04/16 22:50:23 deraadt Exp $ */ +/* $NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: sel_subs.c,v 1.18 2004/04/16 22:50:23 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "sel_subs.h" +#include "extern.h" + +static int str_sec(const char *, time_t *); +static int usr_match(ARCHD *); +static int grp_match(ARCHD *); +static int trng_match(ARCHD *); + +static TIME_RNG *trhead = NULL; /* time range list head */ +static TIME_RNG *trtail = NULL; /* time range list tail */ +static USRT **usrtb = NULL; /* user selection table */ +static GRPT **grptb = NULL; /* group selection table */ + +/* + * Routines for selection of archive members + */ + +/* + * sel_chk() + * check if this file matches a specified uid, gid or time range + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +int +sel_chk(ARCHD *arcn) +{ + if (((usrtb != NULL) && usr_match(arcn)) || + ((grptb != NULL) && grp_match(arcn)) || + ((trhead != NULL) && trng_match(arcn))) + return(1); + return(0); +} + +/* + * User/group selection routines + * + * Routines to handle user selection of files based on the file uid/gid. To + * add an entry, the user supplies either the name or the uid/gid starting with + * a # on the command line. A \# will escape the #. + */ + +/* + * usr_add() + * add a user match to the user match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +int +usr_add(char *str) +{ + u_int indx; + USRT *pt; + struct passwd *pw; + uid_t uid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((usrtb == NULL) && + ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { + paxwarn(1, "Unable to allocate memory for user selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a user name, \# escapes # as first char in user name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((pw = getpwnam(str)) == NULL) { + paxwarn(1, "Unable to find uid for user: %s", str); + return(-1); + } + uid = (uid_t)pw->pw_uid; + } else + uid = (uid_t)strtoul(str+1, NULL, 10); + endpwent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)uid) % USR_TB_SZ; + if ((pt = usrtb[indx]) != NULL) { + while (pt != NULL) { + if (pt->uid == uid) + return(0); + pt = pt->fow; + } + } + + /* + * uid is not yet in the table, add it to the front of the chain + */ + if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { + pt->uid = uid; + pt->fow = usrtb[indx]; + usrtb[indx] = pt; + return(0); + } + paxwarn(1, "User selection table out of memory"); + return(-1); +} + +/* + * usr_match() + * check if this files uid matches a selected uid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +static int +usr_match(ARCHD *arcn) +{ + USRT *pt; + + /* + * hash and look for it in the table + */ + pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; + while (pt != NULL) { + if (pt->uid == arcn->sb.st_uid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * grp_add() + * add a group match to the group match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +int +grp_add(char *str) +{ + u_int indx; + GRPT *pt; + struct group *gr; + gid_t gid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((grptb == NULL) && + ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { + paxwarn(1, "Unable to allocate memory fo group selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a group name, \# escapes # as first char in group name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((gr = getgrnam(str)) == NULL) { + paxwarn(1,"Cannot determine gid for group name: %s", str); + return(-1); + } + gid = (gid_t)gr->gr_gid; + } else + gid = (gid_t)strtoul(str+1, NULL, 10); + endgrent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)gid) % GRP_TB_SZ; + if ((pt = grptb[indx]) != NULL) { + while (pt != NULL) { + if (pt->gid == gid) + return(0); + pt = pt->fow; + } + } + + /* + * gid not in the table, add it to the front of the chain + */ + if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { + pt->gid = gid; + pt->fow = grptb[indx]; + grptb[indx] = pt; + return(0); + } + paxwarn(1, "Group selection table out of memory"); + return(-1); +} + +/* + * grp_match() + * check if this files gid matches a selected gid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +static int +grp_match(ARCHD *arcn) +{ + GRPT *pt; + + /* + * hash and look for it in the table + */ + pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; + while (pt != NULL) { + if (pt->gid == arcn->sb.st_gid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * Time range selection routines + * + * Routines to handle user selection of files based on the modification and/or + * inode change time falling within a specified time range (the non-standard + * -T flag). The user may specify any number of different file time ranges. + * Time ranges are checked one at a time until a match is found (if at all). + * If the file has a mtime (and/or ctime) which lies within one of the time + * ranges, the file is selected. Time ranges may have a lower and/or a upper + * value. These ranges are inclusive. When no time ranges are supplied to pax + * with the -T option, all members in the archive will be selected by the time + * range routines. When only a lower range is supplied, only files with a + * mtime (and/or ctime) equal to or younger are selected. When only a upper + * range is supplied, only files with a mtime (and/or ctime) equal to or older + * are selected. When the lower time range is equal to the upper time range, + * only files with a mtime (or ctime) of exactly that time are selected. + */ + +/* + * trng_add() + * add a time range match to the time range list. + * This is a non-standard pax option. Lower and upper ranges are in the + * format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated. + * Time ranges are based on current time, so 1234 would specify a time of + * 12:34 today. + * Return: + * 0 if the time range was added to the list, -1 otherwise + */ + +int +trng_add(char *str) +{ + TIME_RNG *pt; + char *up_pt = NULL; + char *stpt; + char *flgpt; + int dot = 0; + + /* + * throw out the badly formed time ranges + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty time range string"); + return(-1); + } + + /* + * locate optional flags suffix /{cm}. + */ + if ((flgpt = strrchr(str, '/')) != NULL) + *flgpt++ = '\0'; + + for (stpt = str; *stpt != '\0'; ++stpt) { + if ((*stpt >= '0') && (*stpt <= '9')) + continue; + if ((*stpt == ',') && (up_pt == NULL)) { + *stpt = '\0'; + up_pt = stpt + 1; + dot = 0; + continue; + } + + /* + * allow only one dot per range (secs) + */ + if ((*stpt == '.') && (!dot)) { + ++dot; + continue; + } + paxwarn(1, "Improperly specified time range: %s", str); + goto out; + } + + /* + * allocate space for the time range and store the limits + */ + if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { + paxwarn(1, "Unable to allocate memory for time range"); + return(-1); + } + + /* + * by default we only will check file mtime, but user can specify + * mtime, ctime (inode change time) or both. + */ + if ((flgpt == NULL) || (*flgpt == '\0')) + pt->flgs = CMPMTME; + else { + pt->flgs = 0; + while (*flgpt != '\0') { + switch (*flgpt) { + case 'M': + case 'm': + pt->flgs |= CMPMTME; + break; + case 'C': + case 'c': + pt->flgs |= CMPCTME; + break; + default: + paxwarn(1, "Bad option %c with time range %s", + *flgpt, str); + free(pt); + goto out; + } + ++flgpt; + } + } + + /* + * start off with the current time + */ + pt->low_time = pt->high_time = time(NULL); + if (*str != '\0') { + /* + * add lower limit + */ + if (str_sec(str, &(pt->low_time)) < 0) { + paxwarn(1, "Illegal lower time range %s", str); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASLOW; + } + + if ((up_pt != NULL) && (*up_pt != '\0')) { + /* + * add upper limit + */ + if (str_sec(up_pt, &(pt->high_time)) < 0) { + paxwarn(1, "Illegal upper time range %s", up_pt); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASHIGH; + + /* + * check that the upper and lower do not overlap + */ + if (pt->flgs & HASLOW) { + if (pt->low_time > pt->high_time) { + paxwarn(1, "Upper %s and lower %s time overlap", + up_pt, str); + (void)free((char *)pt); + return(-1); + } + } + } + + pt->fow = NULL; + if (trhead == NULL) { + trtail = trhead = pt; + return(0); + } + trtail->fow = pt; + trtail = pt; + return(0); + + out: + paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]"); + return(-1); +} + +/* + * trng_match() + * check if this files mtime/ctime falls within any supplied time range. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +static int +trng_match(ARCHD *arcn) +{ + TIME_RNG *pt; + + /* + * have to search down the list one at a time looking for a match. + * remember time range limits are inclusive. + */ + pt = trhead; + while (pt != NULL) { + switch (pt->flgs & CMPBOTH) { + case CMPBOTH: + /* + * user wants both mtime and ctime checked for this + * time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPCTME: + /* + * user wants only ctime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPMTME: + default: + /* + * user wants only mtime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + } + break; + } + + if (pt == NULL) + return(1); + return(0); +} + +/* + * str_sec() + * Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to + * seconds UTC. Tval already has current time loaded into it at entry. + * Return: + * 0 if converted ok, -1 otherwise + */ + +static int +str_sec(const char *p, time_t *tval) +{ + struct tm *lt; + const char *dot, *t; + size_t len; + int bigyear; + int yearset; + + yearset = 0; + len = strlen(p); + + for (t = p, dot = NULL; *t; ++t) { + if (isdigit(*t)) + continue; + if (*t == '.' && dot == NULL) { + dot = t; + continue; + } + return(-1); + } + + lt = localtime(tval); + + if (dot != NULL) { /* .SS */ + if (strlen(++dot) != 2) + return(-1); + lt->tm_sec = ATOI2(dot); + if (lt->tm_sec > 61) + return(-1); + len -= 3; + } else + lt->tm_sec = 0; + + switch (len) { + case 12: /* cc */ + bigyear = ATOI2(p); + lt->tm_year = (bigyear * 100) - TM_YEAR_BASE; + yearset = 1; + /* FALLTHROUGH */ + case 10: /* yy */ + if (yearset) { + lt->tm_year += ATOI2(p); + } else { + lt->tm_year = ATOI2(p); + if (lt->tm_year < 69) /* hack for 2000 ;-} */ + lt->tm_year += (2000 - TM_YEAR_BASE); + else + lt->tm_year += (1900 - TM_YEAR_BASE); + } + /* FALLTHROUGH */ + case 8: /* mm */ + lt->tm_mon = ATOI2(p); + if ((lt->tm_mon > 12) || !lt->tm_mon) + return(-1); + --lt->tm_mon; /* time struct is 0 - 11 */ + /* FALLTHROUGH */ + case 6: /* dd */ + lt->tm_mday = ATOI2(p); + if ((lt->tm_mday > 31) || !lt->tm_mday) + return(-1); + /* FALLTHROUGH */ + case 4: /* HH */ + lt->tm_hour = ATOI2(p); + if (lt->tm_hour > 23) + return(-1); + /* FALLTHROUGH */ + case 2: /* MM */ + lt->tm_min = ATOI2(p); + if (lt->tm_min > 59) + return(-1); + break; + default: + return(-1); + } + + /* convert broken-down time to UTC clock time seconds */ + if ((*tval = mktime(lt)) == -1) + return(-1); + return(0); +} diff --git a/file_cmds/pax/sel_subs.h b/file_cmds/pax/sel_subs.h new file mode 100644 index 00000000..b699ee83 --- /dev/null +++ b/file_cmds/pax/sel_subs.h @@ -0,0 +1,72 @@ +/* $OpenBSD: sel_subs.h,v 1.4 2003/06/02 23:32:09 millert Exp $ */ +/* $NetBSD: sel_subs.h,v 1.3 1995/03/21 09:07:44 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing uid/grp selects (-U, -G non standard options) + */ + +#define USR_TB_SZ 317 /* user selection table size */ +#define GRP_TB_SZ 317 /* user selection table size */ + +typedef struct usrt { + uid_t uid; + struct usrt *fow; /* next uid */ +} USRT; + +typedef struct grpt { + gid_t gid; + struct grpt *fow; /* next gid */ +} GRPT; + +/* + * data structure for storing user supplied time ranges (-T option) + */ + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; + +typedef struct time_rng { + time_t low_time; /* lower inclusive time limit */ + time_t high_time; /* higher inclusive time limit */ + int flgs; /* option flags */ +#define HASLOW 0x01 /* has lower time limit */ +#define HASHIGH 0x02 /* has higher time limit */ +#define CMPMTME 0x04 /* compare file modification time */ +#define CMPCTME 0x08 /* compare inode change time */ +#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */ + struct time_rng *fow; /* next pattern */ +} TIME_RNG; diff --git a/file_cmds/pax/tables.c b/file_cmds/pax/tables.c new file mode 100644 index 00000000..f45d2913 --- /dev/null +++ b/file_cmds/pax/tables.c @@ -0,0 +1,1278 @@ +/* $OpenBSD: tables.c,v 1.25 2007/09/02 15:19:08 deraadt Exp $ */ +/* $NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 5/31/93"; +#else +__used static const char rcsid[] = "$OpenBSD: tables.c,v 1.25 2007/09/02 15:19:08 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "tables.h" +#include "extern.h" + +/* + * Routines for controlling the contents of all the different databases pax + * keeps. Tables are dynamically created only when they are needed. The + * goal was speed and the ability to work with HUGE archives. The databases + * were kept simple, but do have complex rules for when the contents change. + * As of this writing, the posix library functions were more complex than + * needed for this application (pax databases have very short lifetimes and + * do not survive after pax is finished). Pax is required to handle very + * large archives. These database routines carefully combine memory usage and + * temporary file storage in ways which will not significantly impact runtime + * performance while allowing the largest possible archives to be handled. + * Trying to force the fit to the posix database routines was not considered + * time well spent. + */ + +static HRDLNK **ltab = NULL; /* hard link table for detecting hard links */ +static FTM **ftab = NULL; /* file time table for updating arch */ +static NAMT **ntab = NULL; /* interactive rename storage table */ +static DEVT **dtab = NULL; /* device/inode mapping tables */ +static ATDIR **atab = NULL; /* file tree directory time reset table */ +static DIRDATA *dirp = NULL; /* storage for setting created dir time/mode */ +static size_t dirsize; /* size of dirp table */ +static long dircnt = 0; /* entries in dir time/mode storage */ +static int ffd = -1; /* tmp file for file time table name storage */ + +static DEVT *chk_dev(dev_t, int); + +/* + * hard link table routines + * + * The hard link table tries to detect hard links to files using the device and + * inode values. We do this when writing an archive, so we can tell the format + * write routine that this file is a hard link to another file. The format + * write routine then can store this file in whatever way it wants (as a hard + * link if the format supports that like tar, or ignore this info like cpio). + * (Actually a field in the format driver table tells us if the format wants + * hard link info. if not, we do not waste time looking for them). We also use + * the same table when reading an archive. In that situation, this table is + * used by the format read routine to detect hard links from stored dev and + * inode numbers (like cpio). This will allow pax to create a link when one + * can be detected by the archive format. + */ + +/* + * lnk_start + * Creates the hard link table. + * Return: + * 0 if created, -1 if failure + */ + +int +lnk_start(void) +{ + if (ltab != NULL) + return(0); + if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) { + paxwarn(1, "Cannot allocate memory for hard link table"); + return(-1); + } + return(0); +} + +/* + * chk_lnk() + * Looks up entry in hard link hash table. If found, it copies the name + * of the file it is linked to (we already saw that file) into ln_name. + * lnkcnt is decremented and if goes to 1 the node is deleted from the + * database. (We have seen all the links to this file). If not found, + * we add the file to the database if it has the potential for having + * hard links to other files we may process (it has a link count > 1) + * Return: + * if found returns 1; if not found returns 0; -1 on error + */ + +int +chk_lnk(ARCHD *arcn) +{ + HRDLNK *pt; + HRDLNK **ppt; + u_int indx; + + if (ltab == NULL) + return(-1); + /* + * ignore those nodes that cannot have hard links + */ + if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) + return(0); + + /* + * hash inode number and look for this file + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) != NULL) { + /* + * its hash chain in not empty, walk down looking for it + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found a link. set the node type and copy in the + * name of the file it is to link to. we need to + * handle hardlinks to regular files differently than + * other links. + */ + arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name, + sizeof(arcn->ln_name)); + /* XXX truncate? */ + if (arcn->nlen >= sizeof(arcn->name)) + arcn->nlen = sizeof(arcn->name) - 1; + if (arcn->type == PAX_REG) + arcn->type = PAX_HRG; + else + arcn->type = PAX_HLK; + + /* + * if we have found all the links to this file, remove + * it from the database + */ + if (--pt->nlink <= 1) { + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); + } + return(1); + } + } + + /* + * we never saw this file before. It has links so we add it to the + * front of this hash chain + */ + if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) { + if ((pt->name = strdup(arcn->name)) != NULL) { + pt->dev = arcn->sb.st_dev; + pt->ino = arcn->sb.st_ino; + pt->nlink = arcn->sb.st_nlink; + pt->fow = ltab[indx]; + ltab[indx] = pt; + return(0); + } + (void)free((char *)pt); + } + + paxwarn(1, "Hard link table out of memory"); + return(-1); +} + +/* + * purg_lnk + * remove reference for a file that we may have added to the data base as + * a potential source for hard links. We ended up not using the file, so + * we do not want to accidently point another file at it later on. + */ + +void +purg_lnk(ARCHD *arcn) +{ + HRDLNK *pt; + HRDLNK **ppt; + u_int indx; + + if (ltab == NULL) + return; + /* + * do not bother to look if it could not be in the database + */ + if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) || + (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + return; + + /* + * find the hash chain for this inode value, if empty return + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) == NULL) + return; + + /* + * walk down the list looking for the inode/dev pair, unlink and + * free if found + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + if (pt == NULL) + return; + + /* + * remove and free it + */ + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); +} + +/* + * lnk_end() + * pull apart a existing link table so we can reuse it. We do this between + * read and write phases of append with update. (The format may have + * used the link table, and we need to start with a fresh table for the + * write phase + */ + +void +lnk_end(void) +{ + int i; + HRDLNK *pt; + HRDLNK *ppt; + + if (ltab == NULL) + return; + + for (i = 0; i < L_TAB_SZ; ++i) { + if (ltab[i] == NULL) + continue; + pt = ltab[i]; + ltab[i] = NULL; + + /* + * free up each entry on this chain + */ + while (pt != NULL) { + ppt = pt; + pt = ppt->fow; + (void)free((char *)ppt->name); + (void)free((char *)ppt); + } + } + return; +} + +/* + * modification time table routines + * + * The modification time table keeps track of last modification times for all + * files stored in an archive during a write phase when -u is set. We only + * add a file to the archive if it is newer than a file with the same name + * already stored on the archive (if there is no other file with the same + * name on the archive it is added). This applies to writes and appends. + * An append with an -u must read the archive and store the modification time + * for every file on that archive before starting the write phase. It is clear + * that this is one HUGE database. To save memory space, the actual file names + * are stored in a scratch file and indexed by an in-memory hash table. The + * hash table is indexed by hashing the file path. The nodes in the table store + * the length of the filename and the lseek offset within the scratch file + * where the actual name is stored. Since there are never any deletions from + * this table, fragmentation of the scratch file is never a issue. Lookups + * seem to not exhibit any locality at all (files in the database are rarely + * looked up more than once...), so caching is just a waste of memory. The + * only limitation is the amount of scratch file space available to store the + * path names. + */ + +/* + * ftime_start() + * create the file time hash table and open for read/write the scratch + * file. (after created it is unlinked, so when we exit we leave + * no witnesses). + * Return: + * 0 if the table and file was created ok, -1 otherwise + */ + +int +ftime_start(void) +{ + + if (ftab != NULL) + return(0); + if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) { + paxwarn(1, "Cannot allocate memory for file time table"); + return(-1); + } + + /* + * get random name and create temporary scratch file, unlink name + * so it will get removed on exit + */ + memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE)); + if ((ffd = mkstemp(tempfile)) < 0) { + syswarn(1, errno, "Unable to create temporary file: %s", + tempfile); + return(-1); + } + (void)unlink(tempfile); + + return(0); +} + +/* + * chk_ftime() + * looks up entry in file time hash table. If not found, the file is + * added to the hash table and the file named stored in the scratch file. + * If a file with the same name is found, the file times are compared and + * the most recent file time is retained. If the new file was younger (or + * was not in the database) the new file is selected for storage. + * Return: + * 0 if file should be added to the archive, 1 if it should be skipped, + * -1 on error + */ + +int +chk_ftime(ARCHD *arcn) +{ + FTM *pt; + int namelen; + u_int indx; + char ckname[PAXPATHLEN+1]; + + /* + * no info, go ahead and add to archive + */ + if (ftab == NULL) + return(0); + + /* + * hash the pathname and look up in table + */ + namelen = arcn->nlen; + indx = st_hash(arcn->name, namelen, F_TAB_SZ); + if ((pt = ftab[indx]) != NULL) { + /* + * the hash chain is not empty, walk down looking for match + * only read up the path names if the lengths match, speeds + * up the search a lot + */ + while (pt != NULL) { + if (pt->namelen == namelen) { + /* + * potential match, have to read the name + * from the scratch file. + */ + if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) { + syswarn(1, errno, + "Failed ftime table seek"); + return(-1); + } + if (read(ffd, ckname, namelen) != namelen) { + syswarn(1, errno, + "Failed ftime table read"); + return(-1); + } + + /* + * if the names match, we are done + */ + if (!strncmp(ckname, arcn->name, namelen)) + break; + } + + /* + * try the next entry on the chain + */ + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found the file, compare the times, save the newer + */ + if (arcn->sb.st_mtime > pt->mtime) { + /* + * file is newer + */ + pt->mtime = arcn->sb.st_mtime; + return(0); + } + /* + * file is older + */ + return(1); + } + } + + /* + * not in table, add it + */ + if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) { + /* + * add the name at the end of the scratch file, saving the + * offset. add the file to the head of the hash chain + */ + if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) { + if (write(ffd, arcn->name, namelen) == namelen) { + pt->mtime = arcn->sb.st_mtime; + pt->namelen = namelen; + pt->fow = ftab[indx]; + ftab[indx] = pt; + return(0); + } + syswarn(1, errno, "Failed write to file time table"); + } else + syswarn(1, errno, "Failed seek on file time table"); + } else + paxwarn(1, "File time table ran out of memory"); + + if (pt != NULL) + (void)free((char *)pt); + return(-1); +} + +/* + * Interactive rename table routines + * + * The interactive rename table keeps track of the new names that the user + * assigns to files from tty input. Since this map is unique for each file + * we must store it in case there is a reference to the file later in archive + * (a link). Otherwise we will be unable to find the file we know was + * extracted. The remapping of these files is stored in a memory based hash + * table (it is assumed since input must come from /dev/tty, it is unlikely to + * be a very large table). + */ + +/* + * name_start() + * create the interactive rename table + * Return: + * 0 if successful, -1 otherwise + */ + +int +name_start(void) +{ + if (ntab != NULL) + return(0); + if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) { + paxwarn(1, "Cannot allocate memory for interactive rename table"); + return(-1); + } + return(0); +} + +/* + * add_name() + * add the new name to old name mapping just created by the user. + * If an old name mapping is found (there may be duplicate names on an + * archive) only the most recent is kept. + * Return: + * 0 if added, -1 otherwise + */ + +int +add_name(char *oname, int onamelen, char *nname) +{ + NAMT *pt; + u_int indx; + + if (ntab == NULL) { + /* + * should never happen + */ + paxwarn(0, "No interactive rename table, links may fail"); + return(0); + } + + /* + * look to see if we have already mapped this file, if so we + * will update it + */ + indx = st_hash(oname, onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) != NULL) { + /* + * look down the has chain for the file + */ + while ((pt != NULL) && (strcmp(oname, pt->oname) != 0)) + pt = pt->fow; + + if (pt != NULL) { + /* + * found an old mapping, replace it with the new one + * the user just input (if it is different) + */ + if (strcmp(nname, pt->nname) == 0) + return(0); + + (void)free((char *)pt->nname); + if ((pt->nname = strdup(nname)) == NULL) { + paxwarn(1, "Cannot update rename table"); + return(-1); + } + return(0); + } + } + + /* + * this is a new mapping, add it to the table + */ + if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) { + if ((pt->oname = strdup(oname)) != NULL) { + if ((pt->nname = strdup(nname)) != NULL) { + pt->fow = ntab[indx]; + ntab[indx] = pt; + return(0); + } + (void)free((char *)pt->oname); + } + (void)free((char *)pt); + } + paxwarn(1, "Interactive rename table out of memory"); + return(-1); +} + +/* + * sub_name() + * look up a link name to see if it points at a file that has been + * remapped by the user. If found, the link is adjusted to contain the + * new name (oname is the link to name) + */ + +void +sub_name(char *oname, int *onamelen, size_t onamesize) +{ + NAMT *pt; + u_int indx; + + if (ntab == NULL) + return; + /* + * look the name up in the hash table + */ + indx = st_hash(oname, *onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) == NULL) + return; + + while (pt != NULL) { + /* + * walk down the hash chain looking for a match + */ + if (strcmp(oname, pt->oname) == 0) { + /* + * found it, replace it with the new name + * and return (we know that oname has enough space) + */ + *onamelen = strlcpy(oname, pt->nname, onamesize); + if (*onamelen >= onamesize) + *onamelen = onamesize - 1; /* XXX truncate? */ + return; + } + pt = pt->fow; + } + + /* + * no match, just return + */ + return; +} + +/* + * device/inode mapping table routines + * (used with formats that store device and inodes fields) + * + * device/inode mapping tables remap the device field in a archive header. The + * device/inode fields are used to determine when files are hard links to each + * other. However these values have very little meaning outside of that. This + * database is used to solve one of two different problems. + * + * 1) when files are appended to an archive, while the new files may have hard + * links to each other, you cannot determine if they have hard links to any + * file already stored on the archive from a prior run of pax. We must assume + * that these inode/device pairs are unique only within a SINGLE run of pax + * (which adds a set of files to an archive). So we have to make sure the + * inode/dev pairs we add each time are always unique. We do this by observing + * while the inode field is very dense, the use of the dev field is fairly + * sparse. Within each run of pax, we remap any device number of a new archive + * member that has a device number used in a prior run and already stored in a + * file on the archive. During the read phase of the append, we store the + * device numbers used and mark them to not be used by any file during the + * write phase. If during write we go to use one of those old device numbers, + * we remap it to a new value. + * + * 2) Often the fields in the archive header used to store these values are + * too small to store the entire value. The result is an inode or device value + * which can be truncated. This really can foul up an archive. With truncation + * we end up creating links between files that are really not links (after + * truncation the inodes are the same value). We address that by detecting + * truncation and forcing a remap of the device field to split truncated + * inodes away from each other. Each truncation creates a pattern of bits that + * are removed. We use this pattern of truncated bits to partition the inodes + * on a single device to many different devices (each one represented by the + * truncated bit pattern). All inodes on the same device that have the same + * truncation pattern are mapped to the same new device. Two inodes that + * truncate to the same value clearly will always have different truncation + * bit patterns, so they will be split from away each other. When we spot + * device truncation we remap the device number to a non truncated value. + * (for more info see table.h for the data structures involved). + */ + +/* + * dev_start() + * create the device mapping table + * Return: + * 0 if successful, -1 otherwise + */ + +int +dev_start(void) +{ + if (dtab != NULL) + return(0); + if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) { + paxwarn(1, "Cannot allocate memory for device mapping table"); + return(-1); + } + return(0); +} + +/* + * add_dev() + * add a device number to the table. this will force the device to be + * remapped to a new value if it be used during a write phase. This + * function is called during the read phase of an append to prohibit the + * use of any device number already in the archive. + * Return: + * 0 if added ok, -1 otherwise + */ + +int +add_dev(ARCHD *arcn) +{ + if (chk_dev(arcn->sb.st_dev, 1) == NULL) + return(-1); + return(0); +} + +/* + * chk_dev() + * check for a device value in the device table. If not found and the add + * flag is set, it is added. This does NOT assign any mapping values, just + * adds the device number as one that need to be remapped. If this device + * is already mapped, just return with a pointer to that entry. + * Return: + * pointer to the entry for this device in the device map table. Null + * if the add flag is not set and the device is not in the table (it is + * not been seen yet). If add is set and the device cannot be added, null + * is returned (indicates an error). + */ + +static DEVT * +chk_dev(dev_t dev, int add) +{ + DEVT *pt; + u_int indx; + + if (dtab == NULL) + return(NULL); + /* + * look to see if this device is already in the table + */ + indx = ((unsigned)dev) % D_TAB_SZ; + if ((pt = dtab[indx]) != NULL) { + while ((pt != NULL) && (pt->dev != dev)) + pt = pt->fow; + + /* + * found it, return a pointer to it + */ + if (pt != NULL) + return(pt); + } + + /* + * not in table, we add it only if told to as this may just be a check + * to see if a device number is being used. + */ + if (add == 0) + return(NULL); + + /* + * allocate a node for this device and add it to the front of the hash + * chain. Note we do not assign remaps values here, so the pt->list + * list must be NULL. + */ + if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) { + paxwarn(1, "Device map table out of memory"); + return(NULL); + } + pt->dev = dev; + pt->list = NULL; + pt->fow = dtab[indx]; + dtab[indx] = pt; + return(pt); +} +/* + * map_dev() + * given an inode and device storage mask (the mask has a 1 for each bit + * the archive format is able to store in a header), we check for inode + * and device truncation and remap the device as required. Device mapping + * can also occur when during the read phase of append a device number was + * seen (and was marked as do not use during the write phase). WE ASSUME + * that unsigned longs are the same size or bigger than the fields used + * for ino_t and dev_t. If not the types will have to be changed. + * Return: + * 0 if all ok, -1 otherwise. + */ + +int +map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask) +{ + DEVT *pt; + DLIST *dpt; + static dev_t lastdev = 0; /* next device number to try */ + int trc_ino = 0; + int trc_dev = 0; + ino_t trunc_bits = 0; + ino_t nino; + + if (dtab == NULL) + return(0); + /* + * check for device and inode truncation, and extract the truncated + * bit pattern. + */ + if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev) + ++trc_dev; + if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) { + ++trc_ino; + trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask); + } + + /* + * see if this device is already being mapped, look up the device + * then find the truncation bit pattern which applies + */ + if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) { + /* + * this device is already marked to be remapped + */ + for (dpt = pt->list; dpt != NULL; dpt = dpt->fow) + if (dpt->trunc_bits == trunc_bits) + break; + + if (dpt != NULL) { + /* + * we are being remapped for this device and pattern + * change the device number to be stored and return + */ + arcn->sb.st_dev = dpt->dev; + arcn->sb.st_ino = nino; + return(0); + } + } else { + /* + * this device is not being remapped YET. if we do not have any + * form of truncation, we do not need a remap + */ + if (!trc_ino && !trc_dev) + return(0); + + /* + * we have truncation, have to add this as a device to remap + */ + if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL) + goto bad; + + /* + * if we just have a truncated inode, we have to make sure that + * all future inodes that do not truncate (they have the + * truncation pattern of all 0's) continue to map to the same + * device number. We probably have already written inodes with + * this device number to the archive with the truncation + * pattern of all 0's. So we add the mapping for all 0's to the + * same device number. + */ + if (!trc_dev && (trunc_bits != 0)) { + if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL) + goto bad; + dpt->trunc_bits = 0; + dpt->dev = arcn->sb.st_dev; + dpt->fow = pt->list; + pt->list = dpt; + } + } + + /* + * look for a device number not being used. We must watch for wrap + * around on lastdev (so we do not get stuck looking forever!) + */ + while (++lastdev > 0) { + if (chk_dev(lastdev, 0) != NULL) + continue; + /* + * found an unused value. If we have reached truncation point + * for this format we are hosed, so we give up. Otherwise we + * mark it as being used. + */ + if (((lastdev & ((dev_t)dev_mask)) != lastdev) || + (chk_dev(lastdev, 1) == NULL)) + goto bad; + break; + } + + if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)) + goto bad; + + /* + * got a new device number, store it under this truncation pattern. + * change the device number this file is being stored with. + */ + dpt->trunc_bits = trunc_bits; + dpt->dev = lastdev; + dpt->fow = pt->list; + pt->list = dpt; + arcn->sb.st_dev = lastdev; + arcn->sb.st_ino = nino; + return(0); + + bad: + paxwarn(1, "Unable to fix truncated inode/device field when storing %s", + arcn->name); + paxwarn(0, "Archive may create improper hard links when extracted"); + return(0); +} + +/* + * directory access/mod time reset table routines (for directories READ by pax) + * + * The pax -t flag requires that access times of archive files be the same + * before being read by pax. For regular files, access time is restored after + * the file has been copied. This database provides the same functionality for + * directories read during file tree traversal. Restoring directory access time + * is more complex than files since directories may be read several times until + * all the descendants in their subtree are visited by fts. Directory access + * and modification times are stored during the fts pre-order visit (done + * before any descendants in the subtree are visited) and restored after the + * fts post-order visit (after all the descendants have been visited). In the + * case of premature exit from a subtree (like from the effects of -n), any + * directory entries left in this database are reset during final cleanup + * operations of pax. Entries are hashed by inode number for fast lookup. + */ + +/* + * atdir_start() + * create the directory access time database for directories READ by pax. + * Return: + * 0 is created ok, -1 otherwise. + */ + +int +atdir_start(void) +{ + if (atab != NULL) + return(0); + if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) { + paxwarn(1,"Cannot allocate space for directory access time table"); + return(-1); + } + return(0); +} + + +/* + * atdir_end() + * walk through the directory access time table and reset the access time + * of any directory who still has an entry left in the database. These + * entries are for directories READ by pax + */ + +void +atdir_end(void) +{ + ATDIR *pt; + int i; + + if (atab == NULL) + return; + /* + * for each non-empty hash table entry reset all the directories + * chained there. + */ + for (i = 0; i < A_TAB_SZ; ++i) { + if ((pt = atab[i]) == NULL) + continue; + /* + * remember to force the times, set_ftime() looks at pmtime + * and patime, which only applies to things CREATED by pax, + * not read by pax. Read time reset is controlled by -t. + */ + for (; pt != NULL; pt = pt->fow) + set_ftime(pt->name, pt->mtime, pt->atime, 1); + } +} + +/* + * add_atdir() + * add a directory to the directory access time table. Table is hashed + * and chained by inode number. This is for directories READ by pax + */ + +void +add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime) +{ + ATDIR *pt; + u_int indx; + + if (atab == NULL) + return; + + /* + * make sure this directory is not already in the table, if so just + * return (the older entry always has the correct time). The only + * way this will happen is when the same subtree can be traversed by + * different args to pax and the -n option is aborting fts out of a + * subtree before all the post-order visits have been made. + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) != NULL) { + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + pt = pt->fow; + } + + /* + * oops, already there. Leave it alone. + */ + if (pt != NULL) + return; + } + + /* + * add it to the front of the hash chain + */ + if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) { + if ((pt->name = strdup(fname)) != NULL) { + pt->dev = dev; + pt->ino = ino; + pt->mtime = mtime; + pt->atime = atime; + pt->fow = atab[indx]; + atab[indx] = pt; + return; + } + (void)free((char *)pt); + } + + paxwarn(1, "Directory access time reset table ran out of memory"); + return; +} + +/* + * get_atdir() + * look up a directory by inode and device number to obtain the access + * and modification time you want to set to. If found, the modification + * and access time parameters are set and the entry is removed from the + * table (as it is no longer needed). These are for directories READ by + * pax + * Return: + * 0 if found, -1 if not found. + */ + +int +get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime) +{ + ATDIR *pt; + ATDIR **ppt; + u_int indx; + + if (atab == NULL) + return(-1); + /* + * hash by inode and search the chain for an inode and device match + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) == NULL) + return(-1); + + ppt = &(atab[indx]); + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + /* + * no match, go to next one + */ + ppt = &(pt->fow); + pt = pt->fow; + } + + /* + * return if we did not find it. + */ + if (pt == NULL) + return(-1); + + /* + * found it. return the times and remove the entry from the table. + */ + *ppt = pt->fow; + *mtime = pt->mtime; + *atime = pt->atime; + (void)free((char *)pt->name); + (void)free((char *)pt); + return(0); +} + +/* + * directory access mode and time storage routines (for directories CREATED + * by pax). + * + * Pax requires that extracted directories, by default, have their access/mod + * times and permissions set to the values specified in the archive. During the + * actions of extracting (and creating the destination subtree during -rw copy) + * directories extracted may be modified after being created. Even worse is + * that these directories may have been created with file permissions which + * prohibits any descendants of these directories from being extracted. When + * directories are created by pax, access rights may be added to permit the + * creation of files in their subtree. Every time pax creates a directory, the + * times and file permissions specified by the archive are stored. After all + * files have been extracted (or copied), these directories have their times + * and file modes reset to the stored values. The directory info is restored in + * reverse order as entries were added to the data file from root to leaf. To + * restore atime properly, we must go backwards. The data file consists of + * records with two parts, the file name followed by a DIRDATA trailer. The + * fixed sized trailer contains the size of the name plus the off_t location in + * the file. To restore we work backwards through the file reading the trailer + * then the file name. + */ + +/* + * dir_start() + * set up the directory time and file mode storage for directories CREATED + * by pax. + * Return: + * 0 if ok, -1 otherwise + */ + +int +dir_start(void) +{ + if (dirp != NULL) + return(0); + + dirsize = DIRP_SIZE; + if ((dirp = calloc(dirsize, sizeof(DIRDATA))) == NULL) { + paxwarn(1, "Unable to allocate memory for directory times"); + return(-1); + } + return(0); +} + +/* + * add_dir() + * add the mode and times for a newly CREATED directory + * name is name of the directory, psb the stat buffer with the data in it, + * frc_mode is a flag that says whether to force the setting of the mode + * (ignoring the user set values for preserving file mode). Frc_mode is + * for the case where we created a file and found that the resulting + * directory was not writeable and the user asked for file modes to NOT + * be preserved. (we have to preserve what was created by default, so we + * have to force the setting at the end. this is stated explicitly in the + * pax spec) + */ + +void +add_dir(char *name, size_t nlen, struct stat *psb, int frc_mode) +{ + DIRDATA *dblk; + char realname[MAXPATHLEN], *rp; + + if (dirp == NULL) + return; + + if (havechd && *name != '/') { + if ((rp = realpath(name, realname)) == NULL) { + paxwarn(1, "Cannot canonicalize %s", name); + return; + } + name = rp; + } + if (dircnt == dirsize) { + dblk = realloc(dirp, 2 * dirsize * sizeof(DIRDATA)); + if (dblk == NULL) { + paxwarn(1, "Unable to store mode and times for created" + " directory: %s", name); + return; + } + dirp = dblk; + dirsize *= 2; + } + dblk = &dirp[dircnt]; + if ((dblk->name = strdup(name)) == NULL) { + paxwarn(1, "Unable to store mode and times for created" + " directory: %s", name); + return; + } + dblk->mode = psb->st_mode & 0xffff; + dblk->mtime = psb->st_mtime; + dblk->atime = psb->st_atime; + dblk->frc_mode = frc_mode; + ++dircnt; +} + +/* + * proc_dir() + * process all file modes and times stored for directories CREATED + * by pax + */ + +void +proc_dir(void) +{ + DIRDATA *dblk; + long cnt; + + if (dirp == NULL) + return; + /* + * read backwards through the file and process each directory + */ + cnt = dircnt; + while (--cnt >= 0) { + /* + * frc_mode set, make sure we set the file modes even if + * the user didn't ask for it (see file_subs.c for more info) + */ + dblk = &dirp[cnt]; + if (pmode || dblk->frc_mode) + set_pmode(dblk->name, dblk->mode); + if (patime || pmtime) + set_ftime(dblk->name, dblk->mtime, dblk->atime, 0); + free(dblk->name); + } + + free(dirp); + dirp = NULL; + dircnt = 0; +} + +/* + * database independent routines + */ + +/* + * st_hash() + * hashes filenames to a u_int for hashing into a table. Looks at the tail + * end of file, as this provides far better distribution than any other + * part of the name. For performance reasons we only care about the last + * MAXKEYLEN chars (should be at LEAST large enough to pick off the file + * name). Was tested on 500,000 name file tree traversal from the root + * and gave almost a perfectly uniform distribution of keys when used with + * prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int) + * chars at a time and pads with 0 for last addition. + * Return: + * the hash value of the string MOD (%) the table size. + */ + +u_int +st_hash(char *name, int len, int tabsz) +{ + char *pt; + char *dest; + char *end; + int i; + u_int key = 0; + int steps; + int res; + u_int val = 0; + + /* + * only look at the tail up to MAXKEYLEN, we do not need to waste + * time here (remember these are pathnames, the tail is what will + * spread out the keys) + */ + if (len > MAXKEYLEN) { + pt = &(name[len - MAXKEYLEN]); + len = MAXKEYLEN; + } else + pt = name; + + /* + * calculate the number of u_int size steps in the string and if + * there is a runt to deal with + */ + steps = len/sizeof(u_int); + res = len % sizeof(u_int); + + /* + * add up the value of the string in unsigned integer sized pieces + * too bad we cannot have unsigned int aligned strings, then we + * could avoid the expensive copy. + */ + for (i = 0; i < steps; ++i) { + end = pt + sizeof(u_int); + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * add in the runt padded with zero to the right + */ + if (res) { + val = 0; + end = pt + res; + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * return the result mod the table size + */ + return(key % tabsz); +} diff --git a/file_cmds/pax/tables.h b/file_cmds/pax/tables.h new file mode 100644 index 00000000..bc19f066 --- /dev/null +++ b/file_cmds/pax/tables.h @@ -0,0 +1,170 @@ +/* $OpenBSD: tables.h,v 1.8 2006/08/05 23:05:13 ray Exp $ */ +/* $NetBSD: tables.h,v 1.3 1995/03/21 09:07:47 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tables.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structures and constants used by the different databases kept by pax + */ + +/* + * Hash Table Sizes MUST BE PRIME, if set too small performance suffers. + * Probably safe to expect 500000 inodes per tape. Assuming good key + * distribution (inodes) chains of under 50 long (worst case) is ok. + */ +#define L_TAB_SZ 2503 /* hard link hash table size */ +#define F_TAB_SZ 50503 /* file time hash table size */ +#define N_TAB_SZ 541 /* interactive rename hash table */ +#define D_TAB_SZ 317 /* unique device mapping table */ +#define A_TAB_SZ 317 /* ftree dir access time reset table */ +#define MAXKEYLEN 64 /* max number of chars for hash */ +#define DIRP_SIZE 64 /* initial size of created dir table */ + +/* + * file hard link structure (hashed by dev/ino and chained) used to find the + * hard links in a file system or with some archive formats (cpio) + */ +typedef struct hrdlnk { + char *name; /* name of first file seen with this ino/dev */ + dev_t dev; /* files device number */ + ino_t ino; /* files inode number */ + u_long nlink; /* expected link count */ + struct hrdlnk *fow; +} HRDLNK; + +/* + * Archive write update file time table (the -u, -C flag), hashed by filename. + * Filenames are stored in a scratch file at seek offset into the file. The + * file time (mod time) and the file name length (for a quick check) are + * stored in a hash table node. We were forced to use a scratch file because + * with -u, the mtime for every node in the archive must always be available + * to compare against (and this data can get REALLY large with big archives). + * By being careful to read only when we have a good chance of a match, the + * performance loss is not measurable (and the size of the archive we can + * handle is greatly increased). + */ +typedef struct ftm { + int namelen; /* file name length */ + time_t mtime; /* files last modification time */ + off_t seek; /* location in scratch file */ + struct ftm *fow; +} FTM; + +/* + * Interactive rename table (-i flag), hashed by orig filename. + * We assume this will not be a large table as this mapping data can only be + * obtained through interactive input by the user. Nobody is going to type in + * changes for 500000 files? We use chaining to resolve collisions. + */ + +typedef struct namt { + char *oname; /* old name */ + char *nname; /* new name typed in by the user */ + struct namt *fow; +} NAMT; + +/* + * Unique device mapping tables. Some protocols (e.g. cpio) require that the + * pair will uniquely identify a file in an archive unless they + * are links to the same file. Appending to archives can break this. For those + * protocols that have this requirement we map c_dev to a unique value not seen + * in the archive when we append. We also try to handle inode truncation with + * this table. (When the inode field in the archive header are too small, we + * remap the dev on writes to remove accidental collisions). + * + * The list is hashed by device number using chain collision resolution. Off of + * each DEVT are linked the various remaps for this device based on those bits + * in the inode which were truncated. For example if we are just remapping to + * avoid a device number during an update append, off the DEVT we would have + * only a single DLIST that has a truncation id of 0 (no inode bits were + * stripped for this device so far). When we spot inode truncation we create + * a new mapping based on the set of bits in the inode which were stripped off. + * so if the top four bits of the inode are stripped and they have a pattern of + * 0110...... (where . are those bits not truncated) we would have a mapping + * assigned for all inodes that has the same 0110.... pattern (with this dev + * number of course). This keeps the mapping sparse and should be able to store + * close to the limit of files which can be represented by the optimal + * combination of dev and inode bits, and without creating a fouled up archive. + * Note we also remap truncated devs in the same way (an exercise for the + * dedicated reader; always wanted to say that...:) + */ + +typedef struct devt { + dev_t dev; /* the orig device number we now have to map */ + struct devt *fow; /* new device map list */ + struct dlist *list; /* map list based on inode truncation bits */ +} DEVT; + +typedef struct dlist { + ino_t trunc_bits; /* truncation pattern for a specific map */ + dev_t dev; /* the new device id we use */ + struct dlist *fow; +} DLIST; + +/* + * ftree directory access time reset table. When we are done with a + * subtree we reset the access and mod time of the directory when the tflag is + * set. Not really explicitly specified in the pax spec, but easy and fast to + * do (and this may have even been intended in the spec, it is not clear). + * table is hashed by inode with chaining. + */ + +typedef struct atdir { + char *name; /* name of directory to reset */ + dev_t dev; /* dev and inode for fast lookup */ + ino_t ino; + time_t mtime; /* access and mod time to reset to */ + time_t atime; + struct atdir *fow; +} ATDIR; + +/* + * created directory time and mode storage entry. After pax is finished during + * extraction or copy, we must reset directory access modes and times that + * may have been modified after creation (they no longer have the specified + * times and/or modes). We must reset time in the reverse order of creation, + * because entries are added from the top of the file tree to the bottom. + * We MUST reset times from leaf to root (it will not work the other + * direction). + */ + +typedef struct dirdata { + char *name; /* file name */ + time_t mtime; /* mtime to set */ + time_t atime; /* atime to set */ + u_int16_t mode; /* file mode to restore */ + u_int16_t frc_mode; /* do we force mode settings? */ +} DIRDATA; diff --git a/file_cmds/pax/tar.c b/file_cmds/pax/tar.c new file mode 100644 index 00000000..360541fa --- /dev/null +++ b/file_cmds/pax/tar.c @@ -0,0 +1,1206 @@ +/* $OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $ */ +/* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: tar.c,v 1.41 2006/03/04 20:24:55 otto Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#include "tar.h" + +/* + * Routines for reading, writing and header identify of various versions of tar + */ + +static size_t expandname(char *, size_t, char **, const char *, size_t); +static u_long tar_chksm(char *, int); +static char *name_split(char *, int); +static int ul_oct(u_long, char *, int, int); +#ifndef LONG_OFF_T +static int uqd_oct(u_quad_t, char *, int, int); +#endif + +static uid_t uid_nobody; +static uid_t uid_warn; +static gid_t gid_nobody; +static gid_t gid_warn; + +/* + * Routines common to all versions of tar + */ + +static int tar_nodir; /* do not write dirs under old tar */ +char *gnu_name_string; /* GNU ././@LongLink hackery name */ +char *gnu_link_string; /* GNU ././@LongLink hackery link */ + +/* + * tar_endwr() + * add the tar trailer of two null blocks + * Return: + * 0 if ok, -1 otherwise (what wr_skip returns) + */ + +int +tar_endwr(void) +{ + return(wr_skip((off_t)(NULLCNT*BLKMULT))); +} + +/* + * tar_endrd() + * no cleanup needed here, just return size of trailer (for append) + * Return: + * size of trailer (2 * BLKMULT) + */ + +off_t +tar_endrd(void) +{ + return((off_t)(NULLCNT*BLKMULT)); +} + +/* + * tar_trail() + * Called to determine if a header block is a valid trailer. We are passed + * the block, the in_sync flag (which tells us we are in resync mode; + * looking for a valid header), and cnt (which starts at zero) which is + * used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block + * could never contain a header. + */ + +int +tar_trail(ARCHD *ignore, char *buf, int in_resync, int *cnt) +{ + int i; + + /* + * look for all zero, trailer is two consecutive blocks of zero + */ + for (i = 0; i < BLKMULT; ++i) { + if (buf[i] != '\0') + break; + } + + /* + * if not all zero it is not a trailer, but MIGHT be a header. + */ + if (i != BLKMULT) + return(-1); + + /* + * When given a zero block, we must be careful! + * If we are not in resync mode, check for the trailer. Have to watch + * out that we do not mis-identify file data as the trailer, so we do + * NOT try to id a trailer during resync mode. During resync mode we + * might as well throw this block out since a valid header can NEVER be + * a block of all 0 (we must have a valid file name). + */ + if (!in_resync && (++*cnt >= NULLCNT)) + return(0); + return(1); +} + +/* + * ul_oct() + * convert an unsigned long to an octal string. many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +ul_oct(u_long val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch (term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == (u_long)0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef LONG_OFF_T +/* + * uqd_oct() + * convert an u_quad_t to an octal string. one of many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +static int +uqd_oct(u_quad_t val, char *str, int len, int term) +{ + char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch (term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == 0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif + +/* + * tar_chksm() + * calculate the checksum for a tar block counting the checksum field as + * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks). + * NOTE: we use len to short circuit summing 0's on write since we ALWAYS + * pad headers with 0. + * Return: + * unsigned long checksum + */ + +static u_long +tar_chksm(char *blk, int len) +{ + char *stop; + char *pt; + u_long chksm = BLNKSUM; /* initial value is checksum field sum */ + + /* + * add the part of the block before the checksum field + */ + pt = blk; + stop = blk + CHK_OFFSET; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + /* + * move past the checksum field and keep going, spec counts the + * checksum field as the sum of 8 blanks (which is pre-computed as + * BLNKSUM). + * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding + * starts, no point in summing zero's) + */ + pt += CHK_LEN; + stop = blk + len; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + return(chksm); +} + +/* + * Routines for old BSD style tar (also made portable to sysV tar) + */ + +/* + * tar_id() + * determine if a block given to us is a valid tar header (and not a USTAR + * header). We have to be on the lookout for those pesky blocks of all + * zero's. + * Return: + * 0 if a tar header, -1 otherwise + */ + +int +tar_id(char *blk, int size) +{ + HD_TAR *hd; + HD_USTAR *uhd; + + if (size < BLKMULT) + return(-1); + hd = (HD_TAR *)blk; + uhd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test, then make + * sure this is not a ustar header by looking for the ustar magic + * cookie. We should use TMAGLEN, but some USTAR archive programs are + * wrong and create archives missing the \0. Last we check the + * checksum. If this is ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + force_one_volume = 1; + return(0); +} + +/* + * tar_opt() + * handle tar format specific -o options + * Return: + * 0 if ok -1 otherwise + */ + +int +tar_opt(void) +{ + OPLIST *opt; + + while ((opt = opt_next()) != NULL) { + if (strcmp(opt->name, TAR_OPTION) || + strcmp(opt->value, TAR_NODIR)) { + paxwarn(1, "Unknown tar format -o option/value pair %s=%s", + opt->name, opt->value); + paxwarn(1,"%s=%s is the only supported tar format option", + TAR_OPTION, TAR_NODIR); + return(-1); + } + + /* + * we only support one option, and only when writing + */ + if ((act != APPND) && (act != ARCHIVE)) { + paxwarn(1, "%s=%s is only supported when writing.", + opt->name, opt->value); + return(-1); + } + tar_nodir = 1; + } + return(0); +} + + +/* + * tar_rd() + * extract the values out of block already determined to be a tar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +int +tar_rd(ARCHD *arcn, char *buf) +{ + HD_TAR *hd; + char *pt; + + /* + * we only get proper sized buffers passed to us + */ + if (tar_id(buf, BLKMULT) < 0) + return(-1); + memset(arcn, 0, sizeof(*arcn)); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + + /* + * copy out the name and values in the stat buffer + */ + hd = (HD_TAR *)buf; + if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) { + arcn->nlen = expandname(arcn->name, sizeof(arcn->name), + &gnu_name_string, hd->name, sizeof(hd->name)); + arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), + &gnu_link_string, hd->linkname, sizeof(hd->linkname)); + } + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & + 0xfff); + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); +#ifdef LONG_OFF_T + arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT); +#else + arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT); +#endif + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * have to look at the last character, it may be a '/' and that is used + * to encode this as a directory + */ + pt = &(arcn->name[arcn->nlen - 1]); + arcn->pad = 0; + arcn->skip = 0; + switch (hd->linkflag) { + case SYMTYPE: + /* + * symbolic link, need to get the link name and set the type in + * the st_mode so -v printing will look correct. + */ + arcn->type = PAX_SLK; + arcn->sb.st_mode |= S_IFLNK; + arcn->ln_nlen = strlcpy(arcn->ln_name, hd->linkname, sizeof(arcn->ln_name)); + break; + case LNKTYPE: + /* + * hard link, need to get the link name, set the type in the + * st_mode and st_nlink so -v printing will look better. + */ + arcn->type = PAX_HLK; + arcn->sb.st_nlink = 2; + arcn->ln_nlen = strlcpy(arcn->ln_name, hd->linkname, sizeof(arcn->ln_name)); + + /* + * no idea of what type this thing really points at, but + * we set something for printing only. + */ + arcn->sb.st_mode |= S_IFREG; + break; + case LONGLINKTYPE: + case LONGNAMETYPE: + /* + * GNU long link/file; we tag these here and let the + * pax internals deal with it -- too ugly otherwise. + */ + arcn->type = + hd->linkflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + break; + case DIRTYPE: + /* + * It is a directory, set the mode for -v printing + */ + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + break; + case AREGTYPE: + case REGTYPE: + default: + /* + * If we have a trailing / this is a directory and NOT a file. + */ + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + if (*pt == '/') { + /* + * it is a directory, set the mode for -v printing + */ + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + } else { + /* + * have a file that will be followed by data. Set the + * skip value to the size field and calculate the size + * of the padding. + */ + arcn->type = PAX_REG; + arcn->sb.st_mode |= S_IFREG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + } + break; + } + + /* + * strip off any trailing slash. + */ + if (*pt == '/') { + *pt = '\0'; + --arcn->nlen; + } + return(0); +} + +/* + * tar_wr() + * write a tar header for the file specified in the ARCHD to the archive. + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, each field + * of tar has it own spec for the termination character(s). + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +tar_wr(ARCHD *arcn) +{ + HD_TAR *hd; + int len; + HD_TAR hdblk; + + /* + * check for those file system types which tar cannot store + */ + switch (arcn->type) { + case PAX_DIR: + /* + * user asked that dirs not be written to the archive + */ + if (tar_nodir) + return(1); + break; + case PAX_CHR: + paxwarn(1, "Tar cannot archive a character device %s", + arcn->org_name); + return(1); + case PAX_BLK: + paxwarn(1, "Tar cannot archive a block device %s", arcn->org_name); + return(1); + case PAX_SCK: + paxwarn(1, "Tar cannot archive a socket %s", arcn->org_name); + return(1); + case PAX_FIF: + paxwarn(1, "Tar cannot archive a fifo %s", arcn->org_name); + return(1); + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->ln_nlen >= sizeof(hd->linkname)) { + paxwarn(1, "Link name too long for tar %s", + arcn->ln_name); + return(1); + } + break; + case PAX_REG: + case PAX_CTG: + default: + break; + } + + /* + * check file name len, remember extra char for dirs (the / at the end) + */ + len = arcn->nlen; + if (arcn->type == PAX_DIR) + ++len; + if (len >= sizeof(hd->name)) { + paxwarn(1, "File name too long for tar %s", arcn->name); + return(1); + } + + /* + * Copy the data out of the ARCHD into the tar header based on the type + * of the file. Remember, many tar readers want all fields to be + * padded with zero so we zero the header first. We then set the + * linkflag field (type), the linkname, the size, and set the padding + * (if any) to be added after the file data (0 for all other types, + * as they only have a header). + */ + memset(&hdblk, 0, sizeof(hdblk)); + hd = (HD_TAR *)&hdblk; + strlcpy(hd->name, arcn->name, sizeof(hd->name)); + arcn->pad = 0; + + if (arcn->type == PAX_DIR) { + /* + * directories are the same as files, except have a filename + * that ends with a /, we add the slash here. No data follows + * dirs, so no pad. + */ + hd->linkflag = AREGTYPE; + hd->name[len-1] = '/'; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if (arcn->type == PAX_SLK) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = SYMTYPE; + strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = LNKTYPE; + strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else { + /* + * data follows this file, so set the pad + */ + hd->linkflag = AREGTYPE; +# ifdef LONG_OFF_T + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# endif + paxwarn(1,"File is too large for tar %s", arcn->org_name); + return(1); + } + arcn->pad = TAR_PAD(arcn->sb.st_size); + } + + /* + * copy those fields that are independent of the type + */ + if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) || + ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) || + ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) || + ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1)) + goto out; + + /* + * calculate and add the checksum, then write the header. A return of + * 0 tells the caller to now write the file data, 1 says no data needs + * to be written + */ + if (ul_oct(tar_chksm((char *)&hdblk, sizeof(HD_TAR)), hd->chksum, + sizeof(hd->chksum), 3)) + goto out; + if (wr_rdbuf((char *)&hdblk, sizeof(HD_TAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Tar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * Routines for POSIX ustar + */ + +/* + * ustar_strd() + * initialization for ustar read + * Return: + * 0 if ok, -1 otherwise + */ + +int +ustar_strd(void) +{ + if ((usrtb_start() < 0) || (grptb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_stwr() + * initialization for ustar write + * Return: + * 0 if ok, -1 otherwise + */ + +int +ustar_stwr(void) +{ + if ((uidtb_start() < 0) || (gidtb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_id() + * determine if a block given to us is a valid ustar header. We have to + * be on the lookout for those pesky blocks of all zero's + * Return: + * 0 if a ustar header, -1 otherwise + */ + +int +ustar_id(char *blk, int size) +{ + HD_USTAR *hd; + + if (size < BLKMULT) + return(-1); + hd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test then check + * ustar magic cookie. We should use TMAGLEN, but some USTAR archive + * programs are fouled up and create archives missing the \0. Last we + * check the checksum. If ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + return(0); +} + +/* + * ustar_rd() + * extract the values out of block already determined to be a ustar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +int +ustar_rd(ARCHD *arcn, char *buf) +{ + HD_USTAR *hd; + char *dest; + int cnt = 0; + dev_t devmajor; + dev_t devminor; + + /* + * we only get proper sized buffers + */ + if (ustar_id(buf, BLKMULT) < 0) + return(-1); + memset(arcn, 0, sizeof(*arcn)); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + hd = (HD_USTAR *)buf; + + /* + * see if the filename is split into two parts. if, so joint the parts. + * we copy the prefix first and add a / between the prefix and name. + */ + dest = arcn->name; + if (*(hd->prefix) != '\0') { + cnt = strlcpy(dest, hd->prefix, sizeof(arcn->name) - 1); + dest += cnt; + *dest++ = '/'; + cnt++; + } else { + cnt = 0; + } + + if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { + arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt, + &gnu_name_string, hd->name, sizeof(hd->name)); + arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), + &gnu_link_string, hd->linkname, sizeof(hd->linkname)); + } + + /* + * follow the spec to the letter. we should only have mode bits, strip + * off all other crud we may be passed. + */ + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & + 0xfff); +#ifdef LONG_OFF_T + arcn->sb.st_size = (off_t)asc_ul(hd->size, sizeof(hd->size), OCT); +#else + arcn->sb.st_size = (off_t)asc_uqd(hd->size, sizeof(hd->size), OCT); +#endif + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * If we can find the ascii names for gname and uname in the password + * and group files we will use the uid's and gid they bind. Otherwise + * we use the uid and gid values stored in the header. (This is what + * the POSIX spec wants). + */ + hd->gname[sizeof(hd->gname) - 1] = '\0'; + if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + hd->uname[sizeof(hd->uname) - 1] = '\0'; + if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + + /* + * set the defaults, these may be changed depending on the file type + */ + arcn->pad = 0; + arcn->skip = 0; + arcn->sb.st_rdev = (dev_t)0; + + /* + * set the mode and PAX type according to the typeflag in the header + */ + switch (hd->typeflag) { + case FIFOTYPE: + arcn->type = PAX_FIF; + arcn->sb.st_mode |= S_IFIFO; + break; + case DIRTYPE: + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + + /* + * Some programs that create ustar archives append a '/' + * to the pathname for directories. This clearly violates + * ustar specs, but we will silently strip it off anyway. + */ + if (arcn->name[arcn->nlen - 1] == '/') + arcn->name[--arcn->nlen] = '\0'; + break; + case BLKTYPE: + case CHRTYPE: + /* + * this type requires the rdev field to be set. + */ + if (hd->typeflag == BLKTYPE) { + arcn->type = PAX_BLK; + arcn->sb.st_mode |= S_IFBLK; + } else { + arcn->type = PAX_CHR; + arcn->sb.st_mode |= S_IFCHR; + } + devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); + devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + break; + case SYMTYPE: + case LNKTYPE: + if (hd->typeflag == SYMTYPE) { + arcn->type = PAX_SLK; + arcn->sb.st_mode |= S_IFLNK; + } else { + arcn->type = PAX_HLK; + /* + * so printing looks better + */ + arcn->sb.st_mode |= S_IFREG; + arcn->sb.st_nlink = 2; + } + break; + case LONGLINKTYPE: + case LONGNAMETYPE: + /* + * GNU long link/file; we tag these here and let the + * pax internals deal with it -- too ugly otherwise. + */ + arcn->type = + hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + break; + case CONTTYPE: + case AREGTYPE: + case REGTYPE: + default: + /* + * these types have file data that follows. Set the skip and + * pad fields. + */ + arcn->type = PAX_REG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + arcn->sb.st_mode |= S_IFREG; + break; + } + return(0); +} + +/* + * ustar_wr() + * write a ustar header for the file specified in the ARCHD to the archive + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, we only use + * '\0' for the termination character (this is different than picky tar) + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +int +ustar_wr(ARCHD *arcn) +{ + HD_USTAR *hd; + char *pt; + char hdblk[sizeof(HD_USTAR)]; + mode_t mode12only; + int term_char=3; /* orignal setting */ + term_char=1; /* To pass conformance tests 274, 301 */ + + /* + * check for those file system types ustar cannot store + */ + if (arcn->type == PAX_SCK) { + paxwarn(1, "Ustar cannot archive a socket %s", arcn->org_name); + return(1); + } + + /* + * check the length of the linkname + */ + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && (arcn->ln_nlen > sizeof(hd->linkname))){ + paxwarn(1, "Link name too long for ustar %s", arcn->ln_name); + /* + * Conformance: test pax:285 wants error code to be non-zero, and + * test tar:12 wants error code from pax to be 0 + */ + return(1); + } + + /* + * split the path name into prefix and name fields (if needed). if + * pt != arcn->name, the name has to be split + */ + if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { + paxwarn(1, "File name too long for ustar %s", arcn->name); + return(1); + } + + /* + * zero out the header so we don't have to worry about zero fill below + */ + memset(hdblk, 0, sizeof(hdblk)); + hd = (HD_USTAR *)hdblk; + arcn->pad = 0L; + + /* To pass conformance tests 274/301, always set these fields to "zero" */ + ul_oct(0, hd->devmajor, sizeof(hd->devmajor), term_char); + ul_oct(0, hd->devminor, sizeof(hd->devminor), term_char); + + /* + * split the name, or zero out the prefix + */ + if (pt != arcn->name) { + /* + * name was split, pt points at the / where the split is to + * occur, we remove the / and copy the first part to the prefix + */ + *pt = '\0'; + strlcpy(hd->prefix, arcn->name, sizeof(hd->prefix)); + *pt++ = '/'; + } + + /* + * copy the name part. this may be the whole path or the part after + * the prefix. both the name and prefix may fill the entire field. + */ + if (strlen(pt) == sizeof(hd->name)) { /* must account for name just fits in buffer */ + strncpy(hd->name, pt, sizeof(hd->name)); + } else { + strlcpy(hd->name, pt, sizeof(hd->name)); + } + + /* + * set the fields in the header that are type dependent + */ + switch (arcn->type) { + case PAX_DIR: + hd->typeflag = DIRTYPE; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_CHR: + case PAX_BLK: + if (arcn->type == PAX_CHR) + hd->typeflag = CHRTYPE; + else + hd->typeflag = BLKTYPE; + if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, + sizeof(hd->devmajor), term_char) || + ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, + sizeof(hd->devminor), term_char) || + ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_FIF: + hd->typeflag = FIFOTYPE; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->type == PAX_SLK) + hd->typeflag = SYMTYPE; + else + hd->typeflag = LNKTYPE; + if (strlen(arcn->ln_name) == sizeof(hd->linkname)) { /* must account for name just fits in buffer */ + strncpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + } else { + strlcpy(hd->linkname, arcn->ln_name, sizeof(hd->linkname)); + } + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), term_char)) + goto out; + break; + case PAX_REG: + case PAX_CTG: + default: + /* + * file data with this type, set the padding + */ + if (arcn->type == PAX_CTG) + hd->typeflag = CONTTYPE; + else + hd->typeflag = REGTYPE; + arcn->pad = TAR_PAD(arcn->sb.st_size); +# ifdef LONG_OFF_T + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), term_char)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), term_char)) { +# endif + paxwarn(1,"File is too long for ustar %s",arcn->org_name); + return(1); + } + break; + } + + strncpy(hd->magic, TMAGIC, TMAGLEN); + strncpy(hd->version, TVERSION, TVERSLEN); + + /* + * set the remaining fields. Some versions want all 16 bits of mode + * we better humor them (they really do not meet spec though).... + */ + if (ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), term_char)) { + if (uid_nobody == 0) { + if (uid_name("nobody", &uid_nobody) == -1) + goto out; + } + if (uid_warn != arcn->sb.st_uid) { + uid_warn = arcn->sb.st_uid; + paxwarn(1, + "Ustar header field is too small for uid %lu, " + "using nobody", (u_long)arcn->sb.st_uid); + } + if (ul_oct((u_long)uid_nobody, hd->uid, sizeof(hd->uid), term_char)) + goto out; + } + if (ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), term_char)) { + if (gid_nobody == 0) { + if (gid_name("nobody", &gid_nobody) == -1) + goto out; + } + if (gid_warn != arcn->sb.st_gid) { + gid_warn = arcn->sb.st_gid; + paxwarn(1, + "Ustar header field is too small for gid %lu, " + "using nobody", (u_long)arcn->sb.st_gid); + } + if (ul_oct((u_long)gid_nobody, hd->gid, sizeof(hd->gid), term_char)) + goto out; + } + /* However, Unix conformance tests do not like MORE than 12 mode bits: + remove all beyond (see definition of stat.st_mode structure) */ + mode12only = ((u_long)arcn->sb.st_mode) & 0x00000fff; + if (ul_oct((u_long)mode12only, hd->mode, sizeof(hd->mode), term_char) || + ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),term_char)) + goto out; + strncpy(hd->uname, name_uid(arcn->sb.st_uid, 0), sizeof(hd->uname)); + strncpy(hd->gname, name_gid(arcn->sb.st_gid, 0), sizeof(hd->gname)); + + /* + * calculate and store the checksum write the header to the archive + * return 0 tells the caller to now write the file data, 1 says no data + * needs to be written + */ + if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, + sizeof(hd->chksum), term_char)) + goto out; + if (wr_rdbuf((char *)&hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Ustar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * name_split() + * see if the name has to be split for storage in a ustar header. We try + * to fit the entire name in the name field without splitting if we can. + * The split point is always at a / + * Return + * character pointer to split point (always the / that is to be removed + * if the split is not needed, the points is set to the start of the file + * name (it would violate the spec to split there). A NULL is returned if + * the file name is too long + */ + +static char * +name_split(char *name, int len) +{ + char *start; + + /* + * check to see if the file name is small enough to fit in the name + * field. if so just return a pointer to the name. + * The strings can fill the complete name and prefix fields + * without a NUL terminator. + */ + if (len <= TNMSZ) + return(name); + if (len > (TPFSZ + TNMSZ + 1)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk forward looking for a slash to split at. The idea is + * to find the biggest piece to fit in the name field (or the smallest + * prefix we can find) (the -1 is correct the biggest piece would + * include the slash between the two parts that gets thrown away) + */ + start = name + len - TNMSZ - 1; + if ((*start == '/') && (start == name)) + ++start; /* 101 byte paths with leading '/' are dinged otherwise */ + while ((*start != '\0') && (*start != '/')) + ++start; + + /* + * if we hit the end of the string, this name cannot be split, so we + * cannot store this file. + */ + if (*start == '\0') + return(NULL); + len = start - name; + + /* + * NOTE: /str where the length of str == TNMSZ can not be stored under + * the p1003.1-1990 spec for ustar. We could force a prefix of / and + * the file would then expand on extract to //str. The len == 0 below + * makes this special case follow the spec to the letter. + */ + if ((len > TPFSZ) || (len == 0)) + return(NULL); + + /* + * ok have a split point, return it to the caller + */ + return(start); +} + +static size_t +expandname(char *buf, size_t len, char **gnu_name, const char *name, + size_t name_len) +{ + size_t nlen; + + if (*gnu_name) { + /* *gnu_name is NUL terminated */ + if ((nlen = strlcpy(buf, *gnu_name, len)) >= len) + nlen = len - 1; + free(*gnu_name); + *gnu_name = NULL; + } else { + if (name_len < len) { + /* name may not be null terminated: it might be as big as the + field, so copy is limited to the max size of the header field */ + if ((nlen = strlcpy(buf, name, name_len+1)) >= name_len+1) + nlen = name_len; + } else { + if ((nlen = strlcpy(buf, name, len)) >= len) + nlen = len - 1; + } + } + return(nlen); +} diff --git a/file_cmds/pax/tar.h b/file_cmds/pax/tar.h new file mode 100644 index 00000000..61bc6e13 --- /dev/null +++ b/file_cmds/pax/tar.h @@ -0,0 +1,156 @@ +/* $OpenBSD: tar.h,v 1.7 2003/06/02 23:32:09 millert Exp $ */ +/* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)tar.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * defines and data structures common to all tar formats + */ +#define CHK_LEN 8 /* length of checksum field */ +#define TNMSZ 100 /* size of name field */ +#ifdef _PAX_ +#define NULLCNT 2 /* number of null blocks in trailer */ +#define CHK_OFFSET 148 /* start of chksum field */ +#define BLNKSUM 256L /* sum of checksum field using ' ' */ +#endif /* _PAX_ */ + +/* + * Values used in typeflag field in all tar formats + * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers) + */ +#define REGTYPE '0' /* Regular File */ +#define AREGTYPE '\0' /* Regular File */ +#define LNKTYPE '1' /* Link */ +#define SYMTYPE '2' /* Symlink */ +#define CHRTYPE '3' /* Character Special File */ +#define BLKTYPE '4' /* Block Special File */ +#define DIRTYPE '5' /* Directory */ +#define FIFOTYPE '6' /* FIFO */ +#define CONTTYPE '7' /* high perf file */ + +#define PAXXTYPE 'x' /* pax format extended header */ +#define PAXGTYPE 'g' /* pax format global extended header */ + +/* + * GNU tar compatibility; + */ +#define LONGLINKTYPE 'K' /* Long Symlink */ +#define LONGNAMETYPE 'L' /* Long File */ + +/* + * Mode field encoding of the different file types - values in octal + */ +#define TSUID 04000 /* Set UID on execution */ +#define TSGID 02000 /* Set GID on execution */ +#define TSVTX 01000 /* Reserved */ +#define TUREAD 00400 /* Read by owner */ +#define TUWRITE 00200 /* Write by owner */ +#define TUEXEC 00100 /* Execute/Search by owner */ +#define TGREAD 00040 /* Read by group */ +#define TGWRITE 00020 /* Write by group */ +#define TGEXEC 00010 /* Execute/Search by group */ +#define TOREAD 00004 /* Read by other */ +#define TOWRITE 00002 /* Write by other */ +#define TOEXEC 00001 /* Execute/Search by other */ + +#ifdef _PAX_ +/* + * Pad with a bit mask, much faster than doing a mod but only works on powers + * of 2. Macro below is for block of 512 bytes. + */ +#define TAR_PAD(x) ((512 - ((x) & 511)) & 511) +#endif /* _PAX_ */ + +/* + * structure of an old tar header as it appeared in BSD releases + */ +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char linkflag; /* norm, hard, or sym. */ + char linkname[TNMSZ]; /* linked to name */ +} HD_TAR; + +#ifdef _PAX_ +/* + * -o options for BSD tar to not write directories to the archive + */ +#define TAR_NODIR "nodir" +#define TAR_OPTION "write_opt" + +/* + * default device names + */ +#define DEV_0 "/dev/rst0" +#define DEV_1 "/dev/rst1" +#define DEV_4 "/dev/rst4" +#define DEV_5 "/dev/rst5" +#define DEV_7 "/dev/rst7" +#define DEV_8 "/dev/rst8" +#endif /* _PAX_ */ + +/* + * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990 + */ +#define TPFSZ 155 +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGLEN 6 +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char typeflag; /* type of file. */ + char linkname[TNMSZ]; /* linked to name */ + char magic[TMAGLEN]; /* magic cookie */ + char version[TVERSLEN]; /* version */ + char uname[32]; /* ascii owner name */ + char gname[32]; /* ascii group name */ + char devmajor[8]; /* major device number */ + char devminor[8]; /* minor device number */ + char prefix[TPFSZ]; /* linked to name */ +} HD_USTAR; diff --git a/file_cmds/pax/tty_subs.c b/file_cmds/pax/tty_subs.c new file mode 100644 index 00000000..74065c62 --- /dev/null +++ b/file_cmds/pax/tty_subs.c @@ -0,0 +1,200 @@ +/* $OpenBSD: tty_subs.c,v 1.12 2003/06/02 23:32:09 millert Exp $ */ +/* $NetBSD: tty_subs.c,v 1.5 1995/03/21 09:07:52 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = "$OpenBSD: tty_subs.c,v 1.12 2003/06/02 23:32:09 millert Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#include + +/* + * routines that deal with I/O to and from the user + */ + +#define DEVTTY "/dev/tty" /* device for interactive i/o */ +static FILE *ttyoutf = NULL; /* output pointing at control tty */ +static FILE *ttyinf = NULL; /* input pointing at control tty */ + +/* + * tty_init() + * try to open the controlling terminal (if any) for this process. if the + * open fails, future ops that require user input will get an EOF + */ + +int +tty_init(void) +{ + int ttyfd; + + if ((ttyfd = open(DEVTTY, O_RDWR)) >= 0) { + if ((ttyoutf = fdopen(ttyfd, "w")) != NULL) { + if ((ttyinf = fdopen(ttyfd, "r")) != NULL) + return(0); + (void)fclose(ttyoutf); + } + (void)close(ttyfd); + } + + if (iflag) { + paxwarn(1, "Fatal error, cannot open %s", DEVTTY); + return(-1); + } + return(0); +} + +/* + * tty_prnt() + * print a message using the specified format to the controlling tty + * if there is no controlling terminal, just return. + */ + +void +tty_prnt(const char *fmt, ...) +{ + va_list ap; + if (ttyoutf == NULL) + return; + va_start(ap, fmt); + (void)vfprintf(ttyoutf, fmt, ap); + va_end(ap); + (void)fflush(ttyoutf); +} + +/* + * tty_read() + * read a string from the controlling terminal if it is open into the + * supplied buffer + * Return: + * 0 if data was read, -1 otherwise. + */ + +int +tty_read(char *str, int len) +{ + char *pt; + + if ((--len <= 0) || (ttyinf == NULL) || (fgets(str,len,ttyinf) == NULL)) + return(-1); + *(str + len) = '\0'; + + /* + * strip off that trailing newline + */ + if ((pt = strchr(str, '\n')) != NULL) + *pt = '\0'; + return(0); +} + +/* + * paxwarn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +void +paxwarn(int set, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (set && (pax_invalid_action==0)) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fflush(listf); + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fputc('\n', stderr); +} + +/* + * syswarn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +void +syswarn(int set, int errnum, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (set) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fflush(listf); + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + + /* + * format and print the errno + */ + if (errnum > 0) + (void)fprintf(stderr, " <%s>", strerror(errnum)); + (void)fputc('\n', stderr); +} diff --git a/file_cmds/rm/rm.1 b/file_cmds/rm/rm.1 new file mode 100644 index 00000000..c40f8a7b --- /dev/null +++ b/file_cmds/rm/rm.1 @@ -0,0 +1,216 @@ +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rm.1 8.5 (Berkeley) 12/5/94 +.\" $FreeBSD: src/bin/rm/rm.1,v 1.23 2001/07/15 07:49:05 dd Exp $ +.\" +.Dd January 28, 1999 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm , +.Nm unlink +.Nd remove directory entries +.Sh SYNOPSIS +.Nm +.Op Fl dfiPRrvW +.Ar +.Nm unlink +.Ar file +.Sh DESCRIPTION +The +.Nm +utility attempts to remove the non-directory type files specified on the +command line. +If the permissions of the file do not permit writing, and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +.Pp +The options are as follows: +.Bl -tag -width Fl +.It Fl d +Attempt to remove directories as well as other types of files. +.It Fl f +Attempt to remove the files without prompting for confirmation, +regardless of the file's permissions. +If the file does not exist, do not display a diagnostic message or modify +the exit status to reflect an error. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Request confirmation before attempting to remove each file, regardless of +the file's permissions, or whether or not the standard input device is a +terminal. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl P +Overwrite regular files before deleting them. +Files are overwritten three times, first with the byte pattern 0xff, +then 0x00, and then 0xff again, before they are deleted. +.It Fl R +Attempt to remove the file hierarchy rooted in each file argument. +The +.Fl R +option implies the +.Fl d +option. +If the +.Fl i +option is specified, the user is prompted for confirmation before +each directory's contents are processed (as well as before the attempt +is made to remove the directory). +If the user does not respond affirmatively, the file hierarchy rooted in +that directory is skipped. +.Pp +.It Fl r +Equivalent to +.Fl R . +.It Fl v +Be verbose when deleting files, showing them as they are removed. +.It Fl W +Attempt to undelete the named files. +Currently, this option can only be used to recover +files covered by whiteouts. +.El +.Pp +The +.Nm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files +.Dq .\& +or +.Dq .. . +.Pp +When the utility is called as +.Nm unlink , +only one argument, +which must not be a directory, +may be supplied. +No options may be supplied in this simple mode of operation, +which performs an +.Xr unlink 2 +operation on the passed argument. +.Pp +The +.Nm +utility exits 0 if all of the named files or file hierarchies were removed, +or if the +.Fl f +option was specified and all of the existing files or file hierarchies were +removed. +If an error occurs, +.Nm +exits with a value >0. +.Sh NOTE +The +.Nm +command uses +.Xr getopt 3 +to parse its arguments, which allows it to accept +the +.Sq Li -- +option which will cause it to stop processing flag options at that +point. This will allow the removal of file names that begin +with a dash +.Pq Sq - . +For example: +.Dl rm -- -filename +The same behavior can be obtained by using an absolute or relative +path reference. For example: +.Dl rm /home/user/-filename +.Dl rm ./-filename +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr undelete 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr getopt 3 , +.Xr symlink 7 +.Sh BUGS +The +.Fl P +option assumes that the underlying file system is a fixed-block file +system. In addition, only regular files are overwritten, other types of files +are not. +.Sh COMPATIBILITY +The +.Nm +utility differs from historical implementations in that the +.Fl f +option only masks attempts to remove non-existent files instead of +masking a large variety of errors. +The +.Fl v +option is non-standard and its use in scripts is not recommended. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm +command is almost +.St -p1003.2 +compatible, except that +.Tn POSIX +requires +.Nm +to act like +.Xr rmdir 1 +when the +.Ar file +specified is a directory. This implementation requires the +.Fl d +option if such behavior is desired. This follows the historical +behavior of +.Nm +with respect to directories. +.Pp +The simplified +.Nm unlink +command conforms to +.St -susv2 . +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/file_cmds/rm/rm.c b/file_cmds/rm/rm.c new file mode 100644 index 00000000..2acb7442 --- /dev/null +++ b/file_cmds/rm/rm.c @@ -0,0 +1,583 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rm.c 8.5 (Berkeley) 4/18/94"; +#else +__used static const char rcsid[] = + "$FreeBSD: src/bin/rm/rm.c,v 1.33 2001/06/13 15:01:25 ru Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __APPLE__ +#include +#include +#include +// #include "get_compat.h" +// #else +#define COMPAT_MODE(func, mode) 1 +#endif +#include "ios_error.h" + +static int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; +static uid_t uid; + +static int check __P((char *, char *, struct stat *)); +static int checkdir __P((char *)); +static int yes_or_no __P((void)); +static void checkdot __P((char **)); +static void rm_file __P((char **)); +static void rm_overwrite __P((char *, struct stat *)); +static void rm_tree __P((char **)); +static void usage __P((void)); + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +rm_main(argc, argv) + int argc; + char *argv[]; +{ + int ch, rflag; + char *p; + + if (argc < 1) + usage(); + + // init all flags, something quite important for rm + dflag = eval = fflag = iflag = Pflag = vflag = Wflag = stdin_ok = 0; + optind = 1; opterr = 1; optreset = 1; + + /* + * Test for the special case where the utility is called as + * "unlink", for which the functionality provided is greatly + * simplified. + */ + if ((p = rindex(argv[0], '/')) == NULL) + p = argv[0]; + else + ++p; + uid = geteuid(); + if (strcmp(p, "unlink") == 0) { + if (argc == 2) { + rm_file(&argv[1]); + exit(eval); + } else + usage(); + } + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRrvW")) != -1) + switch(ch) { + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + fflag = 0; + iflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + case 'r': /* Compatibility. */ + rflag = 1; + break; + case 'v': + vflag = 1; + break; + case 'W': + Wflag = 1; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) { + if (fflag) + return 0; + usage(); + } + + checkdot(argv); + + if (*argv) { + stdin_ok = ios_isatty(STDIN_FILENO); + + if (rflag) + rm_tree(argv); + else + rm_file(argv); + } + + exit (eval); +} + +void +rm_tree(argv) + char **argv; +{ + FTS *fts; + FTSENT *p; + int needstat; + int flags; + int rval; + int wantConformance = COMPAT_MODE("bin/rm", "unix2003"); + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !uid || (!fflag && !iflag && stdin_ok); + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + flags = FTS_PHYSICAL; + if (!needstat) + flags |= FTS_NOSTAT; + if (Wflag) + flags |= FTS_WHITEOUT; + if (!(fts = fts_open(argv, flags, NULL))) { + if (fflag && errno == ENOENT) + return; + err(1, NULL); + } + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path); + eval = 1; + } + continue; + case FTS_ERR: + errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); + case FTS_NS: + /* + * FTS_NS: assume that if can't stat the file, it + * can't be unlinked. + */ + if (!needstat) + break; + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path); + eval = 1; + } + continue; + case FTS_D: + /* Pre-order: give user chance to skip. */ + /* In conformance mode the user is prompted to skip processing the contents. + * Then the option to delete the dir is presented post-order */ + if (!fflag && + ( (wantConformance && !checkdir(p->fts_path)) || + (!wantConformance && !check(p->fts_path, p->fts_accpath, p->fts_statp)) + ) + ){ + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } + else if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)) < 0) + goto err; + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if(p->fts_number == SKIPPED)/*in legacy mode, the user was prompted pre-order */ + continue; + else if(wantConformance) + { + /* delete directory if force is on, or if user answers Y to prompt */ + if(fflag || check(p->fts_path, p->fts_accpath, p->fts_statp)) + break; + else + continue; + } + break; + default: + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + } + + rval = 0; + if (!uid && + (p->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(p->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(p->fts_accpath, + p->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + rval = rmdir(p->fts_accpath); + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + break; + + case FTS_W: + rval = undelete(p->fts_accpath); + if (rval == 0 && (fflag && errno == ENOENT)) { + if (vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + break; + + default: +#ifdef __APPLE__ + if (Pflag) { + if (removefile(p->fts_accpath, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */ + eval = rval = 1; + } else + rval = unlink(p->fts_accpath); +#else /* !__APPLE_ */ + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + rval = unlink(p->fts_accpath); +#endif /* __APPLE__ */ + if (rval == 0 || (fflag && errno == ENOENT)) { + if (rval == 0 && vflag) + (void)fprintf(thread_stdout, "%s\n", + p->fts_path); + continue; + } + } + } +err: + warn("%s", p->fts_path); + eval = 1; + } + fts_close(fts); + if (errno) { + err(1, "fts_read"); + } +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int rval; + char *f; + + /* + * Remove a file. POSIX 1003.2 states that, by default, attempting + * to remove a directory is an error, so must always stat the file. + */ + while ((f = *argv++) != NULL) { + /* Assume if can't stat the file, can't unlink it. */ + if (lstat(f, &sb)) { + if (Wflag) { + sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; + } else { + if (!fflag || errno != ENOENT) { + warn("%s", f); + eval = 1; + } + continue; + } + } else if (Wflag) { + warnx("%s: %s", f, strerror(EEXIST)); + eval = 1; + continue; + } + + if (S_ISDIR(sb.st_mode) && !dflag) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) + continue; + rval = 0; + if (!uid && + (sb.st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(sb.st_flags & (SF_APPEND|SF_IMMUTABLE))) + rval = chflags(f, sb.st_flags & ~(UF_APPEND|UF_IMMUTABLE)); + if (rval == 0) { + if (S_ISWHT(sb.st_mode)) + rval = undelete(f); + else if (S_ISDIR(sb.st_mode)) + rval = rmdir(f); + else { +#ifdef __APPLE__ + if (Pflag) { + if (removefile(f, NULL, REMOVEFILE_SECURE_7_PASS)) /* overwrites and unlinks */ + eval = rval = 1; + } else + rval = unlink(f); +#else /* !__APPLE__ */ + if (Pflag) + rm_overwrite(f, &sb); + rval = unlink(f); +#endif /* __APPLE__ */ + } + } + if (rval && (!fflag || errno != ENOENT)) { + warn("%s", f); + eval = 1; + } + if (vflag && rval == 0) + (void)fprintf(thread_stdout, "%s\n", f); + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +void +rm_overwrite(file, sbp) + char *file; + struct stat *sbp; +{ + struct stat sb; + struct statfs fsb; + off_t len; + int bsize, fd, wlen; + char *buf = NULL; + + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return; + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; + if (fstatfs(fd, &fsb) == -1) + goto err; + bsize = MAX(fsb.f_iosize, 1024); + if ((buf = malloc(bsize)) == NULL) { + err(1, "malloc"); + } + +#define PASS(byte) { \ + memset(buf, byte, bsize); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < bsize ? (int)len : bsize; \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) { + free(buf); + return; + } + +err: eval = 1; + if (buf) + free(buf); + warn("%s", file); +} + +int +yes_or_no() +{ + int ch, first; + (void)fflush(thread_stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y' || first == 'Y'); +} + +int +checkdir(path) + char *path; +{ + if(!iflag) + return 1; //if not interactive, process directory's contents + (void)fprintf(thread_stderr, "examine files in directory %s? ", path); + return yes_or_no(); +} + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + char modep[15], *flagsp; + + /* Check -i first. */ + if (iflag) + (void)fprintf(thread_stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ + if (!stdin_ok || S_ISLNK(sp->st_mode) || + (!access(name, W_OK) && + !(sp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + (!(sp->st_flags & (UF_APPEND|UF_IMMUTABLE)) || !uid))) + return (1); + strmode(sp->st_mode, modep); + if ((flagsp = fflagstostr(sp->st_flags)) == NULL) { + err(1, NULL); + } + (void)fprintf(thread_stderr, "override %s%s%s/%s %s%sfor %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), + *flagsp ? flagsp : "", *flagsp ? " " : "", + path); + free(flagsp); + } + return yes_or_no(); +} + + +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) +void +checkdot(argv) + char **argv; +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + size_t len = strlen(*t); + char truncated[len]; + + if ((p = strrchr(*t, '/')) != NULL) { + if (p[1] == '\0') { // one or more trailing / -- treat as if not present + for (; (p > *t) && (p[-1] == '/');) { + len--; + p--; + } + strlcpy(truncated, *t, len); + p = strrchr(truncated, '/'); + if (p) { + ++p; + } else { + p = truncated; + } + } else { + ++p; + } + } else { + p = *t; + } + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t) + continue; + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(thread_stderr, "%s\n%s\n", + "usage: rm [-f | -i] [-dPRrvW] file ...", + " unlink file"); + exit(EX_USAGE); +} diff --git a/file_cmds/rm/unlink.1 b/file_cmds/rm/unlink.1 new file mode 100644 index 00000000..6764e6d1 --- /dev/null +++ b/file_cmds/rm/unlink.1 @@ -0,0 +1 @@ +.so man1/rm.1 diff --git a/file_cmds/rmdir/rmdir.1 b/file_cmds/rmdir/rmdir.1 new file mode 100644 index 00000000..577637a5 --- /dev/null +++ b/file_cmds/rmdir/rmdir.1 @@ -0,0 +1,100 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rmdir.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD: src/bin/rmdir/rmdir.1,v 1.9 2000/11/20 11:39:40 ru Exp $ +.\" +.Dd May 31, 1993 +.Dt RMDIR 1 +.Os +.Sh NAME +.Nm rmdir +.Nd remove directories +.Sh SYNOPSIS +.Nm +.Op Fl p +.Ar directory ... +.Sh DESCRIPTION +The +.Nm +utility removes the directory entry specified by +each +.Ar directory +argument, provided it is empty. +.Pp +Arguments are processed in the order given. +In order to remove both a parent directory and a subdirectory +of that parent, the subdirectory +must be specified first so the parent directory +is empty when +.Nm +tries to remove it. +.Pp +The following option is available: +.Bl -tag -width indent +.It Fl p +Each +.Ar directory +argument is treated as a pathname of which all +components will be removed, if they are empty, +starting with the last most component. +(See +.Xr rm 1 +for fully non-discriminant recursive removal.) +.El +.Pp +The +.Nm +utility exits with one of the following values: +.Bl -tag -width Ds +.It Li \&0 +Each directory entry specified by a dir operand +referred to an empty directory and was removed +successfully. +.It Li \&>\&0 +An error occurred. +.El +.Sh SEE ALSO +.Xr rm 1 +.Sh STANDARDS +The +.Nm +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/file_cmds/rmdir/rmdir.c b/file_cmds/rmdir/rmdir.c new file mode 100644 index 00000000..46059355 --- /dev/null +++ b/file_cmds/rmdir/rmdir.c @@ -0,0 +1,125 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__used static char const copyright[] = +"@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94"; +#endif +#endif /* not lint */ +#include +__RCSID("$FreeBSD: src/bin/rmdir/rmdir.c,v 1.13 2002/06/30 05:15:03 obrien Exp $"); + +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static int rm_path(char *); +static void usage(void); + +int +rmdir_main(int argc, char *argv[]) +{ + int ch, errors; + int pflag; + + pflag = 0; + optind = 1; opterr = 1; optreset = 1; + + while ((ch = getopt(argc, argv, "p")) != -1) + switch(ch) { + case 'p': + pflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + for (errors = 0; *argv; argv++) { + if (rmdir(*argv) < 0) { + warn("%s", *argv); + errors = 1; + } else if (pflag) + errors |= rm_path(*argv); + } + + exit(errors); +} + +int +rm_path(char *path) +{ + char *p; + + p = path + strlen(path); + while (--p > path && *p == '/') + ; + *++p = '\0'; + while ((p = strrchr(path, '/')) != NULL) { + /* Delete trailing slashes. */ + while (--p > path && *p == '/') + ; + *++p = '\0'; + + if (rmdir(path) < 0) { + warn("%s", path); + return (1); + } + } + + return (0); +} + +void +usage(void) +{ + + (void)fprintf(thread_stderr, "usage: rmdir [-p] directory ...\n"); + exit(1); +} diff --git a/file_cmds/rmt/rmt.8 b/file_cmds/rmt/rmt.8 new file mode 100644 index 00000000..77a90740 --- /dev/null +++ b/file_cmds/rmt/rmt.8 @@ -0,0 +1,220 @@ +.\" $NetBSD: rmt.8,v 1.6 1997/10/17 13:03:15 lukem Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt RMT 8 +.Os BSD 4.2 +.Sh NAME +.Nm rmt +.Nd remote magtape protocol module +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is a program used by the remote dump and restore programs +in manipulating a magnetic tape drive through an interprocess +communication connection. +.Nm +is normally started up with an +.Xr rexec 3 +or +.Xr rcmd 3 +call. +.Pp +The +.Nm +program accepts requests specific to the manipulation of +magnetic tapes, performs the commands, then responds with +a status indication. All responses are in +.Tn ASCII +and in +one of two forms. +Successful commands have responses of: +.Bd -filled -offset indent +.Sm off +.Sy A Ar number No \en +.Sm on +.Ed +.Pp +.Ar Number +is an +.Tn ASCII +representation of a decimal number. +Unsuccessful commands are responded to with: +.Bd -filled -offset indent +.Sm off +.Xo Sy E Ar error-number +.No \en Ar error-message +.No \en +.Xc +.Sm on +.Ed +.Pp +.Ar Error-number +is one of the possible error +numbers described in +.Xr intro 2 +and +.Ar error-message +is the corresponding error string as printed +from a call to +.Xr perror 3 . +The protocol is comprised of the +following commands, which are sent as indicated - no spaces are supplied +between the command and its arguments, or between its arguments, and +.Ql \en +indicates that a newline should be supplied: +.Bl -tag -width Ds +.Sm off +.It Xo Sy \&O Ar device +.No \en Ar mode No \en +.Xc +Open the specified +.Ar device +using the indicated +.Ar mode . +.Ar Device +is a full pathname and +.Ar mode +is an +.Tn ASCII +representation of a decimal +number suitable for passing to +.Xr open 2 . +If a device had already been opened, it is +closed before a new open is performed. +.It Xo Sy C Ar device No \en +.Xc +Close the currently open device. The +.Ar device +specified is ignored. +.It Xo Sy L +.Ar offset No \en +.Ar whence No \en +.Xc +.Sm on +Perform an +.Xr lseek 2 +operation using the specified parameters. +The response value is that returned from the +.Xr lseek +call. +.Sm off +.It Sy W Ar count No \en +.Sm on +Write data onto the open device. +.Nm +reads +.Ar count +bytes from the connection, aborting if +a premature end-of-file is encountered. +The response value is that returned from +the +.Xr write 2 +call. +.Sm off +.It Sy R Ar count No \en +.Sm on +Read +.Ar count +bytes of data from the open device. +If +.Ar count +exceeds the size of the data buffer (10 kilobytes), it is +truncated to the data buffer size. +.Nm +then performs the requested +.Xr read 2 +and responds with +.Sm off +.Sy A Ar count-read No \en +.Sm on +if the read was +successful; otherwise an error in the +standard format is returned. If the read +was successful, the data read is then sent. +.Sm off +.It Xo Sy I Ar operation +.No \en Ar count No \en +.Xc +.Sm on +Perform a +.Dv MTIOCOP +.Xr ioctl 2 +command using the specified parameters. +The parameters are interpreted as the +.Tn ASCII +representations of the decimal values +to place in the +.Ar mt_op +and +.Ar mt_count +fields of the structure used in the +.Xr ioctl +call. The return value is the +.Ar count +parameter when the operation is successful. +.ne 1i +.It Sy S +Return the status of the open device, as +obtained with a +.Dv MTIOCGET +.Xr ioctl +call. If the operation was successful, +an ``ack'' is sent with the size of the +status buffer, then the status buffer is +sent (in binary). +.El +.Sm on +.Pp +Any other command causes +.Nm +to exit. +.Sh DIAGNOSTICS +All responses are of the form described above. +.Sh SEE ALSO +.Xr rcmd 3 , +.Xr rexec 3 , +.Xr mtio 4 , +.Xr rdump 8 , +.Xr rrestore 8 +.Sh BUGS +People should be discouraged from using this for a remote +file access protocol. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/file_cmds/rmt/rmt.c b/file_cmds/rmt/rmt.c new file mode 100644 index 00000000..bfb4d37e --- /dev/null +++ b/file_cmds/rmt/rmt.c @@ -0,0 +1,259 @@ +/* $NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * rmt + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int tape = -1; + +char *record; +int maxrecsize = -1; + +#define SSIZE 64 +char device[SSIZE]; +char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE]; + +char resp[BUFSIZ]; + +FILE *debug; +#define DEBUG(f) if (debug) fprintf(debug, f) +#define DEBUG1(f,a) if (debug) fprintf(debug, f, a) +#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2) + +char *checkbuf __P((char *, int)); +void error __P((int)); +int main __P((int, char **)); +void getstring __P((char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int rval; + char c; + int n, i, cc; + + argc--, argv++; + if (argc > 0) { + debug = fopen(*argv, "w"); + if (debug == 0) + exit(1); + (void)setbuf(debug, (char *)0); + } +top: + errno = 0; + rval = 0; + if (read(STDIN_FILENO, &c, 1) != 1) + exit(0); + switch (c) { + + case 'O': + if (tape >= 0) + (void) close(tape); + getstring(device); + getstring(mode); + DEBUG2("rmtd: O %s %s\n", device, mode); + tape = open(device, atoi(mode), + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (tape < 0) + goto ioerror; + goto respond; + + case 'C': + DEBUG("rmtd: C\n"); + getstring(device); /* discard */ + if (close(tape) < 0) + goto ioerror; + tape = -1; + goto respond; + + case 'L': + getstring(count); + getstring(pos); + DEBUG2("rmtd: L %s %s\n", count, pos); + rval = lseek(tape, (off_t)strtoq(count, NULL, 10), atoi(pos)); + if (rval < 0) + goto ioerror; + goto respond; + + case 'W': + getstring(count); + n = atoi(count); + DEBUG1("rmtd: W %s\n", count); + record = checkbuf(record, n); + for (i = 0; i < n; i += cc) { + cc = read(STDIN_FILENO, &record[i], n - i); + if (cc <= 0) { + DEBUG("rmtd: premature eof\n"); + exit(2); + } + } + rval = write(tape, record, n); + if (rval < 0) + goto ioerror; + goto respond; + + case 'R': + getstring(count); + DEBUG1("rmtd: R %s\n", count); + n = atoi(count); + record = checkbuf(record, n); + rval = read(tape, record, n); + if (rval < 0) + goto ioerror; + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + (void)write(STDOUT_FILENO, record, rval); + goto top; + + case 'I': + getstring(op); + getstring(count); + DEBUG2("rmtd: I %s %s\n", op, count); + { + struct mtop mtop; + + mtop.mt_op = atoi(op); + mtop.mt_count = atoi(count); + if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0) + goto ioerror; + rval = mtop.mt_count; + } + goto respond; + + case 'S': /* status */ + DEBUG("rmtd: S\n"); + { + struct mtget mtget; + + if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0) + goto ioerror; + rval = sizeof (mtget); + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + (void)write(STDOUT_FILENO, (char *)&mtget, + sizeof (mtget)); + goto top; + } + + default: + DEBUG1("rmtd: garbage command %c\n", c); + exit(3); + } +respond: + DEBUG1("rmtd: A %d\n", rval); + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + goto top; +ioerror: + error(errno); + goto top; +} + +void +getstring(bp) + char *bp; +{ + int i; + char *cp = bp; + + for (i = 0; i < SSIZE - 1; i++) { + if (read(STDIN_FILENO, cp+i, 1) != 1) + exit(0); + if (cp[i] == '\n') + break; + } + cp[i] = '\0'; +} + +char * +checkbuf(record, size) + char *record; + int size; +{ + + if (size <= maxrecsize) + return (record); + if (record != 0) + free(record); + record = malloc(size); + if (record == 0) { + DEBUG("rmtd: cannot allocate buffer space\n"); + exit(4); + } + maxrecsize = size; + while (size > 1024 && + setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0) + size -= 1024; + return (record); +} + +void +error(num) + int num; +{ + + DEBUG2("rmtd: E %d (%s)\n", num, strerror(num)); + (void)sprintf(resp, "E%d\n%s\n", num, strerror(num)); + (void)write(STDOUT_FILENO, resp, strlen(resp)); +} diff --git a/file_cmds/shar/shar.1 b/file_cmds/shar/shar.1 new file mode 100644 index 00000000..3f9dac9a --- /dev/null +++ b/file_cmds/shar/shar.1 @@ -0,0 +1,105 @@ +.\" $NetBSD: shar.1,v 1.6 1998/06/08 12:41:44 lukem Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shar.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt SHAR 1 +.Os BSD 4.4 +.Sh NAME +.Nm shar +.Nd create a shell archive of files +.Sh SYNOPSIS +.Nm +.Ar +.Sh DESCRIPTION +.Nm +writes an +.Xr sh 1 +shell script to the standard output which will recreate the file +hierarchy specified by the command line operands. +Directories will be recreated and must be specified before the +files they contain (the +.Xr find 1 +utility does this correctly). +.Pp +.Nm +is normally used for distributing files by +.Xr ftp 1 +or +.Xr mail 1 . +.Sh SEE ALSO +.Xr compress 1 , +.Xr mail 1 , +.Xr tar 1 , +.Xr uuencode 1 +.Sh BUGS +.Nm +makes no provisions for special types of files or files containing +magic characters. +.Sh EXAMPLES +To create a shell archive of the program +.Xr ls 1 +and mail it to Rick: +.Bd -literal -offset indent +cd ls +shar `find . -print` \&| mail -s "ls source" rick +.Ed +.Pp +To recreate the program directory: +.Bd -literal -offset indent +mkdir ls +cd ls +\&... + +\&... +sh archive +.Ed +.Sh HISTORY +The +.Nm +command appears in +.Bx 4.4 . +.Sh SECURITY CONSIDERATIONS +It is easy to insert trojan horses into +.Nm +files. +It is strongly recommended that all shell archive files be examined +before running them through +.Xr sh 1 . +Archives produced using this implementation of +.Nm +may be easily examined with the command: +.Bd -literal -offset indent +egrep -v '^[X#]' shar.file +.Ed diff --git a/file_cmds/shar/shar.sh b/file_cmds/shar/shar.sh new file mode 100644 index 00000000..7048b446 --- /dev/null +++ b/file_cmds/shar/shar.sh @@ -0,0 +1,76 @@ +#!/bin/sh - +# +# $NetBSD: shar.sh,v 1.2 1994/12/21 08:42:04 jtc Exp $ +# +# Copyright (c) 1990, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)shar.sh 8.1 (Berkeley) 6/6/93 +# + +if [ $# -eq 0 ]; then + echo 'usage: shar file ...' + exit 1 +fi + +cat << EOF +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +EOF + +for i +do + echo "# $i" +done + +echo "#" + +for i +do + if [ -d $i ]; then + echo "echo c - $i" + echo "mkdir -p $i > /dev/null 2>&1" + else + echo "echo x - $i" + echo "sed 's/^X//' >$i << 'END-of-$i'" + sed 's/^/X/' $i + echo "END-of-$i" + fi +done +echo exit +echo "" + +exit 0 diff --git a/file_cmds/stat/readlink.1 b/file_cmds/stat/readlink.1 new file mode 100644 index 00000000..968704b1 --- /dev/null +++ b/file_cmds/stat/readlink.1 @@ -0,0 +1 @@ +.so man1/stat.1 diff --git a/file_cmds/stat/stat.1 b/file_cmds/stat/stat.1 new file mode 100644 index 00000000..79c774fd --- /dev/null +++ b/file_cmds/stat/stat.1 @@ -0,0 +1,534 @@ +.\" $NetBSD: stat.1,v 1.11 2003/05/08 13:07:10 wiz Exp $ +.\" +.\" Copyright (c) 2002 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Brown and Jan Schaumann. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the NetBSD +.\" Foundation, Inc. and its contributors. +.\" 4. Neither the name of The NetBSD Foundation nor the names of its +.\" contributors may be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/stat/stat.1,v 1.8 2005/06/14 11:50:53 ru Exp $ +.\" +.Dd May 8, 2003 +.Dt STAT 1 +.Os +.Sh NAME +.Nm readlink , +.Nm stat +.Nd display file status +.Sh SYNOPSIS +.Nm stat +.Op Fl FLnq +.Op Fl f Ar format | Fl l | r | s | x +.Op Fl t Ar timefmt +.Op Ar +.Nm readlink +.Op Fl n +.Op Ar +.Sh DESCRIPTION +The +.Nm stat +utility displays information about the file pointed to by +.Ar file . +Read, write or execute permissions of the named file are not required, but +all directories listed in the path name leading to the file must be +searchable. +If no argument is given, +.Nm stat +displays information about the file descriptor for standard input. +.Pp +When invoked as +.Nm readlink , +only the target of the symbolic link is printed. +If the given argument is not a symbolic link, +.Nm readlink +will print nothing and exit with an error. +.Pp +The information displayed is obtained by calling +.Xr lstat 2 +with the given argument and evaluating the returned structure. +.Pp +The options are as follows: +.Bl -tag -width indent +.\" ========== +.It Fl F +As in +.Xr ls 1 , +display a slash +.Pq Ql / +immediately after each pathname that is a directory, +an asterisk +.Pq Ql * +after each that is executable, +an at sign +.Pq Ql @ +after each symbolic link, +a percent sign +.Pq Ql % +after each whiteout, +an equal sign +.Pq Ql = +after each socket, +and a vertical bar +.Pq Ql | +after each that is a FIFO. +The use of +.Fl F +implies +.Fl l . +.\" ========== +.It Fl f Ar format +Display information using the specified format. +See the +.Sx FORMATS +section for a description of valid formats. +.\" ========== +.It Fl L +Use +.Xr stat 2 +instead of +.Xr lstat 2 . +The information reported by +.Nm stat +will refer to the target of +.Ar file , +if file is a symbolic link, and not to +.Ar file +itself. +.\" ========== +.It Fl l +Display output in +.Nm ls Fl lT +format. +.\" ========== +.It Fl n +Do not force a newline to appear at the end of each piece of output. +.\" ========== +.It Fl q +Suppress failure messages if calls to +.Xr stat 2 +or +.Xr lstat 2 +fail. +When run as +.Nm readlink , +error messages are automatically suppressed. +.\" ========== +.It Fl r +Display raw information. +That is, for all the fields in the +.Vt stat +structure, +display the raw, numerical value (for example, times in seconds since the +epoch, etc.). +.\" ========== +.It Fl s +Display information in +.Dq "shell output" , +suitable for initializing variables. +.\" ========== +.It Fl t Ar timefmt +Display timestamps using the specified format. +This format is +passed directly to +.Xr strftime 3 . +.\" ========== +.It Fl x +Display information in a more verbose way as known from some +.Tn Linux +distributions. +.El +.Ss Formats +Format strings are similar to +.Xr printf 3 +formats in that they start with +.Cm % , +are then followed by a sequence of formatting characters, and end in +a character that selects the field of the +.Vt "struct stat" +which is to be formatted. +If the +.Cm % +is immediately followed by one of +.Cm n , t , % , +or +.Cm @ , +then a newline character, a tab character, a percent character, +or the current file number is printed, otherwise the string is +examined for the following: +.Pp +Any of the following optional flags: +.Bl -tag -width indent +.It Cm # +Selects an alternate output form for octal and hexadecimal output. +Non-zero octal output will have a leading zero, and non-zero +hexadecimal output will have +.Dq Li 0x +prepended to it. +.It Cm + +Asserts that a sign indicating whether a number is positive or negative +should always be printed. +Non-negative numbers are not usually printed +with a sign. +.It Cm - +Aligns string output to the left of the field, instead of to the right. +.It Cm 0 +Sets the fill character for left padding to the +.Ql 0 +character, instead of a space. +.It space +Reserves a space at the front of non-negative signed output fields. +A +.Sq Cm + +overrides a space if both are used. +.El +.Pp +Then the following fields: +.Bl -tag -width indent +.It Ar size +An optional decimal digit string specifying the minimum field width. +.It Ar prec +An optional precision composed of a decimal point +.Sq Cm \&. +and a decimal digit string that indicates the maximum string length, +the number of digits to appear after the decimal point in floating point +output, or the minimum number of digits to appear in numeric output. +.It Ar fmt +An optional output format specifier which is one of +.Cm D , O , U , X , F , +or +.Cm S . +These represent signed decimal output, octal output, unsigned decimal +output, hexadecimal output, floating point output, and string output, +respectively. +Some output formats do not apply to all fields. +Floating point output only applies to +.Vt timespec +fields (the +.Cm a , m , +and +.Cm c +fields). +.Pp +The special output specifier +.Cm S +may be used to indicate that the output, if +applicable, should be in string format. +May be used in combination with: +.Bl -tag -width indent +.It Cm amc +Display date in +.Xr strftime 3 +format. +.It Cm dr +Display actual device name. +.It Cm gu +Display group or user name. +.It Cm p +Display the mode of +.Ar file +as in +.Nm ls Fl lTd . +.It Cm N +Displays the name of +.Ar file . +.It Cm T +Displays the type of +.Ar file . +.It Cm Y +Insert a +.Dq Li " -\*[Gt] " +into the output. +Note that the default output format +for +.Cm Y +is a string, but if specified explicitly, these four characters are +prepended. +.El +.It Ar sub +An optional sub field specifier (high, middle, low). +Only applies to +the +.Cm p , d , r , +and +.Cm T +output formats. +It can be one of the following: +.Bl -tag -width indent +.It Cm H +.Dq High +\[em] +specifies the major number for devices from +.Cm r +or +.Cm d , +the +.Dq user +bits for permissions from the string form of +.Cm p , +the file +.Dq type +bits from the numeric forms of +.Cm p , +and the long output form of +.Cm T . +.It Cm L +.Dq Low +\[em] +specifies the minor number for devices from +.Cm r +or +.Cm d , +the +.Dq other +bits for permissions from the string form of +.Cm p , +the +.Dq user , +.Dq group , +and +.Dq other +bits from the numeric forms of +.Cm p , +and the +.Nm ls Fl F +style output character for file type when used with +.Cm T +(the use of +.Cm L +for this is optional). +.It Cm M +.Dq Middle +\[em] +specifies the +.Dq group +bits for permissions from the +string output form of +.Cm p , +or the +.Dq suid , +.Dq sgid , +and +.Dq sticky +bits for the numeric forms of +.Cm p . +.El +.It Ar datum +A required field specifier, being one of the following: +.Bl -tag -width indent +.It Cm d +Device upon which +.Ar file +resides. +.It Cm i +.Ar file Ns 's +inode number. +.It Cm p +File type and permissions. +.It Cm l +Number of hard links to +.Ar file . +.It Cm u , g +User ID and group ID of +.Ar file Ns 's +owner. +.It Cm r +Device number for character and block device special files. +.It Cm a , m , c , B +The time +.Ar file +was last accessed or modified, of when the inode was last changed, or +the birth time of the inode. +.It Cm z +The size of +.Ar file +in bytes. +.It Cm b +Number of blocks allocated for +.Ar file . +.It Cm k +Optimal file system I/O operation block size. +.It Cm f +User defined flags for +.Ar file . +.It Cm v +Inode generation number. +.El +.Pp +The following four field specifiers are not drawn directly from the +data in +.Vt "struct stat" , +but are: +.Bl -tag -width indent +.It Cm N +The name of the file. +.It Cm T +The file type, either as in +.Nm ls Fl F +or in a more descriptive form if the +.Ar sub +field specifier +.Cm H +is given. +.It Cm Y +The target of a symbolic link. +.It Cm Z +Expands to +.Dq major,minor +from the +.Va rdev +field for character or block +special devices and gives size output for all others. +.El +.El +.Pp +Only the +.Cm % +and the field specifier are required. +Most field specifiers default to +.Cm U +as an output form, with the +exception of +.Cm p +which defaults to +.Cm O , +.Cm a , m , +and +.Cm c +which default to +.Cm D , +and +.Cm Y , T , +and +.Cm N +which default to +.Cm S . +.Sh EXIT STATUS +.Ex -std stat readlink +.Sh EXAMPLES +Given a symbolic link +.Pa foo +that points from +.Pa /tmp/foo +to +.Pa / , +you would use +.Nm stat +as follows: +.Bd -literal -offset indent +\*[Gt] stat -F /tmp/foo +lrwxrwxrwx 1 jschauma cs 1 Apr 24 16:37:28 2002 /tmp/foo@ -\*[Gt] / + +\*[Gt] stat -LF /tmp/foo +drwxr-xr-x 16 root wheel 512 Apr 19 10:57:54 2002 /tmp/foo/ +.Ed +.Pp +To initialize some shell variables, you could use the +.Fl s +flag as follows: +.Bd -literal -offset indent +\*[Gt] csh +% eval set `stat -s .cshrc` +% echo $st_size $st_mtimespec +1148 1015432481 + +\*[Gt] sh +$ eval $(stat -s .profile) +$ echo $st_size $st_mtimespec +1148 1015432481 +.Ed +.Pp +In order to get a list of the kind of files including files pointed to if the +file is a symbolic link, you could use the following format: +.Bd -literal -offset indent +$ stat -f "%N: %HT%SY" /tmp/* +/tmp/bar: Symbolic Link -\*[Gt] /tmp/foo +/tmp/output25568: Regular File +/tmp/blah: Directory +/tmp/foo: Symbolic Link -\*[Gt] / +.Ed +.Pp +In order to get a list of the devices, their types and the major and minor +device numbers, formatted with tabs and linebreaks, you could use the +following format: +.Bd -literal -offset indent +stat -f "Name: %N%n%tType: %HT%n%tMajor: %Hr%n%tMinor: %Lr%n%n" /dev/* +[...] +Name: /dev/wt8 + Type: Block Device + Major: 3 + Minor: 8 + +Name: /dev/zero + Type: Character Device + Major: 2 + Minor: 12 +.Ed +.Pp +In order to determine the permissions set on a file separately, you could use +the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%Sp -\*[Gt] owner=%SHp group=%SMp other=%SLp" . +drwxr-xr-x -\*[Gt] owner=rwx group=r-x other=r-x +.Ed +.Pp +In order to determine the three files that have been modified most recently, +you could use the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%m%t%Sm %N" /tmp/* | sort -rn | head -3 | cut -f2- +Apr 25 11:47:00 2002 /tmp/blah +Apr 25 10:36:34 2002 /tmp/bar +Apr 24 16:47:35 2002 /tmp/foo +.Ed +.Sh SEE ALSO +.Xr file 1 , +.Xr ls 1 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr printf 3 , +.Xr strftime 3 +.Sh HISTORY +The +.Nm stat +utility appeared in +.Nx 1.6 +and +.Fx 4.10 . +.Sh AUTHORS +.An -nosplit +The +.Nm stat +utility was written by +.An Andrew Brown +.Aq atatat@NetBSD.org . +This man page was written by +.An Jan Schaumann +.Aq jschauma@NetBSD.org . diff --git a/file_cmds/stat/stat.c b/file_cmds/stat/stat.c new file mode 100644 index 00000000..66a4a1a0 --- /dev/null +++ b/file_cmds/stat/stat.c @@ -0,0 +1,1008 @@ +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#if 0 +#ifndef lint +__RCSID("$NetBSD: stat.c,v 1.13 2003/07/25 03:21:17 atatat Exp $"); +#endif +#endif + +__FBSDID("$FreeBSD: src/usr.bin/stat/stat.c,v 1.6 2003/10/06 01:55:17 dougb Exp $"); + +#if HAVE_CONFIG_H +#include "config.h" +#else /* HAVE_CONFIG_H */ +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_STRUCT_STAT_ST_ATIM 0 +#define HAVE_DEVNAME 1 +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +#if HAVE_STRUCT_STAT_ST_FLAGS +#define DEF_F "%#Xf " +#define RAW_F "%f " +#define SHELL_F " st_flags=%f" +#else /* HAVE_STRUCT_STAT_ST_FLAGS */ +#define DEF_F +#define RAW_F +#define SHELL_F +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + +#if HAVE_STRUCT_STAT_ST_BIRTHTIME +#define DEF_B "\"%SB\" " +#define RAW_B "%B " +#define SHELL_B "st_birthtime=%B " +#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ +#define DEF_B +#define RAW_B +#define SHELL_B +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + +#if HAVE_STRUCT_STAT_ST_ATIM +#define st_atimespec st_atim +#define st_ctimespec st_ctim +#define st_mtimespec st_mtim +#endif /* HAVE_STRUCT_STAT_ST_ATIM */ + +#define DEF_FORMAT \ + "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ + "%k %b " DEF_F "%N" +#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ + "%k %b " RAW_F "%N" +#define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" +#define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" +#define SHELL_FORMAT \ + "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ + "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ + "st_atime=%a st_mtime=%m st_ctime=%c " SHELL_B \ + "st_blksize=%k st_blocks=%b" SHELL_F +#define LINUX_FORMAT \ + " File: \"%N\"%n" \ + " Size: %-11z FileType: %HT%n" \ + " Mode: (%04OA/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ + "Device: %Hd,%Ld Inode: %i Links: %l%n" \ + "Access: %Sa%n" \ + "Modify: %Sm%n" \ + "Change: %Sc" + +#define TIME_FORMAT "%b %e %T %Y" + +#define FLAG_POUND 0x01 +#define FLAG_SPACE 0x02 +#define FLAG_PLUS 0x04 +#define FLAG_ZERO 0x08 +#define FLAG_MINUS 0x10 + +/* + * These format characters must all be unique, except the magic one. + */ +#define FMT_MAGIC '%' +#define FMT_DOT '.' + +#define SIMPLE_NEWLINE 'n' +#define SIMPLE_TAB 't' +#define SIMPLE_PERCENT '%' +#define SIMPLE_NUMBER '@' + +#define FMT_POUND '#' +#define FMT_SPACE ' ' +#define FMT_PLUS '+' +#define FMT_ZERO '0' +#define FMT_MINUS '-' + +#define FMT_DECIMAL 'D' +#define FMT_OCTAL 'O' +#define FMT_UNSIGNED 'U' +#define FMT_HEX 'X' +#define FMT_FLOAT 'F' +#define FMT_STRING 'S' + +#define FMTF_DECIMAL 0x01 +#define FMTF_OCTAL 0x02 +#define FMTF_UNSIGNED 0x04 +#define FMTF_HEX 0x08 +#define FMTF_FLOAT 0x10 +#define FMTF_STRING 0x20 + +#define HIGH_PIECE 'H' +#define MIDDLE_PIECE 'M' +#define LOW_PIECE 'L' + +#define SHOW_st_dev 'd' +#define SHOW_st_ino 'i' +#define SHOW_st_mode 'p' +#define SHOW_st_mode2 'A' +#define SHOW_st_nlink 'l' +#define SHOW_st_uid 'u' +#define SHOW_st_gid 'g' +#define SHOW_st_rdev 'r' +#define SHOW_st_atime 'a' +#define SHOW_st_mtime 'm' +#define SHOW_st_ctime 'c' +#define SHOW_st_btime 'B' +#define SHOW_st_size 'z' +#define SHOW_st_blocks 'b' +#define SHOW_st_blksize 'k' +#define SHOW_st_flags 'f' +#define SHOW_st_gen 'v' +#define SHOW_symlink 'Y' +#define SHOW_filetype 'T' +#define SHOW_filename 'N' +#define SHOW_sizerdev 'Z' + +static void usage(const char *); +static void output(const struct stat *, const char *, + const char *, int, int, int); +static int format1(const struct stat *, /* stat info */ + const char *, /* the file name */ + const char *, int, /* the format string itself */ + char *, size_t, /* a place to put the output */ + int, int, int, int, /* the parsed format */ + int, int); + +static char *timefmt; +static int linkfail; + +#define addchar(s, c, nl) \ + do { \ + (void)fputc((c), (s)); \ + (*nl) = ((c) == '\n'); \ + } while (0/*CONSTCOND*/) + +static char* progname; + +int +stat_main(int argc, char *argv[]) +{ + struct stat st; + int ch, rc, errs, am_readlink; + int lsF, fmtchar, usestat, fn, nonl, quiet; + char *statfmt, *options, *synopsis; + + am_readlink = 0; + lsF = 0; + fmtchar = '\0'; + usestat = 0; + nonl = 0; + quiet = 0; + linkfail = 0; + statfmt = NULL; + timefmt = NULL; + optind = 1; opterr = 1; optreset = 1; + progname = argv[0]; + + // if (strcmp(getprogname(), "readlink") == 0) { + if (strcmp(progname, "readlink") == 0) { + am_readlink = 1; + options = "n"; + synopsis = "[-n] [file ...]"; + statfmt = "%Y"; + fmtchar = 'f'; + quiet = 1; + } else { + options = "f:FlLnqrst:x"; + synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; + } + + while ((ch = getopt(argc, argv, options)) != -1) + switch (ch) { + case 'F': + lsF = 1; + break; + case 'L': + usestat = 1; + break; + case 'n': + nonl = 1; + break; + case 'q': + quiet = 1; + break; + case 'f': + statfmt = optarg; + /* FALLTHROUGH */ + case 'l': + case 'r': + case 's': + case 'x': + if (fmtchar != 0) { + errx(1, "can't use format '%c' with '%c'", + fmtchar, ch); + } + fmtchar = ch; + break; + case 't': + timefmt = optarg; + break; + default: + usage(synopsis); + } + + argc -= optind; + argv += optind; + fn = 1; + + if (fmtchar == '\0') { + if (lsF) + fmtchar = 'l'; + else { + fmtchar = 'f'; + statfmt = DEF_FORMAT; + } + } + + if (lsF && fmtchar != 'l') { + errx(1, "can't use format '%c' with -F", fmtchar); + } + + switch (fmtchar) { + case 'f': + /* statfmt already set */ + break; + case 'l': + statfmt = lsF ? LSF_FORMAT : LS_FORMAT; + break; + case 'r': + statfmt = RAW_FORMAT; + break; + case 's': + statfmt = SHELL_FORMAT; + break; + case 'x': + statfmt = LINUX_FORMAT; + if (timefmt == NULL) + timefmt = "%c"; + break; + default: + usage(synopsis); + /*NOTREACHED*/ + } + + if (timefmt == NULL) + timefmt = TIME_FORMAT; + + errs = 0; + do { + if (argc == 0) + rc = fstat(fileno(thread_stdin), &st); + else if (usestat) + rc = stat(argv[0], &st); + else + rc = lstat(argv[0], &st); + + if (rc == -1) { + errs = 1; + linkfail = 1; + if (!quiet) + warn("%s: stat", + argc == 0 ? "(stdin)" : argv[0]); + } + else + output(&st, argv[0], statfmt, fn, nonl, quiet); + + argv++; + argc--; + fn++; + } while (argc > 0); + + return (am_readlink ? linkfail : errs); +} + +void +usage(const char *synopsis) +{ + // (void)fprintf(thread_stderr, "usage: %s %s\n", getprogname(), synopsis); + (void)fprintf(thread_stderr, "usage: %s %s\n", progname, synopsis); + exit(1); +} + +/* + * Parses a format string. + */ +void +output(const struct stat *st, const char *file, + const char *statfmt, int fn, int nonl, int quiet) +{ + int flags, size, prec, ofmt, hilo, what; + char buf[PATH_MAX]; + const char *subfmt; + int nl, t, i; + + nl = 1; + while (*statfmt != '\0') { + + /* + * Non-format characters go straight out. + */ + if (*statfmt != FMT_MAGIC) { + addchar(thread_stdout, *statfmt, &nl); + statfmt++; + continue; + } + + /* + * The current format "substring" starts here, + * and then we skip the magic. + */ + subfmt = statfmt; + statfmt++; + + /* + * Some simple one-character "formats". + */ + switch (*statfmt) { + case SIMPLE_NEWLINE: + addchar(thread_stdout, '\n', &nl); + statfmt++; + continue; + case SIMPLE_TAB: + addchar(thread_stdout, '\t', &nl); + statfmt++; + continue; + case SIMPLE_PERCENT: + addchar(thread_stdout, '%', &nl); + statfmt++; + continue; + case SIMPLE_NUMBER: { + char num[12], *p; + + snprintf(num, sizeof(num), "%d", fn); + for (p = &num[0]; *p; p++) + addchar(thread_stdout, *p, &nl); + statfmt++; + continue; + } + } + + /* + * This must be an actual format string. Format strings are + * similar to printf(3) formats up to a point, and are of + * the form: + * + * % required start of format + * [-# +0] opt. format characters + * size opt. field width + * . opt. decimal separator, followed by + * prec opt. precision + * fmt opt. output specifier (string, numeric, etc.) + * sub opt. sub field specifier (high, middle, low) + * datum required field specifier (size, mode, etc) + * + * Only the % and the datum selector are required. All data + * have reasonable default output forms. The "sub" specifier + * only applies to certain data (mode, dev, rdev, filetype). + * The symlink output defaults to STRING, yet will only emit + * the leading " -> " if STRING is explicitly specified. The + * sizerdev datum will generate rdev output for character or + * block devices, and size output for all others. + */ + flags = 0; + do { + if (*statfmt == FMT_POUND) + flags |= FLAG_POUND; + else if (*statfmt == FMT_SPACE) + flags |= FLAG_SPACE; + else if (*statfmt == FMT_PLUS) + flags |= FLAG_PLUS; + else if (*statfmt == FMT_ZERO) + flags |= FLAG_ZERO; + else if (*statfmt == FMT_MINUS) + flags |= FLAG_MINUS; + else + break; + statfmt++; + } while (1/*CONSTCOND*/); + + size = -1; + if (isdigit((unsigned)*statfmt)) { + size = 0; + while (isdigit((unsigned)*statfmt)) { + size = (size * 10) + (*statfmt - '0'); + statfmt++; + if (size < 0) + goto badfmt; + } + } + + prec = -1; + if (*statfmt == FMT_DOT) { + statfmt++; + + prec = 0; + while (isdigit((unsigned)*statfmt)) { + prec = (prec * 10) + (*statfmt - '0'); + statfmt++; + if (prec < 0) + goto badfmt; + } + } + +#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break +#define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break + switch (*statfmt) { + fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); + fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); + fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); + fmtcasef(ofmt, FMT_HEX, FMTF_HEX); + fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); + fmtcasef(ofmt, FMT_STRING, FMTF_STRING); + default: + ofmt = 0; + break; + } + + switch (*statfmt) { + fmtcase(hilo, HIGH_PIECE); + fmtcase(hilo, MIDDLE_PIECE); + fmtcase(hilo, LOW_PIECE); + default: + hilo = 0; + break; + } + + switch (*statfmt) { + fmtcase(what, SHOW_st_dev); + fmtcase(what, SHOW_st_ino); + fmtcase(what, SHOW_st_mode); + fmtcase(what, SHOW_st_mode2); + fmtcase(what, SHOW_st_nlink); + fmtcase(what, SHOW_st_uid); + fmtcase(what, SHOW_st_gid); + fmtcase(what, SHOW_st_rdev); + fmtcase(what, SHOW_st_atime); + fmtcase(what, SHOW_st_mtime); + fmtcase(what, SHOW_st_ctime); + fmtcase(what, SHOW_st_btime); + fmtcase(what, SHOW_st_size); + fmtcase(what, SHOW_st_blocks); + fmtcase(what, SHOW_st_blksize); + fmtcase(what, SHOW_st_flags); + fmtcase(what, SHOW_st_gen); + fmtcase(what, SHOW_symlink); + fmtcase(what, SHOW_filetype); + fmtcase(what, SHOW_filename); + fmtcase(what, SHOW_sizerdev); + default: + goto badfmt; + } +#undef fmtcasef +#undef fmtcase + + t = format1(st, + file, + subfmt, statfmt - subfmt, + buf, sizeof(buf), + flags, size, prec, ofmt, hilo, what); + + for (i = 0; i < t && i < sizeof(buf); i++) + addchar(thread_stdout, buf[i], &nl); + + continue; + + badfmt: + errx(1, "%.*s: bad format", + (int)(statfmt - subfmt + 1), subfmt); + } + + if (!nl && !nonl) + (void)fputc('\n', thread_stdout); + (void)fflush(thread_stdout); +} + +/* + * Arranges output according to a single parsed format substring. + */ +int +format1(const struct stat *st, + const char *file, + const char *fmt, int flen, + char *buf, size_t blen, + int flags, int size, int prec, int ofmt, + int hilo, int what) +{ + u_int64_t data; + char *sdata, lfmt[24], tmp[20]; + char smode[12], sid[12], path[PATH_MAX + 4]; + struct passwd *pw; + struct group *gr; + const struct timespec *tsp; + struct timespec ts = {0,0}; + struct tm *tm; + int l, small, formats; + + tsp = NULL; +// formats = 0; +// small = 0; + + /* + * First, pick out the data and tweak it based on hilo or + * specified output format (symlink output only). + */ + switch (what) { + case SHOW_st_dev: + case SHOW_st_rdev: + small = (sizeof(st->st_dev) == 4); + data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; +#if HAVE_DEVNAME + sdata = (what == SHOW_st_dev) ? + devname(st->st_dev, S_IFBLK) : + devname(st->st_rdev, + S_ISCHR(st->st_mode) ? S_IFCHR : + S_ISBLK(st->st_mode) ? S_IFBLK : + 0U); + if (sdata == NULL) + sdata = "???"; +#endif /* HAVE_DEVNAME */ + if (hilo == HIGH_PIECE) { + data = major(data); + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data = minor((unsigned)data); + hilo = 0; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | +#if HAVE_DEVNAME + FMTF_STRING; +#else /* HAVE_DEVNAME */ + 0; +#endif /* HAVE_DEVNAME */ + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_ino: + small = (sizeof(st->st_ino) == 4); + data = st->st_ino; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_mode: + case SHOW_st_mode2: + small = (sizeof(st->st_mode) == 4); + data = st->st_mode; + strmode(st->st_mode, smode); + sdata = smode; + l = strlen(sdata); + if (sdata[l - 1] == ' ') + sdata[--l] = '\0'; + if (what == SHOW_st_mode2) + data &= 07777; + if (hilo == HIGH_PIECE) { + data >>= 12; + sdata += 1; + sdata[3] = '\0'; + hilo = 0; + } + else if (hilo == MIDDLE_PIECE) { + data = (data >> 9) & 07; + sdata += 4; + sdata[3] = '\0'; + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data &= 0777; + sdata += 7; + sdata[3] = '\0'; + hilo = 0; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_OCTAL; + break; + case SHOW_st_nlink: + small = (sizeof(st->st_dev) == 4); + data = st->st_nlink; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_uid: + small = (sizeof(st->st_uid) == 4); + data = st->st_uid; + if ((pw = getpwuid(st->st_uid)) != NULL) + sdata = pw->pw_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_gid: + small = (sizeof(st->st_gid) == 4); + data = st->st_gid; + if ((gr = getgrgid(st->st_gid)) != NULL) + sdata = gr->gr_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_atime: + tsp = &st->st_atimespec; + /* FALLTHROUGH */ + case SHOW_st_mtime: + if (tsp == NULL) + tsp = &st->st_mtimespec; + /* FALLTHROUGH */ + case SHOW_st_ctime: + if (tsp == NULL) + tsp = &st->st_ctimespec; + /* FALLTHROUGH */ +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + case SHOW_st_btime: + if (tsp == NULL) + tsp = &st->st_birthtimespec; +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + ts = *tsp; /* copy so we can muck with it */ + small = (sizeof(ts.tv_sec) == 4); + data = ts.tv_sec; + tm = localtime(&ts.tv_sec); + if (tm == NULL) { + ts.tv_sec = 0; + tm = localtime(&ts.tv_sec); + } + (void)strftime(path, sizeof(path), timefmt, tm); + sdata = path; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_FLOAT | FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_DECIMAL; + break; + case SHOW_st_size: + small = (sizeof(st->st_size) == 4); + data = st->st_size; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_blocks: + small = (sizeof(st->st_blocks) == 4); + data = st->st_blocks; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_blksize: + small = (sizeof(st->st_blksize) == 4); + data = st->st_blksize; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#if HAVE_STRUCT_STAT_ST_FLAGS + case SHOW_st_flags: + small = (sizeof(st->st_flags) == 4); + data = st->st_flags; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ +#if HAVE_STRUCT_STAT_ST_GEN + case SHOW_st_gen: + small = (sizeof(st->st_gen) == 4); + data = st->st_gen; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_GEN */ + case SHOW_symlink: + small = 0; + data = 0; + if (S_ISLNK(st->st_mode)) { + snprintf(path, sizeof(path), " -> "); + l = readlink(file, path + 4, sizeof(path) - 4 - 1); + if (l == -1) { + linkfail = 1; + l = 0; + path[0] = '\0'; + } + path[l + 4] = '\0'; + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); + } + else { + linkfail = 1; + sdata = ""; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filetype: + small = 0; + data = 0; + sdata = smode; + sdata[0] = '\0'; + if (hilo == 0 || hilo == LOW_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: (void)strcat(sdata, "|"); break; + case S_IFDIR: (void)strcat(sdata, "/"); break; + case S_IFREG: + if (st->st_mode & + (S_IXUSR | S_IXGRP | S_IXOTH)) + (void)strcat(sdata, "*"); + break; + case S_IFLNK: (void)strcat(sdata, "@"); break; + case S_IFSOCK: (void)strcat(sdata, "="); break; +#ifdef S_IFWHT + case S_IFWHT: (void)strcat(sdata, "%"); break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: (void)strcat(sdata, ">"); break; +#endif /* S_IFDOOR */ + } + hilo = 0; + } + else if (hilo == HIGH_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: sdata = "Fifo File"; break; + case S_IFCHR: sdata = "Character Device"; break; + case S_IFDIR: sdata = "Directory"; break; + case S_IFBLK: sdata = "Block Device"; break; + case S_IFREG: sdata = "Regular File"; break; + case S_IFLNK: sdata = "Symbolic Link"; break; + case S_IFSOCK: sdata = "Socket"; break; +#ifdef S_IFWHT + case S_IFWHT: sdata = "Whiteout File"; break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: sdata = "Door"; break; +#endif /* S_IFDOOR */ + default: sdata = "???"; break; + } + hilo = 0; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filename: + small = 0; + data = 0; + if (file == NULL) + (void)strncpy(path, "(stdin)", sizeof(path)); + else + (void)strncpy(path, file, sizeof(path)); + sdata = path; + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_sizerdev: + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + char majdev[20], mindev[20]; + int l1, l2; + + l1 = format1(st, + file, + fmt, flen, + majdev, sizeof(majdev), + flags, size, prec, + ofmt, HIGH_PIECE, SHOW_st_rdev); + l2 = format1(st, + file, + fmt, flen, + mindev, sizeof(mindev), + flags, size, prec, + ofmt, LOW_PIECE, SHOW_st_rdev); + return (snprintf(buf, blen, "%.*s,%.*s", + l1, majdev, l2, mindev)); + } + else { + return (format1(st, + file, + fmt, flen, + buf, blen, + flags, size, prec, + ofmt, 0, SHOW_st_size)); + } + /*NOTREACHED*/ + default: + errx(1, "%.*s: bad format", (int)flen, fmt); + } + + /* + * If a subdatum was specified but not supported, or an output + * format was selected that is not supported, that's an error. + */ + if (hilo != 0 || (ofmt & formats) == 0) { + errx(1, "%.*s: bad format", (int)flen, fmt); + } + + /* + * Assemble the format string for passing to printf(3). + */ + lfmt[0] = '\0'; + (void)strcat(lfmt, "%"); + if (flags & FLAG_POUND) + (void)strcat(lfmt, "#"); + if (flags & FLAG_SPACE) + (void)strcat(lfmt, " "); + if (flags & FLAG_PLUS) + (void)strcat(lfmt, "+"); + if (flags & FLAG_MINUS) + (void)strcat(lfmt, "-"); + if (flags & FLAG_ZERO) + (void)strcat(lfmt, "0"); + + /* + * Only the timespecs support the FLOAT output format, and that + * requires work that differs from the other formats. + */ + if (ofmt == FMTF_FLOAT) { + /* + * Nothing after the decimal point, so just print seconds. + */ + if (prec == 0) { + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + (void)strcat(lfmt, "d"); + return (snprintf(buf, blen, lfmt, ts.tv_sec)); + } + + /* + * Unspecified precision gets all the precision we have: + * 9 digits. + */ + if (prec == -1) + prec = 9; + + /* + * Adjust the size for the decimal point and the digits + * that will follow. + */ + size -= prec + 1; + + /* + * Any leftover size that's legitimate will be used. + */ + if (size > 0) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + (void)strcat(lfmt, "d"); + + /* + * The stuff after the decimal point always needs zero + * filling. + */ + (void)strcat(lfmt, ".%0"); + + /* + * We can "print" at most nine digits of precision. The + * rest we will pad on at the end. + */ + (void)snprintf(tmp, sizeof(tmp), "%dd", prec > 9 ? 9 : prec); + (void)strcat(lfmt, tmp); + + /* + * For precision of less that nine digits, trim off the + * less significant figures. + */ + for (; prec < 9; prec++) + ts.tv_nsec /= 10; + + /* + * Use the format, and then tack on any zeroes that + * might be required to make up the requested precision. + */ + l = snprintf(buf, blen, lfmt, ts.tv_sec, ts.tv_nsec); + for (; prec > 9 && l < blen; prec--, l++) + (void)strcat(buf, "0"); + return (l); + } + + /* + * Add on size and precision, if specified, to the format. + */ + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + if (prec != -1) { + (void)snprintf(tmp, sizeof(tmp), ".%d", prec); + (void)strcat(lfmt, tmp); + } + + /* + * String output uses the temporary sdata. + */ + if (ofmt == FMTF_STRING) { + if (sdata == NULL) { + errx(1, "%.*s: bad format", (int)flen, fmt); + } + (void)strcat(lfmt, "s"); + return (snprintf(buf, blen, lfmt, sdata)); + } + + /* + * Ensure that sign extension does not cause bad looking output + * for some forms. + */ + if (small && ofmt != FMTF_DECIMAL) + data = (u_int32_t)data; + + /* + * The four "numeric" output forms. + */ + (void)strcat(lfmt, "ll"); + switch (ofmt) { + case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; + case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; + case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; + case FMTF_HEX: (void)strcat(lfmt, "x"); break; + } + + return (snprintf(buf, blen, lfmt, data)); +} diff --git a/file_cmds/touch/touch.1 b/file_cmds/touch/touch.1 new file mode 100644 index 00000000..6e6678b6 --- /dev/null +++ b/file_cmds/touch/touch.1 @@ -0,0 +1,221 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)touch.1 8.3 (Berkeley) 4/28/95 +.\" $FreeBSD: src/usr.bin/touch/touch.1,v 1.16 2007/04/10 07:24:47 grog Exp $ +.\" +.Dd April 28, 1995 +.Dt TOUCH 1 +.Os +.Sh NAME +.Nm touch +.Nd change file access and modification times +.Sh SYNOPSIS +.Nm +.Op Fl A Ar [-][[hh]mm]SS +.Op Fl acfhm +.Op Fl r Ar file +.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS] +.Ar +.Sh DESCRIPTION +The +.Nm +utility sets the modification and access times of files. +If any file does not exist, it is created with default permissions. +.Pp +By default, +.Nm +changes both modification and access times. The +.Fl a +and +.Fl m +flags may be used to select the access time or the modification time +individually. +Selecting both is equivalent to the default. +By default, the timestamps are set to the current time. +The +.Fl t +flag explicitly specifies a different time, and the +.Fl r +flag specifies to set the times those of the specified file. +The +.Fl A +flag adjusts the values by a specified amount. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl A +Adjust the access and modification time stamps for the file by the +specified value. +This flag is intended for use in modifying files with incorrectly set +time stamps. +.Pp +The argument is of the form +.Dq [-][[hh]mm]SS +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar - +Make the adjustment negative: the new time stamp is set to be before +the old one. +.It Ar hh +The number of hours, from 00 to 99. +.It Ar mm +The number of minutes, from 00 to 59. +.It Ar SS +The number of seconds, from 00 to 59. +.El +.Pp +The +.Fl A +flag implies the +.Fl c +flag: if any file specified does not exist, it will be silently ignored. +.It Fl a +Change the access time of the file. +The modification time of the file is not changed unless the +.Fl m +flag is also specified. +.It Fl c +Do not create the file if it does not exist. +The +.Nm +utility does not treat this as an error. +No error messages are displayed and the exit value is not affected. +.It Fl f +Attempt to force the update, even if the file permissions do not +currently permit it. +.It Fl h +If the file is a symbolic link, change the times of the link +itself rather than the file that the link points to. +Note that +.Fl h +implies +.Fl c +and thus will not create any new files. +.It Fl m +Change the modification time of the file. +The access time of the file is not changed unless the +.Fl a +flag is also specified. +.It Fl r +Use the access and modifications times from the specified file +instead of the current time of day. +.It Fl t +Change the access and modification times to the specified time instead +of the current time of day. +The argument is of the form +.Dq [[CC]YY]MMDDhhmm[.SS] +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar CC +The first two digits of the year (the century). +.It Ar YY +The second two digits of the year. +If +.Dq YY +is specified, but +.Dq CC +is not, a value for +.Dq YY +between 69 and 99 results in a +.Dq CC +value of 19. +Otherwise, a +.Dq CC +value of 20 is used. +.It Ar MM +The month of the year, from 01 to 12. +.It Ar DD +the day of the month, from 01 to 31. +.It Ar hh +The hour of the day, from 00 to 23. +.It Ar mm +The minute of the hour, from 00 to 59. +.It Ar SS +The second of the minute, from 00 to 61. +.El +.Pp +If the +.Dq CC +and +.Dq YY +letter pairs are not specified, the values default to the current +year. +If the +.Dq SS +letter pair is not specified, the value defaults to 0. +.El +.Sh EXIT STATUS +.Ex -std +.Sh COMPATIBILITY +The obsolescent form of +.Nm , +where a time format is specified as the first argument, is supported. +When no +.Fl r +or +.Fl t +option is specified, there are at least two arguments, and the first +argument is a string of digits either eight or ten characters in length, +the first argument is interpreted as a time specification of the form +.Dq MMDDhhmm[YY] . +.Pp +The +.Dq MM , +.Dq DD , +.Dq hh +and +.Dq mm +letter pairs are treated as their counterparts specified to the +.Fl t +option. +If the +.Dq YY +letter pair is in the range 39 to 99, the year is set to 1939 to 1999, +otherwise, the year is set in the 21st century. +.Sh SEE ALSO +.Xr utimes 2 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +specification. +.Sh HISTORY +A +.Nm +utility appeared in +.At v7 . diff --git a/file_cmds/touch/touch.c b/file_cmds/touch/touch.c new file mode 100644 index 00000000..3a0a0b67 --- /dev/null +++ b/file_cmds/touch/touch.c @@ -0,0 +1,437 @@ +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +__FBSDID("$FreeBSD: src/usr.bin/touch/touch.c,v 1.25 2010/03/28 13:16:08 ed Exp $"); + +#ifndef lint +__used static const char copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +__used static const char sccsid[] = "@(#)touch.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ios_error.h" + +static int rw(char *, struct stat *, int); +static void stime_arg1(char *, struct timeval *); +static void stime_arg2(char *, int, struct timeval *); +static void stime_file(char *, struct timeval *); +static int timeoffset(char *); +static void usage(char *); + +int +touch_main(int argc, char *argv[]) +{ + struct stat sb; + struct timeval tv[2]; + int (*stat_f)(const char *, struct stat *); + int (*utimes_f)(const char *, const struct timeval *); + int Aflag, aflag, cflag, fflag, mflag, ch, fd, len, rval, timeset; + char *p; + char *myname; + + myname = basename(argv[0]); + Aflag = aflag = cflag = fflag = mflag = timeset = 0; + optind = 1; opterr = 1; optreset = 1; + stat_f = stat; + utimes_f = utimes; + if (gettimeofday(&tv[0], NULL)) { + err(1, "gettimeofday"); + } + + while ((ch = getopt(argc, argv, "A:acfhmr:t:")) != -1) + switch(ch) { + case 'A': + Aflag = timeoffset(optarg); + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + cflag = 1; + stat_f = lstat; + utimes_f = lutimes; + break; + case 'm': + mflag = 1; + break; + case 'r': + timeset = 1; + stime_file(optarg, tv); + break; + case 't': + timeset = 1; + stime_arg1(optarg, tv); + break; + case '?': + default: + usage(myname); + } + argc -= optind; + argv += optind; + + if (aflag == 0 && mflag == 0) + aflag = mflag = 1; + + if (timeset) { + if (Aflag) { + /* + * We're setting the time to an offset from a specified + * time. God knows why, but it means that we can set + * that time once and for all here. + */ + if (aflag) + tv[0].tv_sec += Aflag; + if (mflag) + tv[1].tv_sec += Aflag; + Aflag = 0; /* done our job */ + } + } else { + /* + * If no -r or -t flag, at least two operands, the first of + * which is an 8 or 10 digit number, use the obsolete time + * specification, otherwise use the current time. + */ + if (argc > 1) { + strtol(argv[0], &p, 10); + len = p - argv[0]; + if (*p == '\0' && (len == 8 || len == 10)) { + timeset = 1; + stime_arg2(*argv++, len == 10, tv); + } + } + /* Both times default to the same. */ + tv[1] = tv[0]; + } + + if (*argv == NULL) + usage(myname); + + if (Aflag) + cflag = 1; + + for (rval = 0; *argv; ++argv) { + /* See if the file exists. */ + if (stat_f(*argv, &sb) != 0) { + if (errno != ENOENT) { + rval = 1; + warn("%s", *argv); + continue; + } + if (!cflag) { + /* Create the file. */ + fd = open(*argv, + O_WRONLY | O_CREAT, DEFFILEMODE); + if (fd == -1 || fstat(fd, &sb) || close(fd)) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* If using the current time, we're done. */ + if (!timeset) + continue; + } else + continue; + } + + if (!aflag) + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + if (!mflag) + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + + /* + * We're adjusting the times based on the file times, not a + * specified time (that gets handled above). + */ + if (Aflag) { + if (aflag) { + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + tv[0].tv_sec += Aflag; + } + if (mflag) { + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + tv[1].tv_sec += Aflag; + } + } + + /* Try utimes(2). */ + if (!utimes_f(*argv, tv)) + continue; + + /* If the user specified a time, nothing else we can do. */ + if (timeset || Aflag) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* + * System V and POSIX 1003.1 require that a NULL argument + * set the access/modification times to the current time. + * The permission checks are different, too, in that the + * ability to write the file is sufficient. Take a shot. + */ + if (!utimes_f(*argv, NULL)) + continue; + + /* Try reading/writing. */ + if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode)) { + if (rw(*argv, &sb, fflag)) + rval = 1; + } else { + rval = 1; + warn("%s", *argv); + } + } + exit(rval); +} + +#define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; + +void +stime_arg1(char *arg, struct timeval *tvp) +{ + time_t now; + struct tm *t; + int yearset; + char *p; + /* Start with the current time. */ + now = tvp[0].tv_sec; + if ((t = localtime(&now)) == NULL) { + err(1, "localtime"); + } + /* [[CC]YY]MMDDhhmm[.SS] */ + if ((p = strchr(arg, '.')) == NULL) + t->tm_sec = 0; /* Seconds defaults to 0. */ + else { + if (strlen(p + 1) != 2) + goto terr; + *p++ = '\0'; + t->tm_sec = ATOI2(p); + } + + yearset = 0; + switch(strlen(arg)) { + case 12: /* CCYYMMDDhhmm */ + t->tm_year = ATOI2(arg); + t->tm_year *= 100; + yearset = 1; + /* FALLTHROUGH */ + case 10: /* YYMMDDhhmm */ + if (yearset) { + yearset = ATOI2(arg); + t->tm_year += yearset; + } else { + yearset = ATOI2(arg); + if (yearset < 69) + t->tm_year = yearset + 2000; + else + t->tm_year = yearset + 1900; + } + t->tm_year -= 1900; /* Convert to UNIX time. */ + /* FALLTHROUGH */ + case 8: /* MMDDhhmm */ + t->tm_mon = ATOI2(arg); + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + break; + default: + goto terr; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) + terr: errx(1, + "touch: out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]\n"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_arg2(char *arg, int year, struct timeval *tvp) +{ + time_t now; + struct tm *t; + /* Start with the current time. */ + now = tvp[0].tv_sec; + if ((t = localtime(&now)) == NULL) { + err(1, "localtime"); + } + + t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + if (year) { + t->tm_year = ATOI2(arg); + if (t->tm_year < 39) /* support 2000-2038 not 1902-1969 */ + t->tm_year += 100; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) { + errx(1, + "touch: out of range or illegal time specification: MMDDhhmm[yy]\n"); + } + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +/* Calculate a time offset in seconds, given an arg of the format [-]HHMMSS. */ +int +timeoffset(char *arg) +{ + int offset; + int isneg; + + offset = 0; + isneg = *arg == '-'; + if (isneg) + arg++; + switch (strlen(arg)) { + default: /* invalid */ + errx(1, "Invalid offset spec, must be [-][[HH]MM]SS"); + + case 6: /* HHMMSS */ + offset = ATOI2(arg); + /* FALLTHROUGH */ + case 4: /* MMSS */ + offset = offset * 60 + ATOI2(arg); + /* FALLTHROUGH */ + case 2: /* SS */ + offset = offset * 60 + ATOI2(arg); + } + if (isneg) + return (-offset); + else + return (offset); +} + +void +stime_file(char *fname, struct timeval *tvp) +{ + struct stat sb; + + if (stat(fname, &sb)) { + err(1, "%s", fname); + } + TIMESPEC_TO_TIMEVAL(tvp, &sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(tvp + 1, &sb.st_mtimespec); +} + +int +rw(char *fname, struct stat *sbp, int force) +{ + int fd, needed_chmod, rval; + u_char byte; + + /* Try regular files. */ + if (!S_ISREG(sbp->st_mode)) { + warnx("%s: %s", fname, strerror(EFTYPE)); + return (1); + } + + needed_chmod = rval = 0; + if ((fd = open(fname, O_RDWR, 0)) == -1) { + if (!force || chmod(fname, DEFFILEMODE)) + goto err; + if ((fd = open(fname, O_RDWR, 0)) == -1) + goto err; + needed_chmod = 1; + } + + if (sbp->st_size != 0) { + if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + goto err; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + } else { + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { +err: rval = 1; + warn("%s", fname); + } else if (ftruncate(fd, (off_t)0)) { + rval = 1; + warn("%s: file modified", fname); + } + } + + if (close(fd) && rval != 1) { + rval = 1; + warn("%s", fname); + } + if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { + rval = 1; + warn("%s: permissions modified", fname); + } + return (rval); +} + +void +usage(char *myname) +{ + fprintf(thread_stderr, "usage:\n" "%s [-A [-][[hh]mm]SS] [-acfhm] [-r file] " + "[-t [[CC]YY]MMDDhhmm[.SS]] file ...\n", myname); + exit(1); +} diff --git a/file_cmds/xcodescripts/hardlink.sh b/file_cmds/xcodescripts/hardlink.sh new file mode 100644 index 00000000..86596231 --- /dev/null +++ b/file_cmds/xcodescripts/hardlink.sh @@ -0,0 +1,16 @@ +#!/bin/sh +# Link input files to output files (in order). + +set -e + +if [ "$SCRIPT_INPUT_FILE_COUNT" -ne "$SCRIPT_OUTPUT_FILE_COUNT" ]; then + echo input and output file counts differ + exit 1 +fi + +X=0 + +while [ "$X" -lt "$SCRIPT_INPUT_FILE_COUNT" ]; do + eval ln -fhv \"\$SCRIPT_INPUT_FILE_$X\" \"\$SCRIPT_OUTPUT_FILE_$X\" + X=$((X+1)) +done