From c59d3020309de26dad218fd055f00ee3dca42cf2 Mon Sep 17 00:00:00 2001 From: Apple Date: Mon, 21 Mar 2005 21:58:58 +0000 Subject: [PATCH] file_cmds-116.9.tar.gz --- Makefile | 6 +- PB.project | 2 + chmod/Makefile | 4 +- chmod/chmod.1 | 234 +++++++++- chmod/chmod.c | 275 ++++++++++-- chmod/chmod_acl.c | 799 +++++++++++++++++++++++++++++++++ chmod/chmod_acl.h | 83 ++++ chown/chown.8 | 10 +- chown/chown.c | 14 - compress/Makefile | 8 +- compress/compress.1 | 64 +-- compress/compress.c | 275 +++++------- compress/zopen.3 | 9 +- compress/zopen.c | 71 ++- compress/zopen.h | 34 ++ cp/Makefile | 3 + cp/cp.1 | 18 +- cp/cp.c | 17 +- cp/utils.c | 14 + dd/args.c | 8 +- dd/dd.c | 14 +- dd/misc.c | 2 +- df/Makefile | 2 + df/df.1 | 18 + df/df.c | 48 +- du/du.c | 43 +- install/xinstall.c | 13 + {cp => ipcrm}/Makefile | 10 +- ipcrm/Makefile.postamble | 1 + ipcrm/Makefile.preamble | 1 + PB.project => ipcrm/PB.project | 36 +- ipcrm/ipcrm.1 | 83 ++++ ipcrm/ipcrm.c | 183 ++++++++ {cp => ipcs}/Makefile | 16 +- ipcs/Makefile.postamble | 1 + ipcs/Makefile.preamble | 1 + PB.project => ipcs/PB.project | 36 +- ipcs/ipcs.1 | 132 ++++++ ipcs/ipcs.c | 486 ++++++++++++++++++++ ls/Makefile | 2 +- ls/ls.1 | 23 +- ls/ls.c | 54 +-- ls/ls.h | 5 +- ls/print.c | 216 ++++++++- ls/util.c | 4 +- mv/mv.c | 7 + {df => pathchk}/Makefile | 8 +- pathchk/Makefile.postamble | 1 + pathchk/Makefile.preamble | 1 + pathchk/PB.project | 25 ++ pathchk/pathchk.1 | 121 +++++ pathchk/pathchk.c | 196 ++++++++ pax/ar_io.c | 15 +- pax/ar_subs.c | 134 ++++++ pax/file_subs.c | 5 +- pax/options.c | 3 + pax/pax.1 | 336 +++++++------- pax/tar.c | 22 +- rm/rm.c | 64 ++- {cp => stat}/Makefile | 10 +- stat/Makefile.postamble | 6 + stat/Makefile.preamble | 1 + PB.project => stat/PB.project | 36 +- stat/stat.1 | 522 ++++++++++++++++++++++ stat/stat.c | 990 +++++++++++++++++++++++++++++++++++++++++ 65 files changed, 5186 insertions(+), 695 deletions(-) create mode 100644 chmod/chmod_acl.c create mode 100644 chmod/chmod_acl.h create mode 100644 compress/zopen.h copy {cp => ipcrm}/Makefile (86%) create mode 100644 ipcrm/Makefile.postamble create mode 100644 ipcrm/Makefile.preamble copy PB.project => ipcrm/PB.project (50%) create mode 100644 ipcrm/ipcrm.1 create mode 100644 ipcrm/ipcrm.c copy {cp => ipcs}/Makefile (82%) create mode 100644 ipcs/Makefile.postamble create mode 100644 ipcs/Makefile.preamble copy PB.project => ipcs/PB.project (50%) create mode 100644 ipcs/ipcs.1 create mode 100644 ipcs/ipcs.c copy {df => pathchk}/Makefile (87%) create mode 100644 pathchk/Makefile.postamble create mode 100644 pathchk/Makefile.preamble create mode 100644 pathchk/PB.project create mode 100644 pathchk/pathchk.1 create mode 100644 pathchk/pathchk.c copy {cp => stat}/Makefile (87%) create mode 100644 stat/Makefile.postamble create mode 100644 stat/Makefile.preamble copy PB.project => stat/PB.project (50%) create mode 100644 stat/stat.1 create mode 100644 stat/stat.c diff --git a/Makefile b/Makefile index cd9ede3..20e69e6 100644 --- a/Makefile +++ b/Makefile @@ -12,8 +12,8 @@ NAME = file_cmds PROJECTVERSION = 2.8 PROJECT_TYPE = Aggregate -TOOLS = chflags chmod chown compress cp dd df du install ln ls\ - mkdir mkfifo mknod mtree mv pax rm rmdir rmt shar tcopy\ +TOOLS = chflags chmod chown compress cp dd df du install ipcrm ipcs ln ls\ + mkdir mkfifo mknod mtree mv pathchk pax rm rmdir rmt shar stat tcopy\ touch OTHERSRCS = PROJECT Makefile.preamble Makefile Makefile.postamble @@ -26,7 +26,7 @@ DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) -NEXTSTEP_PB_CFLAGS = -no-cpp-precomp +NEXTSTEP_PB_CFLAGS = -mdynamic-no-pic -no-cpp-precomp -I/System/Library/Frameworks/System.framework/PrivateHeaders NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build diff --git a/PB.project b/PB.project index 09da8c8..5ab9509 100644 --- a/PB.project +++ b/PB.project @@ -12,6 +12,8 @@ df, du, install, + ipcrm, + ipcs, ln, ls, mkdir, diff --git a/chmod/Makefile b/chmod/Makefile index 34a6a63..e46896c 100644 --- a/chmod/Makefile +++ b/chmod/Makefile @@ -12,7 +12,9 @@ NAME = chmod PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -CFILES = chmod.c +HFILES = chmod_acl.h + +CFILES = chmod.c chmod_acl.c OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chmod.1 diff --git a/chmod/chmod.1 b/chmod/chmod.1 index d0b2917..90c7734 100644 --- a/chmod/chmod.1 +++ b/chmod/chmod.1 @@ -35,27 +35,44 @@ .\" @(#)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 March 31, 1994 +.Dd July 08, 2004 .Dt CHMOD 1 .Os .Sh NAME .Nm chmod -.Nd change file modes +.Nd change file modes or Access Control Lists .Sh SYNOPSIS .Nm .Op Fl fv .Op Fl R Op Fl H | L | P .Ar mode .Ar +.Nm +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Op -a | +a | =a +.Ar ACE +.Ar +.Nm +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Op Fl E +.Ar +.Nm +.Op Fl fv +.Op Fl R Op Fl H | L | P +.Op Fl C +.Ar .Sh DESCRIPTION The .Nm utility modifies the file mode bits of the listed files as specified by the .Ar mode -operand. +operand. It may also be used to modify the Access Control +Lists (ACLs) associated with the listed files. .Pp -The options are as follows: +The generic options are as follows: .Bl -tag -width Ds .It Fl H If the @@ -274,7 +291,7 @@ 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 +.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. @@ -301,10 +318,209 @@ clear all mode bits for group and others. .It Li g=u-w set the group bits equal to the user bits, but clear the group write bit. .El -.Sh BUGS -There's no -.Ar perm -option for the naughty bits. +.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. +.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 +.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 write" 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. +.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). +.El .Sh COMPATIBILITY The .Fl v diff --git a/chmod/chmod.c b/chmod/chmod.c index c01c5dd..8651176 100644 --- a/chmod/chmod.c +++ b/chmod/chmod.c @@ -57,21 +57,34 @@ __RCSID("$FreeBSD: src/bin/chmod/chmod.c,v 1.27 2002/08/04 05:29:13 obrien Exp $ #include #include +#ifdef __APPLE__ +#include "chmod_acl.h" + +#endif /*__APPLE__*/ + +int fflag = 0; + int main(int, char *[]); void usage(void); int main(int argc, char *argv[]) { - FTS *ftsp; - FTSENT *p; - mode_t *set; - long val; - int oct; - int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval; + FTS *ftsp = NULL; + FTSENT *p = NULL; + mode_t *set = NULL; + long val = 0; + int oct = 0; + int Hflag, Lflag, Rflag, ch, fts_options, hflag, rval; int vflag; char *ep, *mode; mode_t newmode, omode; +#ifdef __APPLE__ + unsigned int acloptflags = 0; + int aclpos = -1, inheritance_level = 0; + int index = 0, acloptlen = 0, ace_arg_not_required = 0; + acl_t acl_input = NULL; +#endif /* __APPLE__*/ int (*change_mode)(const char *, mode_t); set = NULL; @@ -80,7 +93,7 @@ main(int argc, char *argv[]) #ifndef __APPLE__ while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) #else - while ((ch = getopt(argc, argv, "HLPRXfgorstuvwx")) != -1) + while ((ch = getopt(argc, argv, "ACEHILPRVXafginorstuvwx")) != -1) #endif switch (ch) { case 'H': @@ -112,6 +125,34 @@ main(int argc, char *argv[]) */ hflag = 1; break; +#else + case 'a': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch) + --optind; + 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 'V': + acloptflags |= ACL_FLAG | ACL_INVOKE_EDITOR; + ace_arg_not_required = 1; + goto done; #endif /* __APPLE__ */ /* * XXX @@ -136,8 +177,82 @@ main(int argc, char *argv[]) done: argv += optind; argc -= optind; +#ifdef __APPLE__ + if (argc < ((acloptflags & ACL_FLAG) ? 1 : 2)) + usage(); +#else if (argc < 2) usage(); +#endif + +#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) + 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)) + usage(); + argv++; + argc--; + errno = 0; + aclpos = strtol(argv[0], &ep, 0); + + if (aclpos > ACL_MAX_ENTRIES + || aclpos < 0) + errno = ERANGE; + if (errno || *ep) + err(1, "Invalid ACL entry number: %s", 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) + warn("Inheritance across more than one generation is not currently supported"); + if (inheritance_level >= MAX_INHERITANCE_LEVEL) + goto apdone; + break; + default: + errno = EINVAL; + usage(); + } + } + } +apdone: + argv++; + argc--; + } +apnoacl: +#endif /*__APPLE__*/ if (Rflag) { fts_options = FTS_PHYSICAL; @@ -161,25 +276,72 @@ done: argv += optind; #else change_mode = chmod; #endif /* __APPLE__ */ - - mode = *argv; - 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__ + if (acloptflags & ACL_FROM_STDIN) { + int readval = 0, 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(STDIN_FILENO, mode + readtotal, + MAX_ACL_TEXT_SIZE); + } while ((readval > 0) && (readtotal <= MAX_ACL_TEXT_SIZE)); + + if (0 == readtotal) + err(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) { + errno = EINVAL; + err(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;) { @@ -211,36 +373,55 @@ done: argv += optind; default: break; } - 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) && !fflag) { - warn("%s", p->fts_path); - rval = 1; - } else { - if (vflag) { - (void)printf("%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); +#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, aclpos, inheritance_level)) + 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) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } else { + if (vflag) { + (void)printf("%s", p->fts_accpath); - (void)printf(": 0%o [%s] -> 0%o [%s]", - p->fts_statp->st_mode, m1, + 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)printf(": 0%o [%s] -> 0%o [%s]", + p->fts_statp->st_mode, m1, (p->fts_statp->st_mode & S_IFMT) | - newmode, m2); + newmode, m2); + } + (void)printf("\n"); } - (void)printf("\n"); + } - +#ifdef __APPLE__ } +#endif /* __APPLE__*/ } if (errno) err(1, "fts_read"); - free(set); +#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); } @@ -249,7 +430,7 @@ usage(void) { #ifdef __APPLE__ (void)fprintf(stderr, - "usage: chmod [-fv] [-R [-H | -L | -P]] mode file ...\n"); + "usage: chmod [-fv] [-R [-H | -L | -P]] [-a | +a | =a [i][# [ n]]] mode|entry file ...\n"); #else (void)fprintf(stderr, "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); diff --git a/chmod/chmod_acl.c b/chmod/chmod_acl.c new file mode 100644 index 0000000..eacc60d --- /dev/null +++ b/chmod/chmod_acl.c @@ -0,0 +1,799 @@ +/* + * 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 "chmod_acl.h" + +extern void 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. + */ + +/* Perform a name to uuid mapping - calls through to memberd */ + +uuid_t * +name_to_uuid(char *tok) { + struct passwd *tpass = NULL; + struct group *tgrp = NULL; + uuid_t *entryg = NULL; + char is_uid = 1; + + if ((entryg = (uuid_t *) calloc(1,sizeof(uuid_t))) == NULL) + err(1, "Unable to allocate a uuid"); + + tpass = getpwnam(tok); + + if (tpass == NULL) { + tgrp = getgrnam(tok); + if (tgrp == NULL) { + errno = EINVAL; + err(1, "Unable to translate %s to a UID/GID", tok); + } + is_uid = 0; + } + + if (tpass) { + if (0 != mbr_uid_to_uuid(tpass->pw_uid, entryg)) { + errno = EINVAL; + err(1, "mbr_uid_to_uuid(): Unable to translate"); + } + } + else { + if (0 != mbr_gid_to_uuid(tgrp->gr_gid, entryg)) { + errno = EINVAL; + err(1, "mbr_gid_to_uuid(): Unable to translate"); + } + } + return entryg; +} + +/* 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; + + acl_get_permset(newent, &perms); + acl_get_flagset_np(newent, &flags); + + pebuf = entrybuf; + + tok = strsep(&pebuf, " "); + + if ((tok == NULL) || *tok == '\0') { + errno = EINVAL; + err(1, "Invalid entry format"); + } + + /* parse the name into a qualifier */ + entryg = name_to_uuid(tok); + + tok = strsep(&pebuf, " "); + if ((tok == NULL) || *tok == '\0') { + errno = EINVAL; + err(1, "Invalid entry format"); + } + + /* is the verb 'allow' or 'deny'? */ + if (!strcmp(tok, "allow")) { + tag = ACL_EXTENDED_ALLOW; + } else if (!strcmp(tok, "deny")) { + tag = ACL_EXTENDED_DENY; + } else { + errno = EINVAL; + err(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; + } + } + errno = EINVAL; + err(1,"Invalid permission type %s", tok); + found: + continue; + } + } + if (0 == permcount) { + errno = EINVAL; + err(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); + + 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)) { + errno = EINVAL; + err(1, "Failed parsing entry %s", *bufp); + } + if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) { + errno = ERANGE; + err(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 permset present"); + + switch(tag) { + case ACL_EXTENDED_ALLOW: + break; + case ACL_EXTENDED_DENY: + score++; + break; + default: + errno = EINVAL; + err(1, "Unknown tag type present in ACL entry"); + /* 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; +} + +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; + + if (0 != compare_acl_qualifiers(acl_get_qualifier(a), + acl_get_qualifier(b))) + 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 (acl_get_flag_np(mflags, ACL_ENTRY_INHERITED) == + acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) { + *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) +{ + 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_delete_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_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 */ +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) { + + 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) { + errno = EINVAL; + err(1, "No ACL present"); + } + if (position != -1 ) { + if (0 != acl_get_entry(oacl, position, &rentry)) + err(1, "Invalid entry number"); + 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 +/* In the event of a partial match, remove the specified perms from the + * entry */ + subtract_from_entry(rentry, modifier); + } + } + if (0 == match_found) { + errno = EINVAL; + warn("Entry not found when attempting delete"); + retval = 1; + } + } + } + else + if (optflags & ACL_REWRITE_FLAG) { + acl_entry_t rentry; + + if (-1 == position) { + usage(); + } + if (0 == flag_new_acl) { + if (0 != acl_get_entry(oacl, position, + &rentry)) + err(1, "Invalid entry number"); + + if (0 != acl_delete_entry(oacl, rentry)) + err(1, "Unable to delete entry"); + } + 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) { + + 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 int 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) + usage(); + + if (optflags & ACL_FROM_STDIN) + oacl = acl_dup(modifier); + else { + oacl = acl_get_file(path, ACL_TYPE_EXTENDED); + + 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_CHECK_CANONICITY) { + if (flag_new_acl) { + errno = EINVAL; + warn("No ACL currently associated with file %s", path); + } + return(is_canonical(oacl)); + } + else + if ((optflags & ACL_SET_FLAG) && (position == -1) && + (!is_canonical(oacl))) { + errno = EINVAL; + warn("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); + } + else + 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); + } + } + +/* 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_CHECK_CANONICITY) && + (0 != acl_set_file(path, ACL_TYPE_EXTENDED, oacl))){ + if (!fflag) + warn("Failed to set ACL on file %s", path); + retval = 1; + } + + if (oacl) + acl_free(oacl); + + return retval; +} + +#endif /*__APPLE__*/ diff --git a/chmod/chmod_acl.h b/chmod/chmod_acl.h new file mode 100644 index 0000000..1bab724 --- /dev/null +++ b/chmod/chmod_acl.h @@ -0,0 +1,83 @@ +/* + * 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 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); +extern int modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags, int position, int inheritance_level, unsigned flag_new_acl); +extern int modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level); +extern uuid_t *name_to_uuid(char *tok); +#endif /* __APPLE__*/ diff --git a/chown/chown.8 b/chown/chown.8 index 9f141bf..18d467d 100644 --- a/chown/chown.8 +++ b/chown/chown.8 @@ -40,7 +40,7 @@ .Nd change file owner and group .Sh SYNOPSIS .Nm -.Op Fl fv +.Op Fl fhv .Oo .Fl R .Op Fl H | Fl L | Fl P @@ -48,7 +48,7 @@ .Ar owner Ns Op : Ns Ar group .Ar .Nm -.Op Fl fv +.Op Fl fhv .Oo .Fl R .Op Fl H | Fl L | Fl P @@ -59,6 +59,9 @@ The .Nm 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 @@ -82,6 +85,9 @@ in the files instead of just the files themselves. .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 file is a symbolic link, change the user ID and/or the +group ID of the link itself. .It Fl v Cause .Nm diff --git a/chown/chown.c b/chown/chown.c index 03305f5..44a0f43 100644 --- a/chown/chown.c +++ b/chown/chown.c @@ -84,11 +84,7 @@ main(int argc, char **argv) ischown = (strcmp(cp, "chown") == 0); Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; -#ifndef __APPLE__ while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) -#else - while ((ch = getopt(argc, argv, "HLPRfv")) != -1) -#endif switch (ch) { case 'H': Hflag = 1; @@ -107,11 +103,9 @@ main(int argc, char **argv) case 'f': fflag = 1; break; -#ifndef __APPLE__ case 'h': hflag = 1; break; -#endif case 'v': vflag = 1; break; @@ -192,11 +186,7 @@ main(int argc, char **argv) if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) continue; -#ifndef __APPLE__ if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { -#else - if (chown(p->fts_accpath, uid, gid) == -1) { -#endif /* __APPLE__ */ if (!fflag) { chownerr(p->fts_path); rval = 1; @@ -289,10 +279,6 @@ usage(void) " chown [-fhv] [-R [-H | -L | -P]] :group file ..."); else (void)fprintf(stderr, "%s\n", -#ifndef __APPLE__ "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ..."); -#else - "usage: chgrp [-fv] [-R [-H | -L | -P]] group file ..."); -#endif exit(1); } diff --git a/compress/Makefile b/compress/Makefile index 0c13072..9478ad1 100644 --- a/compress/Makefile +++ b/compress/Makefile @@ -15,8 +15,9 @@ PROJECT_TYPE = Tool CFILES = compress.c zopen.c OTHERSRCS = Makefile Makefile.preamble Makefile.postamble compress.1\ - zopen.3 uncompress.1 + zopen.3 uncompress.1 zopen.h +NEXTSTEP_PB_CFLAGS += -D__FBSDID=__RCSID -D"__printflike(a,b)=" MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles CODE_GEN_STYLE = DYNAMIC @@ -26,10 +27,7 @@ LIBS = DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) - - - -NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/file_cmds/Build NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc diff --git a/compress/compress.1 b/compress/compress.1 index 2534855..b616c13 100644 --- a/compress/compress.1 +++ b/compress/compress.1 @@ -1,5 +1,3 @@ -.\" $NetBSD: compress.1,v 1.6 1997/09/15 10:58:37 lukem Exp $ -.\" .\" Copyright (c) 1986, 1990, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -36,15 +34,14 @@ .\" SUCH DAMAGE. .\" .\" @(#)compress.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/compress/compress.1,v 1.19 2004/07/05 17:12:53 ru Exp $ .\" -.Dd April 18, 1994 +.Dd May 17, 2002 .Dt COMPRESS 1 -.Os BSD 4.3 +.Os .Sh NAME .Nm compress , -.\".Nm uncompress , .Nm uncompress -.\".Nm zcat .Nd compress and expand data .Sh SYNOPSIS .Nm @@ -53,12 +50,11 @@ .Op Ar .Nm uncompress .Op Fl cfv -.Op Ar -.\".Nm zcat -.\".Op Ar +.Op Ar .Sh DESCRIPTION +The .Nm -reduces the size of the named files using adaptive Lempel-Ziv coding. +utility reduces the size of the named files using adaptive Lempel-Ziv coding. Each .Ar file is renamed to the same name plus the extension @@ -70,15 +66,12 @@ If compression would not reduce the size of a .Ar file , the file is ignored. .Pp +The .Nm uncompress -restores the compressed files to their original form, renaming the +utility restores the compressed files to their original form, renaming the files by deleting the .Dq .Z extension. -.\".Pp -.\".Nm Zcat -.\"is an alias for -.\".Dq "uncompress -c" . .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 @@ -86,14 +79,17 @@ output) for confirmation. If prompting is not possible or confirmation is not received, the files are not overwritten. .Pp -If no files are specified, the standard input is compressed or uncompressed -to the standard output. +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. .Pp The options are as follows: -.Bl -tag -width Ds +.Bl -tag -width indent .It Fl b Specify the .Ar bits @@ -110,8 +106,9 @@ Additionally, files are overwritten without prompting for confirmation. Print the percentage reduction of each file. .El .Pp +The .Nm -uses a modified Lempel-Ziv algorithm. +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 @@ -131,13 +128,14 @@ If it is increasing, 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 +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 flag is omitted for -.Ar uncompress +.Nm uncompress since the .Ar bits parameter specified during compression @@ -145,7 +143,6 @@ 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 -.ne 8 The amount of compression obtained depends on the size of the input, the number of .Ar bits @@ -155,12 +152,22 @@ 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 DIAGNOSTICS +.Ex -std compress uncompress .Pp The -.Nm -utility exits 0 on success, and >0 if an error occurs. +.Nm compress +utility exits 2 if attempting to compress the file would not reduce its size +and the +.Fl f +option was not specified. .Sh SEE ALSO -.Xr zcat 1 +.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 @@ -169,6 +176,13 @@ utility exits 0 on success, and >0 if an error occurs. .%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 diff --git a/compress/compress.c b/compress/compress.c index 2aa84ee..14b509f 100644 --- a/compress/compress.c +++ b/compress/compress.c @@ -1,5 +1,3 @@ -/* $NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $ */ - /*- * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -33,77 +31,69 @@ * SUCH DAMAGE. */ -#include #ifndef lint -__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ - The Regents of the University of California. All rights reserved.\n"); -#endif /* not lint */ +static const char copyright[] = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif -#ifndef lint #if 0 +#ifndef lint static char sccsid[] = "@(#)compress.c 8.2 (Berkeley) 1/7/94"; -#else -__RCSID("$NetBSD: compress.c,v 1.16 1998/03/10 12:45:44 kleink Exp $"); #endif -#endif /* not lint */ +#endif + +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/compress.c,v 1.21 2003/06/14 13:41:31 trhodes Exp $"); #include -#include #include +#include #include #include +#include #include #include #include #include -#ifdef __STDC__ -#include -#else -#include -#endif - -void compress __P((char *, char *, int)); -void cwarn __P((const char *, ...)); -void cwarnx __P((const char *, ...)); -void decompress __P((char *, char *, int)); -int permission __P((char *)); -void setfile __P((char *, struct stat *)); -void usage __P((int)); +#include "zopen.h" -int main __P((int, char *[])); -extern FILE *zopen __P((const char *fname, const char *mode, int bits)); +void compress(const char *, const char *, int); +void cwarn(const char *, ...) __printflike(1, 2); +void cwarnx(const char *, ...) __printflike(1, 2); +void decompress(const char *, const char *, int); +int permission(const char *); +void setfile(const char *, struct stat *); +void usage(int); -int eval, force, verbose; -int isstdout, isstdin; +int eval, force, verbose, cat; int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char *argv[]) { - enum {COMPRESS, DECOMPRESS} style = COMPRESS; + enum {COMPRESS, DECOMPRESS} style; size_t len; - int bits, cat, ch; + int bits, ch; char *p, newname[MAXPATHLEN]; - if ((p = strrchr(argv[0], '/')) == NULL) + cat = 0; + 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")) { - style = DECOMPRESS; - cat = 1; - } - else + else if (!strcmp(p, "compress")) + style = COMPRESS; + else if (!strcmp(p, "zcat")) { + cat = 1; + style = DECOMPRESS; + } else errx(1, "unknown program name"); - bits = cat = 0; + bits = 0; while ((ch = getopt(argc, argv, "b:cdfv")) != -1) switch(ch) { case 'b': @@ -131,15 +121,12 @@ main(argc, argv) argv += optind; if (argc == 0) { + cat = 1; switch(style) { case COMPRESS: - isstdout = 1; - isstdin = 1; (void)compress("/dev/stdin", "/dev/stdout", bits); break; case DECOMPRESS: - isstdout = 1; - isstdin = 1; (void)decompress("/dev/stdin", "/dev/stdout", bits); break; } @@ -149,16 +136,18 @@ main(argc, argv) if (cat == 1 && argc > 1) errx(1, "the -c option permits only a single file argument"); - for (; *argv; ++argv) { - isstdout = 0; + for (; *argv; ++argv) switch(style) { case COMPRESS: - if (cat) { - isstdout = 1; + if (strcmp(*argv, "-") == 0) { + cat = 1; + compress("/dev/stdin", "/dev/stdout", bits); + break; + } else if (cat) { compress(*argv, "/dev/stdout", bits); break; } - if ((p = strrchr(*argv, '.')) != NULL && + if ((p = rindex(*argv, '.')) != NULL && !strcmp(p, ".Z")) { cwarnx("%s: name already has trailing .Z", *argv); @@ -176,8 +165,13 @@ main(argc, argv) 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 = strrchr(*argv, '.')) == NULL || + if ((p = rindex(*argv, '.')) == NULL || strcmp(p, ".Z")) { if (len > sizeof(newname) - 3) { cwarnx("%s: name too long", *argv); @@ -189,8 +183,6 @@ main(argc, argv) newname[len + 2] = '\0'; decompress(newname, cat ? "/dev/stdout" : *argv, bits); - if (cat) - isstdout = 1; } else { if (len - 2 > sizeof(newname) - 1) { cwarnx("%s: name too long", *argv); @@ -200,50 +192,38 @@ main(argc, argv) newname[len - 2] = '\0'; decompress(*argv, cat ? "/dev/stdout" : newname, bits); - if (cat) - isstdout = 1; } break; } - } exit (eval); } void -compress(in, out, bits) - char *in, *out; - int bits; +compress(const char *in, const char *out, int bits) { - int nr; + size_t nr; struct stat isb, sb; FILE *ifp, *ofp; int exists, isreg, oreg; - u_char buf[BUFSIZ]; + u_char buf[1024]; - if (!isstdout) { - exists = !stat(out, &sb); - if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) - return; - oreg = !exists || S_ISREG(sb.st_mode); - } else - oreg = 0; + 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); ifp = ofp = NULL; if ((ifp = fopen(in, "r")) == NULL) { cwarn("%s", in); return; } - - if (!isstdin) { - if (stat(in, &isb)) { /* DON'T FSTAT! */ - cwarn("%s", in); - goto err; - } - if (!S_ISREG(isb.st_mode)) - isreg = 0; - else - isreg = 1; - } else + 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) { @@ -268,7 +248,7 @@ compress(in, out, bits) } ofp = NULL; - if (isreg && oreg) { + if (!cat && isreg) { if (stat(out, &sb)) { cwarn("%s", out); goto err; @@ -276,7 +256,9 @@ compress(in, out, bits) if (!force && sb.st_size >= isb.st_size) { if (verbose) - (void)printf("%s: file would grow; left unmodified\n", in); + (void)fprintf(stderr, "%s: file would grow; left unmodified\n", + in); + eval = 2; if (unlink(out)) cwarn("%s", out); goto err; @@ -288,19 +270,19 @@ compress(in, out, bits) cwarn("%s", in); if (verbose) { - (void)printf("%s: ", out); + (void)fprintf(stderr, "%s: ", out); if (isb.st_size > sb.st_size) - (void)printf("%.0f%% compression\n", - ((double)sb.st_size / isb.st_size) * 100.0); + (void)fprintf(stderr, "%.0f%% compression\n", + ((float)sb.st_size / isb.st_size) * 100.0); else - (void)printf("%.0f%% expansion\n", - ((double)isb.st_size / sb.st_size) * 100.0); + (void)fprintf(stderr, "%.0f%% expansion\n", + ((float)isb.st_size / sb.st_size) * 100.0); } } return; err: if (ofp) { - if (oreg) + if (!cat && oreg) (void)unlink(out); (void)fclose(ofp); } @@ -309,46 +291,49 @@ err: if (ofp) { } void -decompress(in, out, bits) - char *in, *out; - int bits; +decompress(const char *in, const char *out, int bits) { - int nr; + size_t nr; struct stat sb; FILE *ifp, *ofp; int exists, isreg, oreg; - u_char buf[BUFSIZ]; - - if (!isstdout) { - exists = !stat(out, &sb); - if (!force && exists && S_ISREG(sb.st_mode) && !permission(out)) - return; - oreg = !exists || S_ISREG(sb.st_mode); - } else - oreg = 0; + u_char buf[1024]; - ifp = ofp = NULL; - if ((ofp = fopen(out, "w")) == NULL) { - cwarn("%s", out); + 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); + ifp = ofp = NULL; if ((ifp = zopen(in, "r", bits)) == NULL) { cwarn("%s", in); + return; + } + if (stat(in, &sb)) { + cwarn("%s", in); goto err; } - if (!isstdin) { - if (stat(in, &sb)) { - cwarn("%s", in); - goto err; - } - if (!S_ISREG(sb.st_mode)) - isreg = 0; - else - isreg = 1; - } else + 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); @@ -366,16 +351,27 @@ decompress(in, out, bits) goto err; } - if (isreg && oreg) { + 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 (oreg) + if (!cat && oreg) (void)unlink(out); (void)fclose(ofp); } @@ -384,9 +380,7 @@ err: if (ofp) { } void -setfile(name, fs) - char *name; - struct stat *fs; +setfile(const char *name, struct stat *fs) { static struct timeval tv[2]; @@ -408,21 +402,15 @@ setfile(name, fs) cwarn("chown: %s", name); fs->st_mode &= ~(S_ISUID|S_ISGID); } - if (chmod(name, fs->st_mode)) - cwarn("chown: %s", name); + if (chmod(name, fs->st_mode) && errno != EOPNOTSUPP) + cwarn("chmod: %s", name); - /* - * Restore the file's flags. However, do this only if the original - * file had any flags set; this avoids a warning on file-systems that - * do not support flags. - */ - if (fs->st_flags != 0 && chflags(name, fs->st_flags)) + if (chflags(name, fs->st_flags) && errno != EOPNOTSUPP) cwarn("chflags: %s", name); } int -permission(fname) - char *fname; +permission(const char *fname) { int ch, first; @@ -436,55 +424,34 @@ permission(fname) } void -usage(iscompress) - int iscompress; +usage(int iscompress) { if (iscompress) (void)fprintf(stderr, "usage: compress [-cfv] [-b bits] [file ...]\n"); else (void)fprintf(stderr, - "usage: uncompress [-c] [-b bits] [file ...]\n"); + "usage: uncompress [-cfv] [-b bits] [file ...]\n"); exit(1); } void -#if __STDC__ cwarnx(const char *fmt, ...) -#else -cwarnx(fmt, va_alist) - int eval; - const char *fmt; - va_dcl -#endif { va_list ap; -#if __STDC__ + va_start(ap, fmt); -#else - va_start(ap); -#endif vwarnx(fmt, ap); va_end(ap); eval = 1; } void -#if __STDC__ cwarn(const char *fmt, ...) -#else -cwarn(fmt, va_alist) - int eval; - const char *fmt; - va_dcl -#endif { va_list ap; -#if __STDC__ + va_start(ap, fmt); -#else - va_start(ap); -#endif vwarn(fmt, ap); va_end(ap); eval = 1; diff --git a/compress/zopen.3 b/compress/zopen.3 index 1e75687..730a120 100644 --- a/compress/zopen.3 +++ b/compress/zopen.3 @@ -1,5 +1,3 @@ -.\" $NetBSD: zopen.3,v 1.4 1998/02/06 06:17:43 perry Exp $ -.\" .\" Copyright (c) 1992, 1993 .\" The Regents of the University of California. All rights reserved. .\" @@ -32,6 +30,7 @@ .\" SUCH DAMAGE. .\" .\" @(#)zopen.3 8.1 (Berkeley) 6/9/93 +.\" $FreeBSD: src/usr.bin/compress/zopen.3,v 1.8 2001/10/01 15:01:57 ru Exp $ .\" .Dd June 9, 1993 .Dt ZOPEN 3 @@ -40,7 +39,7 @@ .Nm zopen .Nd compressed stream open function .Sh SYNOPSIS -.Fd #include +.Fd #include \&"zopen.h\&" .Ft FILE * .Fn zopen "const char *path" "const char *mode" "int bits" .Sh DESCRIPTION @@ -88,7 +87,7 @@ The argument, if non-zero, is set to the bits code limit. If zero, the default is 16. See -.Fn compress 1 +.Xr compress 1 for more information. .Sh RETURN VALUES Upon successful completion @@ -130,7 +129,7 @@ or .Xr funopen 3 .Sh HISTORY The -.Nm zopen +.Nm function first appeared in .Bx 4.4 . diff --git a/compress/zopen.c b/compress/zopen.c index c165117..3ec7163 100644 --- a/compress/zopen.c +++ b/compress/zopen.c @@ -1,5 +1,3 @@ -/* $NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $ */ - /*- * Copyright (c) 1985, 1986, 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -38,13 +36,12 @@ */ #if defined(LIBC_SCCS) && !defined(lint) -#if 0 static char sccsid[] = "@(#)zopen.c 8.1 (Berkeley) 6/27/93"; -#else -static char rcsid[] = "$NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $"; -#endif #endif /* LIBC_SCCS and not lint */ +#include +__FBSDID("$FreeBSD: src/usr.bin/compress/zopen.c,v 1.10 2002/07/28 15:32:17 dwmalone Exp $"); + /*- * fcompress.c - File compression ala IEEE Computer, June 1984. * @@ -77,6 +74,7 @@ static char rcsid[] = "$NetBSD: zopen.c,v 1.6 1997/09/15 10:58:39 lukem Exp $"; #include #include #include +#include "zopen.h" #define BITS 16 /* Default bits. */ #define HSIZE 69001 /* 95% occupancy */ @@ -106,8 +104,8 @@ struct s_zstate { 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. */ + 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]; @@ -122,7 +120,7 @@ struct s_zstate { int zs_clear_flg; long zs_ratio; count_int zs_checkpoint; - int zs_offset; + 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). */ @@ -203,14 +201,13 @@ struct s_zstate { #define FIRST 257 /* First free entry. */ #define CLEAR 256 /* Table clear output code. */ -static int cl_block __P((struct s_zstate *)); -static void cl_hash __P((struct s_zstate *, count_int)); -static code_int getcode __P((struct s_zstate *)); -static int output __P((struct s_zstate *, code_int)); -static int zclose __P((void *)); -FILE *zopen __P((const char *, const char *, int)); -static int zread __P((void *, char *, int)); -static int zwrite __P((void *, const char *, int)); +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", @@ -239,10 +236,7 @@ static int zwrite __P((void *, const char *, int)); * questions about this implementation to ames!jaw. */ static int -zwrite(cookie, wbp, num) - void *cookie; - const char *wbp; - int num; +zwrite(void *cookie, const char *wbp, int num) { code_int i; int c, disp; @@ -256,7 +250,7 @@ zwrite(cookie, wbp, num) zs = cookie; count = num; - bp = (u_char *)wbp; + bp = wbp; if (state == S_MIDDLE) goto middle; state = S_MIDDLE; @@ -265,7 +259,7 @@ zwrite(cookie, wbp, num) if (fwrite(magic_header, sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) return (-1); - tmp = (u_char)(maxbits | block_compress); + tmp = (u_char)((maxbits) | block_compress); if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) return (-1); @@ -330,8 +324,7 @@ nomatch: if (output(zs, (code_int) ent) == -1) } static int -zclose(cookie) - void *cookie; +zclose(void *cookie) { struct s_zstate *zs; int rval; @@ -376,11 +369,10 @@ static char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; static int -output(zs, ocode) - struct s_zstate *zs; - code_int ocode; +output(struct s_zstate *zs, code_int ocode) { - int bits, r_off; + int r_off; + u_int bits; char_type *bp; r_off = offset; @@ -465,10 +457,7 @@ output(zs, ocode) * compress() routine. See the definitions above. */ static int -zread(cookie, rbp, num) - void *cookie; - char *rbp; - int num; +zread(void *cookie, char *rbp, int num) { u_int count; struct s_zstate *zs; @@ -576,8 +565,7 @@ eof: return (num - count); * code or -1 is returned. */ static code_int -getcode(zs) - struct s_zstate *zs; +getcode(struct s_zstate *zs) { code_int gcode; int r_off, bits; @@ -635,8 +623,7 @@ getcode(zs) } static int -cl_block(zs) /* Table clear for block compress. */ - struct s_zstate *zs; +cl_block(struct s_zstate *zs) /* Table clear for block compress. */ { long rat; @@ -664,9 +651,7 @@ cl_block(zs) /* Table clear for block compress. */ } static void -cl_hash(zs, cl_hsize) /* Reset code table. */ - struct s_zstate *zs; - count_int cl_hsize; +cl_hash(struct s_zstate *zs, count_int cl_hsize) /* Reset code table. */ { count_int *htab_p; long i, m1; @@ -698,9 +683,7 @@ cl_hash(zs, cl_hsize) /* Reset code table. */ } FILE * -zopen(fname, mode, bits) - const char *fname, *mode; - int bits; +zopen(const char *fname, const char *mode, int bits) { struct s_zstate *zs; @@ -714,7 +697,7 @@ zopen(fname, mode, bits) return (NULL); maxbits = bits ? bits : BITS; /* User settable max # bits/code. */ - maxmaxcode = 1 << maxbits; /* Should NEVER generate this 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; diff --git a/compress/zopen.h b/compress/zopen.h new file mode 100644 index 0000000..8ad5691 --- /dev/null +++ b/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/cp/Makefile b/cp/Makefile index ce6bfd7..e128766 100644 --- a/cp/Makefile +++ b/cp/Makefile @@ -49,3 +49,6 @@ include $(MAKEFILEDIR)/$(MAKEFILE) -include Makefile.postamble -include Makefile.dependencies + +ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders + diff --git a/cp/cp.1 b/cp/cp.1 index 26e247f..81825dd 100644 --- a/cp/cp.1 +++ b/cp/cp.1 @@ -120,10 +120,10 @@ regardless of its permissions. (The .Fl f option overrides any previous -.Fl i -or .Fl n -options.) +option, but not +.Fl i +option. See also the legacy section.) .It Fl i Cause .Nm @@ -242,18 +242,28 @@ The and .Fl n options are non-standard and their use in scripts is not recommended. +.Sh LEGACY DESCRIPTION +When invoked in legacy mode, both +.Fl n , +.Fl i +options are overridden by the +.Fl f +option. .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 command is expected to be .St -p1003.2 -compatible. +compatible. It is also +.St -susv3 +conformant. .Sh HISTORY A .Nm diff --git a/cp/cp.c b/cp/cp.c index 43e094b..7e9dc8f 100644 --- a/cp/cp.c +++ b/cp/cp.c @@ -75,6 +75,11 @@ __RCSID("$FreeBSD: src/bin/cp/cp.c,v 1.42 2002/09/22 11:15:56 mckay Exp $"); #include #include +#ifdef __APPLE__ +#include +#endif + +#include "get_compat.h" #include "extern.h" #define STRIP_TRAILING_SLASH(p) { \ @@ -122,7 +127,11 @@ main(int argc, char *argv[]) break; case 'f': fflag = 1; - iflag = nflag = 0; + /* Determine if the STD is SUSv3 or Legacy */ + if (compat_mode("bin/cp", "unix2003")) + nflag = 0; /* reset nflag, but not iflag */ + else + iflag = nflag = 0; /* reset both */ break; case 'i': iflag = 1; @@ -362,6 +371,9 @@ copy(char *argv[], enum op type, int fts_options) * normally want to preserve them on directories. */ if (pflag) { +#ifdef __APPLE__ + copyfile(curr->fts_path, to.p_path, 0, COPYFILE_ACL); +#endif if (setfile(curr->fts_statp, 0)) rval = 1; } else { @@ -443,6 +455,9 @@ copy(char *argv[], enum op type, int fts_options) * directory, or if the -p flag is in effect. */ curr->fts_number = pflag || dne; +#ifdef __APPLE__ + copyfile(curr->fts_path, to.p_path, 0, COPYFILE_XATTR); +#endif break; case S_IFBLK: case S_IFCHR: diff --git a/cp/utils.c b/cp/utils.c index 56256f9..707266e 100644 --- a/cp/utils.c +++ b/cp/utils.c @@ -40,6 +40,7 @@ static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; __RCSID("$FreeBSD: src/bin/cp/utils.c,v 1.38 2002/07/31 16:52:16 markm Exp $"); #include +#include #include #ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED #include @@ -55,6 +56,10 @@ __RCSID("$FreeBSD: src/bin/cp/utils.c,v 1.38 2002/07/31 16:52:16 markm Exp $"); #include #include +#ifdef __APPLE__ +#include +#endif + #include "extern.h" int @@ -176,6 +181,12 @@ copy_file(FTSENT *entp, int dne) } } +#ifdef __APPLE__ + if (pflag) + copyfile(entp->fts_path, to.p_path, 0, COPYFILE_XATTR | COPYFILE_ACL); + else + copyfile(entp->fts_path, to.p_path, 0, COPYFILE_XATTR); +#endif /* * Don't remove the target even after an error. The target might * not be a regular file, or its attributes might be important, @@ -212,6 +223,9 @@ copy_link(FTSENT *p, int exists) warn("symlink: %s", llink); return (1); } +#ifdef __APPLE__ + copyfile(p->fts_path, to.p_path, 0, COPYFILE_XATTR | COPYFILE_NOFOLLOW_SRC); +#endif return (0); } diff --git a/dd/args.c b/dd/args.c index 2858db1..e7e0627 100644 --- a/dd/args.c +++ b/dd/args.c @@ -75,15 +75,15 @@ static const struct arg { void (*f)(char *); u_int set, noset; } args[] = { - { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|C_OSYNC }, + { "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_BS|C_IBS }, + { "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_BS|C_OBS }, + { "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 }, @@ -103,6 +103,8 @@ jcl(char **argv) 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); diff --git a/dd/dd.c b/dd/dd.c index cbcb87a..125acd7 100644 --- a/dd/dd.c +++ b/dd/dd.c @@ -224,21 +224,27 @@ getfdtype(IO *io) 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; -#ifdef __APPLE__ - else if (type & D_DISK) { -#else else if (type & (D_DISK | D_MEM)) { if (type & D_DISK) { const int one = 1; (void)ioctl(io->fd, DIOCWLABEL, &one); } -#endif +#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; diff --git a/dd/misc.c b/dd/misc.c index e589a73..d70d320 100644 --- a/dd/misc.c +++ b/dd/misc.c @@ -78,7 +78,7 @@ summary(void) } if (st.trunc) { (void)snprintf(buf, sizeof(buf), "%qu truncated %s\n", - st.trunc, (st.trunc == 1) ? "block" : "blocks"); + st.trunc, (st.trunc == 1) ? "record" : "records"); (void)write(STDERR_FILENO, buf, strlen(buf)); } (void)snprintf(buf, sizeof(buf), diff --git a/df/Makefile b/df/Makefile index cbcac45..101cda1 100644 --- a/df/Makefile +++ b/df/Makefile @@ -46,3 +46,5 @@ include $(MAKEFILEDIR)/$(MAKEFILE) -include Makefile.postamble -include Makefile.dependencies + +ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders diff --git a/df/df.1 b/df/df.1 index 99e4c1c..93a445d 100644 --- a/df/df.1 +++ b/df/df.1 @@ -45,7 +45,18 @@ .Fl m | P .Oc .Op Fl ailn +.Op Fl t +.Op Fl T Ar type +.Op Ar file | filesystem ... +.Sh LEGACY SYNOPSIS +.Nm +.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 @@ -115,6 +126,12 @@ Note that this overrides the .Ev BLOCKSIZE specification from the environment. .It Fl t +In legacy mode (see +.Xr compat 5 +for details) acts like +.Fl T +otherwise it is a no-op. +.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 @@ -158,6 +175,7 @@ flags are ignored if a file or filesystem is specified. .Xr getfsstat 2 , .Xr statfs 2 , .Xr getmntinfo 3 , +.Xr compat 5 .Xr fstab 5 , .Xr mount 8 , .Xr quot 8 diff --git a/df/df.c b/df/df.c index fa104d2..dda262c 100644 --- a/df/df.c +++ b/df/df.c @@ -64,7 +64,6 @@ typedef int32_t ufs_daddr_t; #include #include #include - #include #include #include @@ -76,6 +75,12 @@ typedef int32_t ufs_daddr_t; #include #include +#ifdef __APPLE__ +#include "get_compat.h" +#else +#define COMPAT_MODE(func, mode) 1 +#endif + #define UNITS_SI 1 #define UNITS_2 2 @@ -147,12 +152,20 @@ main(int argc, char *argv[]) const char *fstype; char *mntpath, *mntpt, **vfslist; long mntsize; - int ch, i, rv, tflag = 0; + int ch, i, rv, tflag = 0, kludge_tflag = 0; + const char *options = "abgHhiklmnPt:T:"; + if (COMPAT_MODE("bin/df", "unix2003")) { + /* Unix2003 requires -t be "include total capicity". which df + already does, but it conflits 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:"; + } fstype = "ufs"; vfslist = NULL; - while ((ch = getopt(argc, argv, "abgHhiklmnPt:")) != -1) + while ((ch = getopt(argc, argv, options)) != -1) switch (ch) { case 'a': aflag = 1; @@ -184,7 +197,7 @@ main(int argc, char *argv[]) break; case 'l': if (tflag) - errx(1, "-l and -t are mutually exclusive."); + errx(1, "-l and -T are mutually exclusive."); if (vfslist != NULL) break; vfslist = makevfslist(makenetvfslist()); @@ -197,11 +210,17 @@ main(int argc, char *argv[]) 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 -t option may be specified"); + errx(1, "only one -%c option may be specified", ch); else - errx(1, "-l and -t are mutually exclusive."); + errx(1, "-l and -%c are mutually exclusive.", ch); } tflag++; fstype = optarg; @@ -214,6 +233,16 @@ main(int argc, char *argv[]) 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) { + tflag = 1; + fstype = *argv++; + vfslist = makevfslist(fstype); + } + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); bzero(&maxwidths, sizeof(maxwidths)); for (i = 0; i < mntsize; i++) @@ -239,7 +268,7 @@ main(int argc, char *argv[]) rv = 1; continue; } - } else if (S_ISCHR(stbuf.st_mode)) { + } else if (S_ISCHR(stbuf.st_mode) || S_ISBLK(stbuf.st_mode)) { if ((mntpt = getmntpt(*argv)) == 0) { mdev.fspec = *argv; mntpath = strdup("/tmp/df.XXXXXX"); @@ -582,8 +611,9 @@ void usage(void) { + char *t_flag = COMPAT_MODE("bin/df", "unix2003") ? "[-t]" : "[-t type]"; (void)fprintf(stderr, - "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-t type] [file | filesystem ...]\n"); + "usage: df [-b | -H | -h | -k | -m | -P] [-ailn] [-T type] %s [file | filesystem ...]\n", t_flag); exit(EX_USAGE); } @@ -655,7 +685,7 @@ makenetvfslist(void) *strptr = ','; free(listptr[i]); } - *(--strptr) = NULL; + *(--strptr) = '\0'; free(listptr); return (str); diff --git a/du/du.c b/du/du.c index 813ac73..8b1c46b 100644 --- a/du/du.c +++ b/du/du.c @@ -62,6 +62,14 @@ __RCSID("$FreeBSD: src/usr.bin/du/du.c,v 1.28 2002/12/30 18:13:07 mike Exp $"); #include #include #include +#include +#include + +#ifdef __APPLE__ +#include "get_compat.h" +#else +#define COMPAT_MODE(func, mode) 1 +#endif #define KILO_SZ(n) (n) #define MEGA_SZ(n) ((n) * (n)) @@ -130,18 +138,25 @@ main(int argc, char *argv[]) switch (ch) { case 'H': Hflag = 1; + Pflag = Lflag = 0; break; case 'I': ignoreadd(optarg); break; case 'L': - if (Pflag) + if (Pflag && COMPAT_MODE("bin/du", "legacy")) { usage(); + } else { + Hflag = Pflag = 0; + } Lflag = 1; break; case 'P': - if (Lflag) + if (Lflag && COMPAT_MODE("bin/du", "legacy")) { usage(); + } else { + Hflag = Lflag = 0; + } Pflag = 1; break; case 'a': @@ -268,6 +283,9 @@ main(int argc, char *argv[]) } 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: @@ -275,6 +293,14 @@ main(int argc, char *argv[]) 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; @@ -351,8 +377,9 @@ linkchk(FTSENT *p) dev = p->fts_statp->st_dev; if ((start = files) != NULL) for (fp = start + nfiles - 1; fp >= start; --fp) - if (ino == fp->inode && dev == fp->dev) + if (ino == fp->inode && dev == fp->dev) { return (1); + } if (nfiles == maxfiles && (files = realloc((char *)files, (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) @@ -446,6 +473,16 @@ ignorep(FTSENT *ent) { struct ignentry *ign; + 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("fdesc", 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; + } + } SLIST_FOREACH(ign, &ignores, next) if (fnmatch(ign->mask, ent->fts_name, 0) != FNM_NOMATCH) return 1; diff --git a/install/xinstall.c b/install/xinstall.c index a98ca93..31e542d 100644 --- a/install/xinstall.c +++ b/install/xinstall.c @@ -65,6 +65,10 @@ static const char rcsid[] = #include #include +#ifdef __APPLE__ +#include +#endif + #include "pathnames.h" /* Bootstrap aid - this doesn't exist in most older releases */ @@ -504,6 +508,15 @@ install(from_name, to_name, fset, flags) } } +#if __APPLE__ + { + if (copyfile(from_name, to_name, NULL, COPYFILE_ACL | COPYFILE_XATTR) < 0) + { + warn("%s: copyfile", to_name); + } + } +#endif + (void)close(to_fd); if (!devnull) (void)close(from_fd); diff --git a/cp/Makefile b/ipcrm/Makefile similarity index 86% copy from cp/Makefile copy to ipcrm/Makefile index ce6bfd7..c14dc3e 100644 --- a/cp/Makefile +++ b/ipcrm/Makefile @@ -7,22 +7,20 @@ # and Makefile.postamble (both optional), and Makefile will include them. # -NAME = cp +NAME = ipcrm PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -HFILES = extern.h +CFILES = ipcrm.c -CFILES = cp.c utils.c - -OTHERSRCS = Makefile Makefile.preamble Makefile.postamble cp.1 +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ipcrm.1 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles CODE_GEN_STYLE = DYNAMIC MAKEFILE = tool.make -NEXTSTEP_INSTALLDIR = /bin +NEXTSTEP_INSTALLDIR = /usr/bin LIBS = DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) diff --git a/ipcrm/Makefile.postamble b/ipcrm/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/ipcrm/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/ipcrm/Makefile.preamble b/ipcrm/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/ipcrm/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/PB.project b/ipcrm/PB.project similarity index 50% copy from PB.project copy to ipcrm/PB.project index 09da8c8..6304e07 100644 --- a/PB.project +++ b/ipcrm/PB.project @@ -1,48 +1,24 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - OTHER_SOURCES = (PROJECT, Makefile.preamble, Makefile, Makefile.postamble); - SUBPROJECTS = ( - chflags, - chmod, - chown, - compress, - cp, - dd, - df, - du, - install, - ln, - ls, - mkdir, - mkfifo, - mknod, - mtree, - mv, - pax, - rm, - rmdir, - rmt, - shar, - tcopy, - touch - ); + FRAMEWORKS = (); + OTHER_LINKED = (ipcrm.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ipcrm.1); }; LANGUAGE = English; LOCALIZABLE_FILES = {}; MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_COMPILEROPTIONS = "-Wall -Werror"; + NEXTSTEP_INSTALLDIR = /usr/bin; NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = file_cmds; - PROJECTTYPE = Aggregate; + PROJECTNAME = ipcrm; + PROJECTTYPE = Tool; PROJECTVERSION = 2.8; - TARGETS = (); WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; diff --git a/ipcrm/ipcrm.1 b/ipcrm/ipcrm.1 new file mode 100644 index 0000000..4cf67c2 --- /dev/null +++ b/ipcrm/ipcrm.1 @@ -0,0 +1,83 @@ +.\" 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.2.32.1 2005/02/26 03:36:38 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 +.Op Fl q Ar msqid +.Op Fl m Ar shmid +.Op Fl s Ar semid +.Op Fl Q Ar msgkey +.Op Fl M Ar shmkey +.Op Fl S Ar semkey +.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 q Ar msqid +Remove the message queue associated with the id +.Nm msqid +from the system. +.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 s Ar semid +Removes the semaphore set associated with id +.Nm semid +from the system. +.It Fl Q Ar msgkey +Remove the message queue associated with key +.Nm msgkey +from the system. +.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 S Ar semkey +Remove the semaphore set associated with key +.Nm semkey +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/ipcrm/ipcrm.c b/ipcrm/ipcrm.c new file mode 100644 index 0000000..567cfcb --- /dev/null +++ b/ipcrm/ipcrm.c @@ -0,0 +1,183 @@ +/* + * 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. + */ + +#ifndef lint +static const char rcsid[] = + "$Id: ipcrm.c,v 1.2.30.1 2005/02/26 03:36:38 nicolai Exp $"; +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#if 0 +#include +#include +#endif +#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; + +void usage() +{ + fprintf(stderr, "%s\n%s\n", + "usage: ipcrm [-q msqid] [-m shmid] [-s semid]", + " [-Q msgkey] [-M shmkey] [-S semkey] ..."); + exit(1); +} + +int msgrm(key, id) + key_t key; + int id; +{ +#if 0 + if (key) { + id = msgget(key, 0); + if (id == -1) + return -1; + } +#endif + return msgctl(id, IPC_RMID, NULL); +} + +int shmrm(key, id) + key_t key; + int id; +{ + if (key) { + id = shmget(key, 0, 0); + if (id == -1) + return -1; + } + return shmctl(id, IPC_RMID, NULL); +} + +int semrm(key, id) + key_t key; + int id; +{ +#if 0 + union semun arg; + + if (key) { + id = semget(key, 0, 0); + if (id == -1) + return -1; + } + return semctl(id, 0, IPC_RMID, arg); +#endif +} + +void not_configured() +{ + signaled++; +} + +int main(argc, argv) + int argc; + char *argv[]; + +{ + int c, result, errflg, target_id; + key_t target_key; + + 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 = atoi(optarg); + 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 = atol(optarg); + 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("%key(%ld): ", IPC_TO_STR(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/cp/Makefile b/ipcs/Makefile similarity index 82% copy from cp/Makefile copy to ipcs/Makefile index ce6bfd7..2dd4656 100644 --- a/cp/Makefile +++ b/ipcs/Makefile @@ -7,30 +7,25 @@ # and Makefile.postamble (both optional), and Makefile will include them. # -NAME = cp +NAME = ipcs PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -HFILES = extern.h - -CFILES = cp.c utils.c - -OTHERSRCS = Makefile Makefile.preamble Makefile.postamble cp.1 +CFILES = ipcs.c +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ipcs.1 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles CODE_GEN_STYLE = DYNAMIC MAKEFILE = tool.make -NEXTSTEP_INSTALLDIR = /bin +NEXTSTEP_INSTALLDIR = /usr/bin LIBS = DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) - NEXTSTEP_PB_CFLAGS = - NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc @@ -49,3 +44,6 @@ include $(MAKEFILEDIR)/$(MAKEFILE) -include Makefile.postamble -include Makefile.dependencies + +ALL_CFLAGS += -I/System/Library/Frameworks/System.framework/PrivateHeaders + diff --git a/ipcs/Makefile.postamble b/ipcs/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/ipcs/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/ipcs/Makefile.preamble b/ipcs/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/ipcs/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/PB.project b/ipcs/PB.project similarity index 50% copy from PB.project copy to ipcs/PB.project index 09da8c8..3caab57 100644 --- a/PB.project +++ b/ipcs/PB.project @@ -1,48 +1,24 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - OTHER_SOURCES = (PROJECT, Makefile.preamble, Makefile, Makefile.postamble); - SUBPROJECTS = ( - chflags, - chmod, - chown, - compress, - cp, - dd, - df, - du, - install, - ln, - ls, - mkdir, - mkfifo, - mknod, - mtree, - mv, - pax, - rm, - rmdir, - rmt, - shar, - tcopy, - touch - ); + FRAMEWORKS = (); + OTHER_LINKED = (ipcs.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ipcs.1); }; LANGUAGE = English; LOCALIZABLE_FILES = {}; MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_COMPILEROPTIONS = "-Wall -Werror"; + NEXTSTEP_INSTALLDIR = /usr/bin; NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = file_cmds; - PROJECTTYPE = Aggregate; + PROJECTNAME = ipcs; + PROJECTTYPE = Tool; PROJECTVERSION = 2.8; - TARGETS = (); WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; diff --git a/ipcs/ipcs.1 b/ipcs/ipcs.1 new file mode 100644 index 0000000..6b057e2 --- /dev/null +++ b/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 +.Op Fl abcmopqstMQST +.Sh DESCRIPTION +The +.Nm +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 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 information about active message queues. +.It Fl s +Display information about active 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. +.It Fl M +Display system information about shared memory. +.It Fl Q +Display system information about messages queues. +.It Fl S +Display system information about semaphores. +.It Fl T +Display system information about shared memory, message queues +and semaphores. +.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 +is running; the output of +.Nm +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 . +.Sh SEE ALSO +.Xr ipcrm 1 +.Sh AUTHORS +.An Thorsten Lockert Aq tholo@sigmasoft.com diff --git a/ipcs/ipcs.c b/ipcs/ipcs.c new file mode 100644 index 0000000..5094d69 --- /dev/null +++ b/ipcs/ipcs.c @@ -0,0 +1,486 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#define KERNEL + +#include +#include +#include +#include + +#include "sys/ipcs.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) << 16) | (ix & 0xffff)) +#endif + +char * +fmt_perm(mode) + u_short mode; +{ + static char buffer[100]; + + buffer[0] = '-'; + buffer[1] = '-'; + buffer[2] = ((mode & 0400) ? 'r' : '-'); + buffer[3] = ((mode & 0200) ? 'w' : '-'); + buffer[4] = ((mode & 0100) ? 'a' : '-'); + buffer[5] = ((mode & 0040) ? 'r' : '-'); + buffer[6] = ((mode & 0020) ? 'w' : '-'); + buffer[7] = ((mode & 0010) ? 'a' : '-'); + buffer[8] = ((mode & 0004) ? 'r' : '-'); + buffer[9] = ((mode & 0002) ? 'w' : '-'); + buffer[10] = ((mode & 0001) ? 'a' : '-'); + buffer[11] = '\0'; + return (&buffer[0]); +} + +void +cvt_time(t, buf) + time_t t; + char *buf; +{ + struct tm *tm; + + if (t == 0) { + strcpy(buf, "no-entry"); + } else { + tm = localtime(&t); + 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 + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int display = SHMINFO | MSGINFO | SEMINFO; + int option = 0; + char kvmoferr[_POSIX2_LINE_MAX]; /* Error buf for kvm_openfiles. */ + 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 & (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); + + sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size); + 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 ds; + struct msqid_ds *msqptr = &ds; + size_t ic_size = sizeof(ic); + + printf("Message Queues:\n"); + 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("\n"); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_MSG_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_data = msqptr; + ic.ipcs_datalen = sizeof(*msqptr); + + while(!(sysctlbyname(IPCS_MSG_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + 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 %10d %s %8s %8s", + IXSEQ_TO_IPCID(i, msqptr->msg_perm), + (int)msqptr->msg_perm.key, + fmt_perm(msqptr->msg_perm.mode), + 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"); + } + } + printf("\n"); + } + } else + if (display & (MSGINFO | MSGTOTAL)) { + fprintf(stderr, + "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); + + sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size); + printf("shminfo:\n"); + printf("\tshmmax: %7d\t(max shared memory segment size)\n", + shminfo.shmmax); + printf("\tshmmin: %7d\t(min shared memory segment size)\n", + shminfo.shmmin); + printf("\tshmmni: %7d\t(max number of shared memory identifiers)\n", + shminfo.shmmni); + printf("\tshmseg: %7d\t(max shared memory segments per process)\n", + shminfo.shmseg); + printf("\tshmall: %7d\t(max amount of shared memory in pages)\n\n", + shminfo.shmall); + } + if (display & SHMINFO) { + struct IPCS_command ic; + struct shmid_ds ds; + struct shmid_ds *shmptr = &ds; + size_t ic_size = sizeof(ic); + + printf("Shared Memory:\n"); + 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("\n"); + { /* XXX */ + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SHM_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_data = shmptr; + ic.ipcs_datalen = sizeof(*shmptr); + + while(!(sysctlbyname(IPCS_SHM_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + 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 %10d %s %8s %8s", + IXSEQ_TO_IPCID(i, shmptr->shm_perm), + (int)shmptr->shm_perm.key, + fmt_perm(shmptr->shm_perm.mode), + 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(" %6d", + 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"); + } + } + } /* XXX */ + printf("\n"); + } + } +else + if (display & (SHMINFO | SHMTOTAL)) { + fprintf(stderr, + "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); + + sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size); + 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 ds; + struct semid_ds *semaptr = &ds; + size_t ic_size = sizeof(ic); + + printf("Semaphores:\n"); + 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("\n"); + + ic.ipcs_magic = IPCS_MAGIC; + ic.ipcs_op = IPCS_SEM_ITER; + ic.ipcs_cursor = 0; /* start */ + ic.ipcs_data = semaptr; + ic.ipcs_datalen = sizeof(*semaptr); + + while(!(sysctlbyname(IPCS_SEM_SYSCTL, &ic, &ic_size, &ic, ic_size))) { + 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 %10d %s %8s %8s", + IXSEQ_TO_IPCID(i, semaptr->sem_perm), + (int)semaptr->sem_perm.key, + fmt_perm(semaptr->sem_perm.mode), + 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"); + } + } + + printf("\n"); + } + } else + if (display & (SEMINFO | SEMTOTAL)) { + fprintf(stderr, "SVID semaphores facility not configured in the system\n"); + } + + exit(0); +} + +usage() +{ + + fprintf(stderr, + "usage: ipcs [-abcmopqstMQST]\n"); + exit(1); +} diff --git a/ls/Makefile b/ls/Makefile index 33d8b5a..35bb9cc 100644 --- a/ls/Makefile +++ b/ls/Makefile @@ -18,7 +18,7 @@ CFILES = cmp.c ls.c print.c util.c OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ls.1 -OTHER_CFLAGS = -DCOLORLS +OTHER_CFLAGS = -DCOLORLS -I/System/Library/Frameworks/System.framework/PrivateHeaders OTHER_LDFLAGS = -lncurses MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles diff --git a/ls/ls.1 b/ls/ls.1 index 1672c13..dad0f65 100644 --- a/ls/ls.1 +++ b/ls/ls.1 @@ -43,7 +43,7 @@ .Nd list directory contents .Sh SYNOPSIS .Nm -.Op Fl ABCFGHLPRTWZabcdfghiklmnopqrstuwx1 +.Op Fl ABCFGHLPRTWZabcdefghiklmnopqrstuwx1 .Op Ar .Sh DESCRIPTION For each operand that names a @@ -136,6 +136,8 @@ and options. .It Fl R Recursively list subdirectories encountered. +.It Fl S +Sort files by size .It Fl T When used with the .Fl l @@ -159,13 +161,14 @@ escape codes whenever possible. Use time when file status was last changed for sorting or printing. .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. .It Fl f Output is not sorted. .It Fl g -This option is deprecated and is only available for compatibility -with -.Bx 4.3 ; -it was used to display the group name in the long +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. .It Fl h @@ -212,7 +215,10 @@ the character this is the default when output is to a terminal. .It Fl r Reverse the order of the sort to get reverse -lexicographical order or the oldest entries first. +lexicographical order or the oldest entries first (or largest files +last, if combined with sort by size +.Pq Fl S +flag). .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. @@ -312,6 +318,10 @@ 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 security information, 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 @@ -623,6 +633,7 @@ specification. .Xr chmod 1 , .Xr sort 1 , .Xr xterm 1 , +.Xr compat 5 , .Xr termcap 5 , .Xr symlink 7 , .Xr sticky 8 diff --git a/ls/ls.c b/ls/ls.c index 92e38b3..f009917 100644 --- a/ls/ls.c +++ b/ls/ls.c @@ -68,12 +68,13 @@ __RCSID("$FreeBSD: src/bin/ls/ls.c,v 1.66 2002/09/21 01:28:36 wollman Exp $"); #include #include #endif - +#ifdef __APPLE__ +#include +#else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ #include "ls.h" #include "extern.h" -#ifndef __APPLE__ -#include "lomac.h" -#endif /* __APPLE__ */ /* * Upward approximation of the maximum number of characters needed to @@ -121,7 +122,8 @@ static int f_timesort; /* sort by time vice name */ static int f_sizesort; /* sort by size */ int f_type; /* add type character for non-regular files */ static int f_whiteout; /* show whiteout entries */ - int f_lomac; /* show LOMAC attributes */ + int f_acl; /* show ACLs in long listing */ + int f_group; /* show group */ #ifdef COLORLS int f_color; /* add type in color for non-regular files */ @@ -171,7 +173,7 @@ main(int argc, char *argv[]) f_listdot = 1; fts_options = FTS_PHYSICAL; - while ((ch = getopt(argc, argv, "1ABCFGHLPRSTWZabcdfghiklmnopqrstuvwx")) + while ((ch = getopt(argc, argv, "1ABCFGHLPRSTWabcdefghiklmnopqrstuvwx")) != -1) { switch (ch) { /* @@ -246,7 +248,13 @@ main(int argc, char *argv[]) case 'f': f_nosort = 1; break; - case 'g': /* Compatibility with 4.3BSD. */ + 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; @@ -310,8 +318,8 @@ main(int argc, char *argv[]) f_octal = 0; f_octal_escape = 0; break; - case 'Z': - f_lomac = 1; + case 'e': + f_acl = 1; break; default: case '?': @@ -619,10 +627,6 @@ display(FTSENT *p, FTSENT *list) maxnlink = makenines(maxnlink); maxsize = makenines(maxsize); } -#ifndef __APPLE__ - if (f_lomac) - lomac_start(); -#endif /* __APPLE__ */ bcfile = 0; flags = NULL; for (cur = list, entries = 0; cur; cur = cur->fts_link) { @@ -700,16 +704,8 @@ display(FTSENT *p, FTSENT *list) } else flen = 0; lattr = NULL; -#ifndef __APPLE__ - if (f_lomac) { - lattr = get_lattr(cur); - lattrlen = strlen(lattr); - if (lattrlen > maxlattr) - maxlattr = lattrlen; - } else -#endif /* __APPLE__ */ - lattrlen = 0; - + lattrlen = 0; + if ((np = malloc(sizeof(NAMES) + lattrlen + ulen + glen + flen + 4)) == NULL) err(1, "malloc"); @@ -728,14 +724,6 @@ display(FTSENT *p, FTSENT *list) (void)strcpy(np->flags, flags); free(flags); } -#ifndef __APPLE__ - if (f_lomac) { - np->lattr = &np->data[ulen + glen + 2 - + (f_flags ? flen + 1 : 0)]; - (void)strcpy(np->lattr, lattr); - free(lattr); - } -#endif /* __APPLE__ */ cur->fts_pointer = np; } } @@ -770,10 +758,6 @@ display(FTSENT *p, FTSENT *list) if (f_longform) for (cur = list; cur; cur = cur->fts_link) free(cur->fts_pointer); -#ifndef __APPLE__ - if (f_lomac) - lomac_stop(); -#endif /* __APPLE__ */ } /* diff --git a/ls/ls.h b/ls/ls.h index 4f15f98..73e264d 100644 --- a/ls/ls.h +++ b/ls/ls.h @@ -45,9 +45,6 @@ extern long blocksize; /* block size units */ extern int f_accesstime; /* use time of last access */ extern int f_flags; /* show flags associated with a file */ extern int f_humanval; /* show human-readable file sizes */ -#ifndef __APPLE__ -extern int f_lomac; /* show LOMAC attributes */ -#endif /* __APPLE__ */ extern int f_inode; /* print inode */ extern int f_longform; /* long listing format */ extern int f_octal; /* print unprintables in octal */ @@ -60,6 +57,8 @@ extern int f_sortacross; /* sort across rows, not down columns */ extern int f_statustime; /* use time of last mode change */ extern int f_notabs; /* don't use tab-separated multi-col output */ extern int f_type; /* add type character for non-regular files */ +extern int f_acl; /* print ACLs in long format */ +extern int f_group; /* list group without owner */ #ifdef COLORLS extern int f_color; /* add type in color for non-regular files */ #endif diff --git a/ls/print.c b/ls/print.c index 4962a95..a9d651e 100644 --- a/ls/print.c +++ b/ls/print.c @@ -44,6 +44,15 @@ __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 +#endif #include #include @@ -152,6 +161,162 @@ printname(const char *name) return printf("%s", 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} +}; + +static char * +uuid_to_name(uuid_t *uu) +{ + int is_gid = -1; + struct group *tgrp = NULL; + struct passwd *tpass = NULL; + char *name = NULL; + uid_t id; + + +#define MAXNAMETAG (MAXLOGNAME + 6) /* + strlen("group:") */ + name = (char *) malloc(MAXNAMETAG); + + if (NULL == name) + err(1, "malloc"); + + if (0 != mbr_uuid_to_id(uu, &id, &is_gid)) + goto errout; + + switch (is_gid) { + case ID_TYPE_UID: + tpass = getpwuid(id); + if (!tpass) { + goto errout; + } + snprintf(name, MAXNAMETAG, "%s:%s", "user", tpass->pw_name); + break; + case ID_TYPE_GID: + tgrp = getgrgid((gid_t) id); + if (!tgrp) { + goto errout; + } + snprintf(name, MAXNAMETAG, "%s:%s", "group", tgrp->gr_name); + break; + default: + if (0 != mbr_uuid_to_string(uu, name)) + goto errout; + } + return name; + errout: + fprintf(stderr, "Unable to translate qualifier on ACL\n"); + strcpy(name, ""); + return name; +} + +static void +printacl(acl_t acl, int isdir) +{ + acl_entry_t entry; + 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 ((applicable = (uuid_t *) acl_get_qualifier(entry)) == NULL) + continue; + 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; + + 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)printf(" %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)printf("%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)printf("%s%s", first++ ? "," : "", acl_flags[i].name); + } + + (void)putchar('\n'); + } + +} + void printlong(DISPLAY *dp) { @@ -159,6 +324,10 @@ printlong(DISPLAY *dp) FTSENT *p; NAMES *np; char buf[20]; +#ifdef __APPLE__ + acl_t acl = NULL; + char full_path[MAXPATHLEN]; +#endif #ifdef COLORLS int color_printed = 0; #endif @@ -177,15 +346,37 @@ printlong(DISPLAY *dp) dp->s_block, (u_int64_t)howmany(sp->st_blocks, blocksize)); strmode(sp->st_mode, buf); np = p->fts_pointer; - (void)printf("%s %*u %-*s %-*s ", buf, dp->s_nlink, - sp->st_nlink, dp->s_user, np->user, dp->s_group, - np->group); +#ifdef __APPLE__ + if (p->fts_parent->fts_name && *p->fts_parent->fts_name) + { + snprintf(full_path, sizeof full_path, "%s/%s", + p->fts_parent->fts_accpath, p->fts_accpath); + acl = acl_get_file(full_path, ACL_TYPE_EXTENDED); + } else + acl = acl_get_file(p->fts_accpath, ACL_TYPE_EXTENDED); +#endif /* __APPLE__ */ + if (f_group) { +#ifdef __APPLE__ + (void)printf("%s%s %*u %-*s ", buf, acl == NULL ? " " : "+", dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#else /* ! __APPLE__ */ + (void)printf("%s %*u %-*s ", buf, dp->s_nlink, + sp->st_nlink, dp->s_group, np->group); +#endif /* __APPLE__ */ + } + else { +#ifdef __APPLE__ + (void)printf("%s%s %*u %-*s %-*s ", buf, acl == NULL ? " " : "+", dp->s_nlink, + sp->st_nlink, dp->s_user, np->user, dp->s_group, + np->group); +#else /* ! __APPLE__ */ + (void)printf("%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)printf("%-*s ", dp->s_flags, np->flags); -#ifndef __APPLE__ - if (f_lomac) - (void)printf("%-*s ", dp->s_lattr, np->lattr); -#endif /* __APPLE__ */ if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) if (minor(sp->st_rdev) > 255 || minor(sp->st_rdev) < 0) (void)printf("%3d, 0x%08x ", @@ -219,6 +410,11 @@ printlong(DISPLAY *dp) if (S_ISLNK(sp->st_mode)) printlink(p); (void)putchar('\n'); +#ifdef __APPLE__ + if (f_acl && (acl != NULL)) + printacl(acl, S_ISDIR(sp->st_mode)); + acl_free(acl); +#endif /* __APPLE__ */ } } @@ -610,12 +806,12 @@ printsize(size_t width, off_t bytes) unit = unit_adjust(&bytes); if (bytes == 0) - (void)printf("%*s ", width, "0B"); + (void)printf("%*s ", (int)width, "0B"); else - (void)printf("%*lld%c ", width - 1, bytes, + (void)printf("%*lld%c ", (int)width - 1, bytes, "BKMGTPE"[unit]); } else - (void)printf("%*lld ", width, bytes); + (void)printf("%*lld ", (int)width, bytes); } /* diff --git a/ls/util.c b/ls/util.c index 49b4385..e74fa61 100644 --- a/ls/util.c +++ b/ls/util.c @@ -156,9 +156,9 @@ usage(void) { (void)fprintf(stderr, #ifdef COLORLS - "usage: ls [-ABCFGHLPRSTWZabcdfghiklnoqrstuvx1]" + "usage: ls [-ABCFGHLPRSTWZabcdefghiklmnopqrstuwx1]" #else - "usage: ls [-ABCFHLPRSTWZabcdfghiklnoqrstuvx1]" + "usage: ls [-ABCFHLPRSTWZabcdefghiklmnopqrstuwx1]" #endif " [file ...]\n"); exit(1); diff --git a/mv/mv.c b/mv/mv.c index 994aec2..f79cc0e 100644 --- a/mv/mv.c +++ b/mv/mv.c @@ -67,6 +67,10 @@ __RCSID("$FreeBSD: src/bin/mv/mv.c,v 1.39 2002/07/09 17:45:13 johan Exp $"); #include #include +#ifdef __APPLE__ +#include +#endif + #include "pathnames.h" int fflg, iflg, nflg, vflg; @@ -331,6 +335,9 @@ err: if (unlink(to)) (void)close(to_fd); return (1); } +#ifdef __APPLE__ + copyfile(from, to, 0, COPYFILE_ACL | COPYFILE_XATTR); +#endif (void)close(from_fd); oldmode = sbp->st_mode & ALLPERMS; diff --git a/df/Makefile b/pathchk/Makefile similarity index 87% copy from df/Makefile copy to pathchk/Makefile index cbcac45..afd7d69 100644 --- a/df/Makefile +++ b/pathchk/Makefile @@ -7,20 +7,20 @@ # and Makefile.postamble (both optional), and Makefile will include them. # -NAME = df +NAME = pathchk PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -CFILES = df.c vfslist.c +CFILES = pathchk.c -OTHERSRCS = Makefile Makefile.preamble Makefile.postamble df.1 +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble pathchk.1 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles CODE_GEN_STYLE = DYNAMIC MAKEFILE = tool.make -NEXTSTEP_INSTALLDIR = /bin +NEXTSTEP_INSTALLDIR = /usr/bin LIBS = DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) diff --git a/pathchk/Makefile.postamble b/pathchk/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/pathchk/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/pathchk/Makefile.preamble b/pathchk/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/pathchk/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/pathchk/PB.project b/pathchk/PB.project new file mode 100644 index 0000000..2ba4aa7 --- /dev/null +++ b/pathchk/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (pathchk.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, pathchk.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = /tmp/developer_cmds/Build; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /usr/bin; + NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; + NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; + PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; + PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; + PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; + PROJECTNAME = pathchk; + PROJECTTYPE = Tool; + PROJECTVERSION = 2.8; + WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; + WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; + WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; +} diff --git a/pathchk/pathchk.1 b/pathchk/pathchk.1 new file mode 100644 index 0000000..efe2a05 --- /dev/null +++ b/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/pathchk/pathchk.c b/pathchk/pathchk.c new file mode 100644 index 0000000..233a6d0 --- /dev/null +++ b/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/pax/ar_io.c b/pax/ar_io.c index 9fd7346..74e13ca 100644 --- a/pax/ar_io.c +++ b/pax/ar_io.c @@ -128,8 +128,10 @@ ar_open(name) if (name == NULL) { arfd = STDIN_FILENO; arcname = STDN; - } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) + } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) { syswarn(0, errno, "Failed open to read on %s", name); + exit(1); + } if (zflag) ar_start_gzip(arfd); break; @@ -649,7 +651,15 @@ ar_write(buf, 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. @@ -1186,7 +1196,8 @@ ar_next() if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) syswarn(0, errno, "Unable to restore signal mask"); - if (done || !wr_trail || strcmp(NM_TAR, argv0) == 0) + /* Don't query for new volume if format is unknown */ + if (frmt == NULL || done || !wr_trail || strcmp(NM_TAR, argv0) == 0) return(-1); tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); diff --git a/pax/ar_subs.c b/pax/ar_subs.c index 95bd7d5..8a455f2 100644 --- a/pax/ar_subs.c +++ b/pax/ar_subs.c @@ -57,6 +57,11 @@ static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_subs.c,v 1.13 19 #include #include #include +#ifdef __APPLE__ +#include +#include +#include +#endif #include "pax.h" #include "extern.h" @@ -178,6 +183,18 @@ extract() 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; + char *tmp; + LIST_ENTRY(copyfile_list_entry_t) link; + } *cle; + + LIST_INIT(©file_list); +#endif arcn = &archd; /* @@ -347,6 +364,24 @@ extract() 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->tmp, "%s.XXX", cle->src) > MAXPATHLEN) + continue; + if (mktemp(cle->tmp) == NULL) + continue; + if (rename(cle->src, cle->tmp)) + continue; + + if (asprintf(&cle->dst, "%s/%s", + dirname(arcn->name), basename(arcn->name) + 2) != -1) + LIST_INSERT_HEAD(©file_list, cle, link); + } +#endif /* * if required, chdir around. */ @@ -356,6 +391,20 @@ extract() "Can't fchdir to starting directory"); } +#ifdef __APPLE__ + LIST_FOREACH(cle, ©file_list, link) + { + if(copyfile_disable || copyfile(cle->tmp, cle->dst, 0, + COPYFILE_UNPACK | COPYFILE_XATTR | COPYFILE_ACL)) + rename(cle->tmp, cle->src); + else + unlink(cle->tmp); + free(cle->dst); + free(cle->src); + free(cle->tmp); + } +#endif + /* * all done, restore directory modes and times as required; make sure * all patterns supplied by the user were matched; block off signals @@ -391,6 +440,12 @@ wr_archive(arcn, is_app) int (*wrf)(); 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 @@ -429,6 +484,64 @@ wr_archive(arcn, is_app) */ if (sel_chk(arcn) != 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)) + { + int size; + char *new; + + md_fname = strdup("/tmp/pax.md.XXXX"); + 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; + + if(md_fname && mktemp(md_fname)) + if(copyfile(arcn->name, md_fname, NULL, + COPYFILE_PACK | COPYFILE_XATTR | COPYFILE_ACL) < 0) + { + syswarn(1,EPERM, + "Unable to preserve metadata on %s", arcn->name); + goto next; + } + + stat(md_fname, &arcn->sb); + arcn->skip = arcn->sb.st_size; + + if (!strncmp(dirname(arcn->name), ".", 2)) + size = asprintf(&new, "._%s", + basename(arcn->name)); + else + size = asprintf(&new, "%s/._%s", + dirname(arcn->name), basename(arcn->name)); + + if (size != -1 && size < sizeof arcn->name) + { + strncpy(arcn->name, new, PAXPATHLEN+1); + } else { + goto next; + } + arcn->nlen = strlen(arcn->name); + arcn->org_name = arcn->name; + free(new); + metadata = 1; + } else if (metadata) { +next: + metadata = 0; + memcpy(arcn, &arcn_copy, sizeof(ARCHD)); + strncpy(arcn->name, arcn_copy_name, PAXPATHLEN+1); + } +#endif fd = -1; if (uflag) { /* @@ -457,7 +570,18 @@ wr_archive(arcn, is_app) * 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); + } else + fd = open(arcn->org_name, O_RDONLY, 0); + if (fd < 0) { +#else if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) { +#endif syswarn(1,errno, "Unable to open %s to read", arcn->org_name); purg_lnk(arcn); @@ -543,6 +667,10 @@ wr_archive(arcn, is_app) if (((cnt > 0) && (wr_skip(cnt) < 0)) || ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) break; +#if __APPLE__ + if (metadata) + goto next; +#endif } /* @@ -976,6 +1104,12 @@ copy() (void)putc('\n', stderr); vfpart = 0; } +#ifdef __APPLE__ + if (getenv(COPYFILE_DISABLE_VAR) == NULL) { + if (copyfile(arcn->org_name, arcn->name, 0, COPYFILE_ACL | COPYFILE_XATTR) < 0) + paxwarn(1, "File %s had metadata that could not be copied", arcn->org_name); + } +#endif } /* diff --git a/pax/file_subs.c b/pax/file_subs.c index 0a1e285..234dcc3 100644 --- a/pax/file_subs.c +++ b/pax/file_subs.c @@ -174,7 +174,7 @@ file_close(arcn, fd) * if not preserving mode or we cannot set uid/gid, then PROHIBIT * set uid/gid bits */ - if (!pmode || res) + if (!pids || res) arcn->sb.st_mode &= ~(SETBITS); if (pmode) set_pmode(arcn->name, arcn->sb.st_mode); @@ -580,6 +580,9 @@ unlnk_exist(name, type) 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 diff --git a/pax/options.c b/pax/options.c index 9a64e1f..0bbcaa9 100644 --- a/pax/options.c +++ b/pax/options.c @@ -376,6 +376,9 @@ pax_options(argc, argv) * specify an archive format on write */ tmp.name = optarg; + if (0 == strcmp("pax", optarg)) { /* alias for ustar */ + tmp.name = "ustar"; + } if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) { flg |= XF; diff --git a/pax/pax.1 b/pax/pax.1 index 590c9df..8df49bd 100644 --- a/pax/pax.1 +++ b/pax/pax.1 @@ -1,6 +1,3 @@ -.\" $OpenBSD: pax.1,v 1.5 1997/04/06 06:11:13 millert 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. @@ -16,10 +13,6 @@ .\" 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. @@ -37,30 +30,31 @@ .\" SUCH DAMAGE. .\" .\" @(#)pax.1 8.4 (Berkeley) 4/18/94 +.\" $FreeBSD: src/bin/pax/pax.1,v 1.33 2004/07/03 02:03:44 tjr Exp $ .\" -.Dd April 18, 1994 +.Dd July 3, 2004 .Dt PAX 1 -.Os BSD 4.4 +.Os .Sh NAME .Nm pax .Nd read and write file archives and copy directory hierarchies .Sh SYNOPSIS -.Nm pax -.Op Fl cdnv +.Nm +.Op Fl cdnvz .Bk -words .Op Fl f Ar archive .Ek .Bk -words .Op Fl s Ar replstr -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl U Ar user -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl G Ar group -.Ar ... +.Ar ...\& .Ek .Bk -words .Oo @@ -68,35 +62,35 @@ .Op Ar from_date .Op Ar ,to_date .Oc -.Ar ... +.Ar ...\& .Ek -.Op Ar pattern ... -.Nm pax +.Op Ar pattern ...\& +.Nm .Fl r -.Op Fl cdiknuvDYZ +.Op Fl cdiknuvzDYZ .Bk -words .Op Fl f Ar archive .Ek .Bk -words .Op Fl o Ar options -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl p Ar string -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl s Ar replstr -.Ar ... +.Ar ...\& .Ek .Op Fl E Ar limit .Bk -words .Op Fl U Ar user -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl G Ar group -.Ar ... +.Ar ...\& .Ek .Bk -words .Oo @@ -104,12 +98,12 @@ .Op Ar from_date .Op Ar ,to_date .Oc -.Ar ... +.Ar ...\& .Ek -.Op Ar pattern ... -.Nm pax +.Op Ar pattern ...\& +.Nm .Fl w -.Op Fl dituvHLPX +.Op Fl dituvzHLPX .Bk -words .Op Fl b Ar blocksize .Ek @@ -122,19 +116,19 @@ .Ek .Bk -words .Op Fl s Ar replstr -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl o Ar options -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl U Ar user -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl G Ar group -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl B Ar bytes @@ -146,28 +140,28 @@ .Op Ar ,to_date .Op Ar /[c][m] .Oc -.Ar ... +.Ar ...\& .Ek -.Op Ar file ... -.Nm pax +.Op Ar +.Nm .Fl r .Fl w .Op Fl diklntuvDHLPXYZ .Bk -words .Op Fl p Ar string -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl s Ar replstr -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl U Ar user -.Ar ... +.Ar ...\& .Ek .Bk -words .Op Fl G Ar group -.Ar ... +.Ar ...\& .Ek .Bk -words .Oo @@ -176,17 +170,17 @@ .Op Ar ,to_date .Op Ar /[c][m] .Oc -.Ar ... +.Ar ...\& .Ek -.Op Ar file ... +.Op Ar .Ar directory .Sh DESCRIPTION -.Nm Pax -will read, write, and list the members of an archive file, +The +.Nm +utility will read, write, and list the members of an archive file, and will copy directory hierarchies. -.Nm Pax -operation is independent of the specific archive format, -and supports a wide variety of different archive formats. +These operations are independent of the specific archive format, +and support a wide variety of different archive formats. A list of supported archive formats can be found under the description of the .Fl x option. @@ -196,30 +190,28 @@ The presence of the and the .Fl w options specifies which of the following functional modes -.Nm pax +.Nm will operate under: .Em list , read , write , and -.Em copy. +.Em copy . .Bl -tag -width 6n .It .Em List . -.Nm Pax -will write to +Write to .Dv standard output a table of contents of the members of the archive file read from .Dv standard input , whose pathnames match the specified -.Ar patterns. +.Ar patterns . The table of contents contains one filename per line and is written using single line buffering. .It Fl r .Em Read . -.Nm Pax -extracts the members of the archive file read from the +Extract the members of the archive file read from the .Dv standard input , -with pathnames matching the specified -.Ar patterns. +with pathnames matching the specified +.Ar patterns . 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. @@ -230,28 +222,26 @@ the extracted files are discussed in more detail under the option. .It Fl w .Em Write . -.Nm Pax -writes an archive containing the +Write an archive containing the .Ar file operands to .Dv standard output using the specified archive format. -When no +When no .Ar file -operands are specified, a list of files to copy with one per line is read from +operands are specified, a list of files to copy with one per line is read from .Dv standard input . -When a +When a .Ar file operand is also a directory, the entire file hierarchy rooted at that directory will be included. .It Fl r Fl w .Em Copy . -.Nm Pax -copies the +Copy the .Ar file operands to the destination .Ar directory . -When no +When no .Ar file operands are specified, a list of files to copy with one per line is read from the @@ -286,13 +276,12 @@ While processing a damaged archive during a or .Em list operation, -.Nm pax +.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). .Sh OPERANDS -.Pp The .Ar directory operand specifies a destination directory pathname. @@ -300,7 +289,7 @@ If the .Ar directory operand does not exist, or it is not writable by the user, or it is not of type directory, -.Nm Pax +.Nm will exit with a non-zero exit status. .Pp The @@ -309,17 +298,17 @@ operand is used to select one or more pathnames of archive members. Archive members are selected using the pattern matching notation described by .Xr fnmatch 3 . -When the +When the .Ar pattern operand is not supplied, all members of the archive will be selected. -When a +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 pax +.Nm will write these .Ar pattern operands in a diagnostic message to @@ -332,14 +321,13 @@ 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 pax +.Nm will write these .Ar file operand pathnames in a diagnostic message to .Dv standard error and then exit with a non-zero exit status. .Sh OPTIONS -.Pp The following options are supported: .Bl -tag -width 4n .It Fl r @@ -351,15 +339,15 @@ 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 -.Dv OR +.Dv OR of .Dv S_IRWXU , S_IRWXG , and -.Dv S_IRWXO +.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 pax +.Nm will write a diagnostic message to .Dv standard error and exit with a non-zero exit status at the completion of operation. @@ -382,8 +370,8 @@ 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 pax +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 @@ -423,7 +411,7 @@ can be separated by 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 +When blocking is not specified, the default .Ar blocksize is dependent on the specific archive format being used (see the .Fl x @@ -455,7 +443,7 @@ or .Em write ) . A single archive may span multiple files and different archive devices. When required, -.Nm pax +.Nm will prompt for the pathname of the file or device of the next volume in the archive. .It Fl i @@ -465,19 +453,21 @@ For each archive member matching a operand or each file matching a .Ar file operand, -.Nm pax +.Nm will prompt to .Pa /dev/tty giving the name of the file, its file mode and its modification time. -.Nm Pax -will then read a line from +The +.Nm +utility 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 Pax -will immediately exit with a non-zero exit status if +The +.Nm +utility will immediately exit with a non-zero exit status if .Dv is encountered when reading a response or if .Pa /dev/tty @@ -485,12 +475,12 @@ cannot be opened for reading and writing. .It Fl k Do not overwrite existing files. .It Fl l -Link files. (The letter ell). +Link files. +(The letter ell). In the .Em copy -mode ( -.Fl r -.Fl w ) , +mode +.Pq Fl r w , hard links are made between the source and destination file hierarchies whenever possible. .It Fl n @@ -501,7 +491,7 @@ 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 +.Fl d is also specified). .It Fl o Ar options Information to modify the algorithm for extracting or writing archive files @@ -523,7 +513,7 @@ and .Cm p . Multiple characteristics can be concatenated within the same string and multiple -.Fl p +.Fl p options can be specified. The meaning of the specification characters are as follows: .Bl -tag -width 2n @@ -538,10 +528,10 @@ 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 +The .Cm e flag is the sum of the -.Cm o +.Cm o and .Cm p flags. @@ -554,7 +544,7 @@ Preserve the user ID and group ID. .Sq Preserve the file mode bits. This intended to be used by a -.Em user +.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 @@ -568,22 +558,22 @@ 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 +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 pax +.Nm will not set the -.Dv S_ISUID +.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 pax +.Nm will write a diagnostic message to .Dv standard error . Failure to preserve these items will affect the final exit status, @@ -628,7 +618,8 @@ 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 +substitution. +The first unsuccessful substitution stops the operation of the .Cm g option. The optional trailing @@ -641,9 +632,9 @@ File or archive member names that substitute to the empty string are not selected and will be skipped. .It Fl t Reset the access times of any file or directory read or accessed by -.Nm pax +.Nm to be the same as they were before being read or accessed by -.Nm pax . +.Nm . .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. @@ -673,13 +664,13 @@ the output has the format: .Dl == For pathnames representing a symbolic link, the output has the format: .Dl => -Where is the output format specified by the +Where 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 ( -.Em read , write , +Otherwise for all the other operational modes +.Em ( read , write , and .Em copy ) , pathnames are written and flushed to @@ -694,8 +685,9 @@ is not buffered, and is written only after the file has been read or written. .It Fl x Ar format Specify the output archive format, with the default format being .Ar ustar . -.Nm Pax -currently supports the following formats: +The +.Nm +utility currently supports the following formats: .Bl -tag -width "sv4cpio" .It Ar cpio The extended cpio interchange format specified in the @@ -704,7 +696,7 @@ 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 pax +.Nm and is repaired. .It Ar bcpio The old binary cpio format. @@ -713,32 +705,35 @@ 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 pax +.Nm and is repaired. .It Ar 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 pax +.Nm and is repaired. .It Ar 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 pax +.Nm and is repaired. .It Ar tar -The old BSD tar format as found in BSD4.3. +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 , +.Em hard links , soft links , and -.Em directories +.Em directories will be archived (other file system types are not supported). For backwards compatibility with even older tar formats, a .Fl o @@ -753,12 +748,19 @@ The default blocksize for this format is 10240 bytes. Pathnames stored by this format must be 250 characters or less in length. .El .Pp -.Nm Pax -will detect and report any file that it is unable to store or extract +The +.Nm +utility 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 z +Use +.Xr gzip 1 +to compress (decompress) the archive while writing (reading). +Incompatible with +.Fl a . .It Fl B Ar bytes Limit the number of bytes written to a single archive volume to .Ar bytes . @@ -779,7 +781,7 @@ to indicate a product. .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). +(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 D This option is the same as the @@ -787,7 +789,7 @@ This option is the same as the 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 +(e.g.\& uid, gid, etc.) is newer than a copy of the file in the destination .Ar directory . .It Fl E Ar limit Limit the number of consecutive read faults while trying to read a flawed @@ -795,30 +797,30 @@ archives to .Ar limit . With a positive .Ar limit , -.Nm pax +.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 pax +.Nm to stop operation after the first read error is detected on an archive volume. A .Ar limit of .Li NONE will cause -.Nm pax +.Nm to attempt to recover from read errors forever. -The default +The default .Ar limit is a small positive number of retries. .Pp -.Em Warning: +.Em Warning : Using this option with .Li NONE should be used with extreme caution as -.Nm pax +.Nm may get stuck in an infinite loop on a very badly flawed archive. .It Fl G Ar group Select a file based on its @@ -828,7 +830,7 @@ name, or when starting with a a numeric gid. A '\\' can be used to escape the .Cm # . -Multiple +Multiple .Fl G options may be supplied and checking stops with the first match. .It Fl H @@ -841,12 +843,12 @@ Do not follow symbolic links, perform a physical file system traversal. This is the default mode. .It Fl T Ar [from_date][,to_date][/[c][m]] Allow files to be selected based on a file modification or inode change -time falling within a specified time range of +time falling within a specified time range of .Ar from_date to .Ar to_date (the dates are inclusive). -If only a +If only a .Ar from_date is supplied, all files with a modification or inode change time equal to or younger are selected. @@ -854,7 +856,7 @@ 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 +When the .Ar from_date is equal to the .Ar to_date , @@ -862,8 +864,8 @@ only files with a modification or inode change time of exactly that time will be selected. .Pp When -.Nm pax -is in the +.Nm +is in the .Em write or .Em copy @@ -879,8 +881,8 @@ the file was last written). The .Ar 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 +inode was last changed; e.g.\& a change of owner, group, mode, etc). +When .Ar c and .Ar m @@ -892,7 +894,7 @@ 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 pax +.Nm is used to create a time based incremental archive (only files that were changed during a specified time range will be archived). .Pp @@ -921,14 +923,14 @@ The minute field is required, while the other fields are optional and must be added in the following order: .Dl Cm hh , dd , mm , yy . -The +The .Cm ss field may be added independently of the other fields. Time ranges are relative to the current time, so .Dl Fl T Ar 1234/cm would select all files with a modification or inode change time of 12:34 PM today or later. -Multiple +Multiple .Fl T time range can be supplied and checking stops with the first match. .It Fl U Ar user @@ -939,14 +941,14 @@ name, or when starting with a a numeric uid. A '\\' can be used to escape the .Cm # . -Multiple +Multiple .Fl U options may be supplied and checking stops with the first match. .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 +.Li st_dev field as described in .Xr stat 2 for more information about device ID's. @@ -962,8 +964,8 @@ option, except that the modification time is checked using the pathname created after all the file name modifications have completed. .El .Pp -The options that operate on the names of files or archive members ( -.Fl c , +The options that operate on the names of files or archive members +.Fl ( c , .Fl i , .Fl n , .Fl s , @@ -1001,7 +1003,7 @@ Then the and .Fl Z options will be applied based on the final pathname. -Finally the +Finally the .Fl v option will write the names resulting from these modifications. .Pp @@ -1017,13 +1019,13 @@ based only on the user specified pathnames as modified by the .Fl D , .Fl G , .Fl T , -and +and .Fl U options (the .Fl D option only applies during a copy operation). Then any -.Fl s +.Fl s and .Fl i options will modify in that order, the names of these selected files. @@ -1048,9 +1050,9 @@ option, a file is not considered selected unless it is newer than the file to which it is compared. .Sh EXAMPLES The command: -.Dl pax -w -f /dev/rst0 .\ +.Dl "pax -w -f /dev/sa0 ." copies the contents of the current directory to the device -.Pa /dev/rst0 . +.Pa /dev/sa0 . .Pp The command: .Dl pax -v -f filename @@ -1058,17 +1060,17 @@ gives the verbose table of contents for an archive stored in .Pa filename . .Pp The following commands: -.Dl mkdir newdir -.Dl cd olddir -.Dl pax -rw .\ newdir +.Dl mkdir /tmp/to +.Dl cd /tmp/from +.Dl pax -rw .\ /tmp/to will copy the entire -.Pa olddir +.Pa /tmp/from directory hierarchy to -.Pa newdir . +.Pa /tmp/to . .Pp The command: .Dl pax -r -s ',^//*usr//*,,' -f a.pax -reads the archive +reads the archive .Pa a.pax , with all files rooted in ``/usr'' into the archive extracted relative to the current directory. @@ -1098,11 +1100,12 @@ files with the same name found in the source file tree .Pa home . .Sh STANDARDS The -.Nm pax +.Nm utility is a superset of the .St -p1003.2 standard. The options +.Fl z , .Fl B , .Fl D , .Fl E , @@ -1127,22 +1130,29 @@ operations are extensions to the .Tn POSIX standard. .Sh SEE ALSO -.Xr tar 1 , -.Xr cpio 1 -.Sh AUTHOR -Keith Muller at the University of California, San Diego -.Sh ERRORS -.Nm pax -will exit with one of the following values: +.Xr cpio 1 , +.Xr tar 1 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.4 . +.Sh AUTHORS +.An Keith Muller +at the University of California, San Diego +.Sh DIAGNOSTICS +The +.Nm +utility will exit with one of the following values: .Bl -tag -width 2n .It 0 All files were processed successfully. -.It 1 +.It 1 An error occurred. .El .Pp Whenever -.Nm pax +.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 @@ -1151,28 +1161,32 @@ option is specified, a diagnostic message is written to .Dv standard error and a non-zero exit status will be returned, but processing will continue. In the case where pax cannot create a link to a file, -.Nm pax +.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 pax +.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 pax +.Nm may have only partially created the archive which may violate the specific archive format specification. .Pp If while doing a .Em copy , -.Nm pax +.Nm detects a file is about to overwrite itself, the file is not copied, a diagnostic message is written to .Dv standard error and when -.Nm pax +.Nm completes it will exit with a non-zero exit status. +.Sh BUGS +The +.Nm +utility does not recognize multibyte characters. diff --git a/pax/tar.c b/pax/tar.c index 9044fe5..11ec097 100644 --- a/pax/tar.c +++ b/pax/tar.c @@ -1039,8 +1039,6 @@ ustar_wr(arcn) case PAX_DIR: hd->typeflag = DIRTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); - memset(hd->devmajor, 0, sizeof(hd->devmajor)); - memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; @@ -1051,18 +1049,13 @@ ustar_wr(arcn) else hd->typeflag = BLKTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); - if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, - sizeof(hd->devmajor), 3) || - ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, - sizeof(hd->devminor), 3) || - ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; + break; case PAX_FIF: hd->typeflag = FIFOTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); - memset(hd->devmajor, 0, sizeof(hd->devmajor)); - memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; @@ -1075,8 +1068,6 @@ ustar_wr(arcn) hd->typeflag = LNKTYPE; l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1); hd->linkname[sizeof(hd->linkname) - 1] = '\0'; - memset(hd->devmajor, 0, sizeof(hd->devmajor)); - memset(hd->devminor, 0, sizeof(hd->devminor)); if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 3)) goto out; break; @@ -1091,8 +1082,6 @@ ustar_wr(arcn) else hd->typeflag = REGTYPE; memset(hd->linkname, 0, sizeof(hd->linkname)); - memset(hd->devmajor, 0, sizeof(hd->devmajor)); - memset(hd->devminor, 0, sizeof(hd->devminor)); arcn->pad = TAR_PAD(arcn->sb.st_size); # ifdef NET2_STAT if (ul_oct((u_long)arcn->sb.st_size, hd->size, @@ -1107,6 +1096,13 @@ ustar_wr(arcn) break; } + /* set devmajor and devminor for all types as per spec */ + if (ul_oct((u_long)MAJOR(arcn->sb.st_rdev), hd->devmajor, + sizeof(hd->devmajor), 3) || + ul_oct((u_long)MINOR(arcn->sb.st_rdev), hd->devminor, + sizeof(hd->devminor), 3)) + goto out; + l_strncpy(hd->magic, TMAGIC, TMAGLEN); l_strncpy(hd->version, TVERSION, TVERSLEN); diff --git a/rm/rm.c b/rm/rm.c index 7997af5..c5c4da5 100644 --- a/rm/rm.c +++ b/rm/rm.c @@ -60,10 +60,18 @@ static const char rcsid[] = #include #include +#ifdef __APPLE__ +#include "get_compat.h" +#else +#define COMPAT_MODE(func, mode) 1 +#endif + int dflag, eval, fflag, iflag, Pflag, vflag, Wflag, stdin_ok; uid_t uid; int check __P((char *, char *, struct stat *)); +int checkdir __P((char *)); +int yes_or_no __P((void)); void checkdot __P((char **)); void rm_file __P((char **)); void rm_overwrite __P((char *, struct stat *)); @@ -165,7 +173,7 @@ rm_tree(argv) 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. @@ -183,8 +191,11 @@ rm_tree(argv) flags |= FTS_NOSTAT; if (Wflag) flags |= FTS_WHITEOUT; - if (!(fts = fts_open(argv, flags, NULL))) + 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: @@ -211,8 +222,13 @@ rm_tree(argv) continue; case FTS_D: /* Pre-order: give user chance to skip. */ - if (!fflag && !check(p->fts_path, p->fts_accpath, - p->fts_statp)) { + /* 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; } @@ -225,8 +241,16 @@ rm_tree(argv) continue; case FTS_DP: /* Post-order: see if user skipped. */ - if (p->fts_number == 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 && @@ -414,13 +438,33 @@ err: eval = 1; warn("%s", file); } +int +yes_or_no() +{ + int ch, first; + (void)fflush(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(stderr, "examine files in directory %s? ", path); + return yes_or_no(); +} int check(path, name, sp) char *path, *name; struct stat *sp; { - int ch, first; char modep[15], *flagsp; /* Check -i first. */ @@ -449,14 +493,10 @@ check(path, name, sp) path); free(flagsp); } - (void)fflush(stderr); - - first = ch = getchar(); - while (ch != '\n' && ch != EOF) - ch = getchar(); - return (first == 'y' || first == 'Y'); + return yes_or_no(); } + #define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) void checkdot(argv) diff --git a/cp/Makefile b/stat/Makefile similarity index 87% copy from cp/Makefile copy to stat/Makefile index ce6bfd7..550d148 100644 --- a/cp/Makefile +++ b/stat/Makefile @@ -7,22 +7,20 @@ # and Makefile.postamble (both optional), and Makefile will include them. # -NAME = cp +NAME = stat PROJECTVERSION = 2.8 PROJECT_TYPE = Tool -HFILES = extern.h +CFILES = stat.c -CFILES = cp.c utils.c - -OTHERSRCS = Makefile Makefile.preamble Makefile.postamble cp.1 +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble stat.1 MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles CODE_GEN_STYLE = DYNAMIC MAKEFILE = tool.make -NEXTSTEP_INSTALLDIR = /bin +NEXTSTEP_INSTALLDIR = /usr/bin LIBS = DEBUG_LIBS = $(LIBS) PROF_LIBS = $(LIBS) diff --git a/stat/Makefile.postamble b/stat/Makefile.postamble new file mode 100644 index 0000000..9d53ad2 --- /dev/null +++ b/stat/Makefile.postamble @@ -0,0 +1,6 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common + +after_install:: + $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/readlink + mkdir -p "$(DSTROOT)/usr/share/man/man1" + ln -f "$(DSTROOT)/usr/share/man/man1/stat.1" "$(DSTROOT)/usr/share/man/man1/readlink.1" diff --git a/stat/Makefile.preamble b/stat/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/stat/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/PB.project b/stat/PB.project similarity index 50% copy from PB.project copy to stat/PB.project index 09da8c8..0d40a66 100644 --- a/PB.project +++ b/stat/PB.project @@ -1,48 +1,24 @@ { DYNAMIC_CODE_GEN = YES; FILESTABLE = { - OTHER_SOURCES = (PROJECT, Makefile.preamble, Makefile, Makefile.postamble); - SUBPROJECTS = ( - chflags, - chmod, - chown, - compress, - cp, - dd, - df, - du, - install, - ln, - ls, - mkdir, - mkfifo, - mknod, - mtree, - mv, - pax, - rm, - rmdir, - rmt, - shar, - tcopy, - touch - ); + FRAMEWORKS = (); + OTHER_LINKED = (stat.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, stat.1); }; LANGUAGE = English; LOCALIZABLE_FILES = {}; MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; NEXTSTEP_BUILDTOOL = /bin/gnumake; - NEXTSTEP_COMPILEROPTIONS = "-Wall -Werror"; + NEXTSTEP_INSTALLDIR = /usr/bin; NEXTSTEP_JAVA_COMPILER = /usr/bin/javac; NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc; PDO_UNIX_BUILDTOOL = $NEXT_ROOT/Developer/bin/make; PDO_UNIX_JAVA_COMPILER = "$(NEXTDEV_BIN)/javac"; PDO_UNIX_OBJCPLUS_COMPILER = "$(NEXTDEV_BIN)/gcc"; - PROJECTNAME = file_cmds; - PROJECTTYPE = Aggregate; + PROJECTNAME = stat; + PROJECTTYPE = Tool; PROJECTVERSION = 2.8; - TARGETS = (); WINDOWS_BUILDTOOL = $NEXT_ROOT/Developer/Executables/make; WINDOWS_JAVA_COMPILER = "$(JDKBINDIR)/javac.exe"; WINDOWS_OBJCPLUS_COMPILER = "$(DEVDIR)/gcc"; diff --git a/stat/stat.1 b/stat/stat.1 new file mode 100644 index 0000000..4f4a784 --- /dev/null +++ b/stat/stat.1 @@ -0,0 +1,522 @@ +.\" $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.6 2003/06/02 11:19:23 ru Exp $ +.\" +.Dd May 8, 2003 +.Dt STAT 1 +.Os +.Sh NAME +.Nm stat , +.Nm readlink +.Nd display file status +.Sh SYNOPSIS +.Nm +.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 +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 +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 L +Use +.Xr stat 2 +instead of +.Xr lstat 2 . +The information reported by +.Nm +will refer to the target of +.Ar file , +if file is a symbolic link, and not to +.Ar file +itself. +.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 f Ar format +Display information using the specified format. +See the +.Sx FORMATS +section for a description of valid formats. +.It Fl l +Display output in +.Nm ls Fl lT +format. +.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 x +Display information in a more verbose way as known from some +.Tn Linux +distributions. +.It Fl t Ar timefmt +Display timestamps using the specified format. +This format is +passed directly to +.Xr strftime 3 . +.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 +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 +utility appeared in +.Nx 1.6 . +.Sh AUTHORS +.An -nosplit +The +.Nm +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/stat/stat.c b/stat/stat.c new file mode 100644 index 0000000..78f8107 --- /dev/null +++ b/stat/stat.c @@ -0,0 +1,990 @@ +/* + * 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 + +/* Commenting FBSDID, as it is not needed in OSX. +__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 0 /* was 1; not needed if ! __BSD_VISIBLE ? */ +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_DEVNAME 1 +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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: (%04OLp/%.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_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' + +void usage(const char *); +void output(const struct stat *, const char *, + const char *, int, int, int); +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); + +char *timefmt; +int linkfail; + +#define addchar(s, c, nl) \ + do { \ + (void)fputc((c), (s)); \ + (*nl) = ((c) == '\n'); \ + } while (0/*CONSTCOND*/) + +int +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; + + if (strcmp(getprogname(), "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(STDIN_FILENO, &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(stderr, "usage: %s %s\n", getprogname(), 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(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(stdout, '\n', &nl); + statfmt++; + continue; + case SIMPLE_TAB: + addchar(stdout, '\t', &nl); + statfmt++; + continue; + case SIMPLE_PERCENT: + addchar(stdout, '%', &nl); + statfmt++; + continue; + case SIMPLE_NUMBER: { + char num[12], *p; + + snprintf(num, sizeof(num), "%d", fn); + for (p = &num[0]; *p; p++) + addchar(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_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(stdout, buf[i], &nl); + + continue; + + badfmt: + errx(1, "%.*s: bad format", + (int)(statfmt - subfmt + 1), subfmt); + } + + if (!nl && !nonl) + (void)fputc('\n', stdout); + (void)fflush(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; + 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: + 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 (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; + small = 1; + 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)); +} -- 2.7.4