From 44a7a5ab64e9df65e241260d76d3dac1160b3360 Mon Sep 17 00:00:00 2001 From: Apple Date: Tue, 10 Apr 2001 06:35:09 +0000 Subject: [PATCH] file_cmds-45.tar.gz --- Makefile | 49 ++ Makefile.postamble | 1 + Makefile.preamble | 1 + PB.project | 49 ++ PROJECT | 10 + chflags/Makefile | 48 ++ chflags/Makefile.postamble | 1 + chflags/Makefile.preamble | 5 + chflags/PB.project | 25 + chflags/chflags.1 | 126 +++ chflags/chflags.c | 187 +++++ chmod/Makefile | 48 ++ chmod/Makefile.postamble | 1 + chmod/Makefile.preamble | 1 + chmod/PB.project | 25 + chmod/chmod.1 | 306 +++++++ chmod/chmod.c | 242 ++++++ chown/Makefile | 50 ++ chown/Makefile.postamble | 4 + chown/Makefile.preamble | 1 + chown/PB.project | 26 + chown/chgrp.1 | 146 ++++ chown/chown.8 | 154 ++++ chown/chown.c | 266 ++++++ cksum/crc.c | 149 ++++ compress/Makefile | 49 ++ compress/Makefile.postamble | 5 + compress/Makefile.preamble | 1 + compress/PB.project | 32 + compress/compress.1 | 176 ++++ compress/compress.c | 491 ++++++++++++ compress/uncompress.1 | 1 + compress/zopen.3 | 142 ++++ compress/zopen.c | 748 +++++++++++++++++ cp/Makefile | 51 ++ cp/Makefile.postamble | 1 + cp/Makefile.preamble | 1 + cp/PB.project | 27 + cp/cp.1 | 215 +++++ cp/cp.c | 484 +++++++++++ cp/extern.h | 59 ++ cp/utils.c | 356 ++++++++ csh/strpct.c | 99 +++ dd/Makefile | 50 ++ dd/Makefile.postamble | 1 + dd/Makefile.preamble | 1 + dd/PB.project | 26 + dd/args.c | 412 ++++++++++ dd/conv.c | 283 +++++++ dd/conv_tab.c | 291 +++++++ dd/dd.1 | 355 ++++++++ dd/dd.c | 412 ++++++++++ dd/dd.h | 98 +++ dd/extern.h | 69 ++ dd/misc.c | 108 +++ dd/position.c | 174 ++++ df/Makefile | 48 ++ df/Makefile.postamble | 4 + df/Makefile.preamble | 5 + df/PB.project | 25 + df/df.1 | 122 +++ df/df.c | 455 +++++++++++ du/Makefile | 48 ++ du/Makefile.postamble | 1 + du/Makefile.preamble | 1 + du/PB.project | 25 + du/du.1 | 134 ++++ du/du.c | 255 ++++++ install/Makefile | 50 ++ install/Makefile.postamble | 1 + install/Makefile.preamble | 5 + install/PB.project | 26 + install/install.1 | 183 +++++ install/pathnames.h | 38 + install/xinstall.c | 563 +++++++++++++ ln/Makefile | 49 ++ ln/Makefile.postamble | 1 + ln/Makefile.preamble | 1 + ln/PB.project | 25 + ln/ln.1 | 147 ++++ ln/ln.c | 182 +++++ ln/symlink.7 | 427 ++++++++++ ls/Makefile | 50 ++ ls/Makefile.postamble | 1 + ls/Makefile.preamble | 1 + ls/PB.project | 26 + ls/cmp.c | 203 +++++ ls/extern.h | 56 ++ ls/ls.1 | 367 +++++++++ ls/ls.c | 616 ++++++++++++++ ls/ls.h | 76 ++ ls/print.c | 363 +++++++++ ls/stat_flags.c | 164 ++++ ls/util.c | 80 ++ mkdir/Makefile | 48 ++ mkdir/Makefile.postamble | 1 + mkdir/Makefile.preamble | 1 + mkdir/PB.project | 25 + mkdir/mkdir.1 | 97 +++ mkdir/mkdir.c | 195 +++++ mkfifo/Makefile | 48 ++ mkfifo/Makefile.postamble | 1 + mkfifo/Makefile.preamble | 1 + mkfifo/PB.project | 25 + mkfifo/mkfifo.1 | 91 +++ mkfifo/mkfifo.c | 113 +++ mknod/Makefile | 48 ++ mknod/Makefile.postamble | 1 + mknod/Makefile.preamble | 1 + mknod/PB.project | 25 + mknod/mknod.8 | 149 ++++ mknod/mknod.c | 396 +++++++++ mtree/Makefile | 50 ++ mtree/Makefile.postamble | 1 + mtree/Makefile.preamble | 5 + mtree/PB.project | 26 + mtree/compare.c | 298 +++++++ mtree/create.c | 306 +++++++ mtree/extern.h | 52 ++ mtree/misc.c | 134 ++++ mtree/mtree.8 | 261 ++++++ mtree/mtree.c | 162 ++++ mtree/mtree.h | 97 +++ mtree/spec.c | 293 +++++++ mtree/verify.c | 211 +++++ mv/Makefile | 50 ++ mv/Makefile.postamble | 1 + mv/Makefile.preamble | 1 + mv/PB.project | 26 + mv/mv.1 | 133 +++ mv/mv.c | 361 +++++++++ mv/pathnames.h | 39 + pax/Makefile | 54 ++ pax/Makefile.postamble | 5 + pax/Makefile.preamble | 1 + pax/PB.project | 54 ++ pax/ar_io.c | 1372 +++++++++++++++++++++++++++++++ pax/ar_subs.c | 1288 +++++++++++++++++++++++++++++ pax/buf_subs.c | 1094 +++++++++++++++++++++++++ pax/cache.c | 500 ++++++++++++ pax/cache.h | 77 ++ pax/cpio.1 | 270 +++++++ pax/cpio.c | 1284 +++++++++++++++++++++++++++++ pax/cpio.h | 154 ++++ pax/extern.h | 299 +++++++ pax/file_subs.c | 1112 +++++++++++++++++++++++++ pax/ftree.c | 565 +++++++++++++ pax/ftree.h | 54 ++ pax/gen_subs.c | 467 +++++++++++ pax/getoldopt.c | 73 ++ pax/options.c | 1515 +++++++++++++++++++++++++++++++++++ pax/options.h | 116 +++ pax/pat_rep.c | 1240 ++++++++++++++++++++++++++++ pax/pat_rep.h | 57 ++ pax/pax.1 | 1178 +++++++++++++++++++++++++++ pax/pax.c | 426 ++++++++++ pax/pax.h | 243 ++++++ pax/sel_subs.c | 662 +++++++++++++++ pax/sel_subs.h | 76 ++ pax/tables.c | 1434 +++++++++++++++++++++++++++++++++ pax/tables.h | 175 ++++ pax/tar.1 | 239 ++++++ pax/tar.c | 1213 ++++++++++++++++++++++++++++ pax/tar.h | 151 ++++ pax/tty_subs.c | 251 ++++++ rm/Makefile | 48 ++ rm/Makefile.postamble | 1 + rm/Makefile.preamble | 1 + rm/PB.project | 25 + rm/rm.1 | 157 ++++ rm/rm.c | 448 +++++++++++ rmdir/Makefile | 48 ++ rmdir/Makefile.postamble | 1 + rmdir/Makefile.preamble | 1 + rmdir/PB.project | 25 + rmdir/rmdir.1 | 94 +++ rmdir/rmdir.c | 136 ++++ rmt/Makefile | 48 ++ rmt/Makefile.postamble | 1 + rmt/Makefile.preamble | 1 + rmt/PB.project | 25 + rmt/rmt.8 | 220 +++++ rmt/rmt.c | 259 ++++++ shar/Makefile | 49 ++ shar/Makefile.postamble | 1 + shar/Makefile.preamble | 3 + shar/PB.project | 25 + shar/shar.1 | 105 +++ shar/shar.sh | 76 ++ tcopy/Makefile | 48 ++ tcopy/Makefile.postamble | 1 + tcopy/Makefile.preamble | 1 + tcopy/PB.project | 25 + tcopy/tcopy.1 | 91 +++ tcopy/tcopy.c | 339 ++++++++ touch/Makefile | 48 ++ touch/Makefile.postamble | 1 + touch/Makefile.preamble | 1 + touch/PB.project | 25 + touch/touch.1 | 177 ++++ touch/touch.c | 389 +++++++++ 201 files changed, 36379 insertions(+) create mode 100644 Makefile create mode 100644 Makefile.postamble create mode 100644 Makefile.preamble create mode 100644 PB.project create mode 100644 PROJECT create mode 100644 chflags/Makefile create mode 100644 chflags/Makefile.postamble create mode 100644 chflags/Makefile.preamble create mode 100644 chflags/PB.project create mode 100644 chflags/chflags.1 create mode 100644 chflags/chflags.c create mode 100644 chmod/Makefile create mode 100644 chmod/Makefile.postamble create mode 100644 chmod/Makefile.preamble create mode 100644 chmod/PB.project create mode 100644 chmod/chmod.1 create mode 100644 chmod/chmod.c create mode 100644 chown/Makefile create mode 100644 chown/Makefile.postamble create mode 100644 chown/Makefile.preamble create mode 100644 chown/PB.project create mode 100644 chown/chgrp.1 create mode 100644 chown/chown.8 create mode 100644 chown/chown.c create mode 100644 cksum/crc.c create mode 100644 compress/Makefile create mode 100644 compress/Makefile.postamble create mode 100644 compress/Makefile.preamble create mode 100644 compress/PB.project create mode 100644 compress/compress.1 create mode 100644 compress/compress.c create mode 100644 compress/uncompress.1 create mode 100644 compress/zopen.3 create mode 100644 compress/zopen.c create mode 100644 cp/Makefile create mode 100644 cp/Makefile.postamble create mode 100644 cp/Makefile.preamble create mode 100644 cp/PB.project create mode 100644 cp/cp.1 create mode 100644 cp/cp.c create mode 100644 cp/extern.h create mode 100644 cp/utils.c create mode 100644 csh/strpct.c create mode 100644 dd/Makefile create mode 100644 dd/Makefile.postamble create mode 100644 dd/Makefile.preamble create mode 100644 dd/PB.project create mode 100644 dd/args.c create mode 100644 dd/conv.c create mode 100644 dd/conv_tab.c create mode 100644 dd/dd.1 create mode 100644 dd/dd.c create mode 100644 dd/dd.h create mode 100644 dd/extern.h create mode 100644 dd/misc.c create mode 100644 dd/position.c create mode 100644 df/Makefile create mode 100644 df/Makefile.postamble create mode 100644 df/Makefile.preamble create mode 100644 df/PB.project create mode 100644 df/df.1 create mode 100644 df/df.c create mode 100644 du/Makefile create mode 100644 du/Makefile.postamble create mode 100644 du/Makefile.preamble create mode 100644 du/PB.project create mode 100644 du/du.1 create mode 100644 du/du.c create mode 100644 install/Makefile create mode 100644 install/Makefile.postamble create mode 100644 install/Makefile.preamble create mode 100644 install/PB.project create mode 100644 install/install.1 create mode 100644 install/pathnames.h create mode 100644 install/xinstall.c create mode 100644 ln/Makefile create mode 100644 ln/Makefile.postamble create mode 100644 ln/Makefile.preamble create mode 100644 ln/PB.project create mode 100644 ln/ln.1 create mode 100644 ln/ln.c create mode 100644 ln/symlink.7 create mode 100644 ls/Makefile create mode 100644 ls/Makefile.postamble create mode 100644 ls/Makefile.preamble create mode 100644 ls/PB.project create mode 100644 ls/cmp.c create mode 100644 ls/extern.h create mode 100644 ls/ls.1 create mode 100644 ls/ls.c create mode 100644 ls/ls.h create mode 100644 ls/print.c create mode 100644 ls/stat_flags.c create mode 100644 ls/util.c create mode 100644 mkdir/Makefile create mode 100644 mkdir/Makefile.postamble create mode 100644 mkdir/Makefile.preamble create mode 100644 mkdir/PB.project create mode 100644 mkdir/mkdir.1 create mode 100644 mkdir/mkdir.c create mode 100644 mkfifo/Makefile create mode 100644 mkfifo/Makefile.postamble create mode 100644 mkfifo/Makefile.preamble create mode 100644 mkfifo/PB.project create mode 100644 mkfifo/mkfifo.1 create mode 100644 mkfifo/mkfifo.c create mode 100644 mknod/Makefile create mode 100644 mknod/Makefile.postamble create mode 100644 mknod/Makefile.preamble create mode 100644 mknod/PB.project create mode 100644 mknod/mknod.8 create mode 100644 mknod/mknod.c create mode 100644 mtree/Makefile create mode 100644 mtree/Makefile.postamble create mode 100644 mtree/Makefile.preamble create mode 100644 mtree/PB.project create mode 100644 mtree/compare.c create mode 100644 mtree/create.c create mode 100644 mtree/extern.h create mode 100644 mtree/misc.c create mode 100644 mtree/mtree.8 create mode 100644 mtree/mtree.c create mode 100644 mtree/mtree.h create mode 100644 mtree/spec.c create mode 100644 mtree/verify.c create mode 100644 mv/Makefile create mode 100644 mv/Makefile.postamble create mode 100644 mv/Makefile.preamble create mode 100644 mv/PB.project create mode 100644 mv/mv.1 create mode 100644 mv/mv.c create mode 100644 mv/pathnames.h create mode 100644 pax/Makefile create mode 100644 pax/Makefile.postamble create mode 100644 pax/Makefile.preamble create mode 100644 pax/PB.project create mode 100644 pax/ar_io.c create mode 100644 pax/ar_subs.c create mode 100644 pax/buf_subs.c create mode 100644 pax/cache.c create mode 100644 pax/cache.h create mode 100644 pax/cpio.1 create mode 100644 pax/cpio.c create mode 100644 pax/cpio.h create mode 100644 pax/extern.h create mode 100644 pax/file_subs.c create mode 100644 pax/ftree.c create mode 100644 pax/ftree.h create mode 100644 pax/gen_subs.c create mode 100644 pax/getoldopt.c create mode 100644 pax/options.c create mode 100644 pax/options.h create mode 100644 pax/pat_rep.c create mode 100644 pax/pat_rep.h create mode 100644 pax/pax.1 create mode 100644 pax/pax.c create mode 100644 pax/pax.h create mode 100644 pax/sel_subs.c create mode 100644 pax/sel_subs.h create mode 100644 pax/tables.c create mode 100644 pax/tables.h create mode 100644 pax/tar.1 create mode 100644 pax/tar.c create mode 100644 pax/tar.h create mode 100644 pax/tty_subs.c create mode 100644 rm/Makefile create mode 100644 rm/Makefile.postamble create mode 100644 rm/Makefile.preamble create mode 100644 rm/PB.project create mode 100644 rm/rm.1 create mode 100644 rm/rm.c create mode 100644 rmdir/Makefile create mode 100644 rmdir/Makefile.postamble create mode 100644 rmdir/Makefile.preamble create mode 100644 rmdir/PB.project create mode 100644 rmdir/rmdir.1 create mode 100644 rmdir/rmdir.c create mode 100644 rmt/Makefile create mode 100644 rmt/Makefile.postamble create mode 100644 rmt/Makefile.preamble create mode 100644 rmt/PB.project create mode 100644 rmt/rmt.8 create mode 100644 rmt/rmt.c create mode 100644 shar/Makefile create mode 100644 shar/Makefile.postamble create mode 100644 shar/Makefile.preamble create mode 100644 shar/PB.project create mode 100644 shar/shar.1 create mode 100644 shar/shar.sh create mode 100644 tcopy/Makefile create mode 100644 tcopy/Makefile.postamble create mode 100644 tcopy/Makefile.preamble create mode 100644 tcopy/PB.project create mode 100644 tcopy/tcopy.1 create mode 100644 tcopy/tcopy.c create mode 100644 touch/Makefile create mode 100644 touch/Makefile.postamble create mode 100644 touch/Makefile.preamble create mode 100644 touch/PB.project create mode 100644 touch/touch.1 create mode 100644 touch/touch.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..97ed83a --- /dev/null +++ b/Makefile @@ -0,0 +1,49 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +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\ + touch + +OTHERSRCS = PROJECT Makefile.preamble Makefile Makefile.postamble + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = aggregate.make +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + +NEXTSTEP_PB_CFLAGS = -Wall -Werror + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/Makefile.postamble b/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/Makefile.preamble b/Makefile.preamble new file mode 100644 index 0000000..8f8a3bd --- /dev/null +++ b/Makefile.preamble @@ -0,0 +1 @@ +include $(MAKEFILEPATH)/CoreOS/ProjectBuilder/Makefile.Preamble.Common diff --git a/PB.project b/PB.project new file mode 100644 index 0000000..09da8c8 --- /dev/null +++ b/PB.project @@ -0,0 +1,49 @@ +{ + 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 + ); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_COMPILEROPTIONS = "-Wall -Werror"; + 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; + 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/PROJECT b/PROJECT new file mode 100644 index 0000000..6c7fd2c --- /dev/null +++ b/PROJECT @@ -0,0 +1,10 @@ +Project : file_cmds +Description : File Commands +Distribution : NetBSD Tue Oct 6 14:50:43 PDT 1998 +Source : NetBSD Foundation, Inc. +Location : cvs.netbsd.org:/cvsroot +Owner : wsanchez +Releases : Titan +Dependancies : + +Some file commands in NetBSD implement the -h flag and can otherwise charge permissions on symlinks. But this requires lchmod(2) from NetBSD and we don't currently have that, so it is disabled with -D__APPLE__. diff --git a/chflags/Makefile b/chflags/Makefile new file mode 100644 index 0000000..4f99118 --- /dev/null +++ b/chflags/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = chflags + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = chflags.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chflags.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/chflags/Makefile.postamble b/chflags/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/chflags/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/chflags/Makefile.preamble b/chflags/Makefile.preamble new file mode 100644 index 0000000..7a86fc2 --- /dev/null +++ b/chflags/Makefile.preamble @@ -0,0 +1,5 @@ +vpath stat_flags.c ../ls + +CFILES += stat_flags.c + +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/chflags/PB.project b/chflags/PB.project new file mode 100644 index 0000000..bf7e967 --- /dev/null +++ b/chflags/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (chflags.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chflags.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 = chflags; + 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/chflags/chflags.1 b/chflags/chflags.1 new file mode 100644 index 0000000..c7ab4bc --- /dev/null +++ b/chflags/chflags.1 @@ -0,0 +1,126 @@ +.\" $NetBSD: chflags.1,v 1.6 1997/10/18 12:39:50 lukem Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chflags.1 8.4 (Berkeley) 5/2/95 +.\" +.Dd May 2, 1995 +.Dt CHFLAGS 1 +.Os +.Sh NAME +.Nm chflags +.Nd change file flags +.Sh SYNOPSIS +.Nm +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Ar flags +.Ar file ... +.Sh DESCRIPTION +The +.Nm +utility modifies the file flags of the listed files +as specified by the +.Ar flags +operand. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the file flags for the file hierarchies rooted +in the files instead of just the files themselves. +.El +.Pp +Flags are a comma separated list of keywords. +The following keywords are currently defined: +.Bd -literal -offset indent compact +arch set the archived flag (super-user only) +opaque set the opaque flag (owner or super-user only) +nodump set the nodump flag (owner or super-user only) +sappnd set the system append-only flag (super-user only) +schg set the system immutable flag (super-user only) +uappnd set the user append-only flag (owner or super-user only) +uchg set the user immutable flag (owner or super-user only) +.Ed +.Pp +Putting the letters +.Dq no +before an option causes the flag to be turned off. +For example: +.Bd -literal -offset indent compact +nouchg the immutable bit should be cleared +.Ed +.Pp +Symbolic links do not have flags, so unless the +.Fl H +or +.Fl L +option is set, +.Nm +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr chflags 2 , +.Xr stat 2 , +.Xr fts 3 , +.Xr symlink 7 diff --git a/chflags/chflags.c b/chflags/chflags.c new file mode 100644 index 0000000..54d1bc3 --- /dev/null +++ b/chflags/chflags.c @@ -0,0 +1,187 @@ +/* $NetBSD: chflags.c,v 1.5 1997/10/18 12:39:54 lukem Exp $ */ + +/* + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)chflags.c 8.5 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: chflags.c,v 1.5 1997/10/18 12:39:54 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char **)); +u_long string_to_flags __P((char **, u_long *, u_long *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p; + u_long clear, set; + long val; + int Hflag, Lflag, Pflag, Rflag, ch, fts_options, oct, rval; + char *flags, *ep; + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLPR")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } + + flags = *argv; + if (*flags >= '0' && *flags <= '7') { + errno = 0; + val = strtol(flags, &ep, 8); + if (val < 0) + errno = ERANGE; + if (errno) + err(1, "invalid flags: %s", flags); + if (*ep) + errx(1, "invalid flags: %s", flags); + set = val; + oct = 1; + } else { + if (string_to_flags(&flags, &set, &clear)) + errx(1, "invalid flag: %s", flags); + clear = ~clear; + oct = 0; + } + + if ((ftsp = fts_open(++argv, fts_options , 0)) == NULL) + err(1, "fts_open `%s'", argv[0]); + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (Rflag) /* Change it at FTS_DP. */ + continue; + fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_DNR: /* Warn, chflag, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ + continue; + default: + break; + } + if (oct) { + if (!chflags(p->fts_accpath, set)) + continue; + } else { + p->fts_statp->st_flags |= set; + p->fts_statp->st_flags &= clear; + if (!chflags(p->fts_accpath, p->fts_statp->st_flags)) + continue; + } + warn("%s", p->fts_path); + rval = 1; + } + if (errno) + err(1, "fts_read"); + exit(rval); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: chflags [-R [-H | -L | -P]] flags file ...\n"); + exit(1); +} diff --git a/chmod/Makefile b/chmod/Makefile new file mode 100644 index 0000000..34a6a63 --- /dev/null +++ b/chmod/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = chmod + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = chmod.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chmod.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/chmod/Makefile.postamble b/chmod/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/chmod/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/chmod/Makefile.preamble b/chmod/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/chmod/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/chmod/PB.project b/chmod/PB.project new file mode 100644 index 0000000..644371e --- /dev/null +++ b/chmod/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (chmod.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chmod.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = chmod; + 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/chmod/chmod.1 b/chmod/chmod.1 new file mode 100644 index 0000000..6a52eb6 --- /dev/null +++ b/chmod/chmod.1 @@ -0,0 +1,306 @@ +.\" $NetBSD: chmod.1,v 1.11 1997/10/20 08:51:10 enami Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)chmod.1 8.4 (Berkeley) 3/31/94 +.\" +.Dd March 31, 1994 +.Dt CHMOD 1 +.Os +.Sh NAME +.Nm chmod +.Nd change file modes +.Sh SYNOPSIS +.Nm +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl h +.Ar mode +.Ar file ... +.Sh DESCRIPTION +The +.Nm +utility modifies the file mode bits of the listed files +as specified by the +.Ar mode +operand. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the modes of the file hierarchies rooted in the files +instead of just the files themselves. +.\" .It Fl h +.\" If +.\" .Ar file +.\" is symbolic link, the mode of the link is changed. +.El +.Pp +.\" If the option +.\" .Fl h +.\" is not given, +Unless the +.Fl H +or +.Fl L +option is set, +.Nm +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +Only the owner of a file or the super-user is permitted to change +the mode of a file. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh MODES +Modes may be absolute or symbolic. +An absolute mode is an octal number constructed by +.Ar or-ing +the following values: +.Pp +.Bl -tag -width 6n -compact -offset indent +.It Li 4000 +set-user-ID-on-execution +.It Li 2000 +set-group-ID-on-execution +.It Li 1000 +sticky bit, see +.Xr chmod 2 +.It Li 0400 +read by owner +.It Li 0200 +write by owner +.It Li 0100 +execute (or search for directories) by owner +.It Li 0070 +read, write, execute/search by group +.It Li 0007 +read, write, execute/search by others +.El +.Pp +The read, write, and execute/search values for group and others +are encoded as described for owner. +.Pp +The symbolic mode is described by the following grammar: +.Bd -literal -offset indent +mode ::= clause [, clause ...] +clause ::= [who ...] [action ...] last_action +action ::= op [perm ...] +last_action ::= op [perm ...] +who ::= a | u | g | o +op ::= + | \- | = +perm ::= r | s | t | w | x | X | u | g | o +.Ed +.Pp +The +.Ar who +symbols ``u'', ``g'', and ``o'' specify the user, group, and other parts +of the mode bits, respectively. +The +.Ar who +symbol ``a'' is equivalent to ``ugo''. +.Pp +.ne 1i +The +.Ar perm +symbols represent the portions of the mode bits as follows: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It r +The read bits. +.It s +The set-user-ID-on-execution and set-group-ID-on-execution bits. +.It t +The sticky bit. +.It w +The write bits. +.It x +The execute/search bits. +.It X +The execute/search bits if the file is a directory or any of the +execute/search bits are set in the original (unmodified) mode. +Operations with the +.Ar perm +symbol ``X'' are only meaningful in conjunction with the +.Ar op +symbol ``+'', and are ignored in all other cases. +.It u +The user permission bits in the mode of the original file. +.It g +The group permission bits in the mode of the original file. +.It o +The other permission bits in the mode of the original file. +.El +.Pp +The +.Ar op +symbols represent the operation performed, as follows: +.Bl -tag -width 4n +.It + +If no value is supplied for +.Ar perm , +the ``+'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.It \&\- +If no value is supplied for +.Ar perm , +the ``\-'' operation has no effect. +If no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is cleared. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are cleared. +.It = +The mode bits specified by the +.Ar who +value are cleared, or, if no who value is specified, the owner, group +and other mode bits are cleared. +Then, if no value is supplied for +.Ar who , +each permission bit specified in +.Ar perm , +for which the corresponding bit in the file mode creation mask +is clear, is set. +Otherwise, the mode bits represented by the specified +.Ar who +and +.Ar perm +values are set. +.El +.Pp +Each +.Ar clause +specifies one or more operations to be performed on the mode +bits, and each operation is applied to the mode bits in the +order specified. +.Pp +Operations upon the other permissions only (specified by the symbol +``o'' by itself), in combination with the +.Ar perm +symbols ``s'' or ``t'', are ignored. +.Sh EXAMPLES +.Bl -tag -width "u=rwx,go=u-w" -compact +.It Li 644 +make a file readable by anyone and writable by the owner only. +.Pp +.It Li go-w +deny write permission to group and others. +.Pp +.It Li =rw,+X +set the read and write permissions to the usual defaults, but +retain any execute permissions that are currently set. +.Pp +.It Li +X +make a directory or file searchable/executable by everyone if it is +already searchable/executable by anyone. +.Pp +.It Li 755 +.It Li u=rwx,go=rx +.It Li u=rwx,go=u-w +make a file readable/executable by everyone and writable by the owner only. +.Pp +.It Li go= +clear all mode bits for group and others. +.Pp +.It Li g=u-w +set the group bits equal to the user bits, but clear the group write bit. +.El +.Sh BUGS +There's no +.Ar perm +option for the naughty bits. +.Sh SEE ALSO +.Xr install 1 , +.Xr chmod 2 , +.Xr stat 2 , +.Xr umask 2 , +.Xr fts 3 , +.Xr setmode 3 , +.Xr symlink 7 , +.Xr chown 8 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible with the exception of the +.Ar perm +symbols +.Dq t +and +.Dq X +which are not included in that standard. diff --git a/chmod/chmod.c b/chmod/chmod.c new file mode 100644 index 0000000..4dae785 --- /dev/null +++ b/chmod/chmod.c @@ -0,0 +1,242 @@ +/* $NetBSD: chmod.c,v 1.20 1998/07/28 05:31:22 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: chmod.c,v 1.20 1998/07/28 05:31:22 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char *[])); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p; + mode_t *set; + long val; + int oct, omode; + int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval; + char *ep, *mode; + int (*change_mode) __P((const char *, mode_t)); + + set = NULL; /* XXX gcc -Wuninitialized */ + omode = 0; /* XXX gcc -Wuninitialized */ + + (void)setlocale(LC_ALL, ""); + + Hflag = Lflag = Rflag = fflag = hflag = 0; + while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': /* XXX: undocumented. */ + fflag = 1; + break; +#ifndef __APPLE__ + case 'h': + /* + * In System V (and probably POSIX.2) the -h option + * causes chmod to change the mode of the symbolic + * link. 4.4BSD's symbolic links didn't have modes, + * so it was an undocumented noop. In NetBSD 1.3, + * lchmod(2) is introduced and this option does real + * work. + */ + hflag = 1; + break; +#endif + /* + * XXX + * "-[rwx]" are valid mode commands. If they are the entire + * argument, getopt has moved past them, so decrement optind. + * Regardless, we're done argument processing. + */ + case 'g': case 'o': case 'r': case 's': + case 't': case 'u': case 'w': case 'X': case 'x': + if (argv[optind - 1][0] == '-' && + argv[optind - 1][1] == ch && + argv[optind - 1][2] == '\0') + --optind; + goto done; + case '?': + default: + usage(); + } +done: argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (hflag) + errx(1, + "the -R and -h options may not be specified together."); + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } +#ifndef __APPLE__ + if (hflag) + change_mode = lchmod; + else + change_mode = chmod; +#else + change_mode = chmod; +#endif + + mode = *argv; + if (*mode >= '0' && *mode <= '7') { + errno = 0; + val = strtol(mode, &ep, 8); + if (val > INT_MAX || val < 0) + errno = ERANGE; + if (errno) + err(1, "invalid file mode: %s", mode); + if (*ep) + errx(1, "invalid file mode: %s", mode); + omode = val; + oct = 1; + } else { + if ((set = setmode(mode)) == NULL) + errx(1, "invalid file mode: %s", mode); + oct = 0; + } + + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) + err(1, argv[0]); + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (!Rflag) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + case FTS_DNR: /* Warn, chmod, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_DP: /* Already changed at FTS_D. */ + continue; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ +#ifndef __APPLE__ + if (!hflag) + continue; + /* else */ + /* FALLTHROUGH */ +#else + continue; +#endif + default: + break; + } + if ((*change_mode)(p->fts_accpath, oct ? omode : + getmode(set, p->fts_statp->st_mode)) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } + } + if (errno) + err(1, "fts_read"); + exit(rval); + /* NOTREACHED */ +} + +void +usage() +{ +#ifndef __APPLE__ + (void)fprintf(stderr, + "usage: chmod [-R [-H | -L | -P]] [-h] mode file ...\n"); +#else + (void)fprintf(stderr, + "usage: chmod [-R [-H | -L | -P]] mode file ...\n"); +#endif + exit(1); + /* NOTREACHED */ +} diff --git a/chown/Makefile b/chown/Makefile new file mode 100644 index 0000000..eb0029c --- /dev/null +++ b/chown/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = chown + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = chown.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble chgrp.1\ + chown.8 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/sbin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + +NEXTSTEP_PB_CFLAGS = -DSUPPORT_DOT + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/chown/Makefile.postamble b/chown/Makefile.postamble new file mode 100644 index 0000000..4da9b62 --- /dev/null +++ b/chown/Makefile.postamble @@ -0,0 +1,4 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common + +after_install:: + $(LINKPRODUCT) $(DSTROOT)/usr/bin/chgrp diff --git a/chown/Makefile.preamble b/chown/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/chown/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/chown/PB.project b/chown/PB.project new file mode 100644 index 0000000..d716382 --- /dev/null +++ b/chown/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (chown.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, chgrp.1, chown.8); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_COMPILEROPTIONS = "-DSUPPORT_DOT"; + NEXTSTEP_INSTALLDIR = /usr/sbin; + 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 = chown; + 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/chown/chgrp.1 b/chown/chgrp.1 new file mode 100644 index 0000000..379a94d --- /dev/null +++ b/chown/chgrp.1 @@ -0,0 +1,146 @@ +.\" Copyright (c) 1983, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)chgrp.1 8.3 (Berkeley) 3/31/94 +.\" $NetBSD: chgrp.1,v 1.10 1997/12/21 18:11:19 kleink Exp $ +.\" +.Dd March 31, 1994 +.Dt CHGRP 1 +.Os BSD 4.2 +.Sh NAME +.Nm chgrp +.Nd change group +.Sh SYNOPSIS +.Nm +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fh +.Ar group +.Ar files ... +.Sh DESCRIPTION +The +.Nm +utility sets the group ID of the file named by each +.Ar file +operand to the +.Ar group +ID specified by the group operand. +.Pp +Options: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl f +The force option ignores errors, except for usage errors and doesn't +query about strange modes (unless the user does not have proper permissions). +.It Fl h +If +.Ar file +is a symbolic link, the group of the link is changed. +.El +.Pp +If +.Fl h +is not given, unless the +.Fl H +or +.Fl L +option is set, +.Nm +on a symbolic link always succeeds and has no effect. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Ar group +operand can be either a group name from the group database, +or a numeric group ID. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +The user invoking +.Nm +must belong to the specified group and be the owner of the file, +or be the super-user. +.Pp +Unless invoked by the super-user, +.Nm +clears the set-user-id and set-group-id bits on a file to prevent +accidental or mischievous creation of set-user-id or set-group-id +programs. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh FILES +.Bl -tag -width /etc/group -compact +.It Pa /etc/group +Group ID file +.El +.Sh SEE ALSO +.Xr chown 2 , +.Xr lchown 2 , +.Xr fts 3 , +.Xr group 5 , +.Xr passwd 5 , +.Xr symlink 7 , +.Xr chown 8 +.Sh STANDARDS +The +.Nm +utility is expected to be POSIX 1003.2 compatible. diff --git a/chown/chown.8 b/chown/chown.8 new file mode 100644 index 0000000..6e8065e --- /dev/null +++ b/chown/chown.8 @@ -0,0 +1,154 @@ +.\" Copyright (c) 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)chown.8 8.3 (Berkeley) 3/31/94 +.\" $NetBSD: chown.8,v 1.11 1998/10/05 21:37:38 kim Exp $ +.\" +.Dd March 31, 1994 +.Dt CHOWN 8 +.Os BSD 4 +.Sh NAME +.Nm chown +.Nd change file owner and group +.Sh SYNOPSIS +.Nm +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fh +.Ar owner Op Ar :group +.Ar file ... +.Nm "" +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl fh +.Ar :group +.Ar file ... +.Sh DESCRIPTION +.Nm +sets the user ID and/or the group ID of the specified files. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +Change the user ID and/or the group ID for the file hierarchies rooted +in the files instead of just the files themselves. +.It Fl 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 +.Ar file +is a symbolic link, the owner and/or group of the link is changed. +.El +.Pp +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +The +.Fl L +option cannot be used together with the +.Fl h +option. +.Pp +The +.Ar owner +and +.Ar group +operands are both optional, however, one must be specified. +If the +.Ar group +operand is specified, it must be preceded by a colon (``:'') character. +.Pp +The +.Ar owner +may be either a numeric user ID or a user name. +If a user name is also a numeric user ID, the operand is used as a +user name. +The +.Ar group +may be either a numeric group ID or a group name. +If a group name is also a numeric group ID, the operand is used as a +group name. +.Pp +The ownership of a file may only be altered by a super-user for +obvious security reasons. +.Pp +Unless invoked by the super-user, +.Nm +clears the set-user-id and set-group-id bits on a file to prevent +accidental or mischievous creation of set-user-id and set-group-id +programs. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh COMPATIBILITY +Previous versions of the +.Nm +utility used the dot (``.'') character to distinguish the group name. +This has been changed to be a colon (``:'') character so that user and +group names may contain the dot character. +.Sh SEE ALSO +.Xr chgrp 1 , +.Xr find 1 , +.Xr chown 2 , +.Xr lchown 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +command is expected to be POSIX 1003.2 compliant. diff --git a/chown/chown.c b/chown/chown.c new file mode 100644 index 0000000..c2dcdad --- /dev/null +++ b/chown/chown.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; +#else +__RCSID("$NetBSD: chown.c,v 1.15 1998/10/05 21:37:39 kim Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void a_gid __P((char *)); +void a_uid __P((char *)); +void chownerr __P((char *)); +u_long id __P((char *, char *)); +int main __P((int, char **)); +void usage __P((void)); + +uid_t uid; +gid_t gid; +int Rflag, ischown, fflag; +char *gname, *myname; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p; + int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval; + char *cp; + int (*change_owner) __P((const char *, uid_t, gid_t)); + + setlocale(LC_ALL, ""); + + myname = (cp = strrchr(*argv, '/')) ? cp + 1 : *argv; + ischown = myname[2] == 'o'; + + Hflag = Lflag = Pflag = hflag = 0; + while ((ch = getopt(argc, argv, "HLPRfh")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + break; + case 'h': + /* + * In System V the -h option causes chown/chgrp to + * change the owner/group of the symbolic link. + * 4.4BSD's symbolic links didn't have owners/groups, + * so it was an undocumented noop. + * In NetBSD 1.3, lchown(2) is introduced. + */ + hflag = 1; + break; + case '?': + default: + usage(); + } + argv += optind; + argc -= optind; + + if (argc < 2) + usage(); + + fts_options = FTS_PHYSICAL; + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + if (hflag) + errx(1, "the -L and -h options may not be specified together."); + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } +#ifndef __APPLE__ + if (hflag) + change_owner = lchown; + else + change_owner = chown; +#else + change_owner = chown; +#endif + + uid = gid = -1; + if (ischown) { +#ifdef SUPPORT_DOT + if ((cp = strchr(*argv, '.')) != NULL) { + *cp++ = '\0'; + a_gid(cp); + } else +#endif + if ((cp = strchr(*argv, ':')) != NULL) { + *cp++ = '\0'; + a_gid(cp); + } + a_uid(*argv); + } else + a_gid(*argv); + + if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) + err(1, "%s", ""); + + for (rval = 0; (p = fts_read(ftsp)) != NULL;) { + switch (p->fts_info) { + case FTS_D: + if (!Rflag) /* Change it at FTS_DP. */ + fts_set(ftsp, p, FTS_SKIP); + continue; + case FTS_DNR: /* Warn, chown, continue. */ + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + case FTS_ERR: /* Warn, continue. */ + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + continue; + case FTS_SL: /* Ignore. */ + case FTS_SLNONE: + /* + * The only symlinks that end up here are ones that + * don't point to anything and ones that we found + * doing a physical walk. + */ +#ifndef __APPLE__ + if (!hflag) + continue; +#else + continue; +#endif + /* else */ + /* FALLTHROUGH */ + default: + break; + } + + if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) { + warn("%s", p->fts_path); + rval = 1; + } + } + if (errno) + err(1, "fts_read"); + exit(rval); +} + +void +a_gid(s) + char *s; +{ + struct group *gr; + + if (*s == '\0') /* Argument was "uid[:.]". */ + return; + gname = s; + gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; +} + +void +a_uid(s) + char *s; +{ + struct passwd *pw; + + if (*s == '\0') /* Argument was "[:.]gid". */ + return; + uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; +} + +u_long +id(name, type) + char *name, *type; +{ + u_long val; + char *ep; + + /* + * XXX + * We know that uid_t's and gid_t's are unsigned longs. + */ + errno = 0; + val = strtoul(name, &ep, 10); + if (errno) + err(1, "%s", name); + if (*ep != '\0') + errx(1, "%s: invalid %s name", name, type); + return (val); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: %s [-R [-H | -L | -P]] [-fh] %s file ...\n", + myname, ischown ? "[owner][:group]" : "group"); + exit(1); +} diff --git a/cksum/crc.c b/cksum/crc.c new file mode 100644 index 0000000..ac889d6 --- /dev/null +++ b/cksum/crc.c @@ -0,0 +1,149 @@ +/* $NetBSD: crc.c,v 1.8 1997/10/17 11:37:03 lukem Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; +#else +__RCSID("$NetBSD: crc.c,v 1.8 1997/10/17 11:37:03 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include "extern.h" + +static const u_int32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. + */ +u_int32_t crc_total = ~0; /* The crc over a number of files. */ + +int +crc(fd, cval, clen) + register int fd; + u_int32_t *cval, *clen; +{ + register u_char *p; + register int nr; + register u_int32_t crc, len; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + crc = len = 0; + crc_total = ~crc_total; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(crc, *p); + COMPUTE(crc_total, *p); + } + if (nr < 0) + return (1); + + *clen = len; + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(crc, len & 0xff); + COMPUTE(crc_total, len & 0xff); + } + + *cval = ~crc; + crc_total = ~crc_total; + return (0); +} diff --git a/compress/Makefile b/compress/Makefile new file mode 100644 index 0000000..0c13072 --- /dev/null +++ b/compress/Makefile @@ -0,0 +1,49 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = compress + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = compress.c zopen.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble compress.1\ + zopen.3 uncompress.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/compress/Makefile.postamble b/compress/Makefile.postamble new file mode 100644 index 0000000..cb8937b --- /dev/null +++ b/compress/Makefile.postamble @@ -0,0 +1,5 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common + +after_install:: + $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/uncompress +# $(LINKPRODUCT) $(DSTROOT)$(INSTALLDIR)/zcat diff --git a/compress/Makefile.preamble b/compress/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/compress/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/compress/PB.project b/compress/PB.project new file mode 100644 index 0000000..d9c8269 --- /dev/null +++ b/compress/PB.project @@ -0,0 +1,32 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (compress.c, zopen.c); + OTHER_SOURCES = ( + Makefile, + Makefile.preamble, + Makefile.postamble, + compress.1, + zopen.3, + uncompress.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 = compress; + 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/compress/compress.1 b/compress/compress.1 new file mode 100644 index 0000000..2534855 --- /dev/null +++ b/compress/compress.1 @@ -0,0 +1,176 @@ +.\" $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. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" James A. Woods, derived from original work by Spencer Thomas +.\" and Joseph Orost. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 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. +.\" +.\" @(#)compress.1 8.2 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt COMPRESS 1 +.Os BSD 4.3 +.Sh NAME +.Nm compress , +.\".Nm uncompress , +.Nm uncompress +.\".Nm zcat +.Nd compress and expand data +.Sh SYNOPSIS +.Nm +.Op Fl cfv +.Op Fl b Ar bits +.Op Ar +.Nm uncompress +.Op Fl cfv +.Op Ar +.\".Nm zcat +.\".Op Ar +.Sh DESCRIPTION +.Nm +reduces the size of the named files using adaptive Lempel-Ziv coding. +Each +.Ar file +is renamed to the same name plus the extension +.Dq .Z . +As many of the modification time, access time, file flags, file mode, +user ID, and group ID as allowed by permissions are retained in the +new file. +If compression would not reduce the size of a +.Ar file , +the file is ignored. +.Pp +.Nm uncompress +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 +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 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 +.It Fl b +Specify the +.Ar bits +code limit (see below). +.It Fl c +Compressed or uncompressed output is written to the standard output. +No files are modified. +.It Fl f +Force compression of +.Ar file , +even if it is not actually reduced in size. +Additionally, files are overwritten without prompting for confirmation. +.It Fl v +Print the percentage reduction of each file. +.El +.Pp +.Nm +uses a modified Lempel-Ziv algorithm. +Common substrings in the file are first replaced by 9-bit codes 257 and up. +When code 512 is reached, the algorithm switches to 10-bit codes and +continues to use more bits until the +limit specified by the +.Fl b +flag is reached (the default is 16). +.Ar Bits +must be between 9 and 16. +.Pp +After the +.Ar bits +limit is reached, +.Nm +periodically checks the compression ratio. +If it is increasing, +.Nm +continues to use the existing code dictionary. +However, if the compression ratio decreases, +.Nm +discards the table of substrings and rebuilds it from scratch. This allows +the algorithm to adapt to the next "block" of the file. +.Pp +The +.Fl b +flag is omitted for +.Ar uncompress +since the +.Ar bits +parameter specified during compression +is encoded within the output, along with +a magic number to ensure that neither decompression of random data nor +recompression of compressed data is attempted. +.Pp +.ne 8 +The amount of compression obtained depends on the size of the +input, the number of +.Ar bits +per code, and the distribution of common substrings. +Typically, text such as source code or English is reduced by 50\-60%. +Compression is generally much better than that achieved by Huffman +coding (as used in the historical command pack), or adaptive Huffman +coding (as used in the historical command compact), and takes less +time to compute. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr zcat 1 +.Rs +.%A Welch, Terry A. +.%D June, 1984 +.%T "A Technique for High Performance Data Compression" +.%J "IEEE Computer" +.%V 17:6 +.%P pp. 8-19 +.Re +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/compress/compress.c b/compress/compress.c new file mode 100644 index 0000000..2aa84ee --- /dev/null +++ b/compress/compress.c @@ -0,0 +1,491 @@ +/* $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. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +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 */ + +#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)); + +int main __P((int, char *[])); +extern FILE *zopen __P((const char *fname, const char *mode, int bits)); + +int eval, force, verbose; +int isstdout, isstdin; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + enum {COMPRESS, DECOMPRESS} style = COMPRESS; + size_t len; + int bits, cat, ch; + char *p, newname[MAXPATHLEN]; + + if ((p = strrchr(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 + errx(1, "unknown program name"); + + bits = cat = 0; + while ((ch = getopt(argc, argv, "b:cdfv")) != -1) + switch(ch) { + case 'b': + bits = strtol(optarg, &p, 10); + if (*p) + errx(1, "illegal bit count -- %s", optarg); + break; + case 'c': + cat = 1; + break; + case 'd': /* Backward compatible. */ + style = DECOMPRESS; + break; + case 'f': + force = 1; + break; + case 'v': + verbose = 1; + break; + case '?': + default: + usage(style == COMPRESS); + } + argc -= optind; + argv += optind; + + if (argc == 0) { + 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; + } + exit (eval); + } + + if (cat == 1 && argc > 1) + errx(1, "the -c option permits only a single file argument"); + + for (; *argv; ++argv) { + isstdout = 0; + switch(style) { + case COMPRESS: + if (cat) { + isstdout = 1; + compress(*argv, "/dev/stdout", bits); + break; + } + if ((p = strrchr(*argv, '.')) != NULL && + !strcmp(p, ".Z")) { + cwarnx("%s: name already has trailing .Z", + *argv); + break; + } + len = strlen(*argv); + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + compress(*argv, newname, bits); + break; + case DECOMPRESS: + len = strlen(*argv); + if ((p = strrchr(*argv, '.')) == NULL || + strcmp(p, ".Z")) { + if (len > sizeof(newname) - 3) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len); + newname[len] = '.'; + newname[len + 1] = 'Z'; + newname[len + 2] = '\0'; + decompress(newname, + cat ? "/dev/stdout" : *argv, bits); + if (cat) + isstdout = 1; + } else { + if (len - 2 > sizeof(newname) - 1) { + cwarnx("%s: name too long", *argv); + break; + } + memmove(newname, *argv, len - 2); + newname[len - 2] = '\0'; + decompress(*argv, + cat ? "/dev/stdout" : newname, bits); + if (cat) + isstdout = 1; + } + break; + } + } + exit (eval); +} + +void +compress(in, out, bits) + char *in, *out; + int bits; +{ + int nr; + struct stat isb, 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; + + 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 + isreg = 0; + + if ((ofp = zopen(out, "w", bits)) == NULL) { + cwarn("%s", out); + goto err; + } + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + ofp = NULL; + + if (isreg && oreg) { + if (stat(out, &sb)) { + cwarn("%s", out); + goto err; + } + + if (!force && sb.st_size >= isb.st_size) { + if (verbose) + (void)printf("%s: file would grow; left unmodified\n", in); + if (unlink(out)) + cwarn("%s", out); + goto err; + } + + setfile(out, &isb); + + if (unlink(in)) + cwarn("%s", in); + + if (verbose) { + (void)printf("%s: ", out); + if (isb.st_size > sb.st_size) + (void)printf("%.0f%% compression\n", + ((double)sb.st_size / isb.st_size) * 100.0); + else + (void)printf("%.0f%% expansion\n", + ((double)isb.st_size / sb.st_size) * 100.0); + } + } + return; + +err: if (ofp) { + if (oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +decompress(in, out, bits) + char *in, *out; + int bits; +{ + int 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; + + ifp = ofp = NULL; + if ((ofp = fopen(out, "w")) == NULL) { + cwarn("%s", out); + return; + } + + if ((ifp = zopen(in, "r", bits)) == NULL) { + 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 + isreg = 0; + + while ((nr = fread(buf, 1, sizeof(buf), ifp)) != 0) + if (fwrite(buf, 1, nr, ofp) != nr) { + cwarn("%s", out); + goto err; + } + + if (ferror(ifp) || fclose(ifp)) { + cwarn("%s", in); + goto err; + } + ifp = NULL; + + if (fclose(ofp)) { + cwarn("%s", out); + goto err; + } + + if (isreg && oreg) { + setfile(out, &sb); + + if (unlink(in)) + cwarn("%s", in); + } + return; + +err: if (ofp) { + if (oreg) + (void)unlink(out); + (void)fclose(ofp); + } + if (ifp) + (void)fclose(ifp); +} + +void +setfile(name, fs) + char *name; + struct stat *fs; +{ + static struct timeval tv[2]; + + fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + if (utimes(name, tv)) + cwarn("utimes: %s", name); + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (chown(name, fs->st_uid, fs->st_gid)) { + if (errno != EPERM) + cwarn("chown: %s", name); + fs->st_mode &= ~(S_ISUID|S_ISGID); + } + if (chmod(name, fs->st_mode)) + cwarn("chown: %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)) + cwarn("chflags: %s", name); +} + +int +permission(fname) + char *fname; +{ + int ch, first; + + if (!isatty(fileno(stderr))) + return (0); + (void)fprintf(stderr, "overwrite %s? ", fname); + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y'); +} + +void +usage(iscompress) + 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"); + 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/uncompress.1 b/compress/uncompress.1 new file mode 100644 index 0000000..208c3ce --- /dev/null +++ b/compress/uncompress.1 @@ -0,0 +1 @@ +.so man1/compress.1 \ No newline at end of file diff --git a/compress/zopen.3 b/compress/zopen.3 new file mode 100644 index 0000000..1e75687 --- /dev/null +++ b/compress/zopen.3 @@ -0,0 +1,142 @@ +.\" $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. +.\" +.\" 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. +.\" +.\" @(#)zopen.3 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt ZOPEN 3 +.Os +.Sh NAME +.Nm zopen +.Nd compressed stream open function +.Sh SYNOPSIS +.Fd #include +.Ft FILE * +.Fn zopen "const char *path" "const char *mode" "int bits" +.Sh DESCRIPTION +The +.Fn zopen +function +opens the compressed file whose name is the string pointed to by +.Fa path +and associates a stream with it. +.Pp +The argument +.Fa mode +points to one of the following one-character strings: +.Bl -tag -width indent +.It Dq Li r +Open compressed file for reading. +The stream is positioned at the beginning of the file. +.It Dq Li w +Truncate file to zero length or create compressed file for writing. +The stream is positioned at the beginning of the file. +.El +.Pp +Any created files will have mode +.Pf \\*q Dv S_IRUSR +\&| +.Dv S_IWUSR +\&| +.Dv S_IRGRP +\&| +.Dv S_IWGRP +\&| +.Dv S_IROTH +\&| +.Dv S_IWOTH Ns \\*q +.Pq Li 0666 , +as modified by the process' +umask value (see +.Xr umask 2 ) . +.Pp +Files may only be read or written. +Seek operations are not allowed. +.Pp +The +.Fa bits +argument, if non-zero, is set to the bits code limit. +If zero, the default is 16. +See +.Fn compress 1 +for more information. +.Sh RETURN VALUES +Upon successful completion +.Fn zopen +returns a +.Tn FILE +pointer. +Otherwise, +.Dv NULL +is returned and the global variable +.Va errno +is set to indicate the error. +.Sh ERRORS +.Bl -tag -width [EINVAL] +.It Bq Er EINVAL +The +.Fa mode +or +.Fa bits +arguments specified to +.Fn zopen +were invalid. +.It Bq Er EFTYPE +The compressed file starts with an invalid header, or the compressed +file is compressed with more bits than can be handled. +.El +.Pp +The +.Fn zopen +function may also fail and set +.Va errno +for any of the errors specified for the routines +.Xr fopen 3 +or +.Xr funopen 3 . +.Sh SEE ALSO +.Xr compress 1 , +.Xr fopen 3 , +.Xr funopen 3 +.Sh HISTORY +The +.Nm zopen +function +first appeared in +.Bx 4.4 . +.Sh BUGS +The +.Fn zopen +function +may not be portable to systems other than +.Bx . diff --git a/compress/zopen.c b/compress/zopen.c new file mode 100644 index 0000000..c165117 --- /dev/null +++ b/compress/zopen.c @@ -0,0 +1,748 @@ +/* $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. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis and James A. Woods, derived from original + * work by Spencer Thomas and Joseph Orost. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 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 */ + +/*- + * fcompress.c - File compression ala IEEE Computer, June 1984. + * + * Compress authors: + * Spencer W. Thomas (decvax!utah-cs!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + * Cleaned up and converted to library returning I/O streams by + * Diomidis Spinellis . + * + * zopen(filename, mode, bits) + * Returns a FILE * that can be used for read or write. The modes + * supported are only "r" and "w". Seeking is not allowed. On + * reading the file is decompressed, on writing it is compressed. + * The output is compatible with compress(1) with 16 bit tables. + * Any file produced by compress(1) can be read. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#define BITS 16 /* Default bits. */ +#define HSIZE 69001 /* 95% occupancy */ + +/* A code_int must be able to hold 2**BITS values of type int, and also -1. */ +typedef long code_int; +typedef long count_int; + +typedef u_char char_type; +static char_type magic_header[] = + {'\037', '\235'}; /* 1F 9D */ + +#define BIT_MASK 0x1f /* Defines for third byte of header. */ +#define BLOCK_MASK 0x80 + +/* + * Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + * a fourth header byte (for expansion). + */ +#define INIT_BITS 9 /* Initial number of bits/code. */ + +#define MAXCODE(n_bits) ((1 << (n_bits)) - 1) + +struct s_zstate { + FILE *zs_fp; /* File stream for I/O */ + char zs_mode; /* r or w */ + enum { + S_START, S_MIDDLE, S_EOF + } zs_state; /* State of computation */ + int zs_n_bits; /* Number of bits/code. */ + int zs_maxbits; /* User settable max # bits/code. */ + code_int zs_maxcode; /* Maximum code, given n_bits. */ + code_int zs_maxmaxcode; /* Should NEVER generate this code. */ + count_int zs_htab [HSIZE]; + u_short zs_codetab [HSIZE]; + code_int zs_hsize; /* For dynamic table sizing. */ + code_int zs_free_ent; /* First unused entry. */ + /* + * Block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ + int zs_block_compress; + int zs_clear_flg; + long zs_ratio; + count_int zs_checkpoint; + int zs_offset; + long zs_in_count; /* Length of input. */ + long zs_bytes_out; /* Length of compressed output. */ + long zs_out_count; /* # of codes output (for debugging). */ + char_type zs_buf[BITS]; + union { + struct { + long zs_fcode; + code_int zs_ent; + code_int zs_hsize_reg; + int zs_hshift; + } w; /* Write paramenters */ + struct { + char_type *zs_stackp; + int zs_finchar; + code_int zs_code, zs_oldcode, zs_incode; + int zs_roffset, zs_size; + char_type zs_gbuf[BITS]; + } r; /* Read parameters */ + } u; +}; + +/* Definitions to retain old variable names */ +#define fp zs->zs_fp +#define zmode zs->zs_mode +#define state zs->zs_state +#define n_bits zs->zs_n_bits +#define maxbits zs->zs_maxbits +#define maxcode zs->zs_maxcode +#define maxmaxcode zs->zs_maxmaxcode +#define htab zs->zs_htab +#define codetab zs->zs_codetab +#define hsize zs->zs_hsize +#define free_ent zs->zs_free_ent +#define block_compress zs->zs_block_compress +#define clear_flg zs->zs_clear_flg +#define ratio zs->zs_ratio +#define checkpoint zs->zs_checkpoint +#define offset zs->zs_offset +#define in_count zs->zs_in_count +#define bytes_out zs->zs_bytes_out +#define out_count zs->zs_out_count +#define buf zs->zs_buf +#define fcode zs->u.w.zs_fcode +#define hsize_reg zs->u.w.zs_hsize_reg +#define ent zs->u.w.zs_ent +#define hshift zs->u.w.zs_hshift +#define stackp zs->u.r.zs_stackp +#define finchar zs->u.r.zs_finchar +#define code zs->u.r.zs_code +#define oldcode zs->u.r.zs_oldcode +#define incode zs->u.r.zs_incode +#define roffset zs->u.r.zs_roffset +#define size zs->u.r.zs_size +#define gbuf zs->u.r.zs_gbuf + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type as + * the codetab. The tab_suffix table needs 2**BITS characters. We get this + * from the beginning of htab. The output stack uses the rest of htab, and + * contains characters. There is plenty of room for any possible stack + * (stack used to be 8000 characters). + */ + +#define htabof(i) htab[i] +#define codetabof(i) codetab[i] + +#define tab_prefixof(i) codetabof(i) +#define tab_suffixof(i) ((char_type *)(htab))[i] +#define de_stack ((char_type *)&tab_suffixof(1 << BITS)) + +#define CHECK_GAP 10000 /* Ratio check interval. */ + +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* First free entry. */ +#define CLEAR 256 /* Table clear output code. */ + +static int cl_block __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)); + +/*- + * Algorithm from "A Technique for High Performance Data Compression", + * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. + * + * Algorithm: + * Modified Lempel-Ziv method (LZW). Basically finds common + * substrings and replaces them with a variable size code. This is + * deterministic, and can be done on the fly. Thus, the decompression + * procedure needs no input table, but tracks the way the table was built. + */ + +/*- + * compress write + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ +static int +zwrite(cookie, wbp, num) + void *cookie; + const char *wbp; + int num; +{ + code_int i; + int c, disp; + struct s_zstate *zs; + const u_char *bp; + u_char tmp; + int count; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)wbp; + if (state == S_MIDDLE) + goto middle; + state = S_MIDDLE; + + maxmaxcode = 1L << maxbits; + if (fwrite(magic_header, + sizeof(char), sizeof(magic_header), fp) != sizeof(magic_header)) + return (-1); + tmp = (u_char)(maxbits | block_compress); + if (fwrite(&tmp, sizeof(char), sizeof(tmp), fp) != sizeof(tmp)) + return (-1); + + offset = 0; + bytes_out = 3; /* Includes 3-byte header mojo. */ + out_count = 0; + clear_flg = 0; + ratio = 0; + in_count = 1; + checkpoint = CHECK_GAP; + maxcode = MAXCODE(n_bits = INIT_BITS); + free_ent = ((block_compress) ? FIRST : 256); + + ent = *bp++; + --count; + + hshift = 0; + for (fcode = (long)hsize; fcode < 65536L; fcode *= 2L) + hshift++; + hshift = 8 - hshift; /* Set hash code range bound. */ + + hsize_reg = hsize; + cl_hash(zs, (count_int)hsize_reg); /* Clear hash table. */ + +middle: for (i = 0; count--;) { + c = *bp++; + in_count++; + fcode = (long)(((long)c << maxbits) + ent); + i = ((c << hshift) ^ ent); /* Xor hashing. */ + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } else if ((long)htabof(i) < 0) /* Empty slot. */ + goto nomatch; + disp = hsize_reg - i; /* Secondary hash (after G. Knott). */ + if (i == 0) + disp = 1; +probe: if ((i -= disp) < 0) + i += hsize_reg; + + if (htabof(i) == fcode) { + ent = codetabof(i); + continue; + } + if ((long)htabof(i) >= 0) + goto probe; +nomatch: if (output(zs, (code_int) ent) == -1) + return (-1); + out_count++; + ent = c; + if (free_ent < maxmaxcode) { + codetabof(i) = free_ent++; /* code -> hashtable */ + htabof(i) = fcode; + } else if ((count_int)in_count >= + checkpoint && block_compress) { + if (cl_block(zs) == -1) + return (-1); + } + } + return (num); +} + +static int +zclose(cookie) + void *cookie; +{ + struct s_zstate *zs; + int rval; + + zs = cookie; + if (zmode == 'w') { /* Put out the final code. */ + if (output(zs, (code_int) ent) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + out_count++; + if (output(zs, (code_int) - 1) == -1) { + (void)fclose(fp); + free(zs); + return (-1); + } + } + rval = fclose(fp) == EOF ? -1 : 0; + free(zs); + return (rval); +} + +/*- + * Output the given code. + * Inputs: + * code: A n_bits-bit integer. If == -1, then EOF. This assumes + * that n_bits =< (long)wordsize - 1. + * Outputs: + * Outputs code to the file. + * Assumptions: + * Chars are 8 bits long. + * Algorithm: + * Maintain a BITS character long buffer (so that 8 codes will + * fit in it exactly). Use the VAX insv instruction to insert each + * code in turn. When the buffer fills up empty it and start over. + */ + +static char_type lmask[9] = + {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; +static char_type rmask[9] = + {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; + +static int +output(zs, ocode) + struct s_zstate *zs; + code_int ocode; +{ + int bits, r_off; + char_type *bp; + + r_off = offset; + bits = n_bits; + bp = buf; + if (ocode >= 0) { + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + /* + * Since ocode is always >= 8 bits, only need to mask the first + * hunk on the left. + */ + *bp = (*bp & rmask[r_off]) | ((ocode << r_off) & lmask[r_off]); + bp++; + bits -= (8 - r_off); + ocode >>= 8 - r_off; + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + *bp++ = ocode; + ocode >>= 8; + bits -= 8; + } + /* Last bits. */ + if (bits) + *bp = ocode; + offset += n_bits; + if (offset == (n_bits << 3)) { + bp = buf; + bits = n_bits; + bytes_out += bits; + if (fwrite(bp, sizeof(char), bits, fp) != bits) + return (-1); + bp += bits; + bits = 0; + offset = 0; + } + /* + * If the next entry is going to be too big for the ocode size, + * then increase it, if possible. + */ + if (free_ent > maxcode || (clear_flg > 0)) { + /* + * Write the whole buffer, because the input side won't + * discover the size increase until after it has read it. + */ + if (offset > 0) { + if (fwrite(buf, 1, n_bits, fp) != n_bits) + return (-1); + bytes_out += n_bits; + } + offset = 0; + + if (clear_flg) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } else { + n_bits++; + if (n_bits == maxbits) + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + } + } else { + /* At EOF, write the rest of the buffer. */ + if (offset > 0) { + offset = (offset + 7) / 8; + if (fwrite(buf, 1, offset, fp) != offset) + return (-1); + bytes_out += offset; + } + offset = 0; + } + return (0); +} + +/* + * Decompress read. This routine adapts to the codes in the file building + * the "string" table on-the-fly; requiring no table to be stored in the + * compressed file. The tables used herein are shared with those of the + * compress() routine. See the definitions above. + */ +static int +zread(cookie, rbp, num) + void *cookie; + char *rbp; + int num; +{ + u_int count; + struct s_zstate *zs; + u_char *bp, header[3]; + + if (num == 0) + return (0); + + zs = cookie; + count = num; + bp = (u_char *)rbp; + switch (state) { + case S_START: + state = S_MIDDLE; + break; + case S_MIDDLE: + goto middle; + case S_EOF: + goto eof; + } + + /* Check the magic number */ + if (fread(header, + sizeof(char), sizeof(header), fp) != sizeof(header) || + memcmp(header, magic_header, sizeof(magic_header)) != 0) { + errno = EFTYPE; + return (-1); + } + maxbits = header[2]; /* Set -b from file. */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1L << maxbits; + if (maxbits > BITS) { + errno = EFTYPE; + return (-1); + } + /* As above, initialize the first 256 entries in the table. */ + maxcode = MAXCODE(n_bits = INIT_BITS); + for (code = 255; code >= 0; code--) { + tab_prefixof(code) = 0; + tab_suffixof(code) = (char_type) code; + } + free_ent = block_compress ? FIRST : 256; + + finchar = oldcode = getcode(zs); + if (oldcode == -1) /* EOF already? */ + return (0); /* Get out of here */ + + /* First code must be 8 bits = char. */ + *bp++ = (u_char)finchar; + count--; + stackp = de_stack; + + while ((code = getcode(zs)) > -1) { + + if ((code == CLEAR) && block_compress) { + for (code = 255; code >= 0; code--) + tab_prefixof(code) = 0; + clear_flg = 1; + free_ent = FIRST - 1; + if ((code = getcode(zs)) == -1) /* O, untimely death! */ + break; + } + incode = code; + + /* Special case for KwKwK string. */ + if (code >= free_ent) { + *stackp++ = finchar; + code = oldcode; + } + + /* Generate output characters in reverse order. */ + while (code >= 256) { + *stackp++ = tab_suffixof(code); + code = tab_prefixof(code); + } + *stackp++ = finchar = tab_suffixof(code); + + /* And put them out in forward order. */ +middle: do { + if (count-- == 0) + return (num); + *bp++ = *--stackp; + } while (stackp > de_stack); + + /* Generate the new entry. */ + if ((code = free_ent) < maxmaxcode) { + tab_prefixof(code) = (u_short) oldcode; + tab_suffixof(code) = finchar; + free_ent = code + 1; + } + + /* Remember previous code. */ + oldcode = incode; + } + state = S_EOF; +eof: return (num - count); +} + +/*- + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ +static code_int +getcode(zs) + struct s_zstate *zs; +{ + code_int gcode; + int r_off, bits; + char_type *bp; + + bp = gbuf; + if (clear_flg > 0 || roffset >= size || free_ent > maxcode) { + /* + * If the next entry will be too big for the current gcode + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if (free_ent > maxcode) { + n_bits++; + if (n_bits == maxbits) /* Won't get any bigger now. */ + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } + if (clear_flg > 0) { + maxcode = MAXCODE(n_bits = INIT_BITS); + clear_flg = 0; + } + size = fread(gbuf, 1, n_bits, fp); + if (size <= 0) /* End of file. */ + return (-1); + roffset = 0; + /* Round size down to integral number of codes. */ + size = (size << 3) - (n_bits - 1); + } + r_off = roffset; + bits = n_bits; + + /* Get to the first byte. */ + bp += (r_off >> 3); + r_off &= 7; + + /* Get first part (low order bits). */ + gcode = (*bp++ >> r_off); + bits -= (8 - r_off); + r_off = 8 - r_off; /* Now, roffset into gcode word. */ + + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if (bits >= 8) { + gcode |= *bp++ << r_off; + r_off += 8; + bits -= 8; + } + + /* High order bits. */ + gcode |= (*bp & rmask[bits]) << r_off; + roffset += n_bits; + + return (gcode); +} + +static int +cl_block(zs) /* Table clear for block compress. */ + struct s_zstate *zs; +{ + long rat; + + checkpoint = in_count + CHECK_GAP; + + if (in_count > 0x007fffff) { /* Shift will overflow. */ + rat = bytes_out >> 8; + if (rat == 0) /* Don't divide by zero. */ + rat = 0x7fffffff; + else + rat = in_count / rat; + } else + rat = (in_count << 8) / bytes_out; /* 8 fractional bits. */ + if (rat > ratio) + ratio = rat; + else { + ratio = 0; + cl_hash(zs, (count_int) hsize); + free_ent = FIRST; + clear_flg = 1; + if (output(zs, (code_int) CLEAR) == -1) + return (-1); + } + return (0); +} + +static void +cl_hash(zs, cl_hsize) /* Reset code table. */ + struct s_zstate *zs; + count_int cl_hsize; +{ + count_int *htab_p; + long i, m1; + + m1 = -1; + htab_p = htab + cl_hsize; + i = cl_hsize - 16; + do { /* Might use Sys V memset(3) here. */ + *(htab_p - 16) = m1; + *(htab_p - 15) = m1; + *(htab_p - 14) = m1; + *(htab_p - 13) = m1; + *(htab_p - 12) = m1; + *(htab_p - 11) = m1; + *(htab_p - 10) = m1; + *(htab_p - 9) = m1; + *(htab_p - 8) = m1; + *(htab_p - 7) = m1; + *(htab_p - 6) = m1; + *(htab_p - 5) = m1; + *(htab_p - 4) = m1; + *(htab_p - 3) = m1; + *(htab_p - 2) = m1; + *(htab_p - 1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); + for (i += 16; i > 0; i--) + *--htab_p = m1; +} + +FILE * +zopen(fname, mode, bits) + const char *fname, *mode; + int bits; +{ + struct s_zstate *zs; + + if ((mode[0] != 'r' && mode[0] != 'w') || mode[1] != '\0' || + bits < 0 || bits > BITS) { + errno = EINVAL; + return (NULL); + } + + if ((zs = calloc(1, sizeof(struct s_zstate))) == NULL) + return (NULL); + + maxbits = bits ? bits : BITS; /* User settable max # bits/code. */ + maxmaxcode = 1 << maxbits; /* Should NEVER generate this code. */ + hsize = HSIZE; /* For dynamic table sizing. */ + free_ent = 0; /* First unused entry. */ + block_compress = BLOCK_MASK; + clear_flg = 0; + ratio = 0; + checkpoint = CHECK_GAP; + in_count = 1; /* Length of input. */ + out_count = 0; /* # of codes output (for debugging). */ + state = S_START; + roffset = 0; + size = 0; + + /* + * Layering compress on top of stdio in order to provide buffering, + * and ensure that reads and write work with the data specified. + */ + if ((fp = fopen(fname, mode)) == NULL) { + free(zs); + return (NULL); + } + switch (*mode) { + case 'r': + zmode = 'r'; + return (funopen(zs, zread, NULL, NULL, zclose)); + case 'w': + zmode = 'w'; + return (funopen(zs, NULL, zwrite, NULL, zclose)); + } + /* NOTREACHED */ + return (NULL); +} diff --git a/cp/Makefile b/cp/Makefile new file mode 100644 index 0000000..8d778e8 --- /dev/null +++ b/cp/Makefile @@ -0,0 +1,51 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = cp + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = extern.h + +CFILES = cp.c utils.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble cp.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + +NEXTSTEP_PB_CFLAGS = -DVM_AND_BUFFER_CACHE_SYNCHRONIZED + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/cp/Makefile.postamble b/cp/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/cp/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/cp/Makefile.preamble b/cp/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/cp/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/cp/PB.project b/cp/PB.project new file mode 100644 index 0000000..e3be88f --- /dev/null +++ b/cp/PB.project @@ -0,0 +1,27 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (extern.h); + OTHER_LINKED = (cp.c, utils.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, cp.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_COMPILEROPTIONS = "-DVM_AND_BUFFER_CACHE_SYNCHRONIZED"; + NEXTSTEP_INSTALLDIR = /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 = cp; + 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/cp/cp.1 b/cp/cp.1 new file mode 100644 index 0000000..e5016c8 --- /dev/null +++ b/cp/cp.1 @@ -0,0 +1,215 @@ +.\" $NetBSD: cp.1,v 1.12 1997/10/11 02:14:42 enami Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)cp.1 8.3 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt CP 1 +.Os BSD 4 +.Sh NAME +.Nm cp +.Nd copy files +.Sh SYNOPSIS +.Nm +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl f | i +.Op Fl p +.Ar source_file target_file +.Nm cp +.Oo +.Fl R +.Op Fl H | Fl L | Fl P +.Oc +.Op Fl f | i +.Op Fl p +.Ar source_file ... target_directory +.Sh DESCRIPTION +In the first synopsis form, the +.Nm +utility copies the contents of the +.Ar source_file +to the +.Ar target_file . +In the second synopsis form, +the contents of each named +.Ar source_file +is copied to the destination +.Ar target_directory . +The names of the files themselves are not changed. +If +.Nm +detects an attempt to copy a file to itself, the copy will fail. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl H +If the +.Fl R +option is specified, symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +If the +.Fl R +option is specified, all symbolic links are followed. +.It Fl P +If the +.Fl R +option is specified, no symbolic links are followed. +.It Fl R +If +.Ar source_file +designates a directory, +.Nm +copies the directory and the entire subtree connected at that point. +This option also causes symbolic links to be copied, rather than +indirected through, and for +.Nm +to create special files rather than copying them as normal files. +Created directories have the same mode as the corresponding source +directory, unmodified by the process' umask. +.It Fl f +Foreach existing destination pathname, attempt to overwrite it. If permissions +do not allow copy to succeed, remove it and create a new file, without +prompting for confirmation. +(The +.Fl i +option is ignored if the +.Fl f +option is specified.) +.It Fl i +Causes +.Nm +to write a prompt to the standard error output before copying a file +that would overwrite an existing file. +If the response from the standard input begins with the character +.Sq Li y , +the file copy is attempted. +.It Fl p +Causes +.Nm +to preserve in the copy as many of the modification time, access time, +file flags, file mode, user ID, and group ID as allowed by permissions. +.Pp +If the user ID and group ID cannot be preserved, no error message +is displayed and the exit value is not altered. +.Pp +If the source file has its set user ID bit on and the user ID cannot +be preserved, the set user ID bit is not preserved +in the copy's permissions. +If the source file has its set group ID bit on and the group ID cannot +be preserved, the set group ID bit is not preserved +in the copy's permissions. +If the source file has both its set user ID and set group ID bits on, +and either the user ID or group ID cannot be preserved, neither +the set user ID or set group ID bits are preserved in the copy's +permissions. +.El +.Pp +For each destination file that already exists, its contents are +overwritten if permissions allow, but its mode, user ID, and group +ID are unchanged. +.Pp +In the second synopsis form, +.Ar target_directory +must exist unless there is only one named +.Ar source_file +which is a directory and the +.Fl R +flag is specified. +.Pp +If the destination file does not exist, the mode of the source file is +used as modified by the file mode creation mask +.Pf ( Ic umask , +see +.Xr csh 1 ) . +If the source file has its set user ID bit on, that bit is removed +unless both the source file and the destination file are owned by the +same user. +If the source file has its set group ID bit on, that bit is removed +unless both the source file and the destination file are in the same +group and the user is a member of that group. +If both the set user ID and set group ID bits are set, all of the above +conditions must be fulfilled or both bits are removed. +.Pp +Appropriate permissions are required for file creation or overwriting. +.Pp +Symbolic links are always followed unless the +.Fl R +flag is set, in which case symbolic links are not followed, by default. +The +.Fl H +or +.Fl L +flags (in conjunction with the +.Fl R +flag) cause symbolic links to be followed as described above. +The +.Fl H , +.Fl L +and +.Fl P +options are ignored unless the +.Fl R +option is specified. +In addition, these options override each other and the +command's actions are determined by the last one specified. +.Pp +.Nm +exits 0 on success, >0 if an error occurred. +.Sh COMPATIBILITY +Historic versions of the +.Nm +utility had a +.Fl r +option. +This implementation supports that option, however, its use is strongly +discouraged, as it does not correctly copy special files, symbolic links +or fifo's. +.Sh SEE ALSO +.Xr mv 1 , +.Xr rcp 1 , +.Xr umask 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. diff --git a/cp/cp.c b/cp/cp.c new file mode 100644 index 0000000..55377bc --- /dev/null +++ b/cp/cp.c @@ -0,0 +1,484 @@ +/* $NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $ */ + +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * David Hitz of Auspex Systems Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cp.c 8.5 (Berkeley) 4/29/95"; +#else +__RCSID("$NetBSD: cp.c,v 1.24 1998/08/19 01:29:11 thorpej Exp $"); +#endif +#endif /* not lint */ + +/* + * Cp copies source files to target files. + * + * The global PATH_T structure "to" always contains the path to the + * current target file. Since fts(3) does not change directories, + * this path can be either absolute or dot-relative. + * + * The basic algorithm is to initialize "to" and use fts(3) to traverse + * the file hierarchy rooted in the argument list. A trivial case is the + * case of 'cp file1 file2'. The more interesting case is the case of + * 'cp file1 file2 ... fileN dir' where the hierarchy is traversed and the + * path (relative to the root of the traversal) is appended to dir (stored + * in "to") to form the final target path. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +#define STRIP_TRAILING_SLASH(p) { \ + while ((p).p_end > (p).p_path + 1 && (p).p_end[-1] == '/') \ + *--(p).p_end = 0; \ +} + +PATH_T to = { to.p_path, "" }; + +uid_t myuid; +int Rflag, iflag, pflag, rflag, fflag; +mode_t myumask; + +enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; + +int main __P((int, char *[])); +int copy __P((char *[], enum op, int)); +int mastercmp __P((const FTSENT **, const FTSENT **)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat to_stat, tmp_stat; + enum op type; + int Hflag, Lflag, Pflag, ch, fts_options, r; + char *target; + + Hflag = Lflag = Pflag = Rflag = 0; + while ((ch = getopt(argc, argv, "HLPRfipr")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'R': + Rflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + iflag = isatty(fileno(stdin)); + fflag = 0; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case '?': + default: + usage(); + break; + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + fts_options = FTS_NOCHDIR | FTS_PHYSICAL; + if (rflag) { + if (Rflag) + errx(1, + "the -R and -r options may not be specified together."); + if (Hflag || Lflag || Pflag) + errx(1, + "the -H, -L, and -P options may not be specified with the -r option."); + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + if (Rflag) { + if (Hflag) + fts_options |= FTS_COMFOLLOW; + if (Lflag) { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + } else { + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + } + + myuid = getuid(); + + /* Copy the umask for explicit mode setting. */ + myumask = umask(0); + (void)umask(myumask); + + /* Save the target base in "to". */ + target = argv[--argc]; + if (strlen(target) > MAXPATHLEN) + errx(1, "%s: name too long", target); + (void)strcpy(to.p_path, target); + to.p_end = to.p_path + strlen(to.p_path); + if (to.p_path == to.p_end) { + *to.p_end++ = '.'; + *to.p_end = 0; + } + STRIP_TRAILING_SLASH(to); + to.target_end = to.p_end; + + /* Set end of argument list for fts(3). */ + argv[argc] = NULL; + + /* + * Cp has two distinct cases: + * + * cp [-R] source target + * cp [-R] source1 ... sourceN directory + * + * In both cases, source can be either a file or a directory. + * + * In (1), the target becomes a copy of the source. That is, if the + * source is a file, the target will be a file, and likewise for + * directories. + * + * In (2), the real target is not directory, but "directory/source". + */ + r = stat(to.p_path, &to_stat); + if (r == -1 && errno != ENOENT) + err(1, "%s", to.p_path); + if (r == -1 || !S_ISDIR(to_stat.st_mode)) { + /* + * Case (1). Target is not a directory. + */ + if (argc > 1) { + usage(); + exit(1); + } + /* + * Need to detect the case: + * cp -R dir foo + * Where dir is a directory and foo does not exist, where + * we want pathname concatenations turned on but not for + * the initial mkdir(). + */ + if (r == -1) { + if (rflag || (Rflag && (Lflag || Hflag))) + r = stat(*argv, &tmp_stat); + else + r = lstat(*argv, &tmp_stat); + if (r == -1) + err(1, "%s", *argv); + + if (S_ISDIR(tmp_stat.st_mode) && (Rflag || rflag)) + type = DIR_TO_DNE; + else + type = FILE_TO_FILE; + } else + type = FILE_TO_FILE; + } else { + /* + * Case (2). Target is a directory. + */ + type = FILE_TO_DIR; + } + + exit (copy(argv, type, fts_options)); + /* NOTREACHED */ +} + +int +copy(argv, type, fts_options) + char *argv[]; + enum op type; + int fts_options; +{ + struct stat to_stat; + FTS *ftsp; + FTSENT *curr; + int base, dne, nlen, rval; + char *p, *tmp; + + base = 0; /* XXX gcc -Wuninitialized (see comment below) */ + + if ((ftsp = fts_open(argv, fts_options, mastercmp)) == NULL) + err(1, argv[0]); + for (rval = 0; (curr = fts_read(ftsp)) != NULL;) { + switch (curr->fts_info) { + case FTS_NS: + case FTS_ERR: + warnx("%s: %s", + curr->fts_path, strerror(curr->fts_errno)); + rval = 1; + continue; + case FTS_DC: /* Warn, continue. */ + warnx("%s: directory causes a cycle", curr->fts_path); + rval = 1; + continue; + } + + /* + * If we are in case (2) or (3) above, we need to append the + * source name to the target name. + */ + if (type != FILE_TO_FILE) { + if ((curr->fts_namelen + + to.target_end - to.p_path + 1) > MAXPATHLEN) { + warnx("%s/%s: name too long (not copied)", + to.p_path, curr->fts_name); + rval = 1; + continue; + } + + /* + * Need to remember the roots of traversals to create + * correct pathnames. If there's a directory being + * copied to a non-existent directory, e.g. + * cp -R a/dir noexist + * the resulting path name should be noexist/foo, not + * noexist/dir/foo (where foo is a file in dir), which + * is the case where the target exists. + * + * Also, check for "..". This is for correct path + * concatentation for paths ending in "..", e.g. + * cp -R .. /tmp + * Paths ending in ".." are changed to ".". This is + * tricky, but seems the easiest way to fix the problem. + * + * XXX + * Since the first level MUST be FTS_ROOTLEVEL, base + * is always initialized. + */ + if (curr->fts_level == FTS_ROOTLEVEL) { + if (type != DIR_TO_DNE) { + p = strrchr(curr->fts_path, '/'); + base = (p == NULL) ? 0 : + (int)(p - curr->fts_path + 1); + + if (!strcmp(&curr->fts_path[base], + "..")) + base += 1; + } else + base = curr->fts_pathlen; + } + + p = &curr->fts_path[base]; + nlen = curr->fts_pathlen - base; + + tmp = to.target_end; + if (*p != '/' && *(tmp - 1) != '/') + *tmp++ = '/'; + *tmp = 0; + + (void)strncat(tmp, p, nlen); + to.p_end = tmp + nlen; + *to.p_end = 0; + STRIP_TRAILING_SLASH(to); + } + + /* Not an error but need to remember it happened */ + if (stat(to.p_path, &to_stat) == -1) + dne = 1; + else { + if (to_stat.st_dev == curr->fts_statp->st_dev && + to_stat.st_ino == curr->fts_statp->st_ino) { + warnx("%s and %s are identical (not copied).", + to.p_path, curr->fts_path); + rval = 1; + if (S_ISDIR(curr->fts_statp->st_mode)) + (void)fts_set(ftsp, curr, FTS_SKIP); + continue; + } + if (!S_ISDIR(curr->fts_statp->st_mode) && + S_ISDIR(to_stat.st_mode)) { + warnx("cannot overwrite directory %s with non-directory %s", + to.p_path, curr->fts_path); + rval = 1; + continue; + } + dne = 0; + } + + switch (curr->fts_statp->st_mode & S_IFMT) { + case S_IFLNK: + if (copy_link(curr, !dne)) + rval = 1; + break; + case S_IFDIR: + if (!Rflag && !rflag) { + if (curr->fts_info == FTS_DP) + warnx("%s is a directory (not copied).", + curr->fts_path); + (void)fts_set(ftsp, curr, FTS_SKIP); + rval = 1; + break; + } + + /* + * Directories get noticed twice: + * In the first pass, create it if needed. + * In the second pass, after the children have been copied, set the permissions. + */ + if (curr->fts_info == FTS_D) /* First pass */ + { + /* + * If the directory doesn't exist, create the new + * one with the from file mode plus owner RWX bits, + * modified by the umask. Trade-off between being + * able to write the directory (if from directory is + * 555) and not causing a permissions race. If the + * umask blocks owner writes, we fail.. + */ + if (dne) { + if (mkdir(to.p_path, + curr->fts_statp->st_mode | S_IRWXU) < 0) + err(1, "%s", to.p_path); + } else if (!S_ISDIR(to_stat.st_mode)) { + errno = ENOTDIR; + err(1, "%s", to.p_path); + } + } + else if (curr->fts_info == FTS_DP) /* Second pass */ + { + /* + * If not -p and directory didn't exist, set it to be + * the same as the from directory, umodified by the + * umask; arguably wrong, but it's been that way + * forever. + */ + if (pflag && setfile(curr->fts_statp, 0)) + rval = 1; + else if (dne) + (void)chmod(to.p_path, + curr->fts_statp->st_mode); + } + else + { + warnx("directory %s encountered when not expected.", curr->fts_path); + rval = 1; + break; + } + + break; + case S_IFBLK: + case S_IFCHR: + if (Rflag) { + if (copy_special(curr->fts_statp, !dne)) + rval = 1; + } else + if (copy_file(curr, dne)) + rval = 1; + break; + case S_IFIFO: + if (Rflag) { + if (copy_fifo(curr->fts_statp, !dne)) + rval = 1; + } else + if (copy_file(curr, dne)) + rval = 1; + break; + default: + if (copy_file(curr, dne)) + rval = 1; + break; + } + } + if (errno) + err(1, "fts_read"); + return (rval); +} + +/* + * mastercmp -- + * The comparison function for the copy order. The order is to copy + * non-directory files before directory files. The reason for this + * is because files tend to be in the same cylinder group as their + * parent directory, whereas directories tend not to be. Copying the + * files first reduces seeking. + */ +int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR || a_info == FTS_NS || a_info == FTS_DNR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR || b_info == FTS_NS || b_info == FTS_DNR) + return (0); + if (a_info == FTS_D) + return (-1); + if (b_info == FTS_D) + return (1); + return (0); +} diff --git a/cp/extern.h b/cp/extern.h new file mode 100644 index 0000000..7ae079e --- /dev/null +++ b/cp/extern.h @@ -0,0 +1,59 @@ +/* $NetBSD: extern.h,v 1.4 1998/07/28 03:47:14 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/1/94 + */ + +typedef struct { + char *p_end; /* pointer to NULL at end of path */ + char *target_end; /* pointer to end of target base */ + char p_path[MAXPATHLEN + 1]; /* pointer to the start of a path */ +} PATH_T; + +extern PATH_T to; +extern uid_t myuid; +extern int iflag, pflag, fflag; +extern mode_t myumask; + +#include + +__BEGIN_DECLS +int copy_fifo __P((struct stat *, int)); +int copy_file __P((FTSENT *, int)); +int copy_link __P((FTSENT *, int)); +int copy_special __P((struct stat *, int)); +int set_utimes __P((const char *, struct stat *)); +int setfile __P((struct stat *, int)); +void usage __P((void)); +__END_DECLS diff --git a/cp/utils.c b/cp/utils.c new file mode 100644 index 0000000..5a61282 --- /dev/null +++ b/cp/utils.c @@ -0,0 +1,356 @@ +/* $NetBSD: utils.c,v 1.15 1998/08/19 01:29:11 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)utils.c 8.3 (Berkeley) 4/1/94"; +#else +__RCSID("$NetBSD: utils.c,v 1.15 1998/08/19 01:29:11 thorpej Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "extern.h" + +int +set_utimes(file, fs) + const char * file; + struct stat * fs; +{ + static struct timeval tv[2]; + + TIMESPEC_TO_TIMEVAL(&tv[0], &fs->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &fs->st_mtimespec); + + if (utimes(file, tv)) { + warn("utimes: %s", file); + return (1); + } + return (0); +} + +int +copy_file(entp, dne) + FTSENT *entp; + int dne; +{ + static char buf[MAXBSIZE]; + struct stat to_stat, *fs; + int ch, checkch, from_fd, rcount, rval, to_fd, wcount; +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + char *p; +#endif + + if ((from_fd = open(entp->fts_path, O_RDONLY, 0)) == -1) { + warn("%s", entp->fts_path); + return (1); + } + + fs = entp->fts_statp; + + /* + * If the file exists and we're interactive, verify with the user. + * If the file DNE, set the mode to be the from file, minus setuid + * bits, modified by the umask; arguably wrong, but it makes copying + * executables work right and it's been that way forever. (The + * other choice is 666 or'ed with the execute bits on the from file + * modified by the umask.) + */ + if (!dne) { + if (iflag) { + (void)fprintf(stderr, "overwrite %s? ", to.p_path); + checkch = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + if (checkch != 'y' && checkch != 'Y') { + (void)close(from_fd); + return (0); + } + } + /* overwrite existing destination file name */ + to_fd = open(to.p_path, O_WRONLY | O_TRUNC, 0); + } else + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + + if (to_fd == -1 && fflag) { + /* + * attempt to remove existing destination file name and + * create a new file + */ + (void)unlink(to.p_path); + to_fd = open(to.p_path, O_WRONLY | O_TRUNC | O_CREAT, + fs->st_mode & ~(S_ISUID | S_ISGID)); + } + + if (to_fd == -1) { + warn("%s", to.p_path); + (void)close(from_fd); + return (1);; + } + + rval = 0; + + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (fs->st_size > 0) { + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files). This is really a minor hack, but it + * wins some CPU back. + */ +#ifdef VM_AND_BUFFER_CACHE_SYNCHRONIZED + if (fs->st_size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)fs->st_size, PROT_READ, + MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == (char *)-1) { + warn("%s", entp->fts_path); + rval = 1; + } else { + if (write(to_fd, p, fs->st_size) != fs->st_size) { + warn("%s", to.p_path); + rval = 1; + } + /* Some systems don't unmap on close(2). */ + if (munmap(p, fs->st_size) < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } else +#endif + { + while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { + wcount = write(to_fd, buf, rcount); + if (rcount != wcount || wcount == -1) { + warn("%s", to.p_path); + rval = 1; + break; + } + } + if (rcount < 0) { + warn("%s", entp->fts_path); + rval = 1; + } + } + } + + if (rval == 1) { + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + + if (pflag && setfile(fs, to_fd)) + rval = 1; + /* + * If the source was setuid or setgid, lose the bits unless the + * copy is owned by the same user and group. + */ +#define RETAINBITS \ + (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) + else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) { + if (fstat(to_fd, &to_stat)) { + warn("%s", to.p_path); + rval = 1; + } else if (fs->st_gid == to_stat.st_gid && + fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) { + warn("%s", to.p_path); + rval = 1; + } + } + (void)close(from_fd); + if (close(to_fd)) { + warn("%s", to.p_path); + rval = 1; + } + /* set the mod/access times now after close of the fd */ + if (pflag && set_utimes(to.p_path, fs)) { + rval = 1; + } + return (rval); +} + +int +copy_link(p, exists) + FTSENT *p; + int exists; +{ + int len; + char target[MAXPATHLEN]; + + if ((len = readlink(p->fts_path, target, sizeof(target))) == -1) { + warn("readlink: %s", p->fts_path); + return (1); + } + target[len] = '\0'; + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (symlink(target, to.p_path)) { + warn("symlink: %s", target); + return (1); + } + return (pflag ? setfile(p->fts_statp, 0) : 0); +} + +int +copy_fifo(from_stat, exists) + struct stat *from_stat; + int exists; +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mkfifo(to.p_path, from_stat->st_mode)) { + warn("mkfifo: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + +int +copy_special(from_stat, exists) + struct stat *from_stat; + int exists; +{ + if (exists && unlink(to.p_path)) { + warn("unlink: %s", to.p_path); + return (1); + } + if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { + warn("mknod: %s", to.p_path); + return (1); + } + return (pflag ? setfile(from_stat, 0) : 0); +} + + +/* + * Function: setfile + * + * Purpose: + * Set the owner/group/permissions for the "to" file to the information + * in the stat structure. If fd is zero, also call set_utimes() to set + * the mod/access times. If fd is non-zero, the caller must do a utimes + * itself after close(fd). + */ +int +setfile(fs, fd) + struct stat *fs; + int fd; +{ + int rval, islink; + + rval = 0; + islink = S_ISLNK(fs->st_mode); + fs->st_mode &= S_ISUID | S_ISGID | S_IRWXU | S_IRWXG | S_IRWXO; + + /* + * Changing the ownership probably won't succeed, unless we're root + * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting + * the mode; current BSD behavior is to remove all setuid bits on + * chown. If chown fails, lose setuid/setgid bits. + */ + if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : +#ifdef __APPLE__ + chown(to.p_path, fs->st_uid, fs->st_gid)) { +#else + lchown(to.p_path, fs->st_uid, fs->st_gid)) { +#endif + if (errno != EPERM) { + warn("chown: %s", to.p_path); + rval = 1; + } + fs->st_mode &= ~(S_ISUID | S_ISGID); + } +#ifdef __APPLE__ + if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) { +#else + if (fd ? fchmod(fd, fs->st_mode) : lchmod(to.p_path, fs->st_mode)) { +#endif + warn("chmod: %s", to.p_path); + rval = 1; + } + + if (!islink) { + /* + * XXX + * NFS doesn't support chflags; ignore errors unless + * there's reason to believe we're losing bits. + * (Note, this still won't be right if the server + * supports flags and we were trying to *remove* flags + * on a file that we copied, i.e., that we didn't create.) + */ + errno = 0; + if (fd ? fchflags(fd, fs->st_flags) : + chflags(to.p_path, fs->st_flags)) + if (errno != EOPNOTSUPP || fs->st_flags != 0) { + warn("chflags: %s", to.p_path); + rval = 1; + } + } + /* if fd is non-zero, caller must call set_utimes() after close() */ + if (fd == 0 && set_utimes(to.p_path, fs)) + rval = 1; + return (rval); +} + +void +usage() +{ + (void)fprintf(stderr, "%s\n%s\n", + "usage: cp [-R [-H | -L | -P]] [-f | -i] [-p] src target", + " cp [-R [-H | -L | -P]] [-f | -i] [-p] src1 ... srcN directory"); + exit(1); + /* NOTREACHED */ +} diff --git a/csh/strpct.c b/csh/strpct.c new file mode 100644 index 0000000..ac3b32d --- /dev/null +++ b/csh/strpct.c @@ -0,0 +1,99 @@ +/* $NetBSD: strpct.c,v 1.2 1998/05/08 18:43:54 fair Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Erik E. Fair + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Calculate a percentage without resorting to floating point + * and return a pointer to a string + * + * "digits" is the number of digits past the decimal place you want + * (zero being the straight percentage with no decimals) + * + * Erik E. Fair , May 8, 1997 + */ + +#include +#include + +#include + +char * strpct __P((u_long, u_long, u_int)); + +char * +strpct(numerator, denominator, digits) + u_long numerator, denominator; + u_int digits; +{ + int i; + u_long result, factor; + static char percent[32]; + + /* I should check for digit overflow here, too XXX */ + factor = 100L; + for(i = 0; i < digits; i++) { + factor *= 10; + } + + /* watch out for overflow! */ + if (numerator < (ULONG_MAX / factor)) { + numerator *= factor; + } else { + /* toss some of the bits of lesser significance */ + denominator /= factor; + } + + if (denominator == 0L) + denominator = 1L; + + result = numerator / denominator; + + if (digits == 0) { + (void) snprintf(percent, sizeof(percent), "%lu%%", result); + } else { + char fmt[32]; + + /* indirection to produce the right output format */ + (void) snprintf(fmt, sizeof(fmt), "%%lu.%%0%ulu%%%%", digits); + + factor /= 100L; /* undo initialization */ + + (void) snprintf(percent, sizeof(percent), + fmt, result / factor, result % factor); + } + + return(percent); +} diff --git a/dd/Makefile b/dd/Makefile new file mode 100644 index 0000000..323723a --- /dev/null +++ b/dd/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = dd + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = dd.h extern.h + +CFILES = args.c conv.c conv_tab.c dd.c misc.c position.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble dd.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/dd/Makefile.postamble b/dd/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/dd/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/dd/Makefile.preamble b/dd/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/dd/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/dd/PB.project b/dd/PB.project new file mode 100644 index 0000000..db89643 --- /dev/null +++ b/dd/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (dd.h, extern.h); + OTHER_LINKED = (args.c, conv.c, conv_tab.c, dd.c, misc.c, position.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, dd.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = dd; + 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/dd/args.c b/dd/args.c new file mode 100644 index 0000000..733b147 --- /dev/null +++ b/dd/args.c @@ -0,0 +1,412 @@ +/* $NetBSD: args.c,v 1.13 1998/07/28 03:47:15 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)args.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: args.c,v 1.13 1998/07/28 03:47:15 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +static int c_arg __P((const void *, const void *)); +static int c_conv __P((const void *, const void *)); +static void f_bs __P((char *)); +static void f_cbs __P((char *)); +static void f_conv __P((char *)); +static void f_count __P((char *)); +static void f_files __P((char *)); +static void f_ibs __P((char *)); +static void f_if __P((char *)); +static void f_obs __P((char *)); +static void f_of __P((char *)); +static void f_seek __P((char *)); +static void f_skip __P((char *)); +static u_long get_bsz __P((char *)); + +static const struct arg { + char *name; + void (*f) __P((char *)); + u_int set, noset; +} args[] = { + { "bs", f_bs, C_BS, C_BS|C_IBS|C_OBS|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 }, + { "if", f_if, C_IF, C_IF }, + { "obs", f_obs, C_OBS, C_BS|C_OBS }, + { "of", f_of, C_OF, C_OF }, + { "seek", f_seek, C_SEEK, C_SEEK }, + { "skip", f_skip, C_SKIP, C_SKIP }, +}; + +static char *oper; + +/* + * args -- parse JCL syntax of dd. + */ +void +jcl(argv) + char **argv; +{ + struct arg *ap, tmp; + char *arg; + + in.dbsz = out.dbsz = 512; + + while ((oper = *++argv) != NULL) { + if ((arg = strchr(oper, '=')) == NULL) + errx(1, "unknown operand %s", oper); + *arg++ = '\0'; + if (!*arg) + errx(1, "no value specified for %s", oper); + tmp.name = oper; + if (!(ap = (struct arg *)bsearch(&tmp, args, + sizeof(args)/sizeof(struct arg), sizeof(struct arg), + c_arg))) + errx(1, "unknown operand %s", tmp.name); + if (ddflags & ap->noset) + errx(1, "%s: illegal argument combination or already set", + tmp.name); + ddflags |= ap->set; + ap->f(arg); + } + + /* Final sanity checks. */ + + if (ddflags & C_BS) { + /* + * Bs is turned off by any conversion -- we assume the user + * just wanted to set both the input and output block sizes + * and didn't want the bs semantics, so we don't warn. + */ + if (ddflags & (C_BLOCK|C_LCASE|C_SWAB|C_UCASE|C_UNBLOCK)) + ddflags &= ~C_BS; + + /* Bs supersedes ibs and obs. */ + if (ddflags & C_BS && ddflags & (C_IBS|C_OBS)) + warnx("bs supersedes ibs and obs"); + } + + /* + * Ascii/ebcdic and cbs implies block/unblock. + * Block/unblock requires cbs and vice-versa. + */ + if (ddflags & (C_BLOCK|C_UNBLOCK)) { + if (!(ddflags & C_CBS)) + errx(1, "record operations require cbs"); + if (cbsz == 0) + errx(1, "cbs cannot be zero"); + cfunc = ddflags & C_BLOCK ? block : unblock; + } else if (ddflags & C_CBS) { + if (ddflags & (C_ASCII|C_EBCDIC)) { + if (ddflags & C_ASCII) { + ddflags |= C_UNBLOCK; + cfunc = unblock; + } else { + ddflags |= C_BLOCK; + cfunc = block; + } + } else + errx(1, "cbs meaningless if not doing record operations"); + if (cbsz == 0) + errx(1, "cbs cannot be zero"); + } else + cfunc = def; + + if (in.dbsz == 0 || out.dbsz == 0) + errx(1, "buffer sizes cannot be zero"); + + /* + * Check to make sure that the buffers are not too large. + */ + if (in.dbsz > INT_MAX || out.dbsz > INT_MAX) + errx(1, "buffer sizes cannot be greater than %d", INT_MAX); + + /* Read, write and seek calls take off_t as arguments. + * + * The following check is not done because an off_t is a quad + * for current NetBSD implementations. + * + * if (in.offset > INT_MAX/in.dbsz || out.offset > INT_MAX/out.dbsz) + * errx(1, "seek offsets cannot be larger than %d", INT_MAX); + */ +} + +static int +c_arg(a, b) + const void *a, *b; +{ + + return (strcmp(((const struct arg *)a)->name, + ((const struct arg *)b)->name)); +} + +static void +f_bs(arg) + char *arg; +{ + + in.dbsz = out.dbsz = (int)get_bsz(arg); +} + +static void +f_cbs(arg) + char *arg; +{ + + cbsz = (int)get_bsz(arg); +} + +static void +f_count(arg) + char *arg; +{ + + cpy_cnt = (u_int)get_bsz(arg); + if (!cpy_cnt) + terminate(0); +} + +static void +f_files(arg) + char *arg; +{ + + files_cnt = (int)get_bsz(arg); +} + +static void +f_ibs(arg) + char *arg; +{ + + if (!(ddflags & C_BS)) + in.dbsz = (int)get_bsz(arg); +} + +static void +f_if(arg) + char *arg; +{ + + in.name = arg; +} + +static void +f_obs(arg) + char *arg; +{ + + if (!(ddflags & C_BS)) + out.dbsz = (int)get_bsz(arg); +} + +static void +f_of(arg) + char *arg; +{ + + out.name = arg; +} + +static void +f_seek(arg) + char *arg; +{ + + out.offset = (u_int)get_bsz(arg); +} + +static void +f_skip(arg) + char *arg; +{ + + in.offset = (u_int)get_bsz(arg); +} + +#ifdef NO_CONV +/* Build a small version (i.e. for a ramdisk root) */ +static void +f_conv(arg) + char *arg; +{ + errx(1, "conv option disabled"); +} +#else /* NO_CONV */ + +static const struct conv { + char *name; + u_int set, noset; + const u_char *ctab; +} clist[] = { + { "ascii", C_ASCII, C_EBCDIC, e2a_POSIX }, + { "block", C_BLOCK, C_UNBLOCK, NULL }, + { "ebcdic", C_EBCDIC, C_ASCII, a2e_POSIX }, + { "ibm", C_EBCDIC, C_ASCII, a2ibm_POSIX }, + { "lcase", C_LCASE, C_UCASE, NULL }, + { "noerror", C_NOERROR, 0, NULL }, + { "notrunc", C_NOTRUNC, 0, NULL }, + { "oldascii", C_ASCII, C_EBCDIC, e2a_32V }, + { "oldebcdic", C_EBCDIC, C_ASCII, a2e_32V }, + { "oldibm", C_EBCDIC, C_ASCII, a2ibm_32V }, + { "osync", C_OSYNC, C_BS, NULL }, + { "swab", C_SWAB, 0, NULL }, + { "sync", C_SYNC, 0, NULL }, + { "ucase", C_UCASE, C_LCASE, NULL }, + { "unblock", C_UNBLOCK, C_BLOCK, NULL }, +}; + +static void +f_conv(arg) + char *arg; +{ + struct conv *cp, tmp; + + while (arg != NULL) { + tmp.name = strsep(&arg, ","); + if (!(cp = (struct conv *)bsearch(&tmp, clist, + sizeof(clist)/sizeof(struct conv), sizeof(struct conv), + c_conv))) + errx(1, "unknown conversion %s", tmp.name); + if (ddflags & cp->noset) + errx(1, "%s: illegal conversion combination", tmp.name); + ddflags |= cp->set; + if (cp->ctab) + ctab = cp->ctab; + } +} + +static int +c_conv(a, b) + const void *a, *b; +{ + + return (strcmp(((const struct conv *)a)->name, + ((const struct conv *)b)->name)); +} + +#endif /* NO_CONV */ + +/* + * Convert an expression of the following forms to an unsigned long. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * seperated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + */ +static u_long +get_bsz(val) + char *val; +{ + u_long num, t; + char *expr; + + num = strtoul(val, &expr, 0); + if (num == ULONG_MAX) /* Overflow. */ + err(1, "%s", oper); + if (expr == val) /* No digits. */ + errx(1, "%s: illegal numeric value", oper); + + switch (*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + goto erange; + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + goto erange; + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + goto erange; + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + goto erange; + ++expr; + break; + } + + switch (*expr) { + case '\0': + break; + case '*': /* Backward compatible. */ + case 'x': + t = num; + num *= get_bsz(expr + 1); + if (t > num) +erange: errx(1, "%s: %s", oper, strerror(ERANGE)); + break; + default: + errx(1, "%s: illegal numeric value", oper); + } + return (num); +} diff --git a/dd/conv.c b/dd/conv.c new file mode 100644 index 0000000..552e0d9 --- /dev/null +++ b/dd/conv.c @@ -0,0 +1,283 @@ +/* $NetBSD: conv.c,v 1.8 1998/07/28 05:15:46 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: conv.c,v 1.8 1998/07/28 05:15:46 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include + +#include "dd.h" +#include "extern.h" + +/* + * def -- + * Copy input to output. Input is buffered until reaches obs, and then + * output until less than obs remains. Only a single buffer is used. + * Worst case buffer calculation is (ibs + obs - 1). + */ +void +def() +{ + int cnt; + u_char *inp; + const u_char *t; + + if ((t = ctab) != NULL) + for (inp = in.dbp - (cnt = in.dbrcnt); cnt--; ++inp) + *inp = t[*inp]; + + /* Make the output buffer look right. */ + out.dbp = in.dbp; + out.dbcnt = in.dbcnt; + + if (in.dbcnt >= out.dbsz) { + /* If the output buffer is full, write it. */ + dd_out(0); + + /* + * Ddout copies the leftover output to the beginning of + * the buffer and resets the output buffer. Reset the + * input buffer to match it. + */ + in.dbp = out.dbp; + in.dbcnt = out.dbcnt; + } +} + +void +def_close() +{ + /* Just update the count, everything is already in the buffer. */ + if (in.dbcnt) + out.dbcnt = in.dbcnt; +} + +#ifdef NO_CONV +/* Build a smaller version (i.e. for a miniroot) */ +/* These can not be called, but just in case... */ +static char no_block[] = "unblock and -DNO_CONV?"; +void block() { errx(1, no_block + 2); } +void block_close() { errx(1, no_block + 2); } +void unblock() { errx(1, no_block); } +void unblock_close() { errx(1, no_block); } +#else /* NO_CONV */ + +/* + * Copy variable length newline terminated records with a max size cbsz + * bytes to output. Records less than cbs are padded with spaces. + * + * max in buffer: MAX(ibs, cbsz) + * max out buffer: obs + cbsz + */ +void +block() +{ + static int intrunc; + int ch = 0; /* pacify gcc */ + int cnt, maxlen; + u_char *inp, *outp; + const u_char *t; + + /* + * Record truncation can cross block boundaries. If currently in a + * truncation state, keep tossing characters until reach a newline. + * Start at the beginning of the buffer, as the input buffer is always + * left empty. + */ + if (intrunc) { + for (inp = in.db, cnt = in.dbrcnt; + cnt && *inp++ != '\n'; --cnt); + if (!cnt) { + in.dbcnt = 0; + in.dbp = in.db; + return; + } + intrunc = 0; + /* Adjust the input buffer numbers. */ + in.dbcnt = cnt - 1; + in.dbp = inp + cnt - 1; + } + + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation is done as we copy into the output buffer. + */ + for (inp = in.dbp - in.dbcnt, outp = out.dbp; in.dbcnt;) { + maxlen = MIN(cbsz, in.dbcnt); + if ((t = ctab) != NULL) + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = t[ch]; + else + for (cnt = 0; + cnt < maxlen && (ch = *inp++) != '\n'; ++cnt) + *outp++ = ch; + /* + * Check for short record without a newline. Reassemble the + * input block. + */ + if (ch != '\n' && in.dbcnt < cbsz) { + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + break; + } + + /* Adjust the input buffer numbers. */ + in.dbcnt -= cnt; + if (ch == '\n') + --in.dbcnt; + + /* Pad short records with spaces. */ + if (cnt < cbsz) + (void)memset(outp, ctab ? ctab[' '] : ' ', cbsz - cnt); + else { + /* + * If the next character wouldn't have ended the + * block, it's a truncation. + */ + if (!in.dbcnt || *inp != '\n') + ++st.trunc; + + /* Toss characters to a newline. */ + for (; in.dbcnt && *inp++ != '\n'; --in.dbcnt); + if (!in.dbcnt) + intrunc = 1; + else + --in.dbcnt; + } + + /* Adjust output buffer numbers. */ + out.dbp += cbsz; + if ((out.dbcnt += cbsz) >= out.dbsz) + dd_out(0); + outp = out.dbp; + } + in.dbp = in.db + in.dbcnt; +} + +void +block_close() +{ + /* + * Copy any remaining data into the output buffer and pad to a record. + * Don't worry about truncation or translation, the input buffer is + * always empty when truncating, and no characters have been added for + * translation. The bottom line is that anything left in the input + * buffer is a truncated record. Anything left in the output buffer + * just wasn't big enough. + */ + if (in.dbcnt) { + ++st.trunc; + (void)memmove(out.dbp, in.dbp - in.dbcnt, in.dbcnt); + (void)memset(out.dbp + in.dbcnt, + ctab ? ctab[' '] : ' ', cbsz - in.dbcnt); + out.dbcnt += cbsz; + } +} + +/* + * Convert fixed length (cbsz) records to variable length. Deletes any + * trailing blanks and appends a newline. + * + * max in buffer: MAX(ibs, cbsz) + cbsz + * max out buffer: obs + cbsz + */ +void +unblock() +{ + int cnt; + u_char *inp; + const u_char *t; + + /* Translation and case conversion. */ + if ((t = ctab) != NULL) + for (cnt = in.dbrcnt, inp = in.dbp; cnt--;) + *--inp = t[*inp]; + /* + * Copy records (max cbsz size chunks) into the output buffer. The + * translation has to already be done or we might not recognize the + * spaces. + */ + for (inp = in.db; in.dbcnt >= cbsz; inp += cbsz, in.dbcnt -= cbsz) { + for (t = inp + cbsz - 1; t >= inp && *t == ' '; --t); + if (t >= inp) { + cnt = t - inp + 1; + (void)memmove(out.dbp, inp, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + if (out.dbcnt >= out.dbsz) + dd_out(0); + } + if (in.dbcnt) + (void)memmove(in.db, in.dbp - in.dbcnt, in.dbcnt); + in.dbp = in.db + in.dbcnt; +} + +void +unblock_close() +{ + int cnt; + u_char *t; + + if (in.dbcnt) { + warnx("%s: short input record", in.name); + for (t = in.db + in.dbcnt - 1; t >= in.db && *t == ' '; --t); + if (t >= in.db) { + cnt = t - in.db + 1; + (void)memmove(out.dbp, in.db, cnt); + out.dbp += cnt; + out.dbcnt += cnt; + } + ++out.dbcnt; + *out.dbp++ = '\n'; + } +} + +#endif /* NO_CONV */ diff --git a/dd/conv_tab.c b/dd/conv_tab.c new file mode 100644 index 0000000..2f41d87 --- /dev/null +++ b/dd/conv_tab.c @@ -0,0 +1,291 @@ +/* $NetBSD: conv_tab.c,v 1.8 1997/07/20 21:58:38 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)conv_tab.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: conv_tab.c,v 1.8 1997/07/20 21:58:38 christos Exp $"); +#endif +#endif /* not lint */ + +#include + +/* + * There are currently six tables: + * + * ebcdic -> ascii 32V conv=oldascii + * ascii -> ebcdic 32V conv=oldebcdic + * ascii -> ibm ebcdic 32V conv=oldibm + * + * ebcdic -> ascii POSIX/S5 conv=ascii + * ascii -> ebcdic POSIX/S5 conv=ebcdic + * ascii -> ibm ebcdic POSIX/S5 conv=ibm + * + * Other tables are built from these if multiple conversions are being + * done. + * + * Tables used for conversions to/from IBM and EBCDIC to support an extension + * to POSIX P1003.2/D11. The tables referencing POSIX contain data extracted + * from tables 4-3 and 4-4 in P1003.2/Draft 11. The historic tables were + * constructed by running against a file with all possible byte values. + * + * More information can be obtained in "Correspondences of 8-Bit and Hollerith + * Codes for Computer Environments-A USASI Tutorial", Communications of the + * ACM, Volume 11, Number 11, November 1968, pp. 783-789. + */ + +u_char casetab[256]; + +/* EBCDIC to ASCII -- 32V compatible. */ +const u_char e2a_32V[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0133, 0056, 0074, 0050, 0053, 0041, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0135, 0044, 0052, 0051, 0073, 0136, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0174, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0313, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0176, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0325, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- 32V compatible. */ +const u_char a2e_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0117, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0112, 0340, 0132, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0152, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- 32V compatible. */ +const u_char a2ibm_32V[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* EBCDIC to ASCII -- POSIX and System V compatible. */ +const u_char e2a_POSIX[] = { + 0000, 0001, 0002, 0003, 0234, 0011, 0206, 0177, /* 0000 */ + 0227, 0215, 0216, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0235, 0205, 0010, 0207, /* 0020 */ + 0030, 0031, 0222, 0217, 0034, 0035, 0036, 0037, /* 0030 */ + 0200, 0201, 0202, 0203, 0204, 0012, 0027, 0033, /* 0040 */ + 0210, 0211, 0212, 0213, 0214, 0005, 0006, 0007, /* 0050 */ + 0220, 0221, 0026, 0223, 0224, 0225, 0226, 0004, /* 0060 */ + 0230, 0231, 0232, 0233, 0024, 0025, 0236, 0032, /* 0070 */ + 0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246, /* 0100 */ + 0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174, /* 0110 */ + 0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257, /* 0120 */ + 0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176, /* 0130 */ + 0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267, /* 0140 */ + 0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077, /* 0150 */ + 0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301, /* 0160 */ + 0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042, /* 0170 */ + 0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147, /* 0200 */ + 0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311, /* 0210 */ + 0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160, /* 0220 */ + 0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320, /* 0230 */ + 0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170, /* 0240 */ + 0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327, /* 0250 */ + 0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337, /* 0260 */ + 0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347, /* 0270 */ + 0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107, /* 0300 */ + 0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355, /* 0310 */ + 0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120, /* 0320 */ + 0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363, /* 0330 */ + 0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130, /* 0340 */ + 0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371, /* 0350 */ + 0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067, /* 0360 */ + 0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to EBCDIC -- POSIX and System V compatible. */ +const u_char a2e_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0232, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0137, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0152, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0112, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0241, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; + +/* ASCII to IBM EBCDIC -- POSIX and System V compatible. */ +const u_char a2ibm_POSIX[] = { + 0000, 0001, 0002, 0003, 0067, 0055, 0056, 0057, /* 0000 */ + 0026, 0005, 0045, 0013, 0014, 0015, 0016, 0017, /* 0010 */ + 0020, 0021, 0022, 0023, 0074, 0075, 0062, 0046, /* 0020 */ + 0030, 0031, 0077, 0047, 0034, 0035, 0036, 0037, /* 0030 */ + 0100, 0132, 0177, 0173, 0133, 0154, 0120, 0175, /* 0040 */ + 0115, 0135, 0134, 0116, 0153, 0140, 0113, 0141, /* 0050 */ + 0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367, /* 0060 */ + 0370, 0371, 0172, 0136, 0114, 0176, 0156, 0157, /* 0070 */ + 0174, 0301, 0302, 0303, 0304, 0305, 0306, 0307, /* 0100 */ + 0310, 0311, 0321, 0322, 0323, 0324, 0325, 0326, /* 0110 */ + 0327, 0330, 0331, 0342, 0343, 0344, 0345, 0346, /* 0120 */ + 0347, 0350, 0351, 0255, 0340, 0275, 0137, 0155, /* 0130 */ + 0171, 0201, 0202, 0203, 0204, 0205, 0206, 0207, /* 0140 */ + 0210, 0211, 0221, 0222, 0223, 0224, 0225, 0226, /* 0150 */ + 0227, 0230, 0231, 0242, 0243, 0244, 0245, 0246, /* 0160 */ + 0247, 0250, 0251, 0300, 0117, 0320, 0241, 0007, /* 0170 */ + 0040, 0041, 0042, 0043, 0044, 0025, 0006, 0027, /* 0200 */ + 0050, 0051, 0052, 0053, 0054, 0011, 0012, 0033, /* 0210 */ + 0060, 0061, 0032, 0063, 0064, 0065, 0066, 0010, /* 0220 */ + 0070, 0071, 0072, 0073, 0004, 0024, 0076, 0341, /* 0230 */ + 0101, 0102, 0103, 0104, 0105, 0106, 0107, 0110, /* 0240 */ + 0111, 0121, 0122, 0123, 0124, 0125, 0126, 0127, /* 0250 */ + 0130, 0131, 0142, 0143, 0144, 0145, 0146, 0147, /* 0260 */ + 0150, 0151, 0160, 0161, 0162, 0163, 0164, 0165, /* 0270 */ + 0166, 0167, 0170, 0200, 0212, 0213, 0214, 0215, /* 0300 */ + 0216, 0217, 0220, 0232, 0233, 0234, 0235, 0236, /* 0310 */ + 0237, 0240, 0252, 0253, 0254, 0255, 0256, 0257, /* 0320 */ + 0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267, /* 0330 */ + 0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277, /* 0340 */ + 0312, 0313, 0314, 0315, 0316, 0317, 0332, 0333, /* 0350 */ + 0334, 0335, 0336, 0337, 0352, 0353, 0354, 0355, /* 0360 */ + 0356, 0357, 0372, 0373, 0374, 0375, 0376, 0377, /* 0370 */ +}; diff --git a/dd/dd.1 b/dd/dd.1 new file mode 100644 index 0000000..5f98028 --- /dev/null +++ b/dd/dd.1 @@ -0,0 +1,355 @@ +.\" $NetBSD: dd.1,v 1.7 1998/02/06 05:39:31 perry Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)dd.1 8.2 (Berkeley) 1/13/94 +.\" +.Dd January 13, 1994 +.Dt DD 1 +.Os +.Sh NAME +.Nm dd +.Nd convert and copy a file +.Sh SYNOPSIS +.Nm +.Op operands ... +.Sh DESCRIPTION +The +.Nm +utility copies the standard input to the standard output. +Input data is read and written in 512-byte blocks. +If input reads are short, input from multiple reads are aggregated +to form the output block. +When finished, +.Nm +displays the number of complete and partial input and output blocks +and truncated input records to the standard error output. +.Pp +The following operands are available: +.Bl -tag -width of=file +.It Cm bs= Ns Ar n +Set both input and output block size, superseding the +.Cm ibs +and +.Cm obs +operands. +If no conversion values other than +.Cm noerror , +.Cm notrunc +or +.Cm sync +are specified, then each input block is copied to the output as a +single block without any aggregation of short blocks. +.It Cm cbs= Ns Ar n +Set the conversion record size to +.Va n +bytes. +The conversion record size is required by the record oriented conversion +values. +.It Cm count= Ns Ar n +Copy only +.Va n +input blocks. +.It Cm files= Ns Ar n +Copy +.Va n +input files before terminating. +This operand is only applicable when the input device is a tape. +.It Cm ibs= Ns Ar n +Set the input block size to +.Va n +bytes instead of the default 512. +.It Cm if= Ns Ar file +Read input from +.Ar file +instead of the standard input. +.It Cm obs= Ns Ar n +Set the output block size to +.Va n +bytes instead of the default 512. +.It Cm of= Ns Ar file +Write output to +.Ar file +instead of the standard output. +Any regular output file is truncated unless the +.Cm notrunc +conversion value is specified. +If an initial portion of the output file is skipped (see the +.Cm seek +operand) +the output file is truncated at that point. +.It Cm seek= Ns Ar n +Seek +.Va n +blocks from the beginning of the output before copying. +On non-tape devices, a +.Xr lseek 2 +operation is used. +Otherwise, existing blocks are read and the data discarded. +If the user does not have read permission for the tape, it is positioned +using the tape +.Xr ioctl 2 +function calls. +If the seek operation is past the end of file, space from the current +end of file to the specified offset is filled with blocks of +.Tn NUL +bytes. +.It Cm skip= Ns Ar n +Skip +.Va n +blocks from the beginning of the input before copying. +On input which supports seeks, a +.Xr lseek 2 +operation is used. +Otherwise, input data is read and discarded. +For pipes, the correct number of bytes is read. +For all other devices, the correct number of blocks is read without +distinguishing between a partial or complete block being read. +.It Xo +.Cm conv= +.Ns Cm value Ns Op \&, Cm value \&... +.Xc +Where +.Cm value +is one of the symbols from the following list. +.Bl -tag -width unblock +.It Cm ascii , oldascii +The same as the +.Cm unblock +value except that characters are translated from +.Tn EBCDIC +to +.Tn ASCII +before the +records are converted. +(These values imply +.Cm unblock +if the operand +.Cm cbs +is also specified.) +There are two conversion maps for +.Tn ASCII . +The value +.Cm ascii +specifies the recommended one which is compatible with +.At V . +The value +.Cm oldascii +specifies the one used in historic +.Tn AT&T +and pre- +.Bx 4.3 Reno +systems. +.It Cm block +Treats the input as a sequence of newline or end-of-file terminated variable +length records independent of input and output block boundaries. +Any trailing newline character is discarded. +Each input record is converted to a fixed length output record where the +length is specified by the +.Cm cbs +operand. +Input records shorter than the conversion record size are padded with spaces. +Input records longer than the conversion record size are truncated. +The number of truncated input records, if any, are reported to the standard +error output at the completion of the copy. +.It Cm ebcdic , ibm , oldebcdic , oldibm +The same as the +.Cm block +value except that characters are translated from +.Tn ASCII +to +.Tn EBCDIC +after the +records are converted. +(These values imply +.Cm block +if the operand +.Cm cbs +is also specified.) +There are four conversion maps for +.Tn EBCDIC . +The value +.Cm ebcdic +specifies the recommended one which is compatible with +.At V . +The value +.Cm ibm +is a slightly different mapping, which is compatible with the +.At V +.Cm ibm +value. +The values +.Cm oldebcdic +and +.Cm oldibm +are maps used in historic +.Tn AT&T +and pre +.Bx 4.3 Reno +systems. +.It Cm lcase +Transform uppercase characters into lowercase characters. +.It Cm noerror +Do not stop processing on an input error. +When an input error occurs, a diagnostic message followed by the current +input and output block counts will be written to the standard error output +in the same format as the standard completion message. +If the +.Cm sync +conversion is also specified, any missing input data will be replaced +with +.Tn NUL +bytes (or with spaces if a block oriented conversion value was +specified) and processed as a normal input buffer. +If the +.Cm sync +conversion is not specified, the input block is omitted from the output. +On input files which are not tapes or pipes, the file offset +will be positioned past the block in which the error occurred using +.Xr lseek 2 . +.It Cm notrunc +Do not truncate the output file. +This will preserve any blocks in the output file not explicitly written +by +.Nm "" . +The +.Cm notrunc +value is not supported for tapes. +.It Cm osync +Pad the final output block to the full output block size. +If the input file is not a multiple of the output block size +after conversion, this conversion forces the final output block +to be the same size as preceding blocks for use on devices that require +regularly sized blocks to be written. +This option is incompatible with use of the +.Cm bs= Ns Ar n +block size specification. +.It Cm swab +Swap every pair of input bytes. +If an input buffer has an odd number of bytes, the last byte will be +ignored during swapping. +.It Cm sync +Pad every input block to the input buffer size. +Spaces are used for pad bytes if a block oriented conversion value is +specified, otherwise +.Tn NUL +bytes are used. +.It Cm ucase +Transform lowercase characters into uppercase characters. +.It Cm unblock +Treats the input as a sequence of fixed length records independent of input +and output block boundaries. +The length of the input records is specified by the +.Cm cbs +operand. +Any trailing space characters are discarded and a newline character is +appended. +.El +.El +.Pp +Where sizes are specified, a decimal number of bytes is expected. +If the number ends with a ``b'', ``k'', ``m'' or ``w'', the number +is multiplied by 512, 1024 (1K), 1048576 (1M) or the number of bytes +in an integer, respectively. +Two or more numbers may be separated by an ``x'' to indicate a product. +.Pp +When finished, +.Nm +displays the number of complete and partial input and output blocks, +truncated input records and odd-length byte-swapping blocks to the +standard error output. +A partial input block is one where less than the input block size +was read. +A partial output block is one where less than the output block size +was written. +Partial output blocks to tape devices are considered fatal errors. +Otherwise, the rest of the block will be written. +Partial output blocks to character devices will produce a warning message. +A truncated input block is one where a variable length record oriented +conversion value was specified and the input line was too long to +fit in the conversion record or was not newline terminated. +.Pp +Normally, data resulting from input or conversion or both are aggregated +into output blocks of the specified size. +After the end of input is reached, any remaining output is written as +a block. +This means that the final output block may be shorter than the output +block size. +.Pp +If +.Nm +receives a +.Dv SIGINFO +(see the ``status'' argument for +.Xr stty 1 ) +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message. +If +.Nm +receives a +.Dv SIGINT +signal, the current input and output block counts will +be written to the standard error output +in the same format as the standard completion message and +.Nm +will exit. +.Pp +The +.Nm +utility exits 0 on success and >0 if an error occurred. +.Sh SEE ALSO +.Xr cp 1 , +.Xr mt 1 , +.Xr tr 1 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +standard. +The +.Cm files +operand and the +.Cm ascii , +.Cm ebcdic , +.Cm ibm , +.Cm oldascii , +.Cm oldebcdic +and +.Cm oldibm +values are extensions to the +.Tn POSIX +standard. diff --git a/dd/dd.c b/dd/dd.c new file mode 100644 index 0000000..17f870f --- /dev/null +++ b/dd/dd.c @@ -0,0 +1,412 @@ +/* $NetBSD: dd.c,v 1.12 1998/08/19 01:32:44 thorpej Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dd.c 8.5 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: dd.c,v 1.12 1998/08/19 01:32:44 thorpej Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +static void dd_close __P((void)); +static void dd_in __P((void)); +static void getfdtype __P((IO *)); +static void setup __P((void)); + +int main __P((int, char *[])); + +IO in, out; /* input/output state */ +STAT st; /* statistics */ +void (*cfunc) __P((void)); /* conversion function */ +u_long cpy_cnt; /* # of blocks to copy */ +u_int ddflags; /* conversion options */ +u_int cbsz; /* conversion block size */ +u_int files_cnt = 1; /* # of files to copy */ +const u_char *ctab; /* conversion table */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + jcl(argv); + setup(); + + (void)signal(SIGINFO, summaryx); + (void)signal(SIGINT, terminate); + + (void)atexit(summary); + + while (files_cnt--) + dd_in(); + + dd_close(); + exit(0); + /* NOTREACHED */ +} + +static void +setup() +{ + u_int cnt; + + if (in.name == NULL) { + in.name = "stdin"; + in.fd = STDIN_FILENO; + } else { + in.fd = open(in.name, O_RDONLY, 0); + if (in.fd < 0) + err(1, "%s", in.name); + } + + getfdtype(&in); + + if (files_cnt > 1 && !(in.flags & ISTAPE)) + errx(1, "files is not supported for non-tape devices"); + + if (out.name == NULL) { + /* No way to check for read access here. */ + out.fd = STDOUT_FILENO; + out.name = "stdout"; + } else { +#define OFLAGS \ + (O_CREAT | (ddflags & (C_SEEK | C_NOTRUNC) ? 0 : O_TRUNC)) + out.fd = open(out.name, O_RDWR | OFLAGS, DEFFILEMODE); + /* + * May not have read access, so try again with write only. + * Without read we may have a problem if output also does + * not support seeks. + */ + if (out.fd < 0) { + out.fd = open(out.name, O_WRONLY | OFLAGS, DEFFILEMODE); + out.flags |= NOREAD; + } + if (out.fd < 0) + err(1, "%s", out.name); + } + + getfdtype(&out); + + /* + * Allocate space for the input and output buffers. If not doing + * record oriented I/O, only need a single buffer. + */ + if (!(ddflags & (C_BLOCK|C_UNBLOCK))) { + if ((in.db = malloc(out.dbsz + in.dbsz - 1)) == NULL) + err(1, "%s", ""); + out.db = in.db; + } else if ((in.db = + malloc((u_int)(MAX(in.dbsz, cbsz) + cbsz))) == NULL || + (out.db = malloc((u_int)(out.dbsz + cbsz))) == NULL) + err(1, "%s", ""); + in.dbp = in.db; + out.dbp = out.db; + + /* Position the input/output streams. */ + if (in.offset) + pos_in(); + if (out.offset) + pos_out(); + + /* + * Truncate the output file; ignore errors because it fails on some + * kinds of output files, tapes, for example. + */ + if ((ddflags & (C_OF | C_SEEK | C_NOTRUNC)) == (C_OF | C_SEEK)) + (void)ftruncate(out.fd, (off_t)out.offset * out.dbsz); + + /* + * If converting case at the same time as another conversion, build a + * table that does both at once. If just converting case, use the + * built-in tables. + */ + if (ddflags & (C_LCASE|C_UCASE)) { +#ifdef NO_CONV + /* Should not get here, but just in case... */ + errx(1, "case conv and -DNO_CONV"); +#else /* NO_CONV */ + if (ddflags & C_ASCII || ddflags & C_EBCDIC) { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(ctab[cnt]); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(ctab[cnt]); + } + } else { + if (ddflags & C_LCASE) { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = tolower(cnt); + } else { + for (cnt = 0; cnt < 0377; ++cnt) + casetab[cnt] = toupper(cnt); + } + } + + ctab = casetab; +#endif /* NO_CONV */ + } + + (void)time(&st.start); /* Statistics timestamp. */ +} + +static void +getfdtype(io) + IO *io; +{ + struct mtget mt; + struct stat sb; + + if (fstat(io->fd, &sb)) + err(1, "%s", io->name); + if (S_ISCHR(sb.st_mode)) + io->flags |= ioctl(io->fd, MTIOCGET, &mt) ? ISCHR : ISTAPE; + else if (lseek(io->fd, (off_t)0, SEEK_CUR) == -1 && errno == ESPIPE) + io->flags |= ISPIPE; /* XXX fixed in 4.4BSD */ +} + +static void +dd_in() +{ + int flags, n; + + for (flags = ddflags;;) { + if (cpy_cnt && (st.in_full + st.in_part) >= cpy_cnt) + return; + + /* + * Clear the buffer first if doing "sync" on input. + * If doing block operations use spaces. This will + * affect not only the C_NOERROR case, but also the + * last partial input block which should be padded + * with zero and not garbage. + */ + if (flags & C_SYNC) { + if (flags & (C_BLOCK|C_UNBLOCK)) + (void)memset(in.dbp, ' ', in.dbsz); + else + (void)memset(in.dbp, 0, in.dbsz); + } + + n = read(in.fd, in.dbp, in.dbsz); + if (n == 0) { + in.dbrcnt = 0; + return; + } + + /* Read error. */ + if (n < 0) { + /* + * If noerror not specified, die. POSIX requires that + * the warning message be followed by an I/O display. + */ + if (!(flags & C_NOERROR)) + err(1, "%s", in.name); + warn("%s", in.name); + summary(); + + /* + * If it's not a tape drive or a pipe, seek past the + * error. If your OS doesn't do the right thing for + * raw disks this section should be modified to re-read + * in sector size chunks. + */ + if (!(in.flags & (ISPIPE|ISTAPE)) && + lseek(in.fd, (off_t)in.dbsz, SEEK_CUR)) + warn("%s", in.name); + + /* If sync not specified, omit block and continue. */ + if (!(ddflags & C_SYNC)) + continue; + + /* Read errors count as full blocks. */ + in.dbcnt += in.dbrcnt = in.dbsz; + ++st.in_full; + + /* Handle full input blocks. */ + } else if (n == in.dbsz) { + in.dbcnt += in.dbrcnt = n; + ++st.in_full; + + /* Handle partial input blocks. */ + } else { + /* If sync, use the entire block. */ + if (ddflags & C_SYNC) + in.dbcnt += in.dbrcnt = in.dbsz; + else + in.dbcnt += in.dbrcnt = n; + ++st.in_part; + } + + /* + * POSIX states that if bs is set and no other conversions + * than noerror, notrunc or sync are specified, the block + * is output without buffering as it is read. + */ + if (ddflags & C_BS) { + out.dbcnt = in.dbcnt; + dd_out(1); + in.dbcnt = 0; + continue; + } + + if (ddflags & C_SWAB) { + if ((n = in.dbcnt) & 1) { + ++st.swab; + --n; + } + swab(in.dbp, in.dbp, n); + } + + in.dbp += in.dbrcnt; + (*cfunc)(); + } +} + +/* + * Cleanup any remaining I/O and flush output. If necesssary, output file + * is truncated. + */ +static void +dd_close() +{ + if (cfunc == def) + def_close(); + else if (cfunc == block) + block_close(); + else if (cfunc == unblock) + unblock_close(); + if (ddflags & C_OSYNC && out.dbcnt < out.dbsz) { + (void)memset(out.dbp, 0, out.dbsz - out.dbcnt); + out.dbcnt = out.dbsz; + } + if (out.dbcnt) + dd_out(1); +} + +void +dd_out(force) + int force; +{ + static int warned; + int cnt, n, nw; + u_char *outp; + + /* + * Write one or more blocks out. The common case is writing a full + * output block in a single write; increment the full block stats. + * Otherwise, we're into partial block writes. If a partial write, + * and it's a character device, just warn. If a tape device, quit. + * + * The partial writes represent two cases. 1: Where the input block + * was less than expected so the output block was less than expected. + * 2: Where the input block was the right size but we were forced to + * write the block in multiple chunks. The original versions of dd(1) + * never wrote a block in more than a single write, so the latter case + * never happened. + * + * One special case is if we're forced to do the write -- in that case + * we play games with the buffer size, and it's usually a partial write. + */ + outp = out.db; + for (n = force ? out.dbcnt : out.dbsz;; n = out.dbsz) { + for (cnt = n;; cnt -= nw) { + nw = write(out.fd, outp, cnt); + if (nw <= 0) { + if (nw == 0) + errx(1, "%s: end of device", out.name); + if (errno != EINTR) + err(1, "%s", out.name); + nw = 0; + } + outp += nw; + st.bytes += nw; + if (nw == n) { + if (n != out.dbsz) + ++st.out_part; + else + ++st.out_full; + break; + } + ++st.out_part; + if (nw == cnt) + break; + if (out.flags & ISCHR && !warned) { + warned = 1; + warnx("%s: short write on character device", + out.name); + } + if (out.flags & ISTAPE) + errx(1, "%s: short write on tape device", out.name); + } + if ((out.dbcnt -= n) < out.dbsz) + break; + } + + /* Reassemble the output block. */ + if (out.dbcnt) + (void)memmove(out.db, out.dbp - out.dbcnt, out.dbcnt); + out.dbp = out.db + out.dbcnt; +} diff --git a/dd/dd.h b/dd/dd.h new file mode 100644 index 0000000..6d001ed --- /dev/null +++ b/dd/dd.h @@ -0,0 +1,98 @@ +/* $NetBSD: dd.h,v 1.5 1998/02/04 06:42:31 enami Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)dd.h 8.3 (Berkeley) 4/2/94 + */ + +/* Input/output stream state. */ +typedef struct { + u_char *db; /* buffer address */ + u_char *dbp; /* current buffer I/O address */ + u_long dbcnt; /* current buffer byte count */ + int dbrcnt; /* last read byte count */ + u_long dbsz; /* buffer size */ + +#define ISCHR 0x01 /* character device (warn on short) */ +#define ISPIPE 0x02 /* pipe (not truncatable) */ +#define ISTAPE 0x04 /* tape (not seekable) */ +#define NOREAD 0x08 /* not readable */ + u_int flags; + + char *name; /* name */ + int fd; /* file descriptor */ + u_long offset; /* # of blocks to skip */ + + u_long f_stats; /* # of full blocks processed */ + u_long p_stats; /* # of partial blocks processed */ + u_long s_stats; /* # of odd swab blocks */ + u_long t_stats; /* # of truncations */ +} IO; + +typedef struct { + u_long in_full; /* # of full input blocks */ + u_long in_part; /* # of partial input blocks */ + u_long out_full; /* # of full output blocks */ + u_long out_part; /* # of partial output blocks */ + u_long trunc; /* # of truncated records */ + u_long swab; /* # of odd-length swab blocks */ + u_quad_t bytes; /* # of bytes written */ + time_t start; /* start time of dd */ +} STAT; + +/* Flags (in ddflags). */ +#define C_ASCII 0x00001 +#define C_BLOCK 0x00002 +#define C_BS 0x00004 +#define C_CBS 0x00008 +#define C_COUNT 0x00010 +#define C_EBCDIC 0x00020 +#define C_FILES 0x00040 +#define C_IBS 0x00080 +#define C_IF 0x00100 +#define C_LCASE 0x00200 +#define C_NOERROR 0x00400 +#define C_NOTRUNC 0x00800 +#define C_OBS 0x01000 +#define C_OF 0x02000 +#define C_SEEK 0x04000 +#define C_SKIP 0x08000 +#define C_SWAB 0x10000 +#define C_SYNC 0x20000 +#define C_UCASE 0x40000 +#define C_UNBLOCK 0x80000 +#define C_OSYNC 0x100000 diff --git a/dd/extern.h b/dd/extern.h new file mode 100644 index 0000000..79e272b --- /dev/null +++ b/dd/extern.h @@ -0,0 +1,69 @@ +/* $NetBSD: extern.h,v 1.8 1997/07/20 21:58:40 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.3 (Berkeley) 4/2/94 + */ + +#include + +void block __P((void)); +void block_close __P((void)); +void dd_out __P((int)); +void def __P((void)); +void def_close __P((void)); +void jcl __P((char **)); +void pos_in __P((void)); +void pos_out __P((void)); +void summary __P((void)); +void summaryx __P((int)); +void terminate __P((int)); +void unblock __P((void)); +void unblock_close __P((void)); + +extern IO in, out; +extern STAT st; +extern void (*cfunc) __P((void)); +extern u_long cpy_cnt; +extern u_int cbsz; +extern u_int ddflags; +extern u_int files_cnt; +extern const u_char *ctab; +extern const u_char a2e_32V[], a2e_POSIX[]; +extern const u_char e2a_32V[], e2a_POSIX[]; +extern const u_char a2ibm_32V[], a2ibm_POSIX[]; +extern u_char casetab[]; diff --git a/dd/misc.c b/dd/misc.c new file mode 100644 index 0000000..8254392 --- /dev/null +++ b/dd/misc.c @@ -0,0 +1,108 @@ +/* $NetBSD: misc.c,v 1.8 1998/07/28 05:31:23 mycroft Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: misc.c,v 1.8 1998/07/28 05:31:23 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +void +summary() +{ + time_t secs; + char buf[100]; + + (void)time(&secs); + if ((secs -= st.start) == 0) + secs = 1; + /* Use snprintf(3) so that we don't reenter stdio(3). */ + (void)snprintf(buf, sizeof(buf), + "%lu+%lu records in\n%lu+%lu records out\n", + st.in_full, st.in_part, st.out_full, st.out_part); + (void)write(STDERR_FILENO, buf, strlen(buf)); + if (st.swab) { + (void)snprintf(buf, sizeof(buf), "%lu odd length swab %s\n", + st.swab, (st.swab == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + if (st.trunc) { + (void)snprintf(buf, sizeof(buf), "%lu truncated %s\n", + st.trunc, (st.trunc == 1) ? "block" : "blocks"); + (void)write(STDERR_FILENO, buf, strlen(buf)); + } + (void)snprintf(buf, sizeof(buf), + "%qu bytes transferred in %lu secs (%qu bytes/sec)\n", + (long long) st.bytes, (long) secs, (long long) (st.bytes / secs)); + (void)write(STDERR_FILENO, buf, strlen(buf)); +} + +/* ARGSUSED */ +void +summaryx(notused) + int notused; +{ + + summary(); +} + +/* ARGSUSED */ +void +terminate(notused) + int notused; +{ + + exit(0); + /* NOTREACHED */ +} diff --git a/dd/position.c b/dd/position.c new file mode 100644 index 0000000..e302477 --- /dev/null +++ b/dd/position.c @@ -0,0 +1,174 @@ +/* $NetBSD: position.c,v 1.6 1997/07/25 06:46:24 phil Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego and Lance + * Visser of Convex Computer Corporation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)position.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: position.c,v 1.6 1997/07/25 06:46:24 phil Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "dd.h" +#include "extern.h" + +/* + * Position input/output data streams before starting the copy. Device type + * dependent. Seekable devices use lseek, and the rest position by reading. + * Seeking past the end of file can cause null blocks to be written to the + * output. + */ +void +pos_in() +{ + int bcnt, cnt, nr, warned; + + /* If not a character, pipe or tape device, try to seek on it. */ + if (!(in.flags & (ISCHR|ISPIPE|ISTAPE))) { + if (lseek(in.fd, (off_t)in.offset * (off_t)in.dbsz, SEEK_CUR) + == -1) + err(1, "%s", in.name); + return; + } + + /* + * Read the data. If a pipe, read until satisfy the number of bytes + * being skipped. No differentiation for reading complete and partial + * blocks for other devices. + */ + for (bcnt = in.dbsz, cnt = in.offset, warned = 0; cnt;) { + if ((nr = read(in.fd, in.db, bcnt)) > 0) { + if (in.flags & ISPIPE) { + if (!(bcnt -= nr)) { + bcnt = in.dbsz; + --cnt; + } + } else + --cnt; + continue; + } + + if (nr == 0) { + if (files_cnt > 1) { + --files_cnt; + continue; + } + errx(1, "skip reached end of input"); + } + + /* + * Input error -- either EOF with no more files, or I/O error. + * If noerror not set die. POSIX requires that the warning + * message be followed by an I/O display. + */ + if (ddflags & C_NOERROR) { + if (!warned) { + warn("%s", in.name); + warned = 1; + summary(); + } + continue; + } + err(1, "%s", in.name); + } +} + +void +pos_out() +{ + struct mtop t_op; + int cnt, n; + + /* + * If not a tape, try seeking on the file. Seeking on a pipe is + * going to fail, but don't protect the user -- they shouldn't + * have specified the seek operand. + */ + if (!(out.flags & ISTAPE)) { + if (lseek(out.fd, + (off_t)out.offset * (off_t)out.dbsz, SEEK_SET) == -1) + err(1, "%s", out.name); + return; + } + + /* If no read access, try using mtio. */ + if (out.flags & NOREAD) { + t_op.mt_op = MTFSR; + t_op.mt_count = out.offset; + + if (ioctl(out.fd, MTIOCTOP, &t_op) < 0) + err(1, "%s", out.name); + return; + } + + /* Read it. */ + for (cnt = 0; cnt < out.offset; ++cnt) { + if ((n = read(out.fd, out.db, out.dbsz)) > 0) + continue; + + if (n < 0) + err(1, "%s", out.name); + + /* + * If reach EOF, fill with NUL characters; first, back up over + * the EOF mark. Note, cnt has not yet been incremented, so + * the EOF read does not count as a seek'd block. + */ + t_op.mt_op = MTBSR; + t_op.mt_count = 1; + if (ioctl(out.fd, MTIOCTOP, &t_op) == -1) + err(1, "%s", out.name); + + while (cnt++ < out.offset) + if ((n = write(out.fd, out.db, out.dbsz)) != out.dbsz) + err(1, "%s", out.name); + break; + } +} diff --git a/df/Makefile b/df/Makefile new file mode 100644 index 0000000..154ddff --- /dev/null +++ b/df/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = df + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = df.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble df.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/df/Makefile.postamble b/df/Makefile.postamble new file mode 100644 index 0000000..234330d --- /dev/null +++ b/df/Makefile.postamble @@ -0,0 +1,4 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common + +INSTALL_AS_GROUP = operator +INSTALL_PERMISSIONS = 2555 diff --git a/df/Makefile.preamble b/df/Makefile.preamble new file mode 100644 index 0000000..7782dbf --- /dev/null +++ b/df/Makefile.preamble @@ -0,0 +1,5 @@ +vpath strpct.c ../csh + +CFILES += strpct.c + +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/df/PB.project b/df/PB.project new file mode 100644 index 0000000..b036be4 --- /dev/null +++ b/df/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (df.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, df.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = df; + 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/df/df.1 b/df/df.1 new file mode 100644 index 0000000..ccedb98 --- /dev/null +++ b/df/df.1 @@ -0,0 +1,122 @@ +.\" $NetBSD: df.1,v 1.14 1997/10/20 08:51:31 enami Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)df.1 8.2 (Berkeley) 1/13/92 +.\" +.Dd January 13, 1994 +.Dt DF 1 +.Os BSD 4 +.Sh NAME +.Nm df +.Nd display free disk space +.Sh SYNOPSIS +.Nm +.Op Fl ikln +.Op Fl t Ar type +.Op Ar file | Ar file_system ... +.Sh DESCRIPTION +.Nm +displays statistics about the amount of free disk space on the specified +.Ar file_system +or on the file system of which +.Ar file +is a part. +Values are displayed in 512-byte per block block counts. +If neither a file or a +.Ar file_system +operand is specified, +statistics for all mounted file systems are displayed +(subject to the +.Fl l +and +.Fl t +options below). +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl i +Include statistics on the number of free inodes. +.It Fl k +By default, all sizes are reported in 512-byte block counts. +The +.Fl k +option causes the numbers to be reported in kilobyte counts. +.It Fl l +Display statistics only about mounted file systems with the MNT_LOCAL +flag set. If a non-local file system is given as an argument, a +warning is issued and no information is given on that file system. +.It Fl n +Print out the previously obtained statistics from the file systems. +This option should be used if it is possible that one or more +file systems are in a state such that they will not be able to provide +statistics without a long delay. +When this option is specified, +.Nm +will not request new statistics from the file systems, but will respond +with the possibly stale statistics that were previously obtained. +.It Fl t Ar type +Is used to indicate the actions should only be taken on +filesystems of the specified type. +More than one type may be specified in a comma separated list. +The list of filesystem types can be prefixed with +.Dq no +to specify the filesystem types for which action should +.Em not +be taken. If a file system is given on the command line that is not of +the specified type, a warning is issued and no information is given on +that file system. +.El +.Sh ENVIRONMENT VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts will be displayed in units of that +size block. +.El +.Sh SEE ALSO +.Xr quota 1 , +.Xr statfs 2 , +.Xr fstatfs 2 , +.Xr getfsstat 2 , +.Xr getmntinfo 3 , +.Xr fstab 5 , +.Xr mount 8 , +.Xr quot 8 +.Sh HISTORY +A +.Nm +utility appeared in +.At v6 . diff --git a/df/df.c b/df/df.c new file mode 100644 index 0000000..0a1cadd --- /dev/null +++ b/df/df.c @@ -0,0 +1,455 @@ +/* $NetBSD: df.c,v 1.30 1998/07/28 05:31:24 mycroft Exp $ */ + +/* + * Copyright (c) 1980, 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT( +"@(#) Copyright (c) 1980, 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)df.c 8.7 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: df.c,v 1.30 1998/07/28 05:31:24 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include + +#include + +#ifdef __APPLE__ +#define MOUNT_FFS "ffs" +#endif + +#include +#include +#include +#include +#include +#include +#include + +extern char * strpct __P((u_long num, u_long denom, u_int digits)); + +int main __P((int, char *[])); +int bread __P((off_t, void *, int)); +char *getmntpt __P((char *)); +void prtstat __P((struct statfs *, int)); +int ufs_df __P((char *, struct statfs *)); +int selected __P((const char *)); +void maketypelist __P((char *)); +long regetmntinfo __P((struct statfs **, long)); +void usage __P((void)); + +int iflag, kflag, lflag, nflag; +char **typelist = NULL; +struct ufs_args mdev; + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat stbuf; + struct statfs *mntbuf; + long mntsize; + int ch, i, maxwidth, width; + char *mntpt; + + while ((ch = getopt(argc, argv, "iklnt:")) != -1) + switch (ch) { + case 'i': + iflag = 1; + break; + case 'k': + kflag = 1; + break; + case 'l': + lflag = 1; + break; + case 'n': + nflag = 1; + break; + case 't': + if (typelist != NULL) + errx(1, "only one -t option may be specified."); + maketypelist(optarg); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + if (mntsize == 0) + err(1, "retrieving information on mounted file systems"); + + if (!*argv) { + mntsize = regetmntinfo(&mntbuf, mntsize); + } else { + mntbuf = malloc(argc * sizeof(struct statfs)); + mntsize = 0; + for (; *argv; argv++) { + if (stat(*argv, &stbuf) < 0) { + if ((mntpt = getmntpt(*argv)) == 0) { + warn("%s", *argv); + continue; + } + } else if (S_ISCHR(stbuf.st_mode)) { + if (!ufs_df(*argv, &mntbuf[mntsize])) + ++mntsize; + continue; + } else if (S_ISBLK(stbuf.st_mode)) { + if ((mntpt = getmntpt(*argv)) == 0) { +#ifdef __APPLE__ /* We lack mkdtemp() */ + mntpt = mktemp(strdup("/tmp/df.XXXXXX")); + mdev.fspec = *argv; + if (mkdir(mntpt, DEFFILEMODE) != 0) { +#else + mntpt = strdup("/tmp/df.XXXXXX"); + if (!mkdtemp(mntpt)) { +#endif + warn("%s", mntpt); + continue; + } + mdev.fspec = *argv; + if (mount(MOUNT_FFS, mntpt, MNT_RDONLY, + &mdev) != 0) { + (void)rmdir(mntpt); + if (!ufs_df(*argv, + &mntbuf[mntsize])) + ++mntsize; + continue; + } else if (!statfs(mntpt, + &mntbuf[mntsize])) { + mntbuf[mntsize].f_mntonname[0] = + '\0'; + ++mntsize; + } else + warn("%s", *argv); + (void)unmount(mntpt, 0); + (void)rmdir(mntpt); + continue; + } + } else + mntpt = *argv; + /* + * Statfs does not take a `wait' flag, so we cannot + * implement nflag here. + */ + if (!statfs(mntpt, &mntbuf[mntsize])) + if (lflag && + (mntbuf[mntsize].f_flags & MNT_LOCAL) == 0) + warnx("Warning: %s is not a local %s", + *argv, "file system"); + else if + (!selected(mntbuf[mntsize].f_fstypename)) + warnx("Warning: %s mounted as a %s %s", + *argv, + mntbuf[mntsize].f_fstypename, + "file system"); + else + ++mntsize; + else + warn("%s", *argv); + } + } + + maxwidth = 0; + for (i = 0; i < mntsize; i++) { + width = strlen(mntbuf[i].f_mntfromname); + if (width > maxwidth) + maxwidth = width; + } + for (i = 0; i < mntsize; i++) + prtstat(&mntbuf[i], maxwidth); + exit(0); + /* NOTREACHED */ +} + +char * +getmntpt(name) + char *name; +{ + long mntsize, i; + struct statfs *mntbuf; + + mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); + for (i = 0; i < mntsize; i++) { + if (!strcmp(mntbuf[i].f_mntfromname, name)) + return (mntbuf[i].f_mntonname); + } + return (0); +} + +static enum { IN_LIST, NOT_IN_LIST } which; + +int +selected(type) + const char *type; +{ + char **av; + + /* If no type specified, it's always selected. */ + if (typelist == NULL) + return (1); + for (av = typelist; *av != NULL; ++av) + if (!strncmp(type, *av, MFSNAMELEN)) + return (which == IN_LIST ? 1 : 0); + return (which == IN_LIST ? 0 : 1); +} + +void +maketypelist(fslist) + char *fslist; +{ + int i; + char *nextcp, **av; + + if ((fslist == NULL) || (fslist[0] == '\0')) + errx(1, "empty type list"); + + /* + * XXX + * Note: the syntax is "noxxx,yyy" for no xxx's and + * no yyy's, not the more intuitive "noyyy,noyyy". + */ + if (fslist[0] == 'n' && fslist[1] == 'o') { + fslist += 2; + which = NOT_IN_LIST; + } else + which = IN_LIST; + + /* Count the number of types. */ + for (i = 1, nextcp = fslist; + (nextcp = strchr(nextcp, ',')) != NULL; i++) + ++nextcp; + + /* Build an array of that many types. */ + if ((av = typelist = malloc((i + 1) * sizeof(char *))) == NULL) + err(1, "can't allocate type array"); + av[0] = fslist; + for (i = 1, nextcp = fslist; + (nextcp = strchr(nextcp, ',')) != NULL; i++) { + *nextcp = '\0'; + av[i] = ++nextcp; + } + /* Terminate the array. */ + av[i] = NULL; +} + +/* + * Make a pass over the filesystem info in ``mntbuf'' filtering out + * filesystem types not in ``fsmask'' and possibly re-stating to get + * current (not cached) info. Returns the new count of valid statfs bufs. + */ +long +regetmntinfo(mntbufp, mntsize) + struct statfs **mntbufp; + long mntsize; +{ + int i, j; + struct statfs *mntbuf; + + if (!lflag && typelist == NULL) + return (nflag ? mntsize : getmntinfo(mntbufp, MNT_WAIT)); + + mntbuf = *mntbufp; + j = 0; + for (i = 0; i < mntsize; i++) { + if (lflag && (mntbuf[i].f_flags & MNT_LOCAL) == 0) + continue; + if (!selected(mntbuf[i].f_fstypename)) + continue; + if (nflag) + mntbuf[j] = mntbuf[i]; + else + (void)statfs(mntbuf[i].f_mntonname, &mntbuf[j]); + j++; + } + return (j); +} + +/* + * Convert statfs returned filesystem size into BLOCKSIZE units. + * Attempts to avoid overflow for large filesystems. + */ +#define fsbtoblk(num, fsbs, bs) \ + ((long int) ((((float) fsbs) != 0 && ((float) fsbs) < ((float) bs)) ? \ + ((float) num) / (((float) bs) / ((float) fsbs)) : ((float) num) * (((float) fsbs) / ((float) bs)))) + +/* + * Print out status about a filesystem. + */ +void +prtstat(sfsp, maxwidth) + struct statfs *sfsp; + int maxwidth; +{ + static long blocksize; + static int headerlen, timesthrough; + static char *header; + long used, availblks, inodes; + static char *full = "100%"; + + if (maxwidth < 11) + maxwidth = 11; + if (++timesthrough == 1) { + if (kflag) { + blocksize = 1024; + header = "1K-blocks"; + headerlen = strlen(header); + } else + header = getbsize(&headerlen, &blocksize); + (void)printf("%-*.*s %s Used Avail Capacity", + maxwidth, maxwidth, "Filesystem", header); + if (iflag) + (void)printf(" iused ifree %%iused"); + (void)printf(" Mounted on\n"); + } + (void)printf("%-*.*s", maxwidth, maxwidth, sfsp->f_mntfromname); + used = sfsp->f_blocks - sfsp->f_bfree; + availblks = sfsp->f_bavail + used; + (void)printf(" %*ld %8ld %8ld", headerlen, + fsbtoblk(sfsp->f_blocks, sfsp->f_bsize, blocksize), + fsbtoblk(used, sfsp->f_bsize, blocksize), + fsbtoblk(sfsp->f_bavail, sfsp->f_bsize, blocksize)); + (void)printf(" %6s", + availblks == 0 ? full : strpct((u_long)used, (u_long)availblks, 0)); + if (iflag) { + inodes = sfsp->f_files; + used = inodes - sfsp->f_ffree; + (void)printf(" %7ld %7ld %6s ", used, sfsp->f_ffree, + inodes == 0 ? full : strpct((u_long)used, (u_long)inodes, 0)); + } else + (void)printf(" "); + (void)printf(" %s\n", sfsp->f_mntonname); +} + +/* + * This code constitutes the pre-system call Berkeley df code for extracting + * information from filesystem superblocks. + */ +#include +#include +#include +#include + +union { + struct fs iu_fs; + char dummy[SBSIZE]; +} sb; +#define sblock sb.iu_fs + +int rfd; + +int +ufs_df(file, sfsp) + char *file; + struct statfs *sfsp; +{ + char *mntpt; + static int synced; + + if (synced++ == 0) + sync(); + + if ((rfd = open(file, O_RDONLY)) < 0) { + warn("%s", file); + return (-1); + } + if (bread((off_t)SBOFF, &sblock, SBSIZE) == 0) { + (void)close(rfd); + return (-1); + } + sfsp->f_type = 0; + sfsp->f_flags = 0; + sfsp->f_bsize = sblock.fs_fsize; + sfsp->f_iosize = sblock.fs_bsize; + sfsp->f_blocks = sblock.fs_dsize; + sfsp->f_bfree = sblock.fs_cstotal.cs_nbfree * sblock.fs_frag + + sblock.fs_cstotal.cs_nffree; + sfsp->f_bavail = freespace(&sblock, sblock.fs_minfree); + sfsp->f_files = sblock.fs_ncg * sblock.fs_ipg; + sfsp->f_ffree = sblock.fs_cstotal.cs_nifree; + sfsp->f_fsid.val[0] = 0; + sfsp->f_fsid.val[1] = 0; + if ((mntpt = getmntpt(file)) == 0) + mntpt = ""; + (void)memmove(&sfsp->f_mntonname[0], mntpt, MNAMELEN); + (void)memmove(&sfsp->f_mntfromname[0], file, MNAMELEN); + (void)strncpy(sfsp->f_fstypename, MOUNT_FFS, MFSNAMELEN); + (void)close(rfd); + return (0); +} + +int +bread(off, buf, cnt) + off_t off; + void *buf; + int cnt; +{ + int nr; + + (void)lseek(rfd, off, SEEK_SET); + if ((nr = read(rfd, buf, cnt)) != cnt) { + /* Probably a dismounted disk if errno == EIO. */ + if (errno != EIO) + (void)fprintf(stderr, "\ndf: %qd: %s\n", + (long long)off, strerror(nr > 0 ? EIO : errno)); + return (0); + } + return (1); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: df [-ikln] [-t type] [file | file_system ...]\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/du/Makefile b/du/Makefile new file mode 100644 index 0000000..d4a2b75 --- /dev/null +++ b/du/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = du + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = du.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble du.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/du/Makefile.postamble b/du/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/du/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/du/Makefile.preamble b/du/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/du/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/du/PB.project b/du/PB.project new file mode 100644 index 0000000..6334025 --- /dev/null +++ b/du/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (du.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, du.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 = du; + 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/du/du.1 b/du/du.1 new file mode 100644 index 0000000..6c5ca9c --- /dev/null +++ b/du/du.1 @@ -0,0 +1,134 @@ +.\" $NetBSD: du.1,v 1.8 1998/02/15 17:08:17 kleink Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)du.1 8.2 (Berkeley) 4/1/94 +.\" +.Dd October 4, 1996 +.Dt DU 1 +.Os +.Sh NAME +.Nm du +.Nd display disk usage statistics +.Sh SYNOPSIS +.Nm +.Op Fl H | Fl L | Fl P +.Op Fl a | Fl s +.Op Fl ckrx +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm +utility displays the file system block usage for each file argument +and for each directory in the file hierarchy rooted in each directory +argument. +If no file is specified, the block usage of the hierarchy rooted in +the current directory is displayed. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl H +Symbolic links on the command line are followed. +(Symbolic links encountered in the tree traversal are not followed.) +.It Fl L +All symbolic links are followed. +.It Fl P +No symbolic links are followed. +.It Fl a +Display an entry for each file in the file hierarchy. +.It Fl k +By default, +.Nm +displays the number of blocks as returned by the +.Xr stat 2 +system call, i.e. 512-byte blocks. +If the +.Fl k +flag is specified, the number displayed is the number of 1024-byte +blocks. +Partial numbers of blocks are rounded up. +.It Fl c +Display the grand total after all the arguments have been processed. +.It Fl r +Generate warning messages about directories that cannot be read. +This is the default behaviour. +.It Fl s +Display only the grand total for the specified files. +.It Fl x +Filesystem mount points are not traversed. +.El +.Pp +.Nm +counts the storage used by symbolic links and not the files they +reference unless the +.Fl H +or +.Fl L +option is specified. +If either the +.Fl H +or +.Fl L +options are specified, storage used by any symbolic links which are +followed is not counted or displayed. +The +.Fl H , +.Fl L +and +.Fl P +options override each other and the command's actions are determined +by the last one specified. +.Pp +Files having multiple hard links are counted (and displayed) a single +time per +.Nm +execution. +.Sh ENVIRONMENT VARIABLES +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts will be displayed in units of that +size block. +.El +.Sh SEE ALSO +.Xr df 1 , +.Xr fts 3 , +.Xr symlink 7 , +.Xr quot 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . diff --git a/du/du.c b/du/du.c new file mode 100644 index 0000000..3d6914f --- /dev/null +++ b/du/du.c @@ -0,0 +1,255 @@ +/* $NetBSD: du.c,v 1.14 1998/02/15 17:08:18 kleink Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Chris Newcomb. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)du.c 8.5 (Berkeley) 5/4/95"; +#else +__RCSID("$NetBSD: du.c,v 1.14 1998/02/15 17:08:18 kleink Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +int linkchk __P((FTSENT *)); +int main __P((int, char **)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + FTS *fts; + FTSENT *p; + long blocksize, totalblocks; + int ftsoptions, listdirs, listfiles; + int Hflag, Lflag, Pflag, aflag, ch, cflag, kflag, notused, rval, sflag; + char **save; + + save = argv; + Hflag = Lflag = Pflag = aflag = cflag = kflag = sflag = 0; + totalblocks = 0; + ftsoptions = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "HLPackrsx")) != -1) + switch (ch) { + case 'H': + Hflag = 1; + Lflag = Pflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = Pflag = 0; + break; + case 'P': + Pflag = 1; + Hflag = Lflag = 0; + break; + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'k': + blocksize = 1024; + kflag = 1; + break; + case 'r': + break; + case 's': + sflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * XXX + * Because of the way that fts(3) works, logical walks will not count + * the blocks actually used by symbolic links. We rationalize this by + * noting that users computing logical sizes are likely to do logical + * copies, so not counting the links is correct. The real reason is + * that we'd have to re-implement the kernel's symbolic link traversing + * algorithm to get this right. If, for example, you have relative + * symbolic links referencing other relative symbolic links, it gets + * very nasty, very fast. The bottom line is that it's documented in + * the man page, so it's a feature. + */ + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + if (aflag) { + if (sflag) + usage(); + listdirs = listfiles = 1; + } else if (sflag) + listdirs = listfiles = 0; + else { + listfiles = 0; + listdirs = 1; + } + + if (!*argv) { + argv = save; + argv[0] = "."; + argv[1] = NULL; + } + + if (!kflag) + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + + if ((fts = fts_open(argv, ftsoptions, NULL)) == NULL) + err(1, "fts_open `%s'", *argv); + + for (rval = 0; (p = fts_read(fts)) != NULL;) + switch (p->fts_info) { + case FTS_D: /* Ignore. */ + break; + case FTS_DP: + p->fts_parent->fts_number += + p->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + /* + * If listing each directory, or not listing files + * or directories and this is post-order of the + * root of a traversal, display the total. + */ + if (listdirs || (!listfiles && !p->fts_level)) + (void)printf("%ld\t%s\n", + howmany(p->fts_number, blocksize), + p->fts_path); + break; + case FTS_DC: /* Ignore. */ + break; + case FTS_DNR: /* Warn, continue. */ + case FTS_ERR: + case FTS_NS: + warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); + rval = 1; + break; + default: + if (p->fts_statp->st_nlink > 1 && linkchk(p)) + break; + /* + * If listing each file, or a non-directory file was + * the root of a traversal, display the total. + */ + if (listfiles || !p->fts_level) + (void)printf("%qd\t%s\n", (long long) + howmany(p->fts_statp->st_blocks, blocksize), + p->fts_path); + p->fts_parent->fts_number += p->fts_statp->st_blocks; + if (cflag) + totalblocks += p->fts_statp->st_blocks; + } + if (errno) + err(1, "fts_read"); + if (cflag) + (void)printf("%ld\ttotal\n", + howmany(totalblocks, blocksize)); + exit(0); +} + +typedef struct _ID { + dev_t dev; + ino_t inode; +} ID; + +int +linkchk(p) + FTSENT *p; +{ + static ID *files; + static int maxfiles, nfiles; + ID *fp, *start; + ino_t ino; + dev_t dev; + + ino = p->fts_statp->st_ino; + 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) + return (1); + + if (nfiles == maxfiles && (files = realloc((char *)files, + (u_int)(sizeof(ID) * (maxfiles += 128)))) == NULL) + err(1, "realloc"); + files[nfiles].inode = ino; + files[nfiles].dev = dev; + ++nfiles; + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, + "usage: du [-H | -L | -P] [-a | -s] [-ckx] [file ...]\n"); + exit(1); +} diff --git a/install/Makefile b/install/Makefile new file mode 100644 index 0000000..d36083c --- /dev/null +++ b/install/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = install + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = pathnames.h + +CFILES = xinstall.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble install.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/install/Makefile.postamble b/install/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/install/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/install/Makefile.preamble b/install/Makefile.preamble new file mode 100644 index 0000000..7a86fc2 --- /dev/null +++ b/install/Makefile.preamble @@ -0,0 +1,5 @@ +vpath stat_flags.c ../ls + +CFILES += stat_flags.c + +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/install/PB.project b/install/PB.project new file mode 100644 index 0000000..519a9e7 --- /dev/null +++ b/install/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (pathnames.h); + OTHER_LINKED = (xinstall.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, install.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 = install; + 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/install/install.1 b/install/install.1 new file mode 100644 index 0000000..e9acb8c --- /dev/null +++ b/install/install.1 @@ -0,0 +1,183 @@ +.\" $NetBSD: install.1,v 1.11 1998/09/28 08:16:15 christos Exp $ +.\" +.\" Copyright (c) 1987, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)install.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt INSTALL 1 +.Os BSD 4.2 +.Sh NAME +.Nm install +.Nd install binaries +.Sh SYNOPSIS +.Nm +.Op Fl cps +.Op Fl f Ar flags +.Op Fl m Ar mode +.Op Fl o Ar owner +.Op Fl g Ar group +.Op Fl l Ar linkflags +.Op Fl S Ar stripflag +.Ar file1 file2 +.Nm "" +.Op Fl cps +.Op Fl f Ar flags +.Op Fl m Ar mode +.Op Fl o Ar owner +.Op Fl g Ar group +.Op Fl l Ar linkflags +.Op Fl S Ar stripflag +.Ar file1 +\&... +.Ar fileN directory +.Nm "" +.Fl pd +.Op Fl m Ar mode +.Op Fl o Ar owner +.Op Fl g Ar group +.Ar directory +\&... +.Sh DESCRIPTION +The file(s) are moved (copied if the +.Fl c +option is specified, or linked if the +.Fl l +option is specified) to the target file or directory. +If the destination is a directory, then the +.Ar file +is moved into +.Ar directory +with its original filename. +If the target file already exists, it is overwritten if permissions +allow. +.Pp +.Bl -tag -width Ds +.It Fl c +Copy the file. +This flag turns off the default behavior of +.Nm +where it deletes the original file after creating the target. +.It Fl f +Specify the target's file flags. +(See +.Xr chflags 1 +for a list of possible flags and their meanings.) +.It Fl g +Specify a group. +.It Fl m +Specify an alternative mode. +The default mode is set to rwxr-xr-x (0755). +The specified mode may be either an octal or symbolic value; see +.Xr chmod 1 +for a description of possible mode values. +.It Fl l Ar linkflags +Instead of copying the file make a link to the source. The type of the +link is determined by the +.Ar linkflags +argument. Valid +.Ar linkflags +are: +.Ar a +(absolute), +.Ar r +(relative), +.Ar h +(hard), +.Ar s +(symbolic), +.Ar m +(mixed). Absolute and relative have effect only for symbolic links. Mixed links +are hard links for files on the same filesystem, symbolic otherwise. +.It Fl o +Specify an owner. +.It Fl p +Preserve the source files access and modification times. +.It Fl s +.Nm +exec's the command +.Xr strip 1 +to strip binaries so that install can be portable over a large +number of systems and binary types. If the environment variable +.Ev STRIP +is set, it is used as the +.Xr strip 1 +program. +.It Fl S Ar stripflags +.Nm +passes +.Ar stripflags +as option arguments to +.Xr strip 1 . +When -S is used, +.Xr strip 1 +is invoked via the +.Xr sh 1 +shell, allowing a single -S argument be to specified to +.Nm +which the shell can then tokenize. Normally, +.Nm +invokes +.Xr strip 1 +directly. This flag implies -s. +.It Fl d +Create directories. +Missing parent directories are created as required. +.El +.Pp +By default, +.Nm +preserves all file flags, with the exception of the ``nodump'' flag. +.Pp +The +.Nm +utility attempts to prevent moving a file onto itself. +.Pp +Installing +.Pa /dev/null +creates an empty file. +.Pp +Upon successful completion a value of 0 is returned. +Otherwise, a value of 1 is returned. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr mv 1 , +.Xr strip 1 , +.Xr chown 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . diff --git a/install/pathnames.h b/install/pathnames.h new file mode 100644 index 0000000..7af9165 --- /dev/null +++ b/install/pathnames.h @@ -0,0 +1,38 @@ +/* $NetBSD: pathnames.h,v 1.4 1997/12/30 22:31:17 thorpej Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#define _PATH_STRIP "/usr/bin/strip" diff --git a/install/xinstall.c b/install/xinstall.c new file mode 100644 index 0000000..fa51bab --- /dev/null +++ b/install/xinstall.c @@ -0,0 +1,563 @@ +/* $NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#else +__RCSID("$NetBSD: xinstall.c,v 1.27 1998/10/01 18:23:52 erh Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +#define STRIP_ARGS_MAX 32 + +struct passwd *pp; +struct group *gp; +int docopy=0, dodir=0, dostrip=0, dolink=0, dopreserve=0; +int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; +char pathbuf[MAXPATHLEN]; +uid_t uid; +gid_t gid; +char *stripArgs=NULL; + +#define LN_ABSOLUTE 0x01 +#define LN_RELATIVE 0x02 +#define LN_HARD 0x04 +#define LN_SYMBOLIC 0x08 +#define LN_MIXED 0x10 + +#define DIRECTORY 0x01 /* Tell install it's a directory. */ +#define SETFLAGS 0x02 /* Tell install to set flags. */ + +void copy __P((int, char *, int, char *, off_t)); +void makelink __P((char *, char *)); +void install __P((char *, char *, u_long, u_int)); +void install_dir __P((char *)); +u_long string_to_flags __P((char **, u_long *, u_long *)); +void strip __P((char *)); +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat from_sb, to_sb; + mode_t *set; + u_long fset; + u_int iflags; + int ch, no_target; + char *p; + char *flags = NULL, *to_name, *group = NULL, *owner = NULL; + + iflags = 0; + while ((ch = getopt(argc, argv, "cdf:g:l:m:o:psS:")) != -1) + switch((char)ch) { + case 'c': + docopy = 1; + break; + case 'd': + dodir = 1; + break; + case 'f': + flags = optarg; + if (string_to_flags(&flags, &fset, NULL)) + errx(1, "%s: invalid flag", flags); + iflags |= SETFLAGS; + break; + case 'g': + group = optarg; + break; + case 'l': + for (p = optarg; *p; p++) + switch (*p) { + case 's': + dolink &= ~(LN_HARD|LN_MIXED); + dolink |= LN_SYMBOLIC; + break; + case 'h': + dolink &= ~(LN_SYMBOLIC|LN_MIXED); + dolink |= LN_HARD; + break; + case 'm': + dolink &= ~(LN_SYMBOLIC|LN_HARD); + dolink |= LN_MIXED; + break; + case 'a': + dolink &= ~LN_RELATIVE; + dolink |= LN_ABSOLUTE; + break; + case 'r': + dolink &= ~LN_ABSOLUTE; + dolink |= LN_RELATIVE; + break; + default: + errx(1, "%c: invalid link type", *p); + break; + } + break; + case 'm': + if (!(set = setmode(optarg))) + errx(1, "%s: invalid file mode", optarg); + mode = getmode(set, 0); + break; + case 'o': + owner = optarg; + break; + case 'p': + dopreserve = 1; + break; + case 'S': + stripArgs = (char*)malloc(sizeof(char)*(strlen(optarg)+1)); + strcpy(stripArgs,optarg); + /* fall through; -S implies -s */ + case 's': + dostrip = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* strip and link options make no sense when creating directories */ + if ((dostrip || dolink) && dodir) + usage(); + + /* strip and flags make no sense with links */ + if ((dostrip || flags) && dolink) + usage(); + + /* must have at least two arguments, except when creating directories */ + if (argc < 2 && !dodir) + usage(); + + /* get group and owner id's */ + if (group && !(gp = getgrnam(group)) && !isdigit(*group)) + errx(1, "unknown group %s", group); + gid = (group) ? ((gp) ? gp->gr_gid : atoi(group)) : -1; + if (owner && !(pp = getpwnam(owner)) && !isdigit(*owner)) + errx(1, "unknown user %s", owner); + uid = (owner) ? ((pp) ? pp->pw_uid : atoi(owner)) : -1; + + if (dodir) { + for (; *argv != NULL; ++argv) + install_dir(*argv); + exit (0); + } + + no_target = stat(to_name = argv[argc - 1], &to_sb); + if (!no_target && S_ISDIR(to_sb.st_mode)) { + for (; *argv != to_name; ++argv) + install(*argv, to_name, fset, iflags | DIRECTORY); + exit(0); + } + + /* can't do file1 file2 directory/file */ + if (argc != 2) + usage(); + + if (!no_target) { + if (stat(*argv, &from_sb)) + err(1, "%s", *argv); + if (!S_ISREG(to_sb.st_mode)) + errx(1, "%s: %s", to_name, strerror(EFTYPE)); + if (!dolink && to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(1, "%s and %s are the same file", *argv, to_name); + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, + to_sb.st_flags & ~(NOCHANGEBITS)); + (void)unlink(to_name); + } + install(*argv, to_name, fset, iflags); + exit(0); +} + +/* + * makelink -- + * make a link from source to destination + */ +void +makelink(from_name, to_name) + char *from_name; + char *to_name; +{ + char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; + + /* Try hard links first */ + if (dolink & (LN_HARD|LN_MIXED)) { + if (link(from_name, to_name) == -1) { + if ((dolink & LN_HARD) || errno != EXDEV) + err(1, "link %s -> %s", from_name, to_name); + } + else + return; + } + + /* Symbolic links */ + if (dolink & LN_ABSOLUTE) { + /* Convert source path to absolute */ + if (realpath(from_name, src) == NULL) + err(1, "%s", src); + if (symlink(src, to_name) == -1) + err(1, "symlink %s -> %s", src, to_name); + return; + } + + if (dolink & LN_RELATIVE) { + char *s, *d; + + /* Resolve pathnames */ + if (realpath(from_name, src) == NULL) + err(1, "%s", src); + if (realpath(to_name, dst) == NULL) + err(1, "%s", dst); + + /* trim common path components */ + for (s = src, d = dst; *s == *d; s++, d++) + continue; + while (*s != '/') + s--, d--; + + /* count the number of directories we need to backtrack */ + for (++d, lnk[0] = '\0'; *d; d++) + if (*d == '/') + (void) strcat(lnk, "../"); + + (void) strcat(lnk, ++s); + + if (symlink(lnk, dst) == -1) + err(1, "symlink %s -> %s", lnk, dst); + return; + } + + /* + * If absolute or relative was not specified, + * try the names the user provided + */ + if (symlink(from_name, to_name) == -1) + err(1, "symlink %s -> %s", from_name, to_name); + +} + +/* + * install -- + * build a path name and install the file + */ +void +install(from_name, to_name, fset, flags) + char *from_name, *to_name; + u_long fset; + u_int flags; +{ + struct stat from_sb, to_sb; + int devnull, from_fd, to_fd, serrno; + char *p; + + /* If try to install NULL file to a directory, fails. */ + if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) { + if (stat(from_name, &from_sb)) + err(1, "%s", from_name); + if (!S_ISREG(from_sb.st_mode)) + errx(1, "%s: %s", from_name, strerror(EFTYPE)); + /* Build the target path. */ + if (flags & DIRECTORY) { + (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + to_name, + (p = strrchr(from_name, '/')) ? ++p : from_name); + to_name = pathbuf; + } + devnull = 0; + } else { + from_sb.st_flags = 0; /* XXX */ + devnull = 1; + } + + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ + if (stat(to_name, &to_sb) == 0 && + to_sb.st_flags & (NOCHANGEBITS)) + (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); + (void)unlink(to_name); + + if (dolink) { + makelink(from_name, to_name); + return; + } + + /* Create target. */ + if ((to_fd = open(to_name, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) + err(1, "%s", to_name); + if (!devnull) { + if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { + (void)unlink(to_name); + err(1, "%s", from_name); + } + copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); + (void)close(from_fd); + } + + if (dostrip) { + strip(to_name); + + /* + * Re-open our fd on the target, in case we used a strip + * that does not work in-place -- like gnu binutils strip. + */ + close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) + err(1, "stripping %s", to_name); + } + + /* + * Set owner, group, mode for target; do the chown first, + * chown may lose the setuid bits. + */ + if ((gid != -1 || uid != -1) && fchown(to_fd, uid, gid)) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); + } + if (fchmod(to_fd, mode)) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chmod: %s", to_name, strerror(serrno)); + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + */ + if (fchflags(to_fd, + flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) { + if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) + warn("%s: chflags", to_name); + } + + /* + * Preserve the date of the source file. + */ + if (dopreserve) { + struct timeval tv[2]; + + TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec); +#ifndef __APPLE__ + if (futimes(to_fd, tv) == -1) + warn("%s: futimes", to_name); +#else + if (utimes(to_name, tv) == -1) + warn("%s: utimes", to_name); +#endif + } + + (void)close(to_fd); + if (!docopy && !devnull && unlink(from_name)) + err(1, "%s", from_name); +} + +/* + * copy -- + * copy from one file to another + */ +void +copy(from_fd, from_name, to_fd, to_name, size) + int from_fd, to_fd; + char *from_name, *to_name; + off_t size; +{ + int nr, nw; + int serrno; + char *p; + char buf[MAXBSIZE]; + + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (size > 0) { + /* + * Mmap and write if less than 8M (the limit is so we don't totally + * trash memory on big files). This is really a minor hack, but it + * wins some CPU back. + */ + if (size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)size, PROT_READ, + MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) == (char *)-1) + err(1, "%s", from_name); + if (write(to_fd, p, size) != size) + err(1, "%s", to_name); + } else { + while ((nr = read(from_fd, buf, sizeof(buf))) > 0) + if ((nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: %s", + to_name, strerror(nw > 0 ? EIO : serrno)); + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: %s", from_name, strerror(serrno)); + } + } + } +} + +/* + * strip -- + * use strip(1) to strip the target file + */ +void +strip(to_name) + char *to_name; +{ + int serrno, status; + char *stripprog; + + switch (vfork()) { + case -1: + serrno = errno; + (void)unlink(to_name); + errx(1, "vfork: %s", strerror(serrno)); + case 0: + stripprog = getenv("STRIP"); + if (stripprog == NULL) + stripprog = _PATH_STRIP; + + if (stripArgs) { + /* build up a command line and let /bin/sh parse the arguments */ + char* cmd = (char*)malloc(sizeof(char)* + (3+strlen(stripprog)+ + strlen(stripArgs)+ + strlen(to_name))); + + sprintf(cmd, "%s %s %s", stripprog, stripArgs, to_name); + + execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); + } else + execl(stripprog, "strip", to_name, NULL); + + warn("%s", stripprog); + _exit(1); + default: + if (wait(&status) == -1 || status) + (void)unlink(to_name); + } +} + +/* + * install_dir -- + * build directory heirarchy + */ +void +install_dir(path) + char *path; +{ + char *p; + struct stat sb; + int ch; + + for (p = path;; ++p) + if (!*p || (p != path && *p == '/')) { + ch = *p; + *p = '\0'; + if (stat(path, &sb)) { + if (errno != ENOENT || mkdir(path, 0777) < 0) { + err(1, "%s", path); + /* NOTREACHED */ + } + } + if (!(*p = ch)) + break; + } + + if (((gid != -1 || uid != -1) && chown(path, uid, gid)) || + chmod(path, mode)) { + warn("%s", path); + } +} + +/* + * usage -- + * print a usage message and die + */ +void +usage() +{ + (void)fprintf(stderr, "\ +usage: install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 file2\n\ + install [-cps] [-f flags] [-m mode] [-o owner] [-g group] [-l linkflags] [-S stripflags] file1 ... fileN directory\n\ + install -pd [-m mode] [-o owner] [-g group] directory ...\n"); + exit(1); +} diff --git a/ln/Makefile b/ln/Makefile new file mode 100644 index 0000000..15d04bd --- /dev/null +++ b/ln/Makefile @@ -0,0 +1,49 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = ln + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = ln.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ln.1\ + symlink.7 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/ln/Makefile.postamble b/ln/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/ln/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/ln/Makefile.preamble b/ln/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/ln/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/ln/PB.project b/ln/PB.project new file mode 100644 index 0000000..fe1d0a7 --- /dev/null +++ b/ln/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (ln.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ln.1, symlink.7); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = ln; + 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/ln/ln.1 b/ln/ln.1 new file mode 100644 index 0000000..0d1a2b9 --- /dev/null +++ b/ln/ln.1 @@ -0,0 +1,147 @@ +.\" $NetBSD: ln.1,v 1.13 1997/10/20 08:52:19 enami Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 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. +.\" +.\" @(#)ln.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd December 30, 1993 +.Dt LN 1 +.Os BSD 4 +.Sh NAME +.Nm ln +.Nd make links +.Sh SYNOPSIS +.Nm +.Op Fl fhns +.Ar source_file +.Op target_file +.Nm "" +.Op Fl fhns +.Ar source_file ... +.Op target_dir +.Sh DESCRIPTION +The +.Nm +utility creates a new directory entry (linked file) which has the +same modes as the original file. +It is useful for maintaining multiple copies of a file in many places +at once without using up storage for the +.Dq copies ; +instead, a link +.Dq points +to the original copy. +There are two types of links; hard links and symbolic links. +How a link +.Dq points +to a file is one of the differences between a hard or symbolic link. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl f +Unlink any already existing file, permitting the link to occur. +.It Fl h +If the +.Ar target_file +or +.Ar target_dir +is a symbolic link, do not follow it. This is most useful with the +.Fl f +option, to replace a symlink which may point to a directory. +.It Fl n +Same as +.Fl h , +for compatibility with other +.Nm +implementations. +.It Fl s +Create a symbolic link. +.El +.Pp +By default +.Nm +makes +.Em hard +links. +A hard link to a file is indistinguishable from the original directory entry; +any changes to a file are effective independent of the name used to reference +the file. +Hard links may not normally refer to directories and may not span file systems. +.Pp +A symbolic link contains the name of the file to +which it is linked. The referenced file is used when an +.Xr open 2 +operation is performed on the link. +A +.Xr stat 2 +on a symbolic link will return the linked-to file; an +.Xr lstat 2 +must be done to obtain information about the link. +The +.Xr readlink 2 +call may be used to read the contents of a symbolic link. +Symbolic links may span file systems and may refer to directories. +.Pp +Given one or two arguments, +.Nm +creates a link to an existing file +.Ar source_file . +If +.Ar target_file +is given, the link has that name; +.Ar target_file +may also be a directory in which to place the link; +otherwise it is placed in the current directory. +If only the directory is specified, the link will be made +to the last component of +.Ar source_file . +.Pp +Given more than two arguments, +.Nm +makes links in +.Ar target_dir +to all the named source files. +The links made will have the same name as the files being linked to. +.Sh SEE ALSO +.Xr link 2 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr symlink 2 , +.Xr symlink 7 +.Sh HISTORY +A +.Nm +utility appeared in +.At v6 . diff --git a/ln/ln.c b/ln/ln.c new file mode 100644 index 0000000..f8bbce3 --- /dev/null +++ b/ln/ln.c @@ -0,0 +1,182 @@ +/* $NetBSD: ln.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $ */ + +/* + * Copyright (c) 1987, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ln.c 8.2 (Berkeley) 3/31/94"; +#else +__RCSID("$NetBSD: ln.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +int fflag; /* Unlink existing files. */ +int hflag; /* Check new name for symlink first. */ +int sflag; /* Symbolic, not hard, link. */ + /* System link call. */ +int (*linkf) __P((const char *, const char *)); + +int linkit __P((char *, char *, int)); +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + int ch, exitval; + char *sourcedir; + + while ((ch = getopt(argc, argv, "fhns")) != -1) + switch (ch) { + case 'f': + fflag = 1; + break; + case 'h': + case 'n': + hflag = 1; + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + + argv += optind; + argc -= optind; + + linkf = sflag ? symlink : link; + + switch(argc) { + case 0: + usage(); + /* NOTREACHED */ + case 1: /* ln target */ + exit(linkit(argv[0], ".", 1)); + /* NOTREACHED */ + case 2: /* ln target source */ + exit(linkit(argv[0], argv[1], 0)); + /* NOTREACHED */ + } + /* ln target1 target2 directory */ + sourcedir = argv[argc - 1]; + if (hflag && lstat(sourcedir, &sb) == 0 && S_ISLNK(sb.st_mode)) { + /* we were asked not to follow symlinks, but found one at + the target--simulate "not a directory" error */ + errno = ENOTDIR; + err(1, "%s", sourcedir); + } + if (stat(sourcedir, &sb)) + err(1, "%s", sourcedir); + if (!S_ISDIR(sb.st_mode)) + usage(); + for (exitval = 0; *argv != sourcedir; ++argv) + exitval |= linkit(*argv, sourcedir, 1); + exit(exitval); + /* NOTREACHED */ +} + +int +linkit(target, source, isdir) + char *target, *source; + int isdir; +{ + struct stat sb; + char *p, path[MAXPATHLEN]; + + if (!sflag) { + /* If target doesn't exist, quit now. */ + if (stat(target, &sb)) { + warn("%s", target); + return (1); + } + } + + /* If the source is a directory (and not a symlink if hflag), + append the target's name. */ + if (isdir || + (!lstat(source, &sb) && S_ISDIR(sb.st_mode)) || + (!hflag && !stat(source, &sb) && S_ISDIR(sb.st_mode))) { + if ((p = strrchr(target, '/')) == NULL) + p = target; + else + ++p; + (void)snprintf(path, sizeof(path), "%s/%s", source, p); + source = path; + } + + /* + * If the file exists, and -f was specified, unlink it. + * Attempt the link. + */ + if ((fflag && unlink(source) < 0 && errno != ENOENT) || + (*linkf)(target, source)) { + warn("%s", source); + return (1); + } + + return (0); +} + +void +usage() +{ + + extern char *__progname; + (void)fprintf(stderr, + "Usage:\t%s [-fhns] file1 file2\n\t%s [-fhns] file ... directory\n", + __progname, __progname); + exit(1); + /* NOTREACHED */ +} diff --git a/ln/symlink.7 b/ln/symlink.7 new file mode 100644 index 0000000..7e6e68a --- /dev/null +++ b/ln/symlink.7 @@ -0,0 +1,427 @@ +.\" $NetBSD: symlink.7,v 1.7 1998/02/06 05:39:47 perry Exp $ +.\" +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)symlink.7 8.3 (Berkeley) 3/31/94 +.\" +.Dd March 31, 1994 +.Dt SYMLINK 7 +.Os BSD 4 +.Sh NAME +.Nm symlink +.Nd symbolic link handling +.Sh SYMBOLIC LINK HANDLING +Symbolic links are files that act as pointers to other files. +To understand their behavior, you must first understand how hard links +work. +A hard link to a file is indistinguishable from the original file because +it is a reference to the object underlying the original file name. +Changes to a file are independent of the name used to reference the +file. +Hard links may not refer to directories and may not reference files +on different file systems. +A symbolic link contains the name of the file to which it is linked, +i.e. it is a pointer to another name, and not to an underlying object. +For this reason, symbolic links may reference directories and may span +file systems. +.Pp +Because a symbolic link and its referenced object coexist in the filesystem +name space, confusion can arise in distinguishing between the link itself +and the referenced object. +Historically, commands and system calls have adopted their own link +following conventions in a somewhat ad-hoc fashion. +Rules for more a uniform approach, as they are implemented in this system, +are outlined here. +It is important that local applications conform to these rules, too, +so that the user interface can be as consistent as possible. +.Pp +Symbolic links are handled either by operating on the link itself, +or by operating on the object referenced by the link. +In the latter case, +an application or system call is said to +.Dq follow +the link. +Symbolic links may reference other symbolic links, +in which case the links are dereferenced until an object that is +not a symbolic link is found, +a symbolic link which references a file which doesn't exist is found, +or a loop is detected. +(Loop detection is done by placing an upper limit on the number of +links that may be followed, and an error results if this limit is +exceeded.) +.Pp +There are three separate areas that need to be discussed. +They are as follows: +.sp +.Bl -enum -compact -offset indent +.It +Symbolic links used as file name arguments for system calls. +.It +Symbolic links specified as command line arguments to utilities that +are not traversing a file tree. +.It +Symbolic links encountered by utilities that are traversing a file tree +(either specified on the command line or encountered as part of the +file hierarchy walk). +.El +.Ss System calls. +The first area is symbolic links used as file name arguments for +system calls. +.Pp +Except as noted below, all system calls follow symbolic links. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the system call +.Dq Li open("slink" ...) +would return a file descriptor to the file +.Dq afile . +.Pp +There are four system calls that do not follow links, and which operate +on the symbolic link itself. +They are: +.Xr lstat 2 , +.Xr readlink 2 , +.Xr rename 2 , +and +.Xr unlink 2 . +Because +.Xr remove 3 +is an alias for +.Xr unlink 2 , +it also does not follow symbolic links. +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the system call +.Xr chown 2 +has been changed to follow symbolic links. +.Ss Commands not traversing a file tree. +The second area is symbolic links, specified as command line file +name arguments, to commands which are not traversing a file tree. +.Pp +Except as noted below, commands follow symbolic links named as command +line arguments. +For example, if there were a symbolic link +.Dq Li slink +which pointed to a file named +.Dq Li afile , +the command +.Dq Li cat slink +would display the contents of the file +.Dq Li afile . +.Pp +It is important to realize that this rule includes commands which may +optionally traverse file trees, e.g. the command +.Dq Li "chown file" +is included in this rule, while the command +.Dq Li "chown -R file" +is not. +(The latter is described in the third area, below.) +.Pp +If it is explicitly intended that the command operate on the symbolic +link instead of following the symbolic link, e.g., it is desired that +.Dq Li "file slink" +display the type of file that +.Dq Li slink +is, whether it is a symbolic link or not, the +.Fl h +option should be used. +In the above example, +.Dq Li "file slink" +would report the type of the file referenced by +.Dq Li slink , +while +.Dq Li "file -h slink" +would report that +.Dq Li slink +was a symbolic link. +.Pp +There are three exceptions to this rule. +The +.Xr mv 1 +and +.Xr rm 1 +commands do not follow symbolic links named as arguments, +but respectively attempt to rename and delete them. +(Note, if the symbolic link references a file via a relative path, +moving it to another directory may very well cause it to stop working, +since the path may no longer be correct.) +.Pp +The +.Xr ls 1 +command is also an exception to this rule. +For compatibility with historic systems (when +.Nm ls +is not doing a tree walk, i.e. the +.Fl R +option is not specified), +the +.Nm ls +command follows symbolic links named as arguments if the +.Fl L +option is specified, +or if the +.Fl F , +.Fl d +or +.Fl l +options are not specified. +(If the +.Fl L +option is specified, +.Nm ls +always follows symbolic links. +.Nm ls +is the only command where the +.Fl L +option affects its behavior even though it is not doing a walk of +a file tree.) +.Pp +The +.Bx 4.4 +system differs from historical +.Bx 4 +systems in that the +.Nm chown , +.Nm chgrp +and +.Nm file +commands follow symbolic links specified on the command line. +.Ss Commands traversing a file tree. +The following commands either optionally or always traverse file trees: +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ls 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 +and +.Xr chown 8 . +.Pp +It is important to realize that the following rules apply equally to +symbolic links encountered during the file tree traversal and symbolic +links listed as command line arguments. +.Pp +The first rule applies to symbolic links that reference files that are +not of type directory. +Operations that apply to symbolic links are performed on the links +themselves, but otherwise the links are ignored. +.Pp +For example, the command +.Dq Li "chown -R user slink directory" +will ignore +.Dq Li slink , +because symbolic links in this system do not have owners. +Any symbolic links encountered during the tree traversal will also be +ignored. +The command +.Dq Li "rm -r slink directory" +will remove +.Dq Li slink , +as well as any symbolic links encountered in the tree traversal of +.Dq Li directory , +because symbolic links may be removed. +In no case will either +.Nm chown +or +.Nm rm +affect the file which +.Dq Li slink +references in any way. +.Pp +The second rule applies to symbolic links that reference files of type +directory. +Symbolic links which reference files of type directory are never +.Dq followed +by default. +This is often referred to as a +.Dq physical +walk, as opposed to a +.Dq logical +walk (where symbolic links referencing directories are followed). +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, regardless +of the type of file they reference, by specifying the +.Fl H +(for +.Dq half\-logical ) +flag. +This flag is intended to make the command line name space look +like the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl H +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -HR user slink" +will traverse the file hierarchy rooted in the file pointed to by +.Dq Li slink . +Note, the +.Fl H +is not the same as the previously discussed +.Fl h +flag. +The +.Fl H +flag causes symbolic links specified on the command line to be +dereferenced both for the purposes of the action to be performed +and the tree walk, and it is as if the user had specified the +name of the file to which the symbolic link pointed. +.Pp +As consistently as possible, you can make commands doing a file tree +walk follow any symbolic links named on the command line, as well as +any symbolic links encountered during the traversal, regardless of +the type of file they reference, by specifying the +.Fl L +(for +.Dq logical ) +flag. +This flag is intended to make the entire name space look like +the logical name space. +(Note, for commands that do not always do file tree traversals, the +.Fl L +flag will be ignored if the +.Fl R +flag is not also specified.) +.Pp +For example, the command +.Dq Li "chown -LR user slink" +will change the owner of the file referenced by +.Dq Li slink . +If +.Dq Li slink +references a directory, +.Nm chown +will traverse the file hierarchy rooted in the directory that it +references. +In addition, if any symbolic links are encountered in any file tree that +.Nm chown +traverses, they will be treated in the same fashion as +.Dq Li slink . +.Pp +As consistently as possible, you can specify the default behavior by +specifying the +.Fl P +(for +.Dq physical ) +flag. +This flag is intended to make the entire name space look like the +physical name space. +.Pp +For commands that do not by default do file tree traversals, the +.Fl H , +.Fl L +and +.Fl P +flags are ignored if the +.Fl R +flag is not also specified. +In addition, you may specify the +.Fl H , +.Fl L +and +.Fl P +options more than once; the last one specified determines the +command's behavior. +This is intended to permit you to alias commands to behave one way +or the other, and then override that behavior on the command line. +.Pp +The +.Xr ls 1 +and +.Xr rm 1 +commands have exceptions to these rules. +The +.Nm rm +command operates on the symbolic link, and not the file it references, +and therefore never follows a symbolic link. +The +.Nm rm +command does not support the +.Fl H , +.Fl L +or +.Fl P +options. +.Pp +To maintain compatibility with historic systems, +the +.Nm ls +command never follows symbolic links unless the +.Fl L +flag is specified. +If the +.Fl L +flag is specified, +.Nm ls +follows all symbolic links, +regardless of their type, +whether specified on the command line or encountered in the tree walk. +The +.Nm ls +command does not support the +.Fl H +or +.Fl P +options. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr du 1 , +.Xr find 1 , +.Xr ln 1 , +.Xr ls 1 , +.Xr mv 1 , +.Xr pax 1 , +.Xr rm 1 , +.Xr tar 1 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr rename 2 , +.Xr symlink 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr remove 3 , +.Xr chown 8 diff --git a/ls/Makefile b/ls/Makefile new file mode 100644 index 0000000..f29efd4 --- /dev/null +++ b/ls/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = ls + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = extern.h ls.h + +CFILES = cmp.c ls.c print.c stat_flags.c util.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble ls.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/ls/Makefile.postamble b/ls/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/ls/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/ls/Makefile.preamble b/ls/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/ls/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/ls/PB.project b/ls/PB.project new file mode 100644 index 0000000..32e2a56 --- /dev/null +++ b/ls/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (extern.h, ls.h); + OTHER_LINKED = (cmp.c, ls.c, print.c, stat_flags.c, util.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, ls.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = ls; + 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/ls/cmp.c b/ls/cmp.c new file mode 100644 index 0000000..31b7565 --- /dev/null +++ b/ls/cmp.c @@ -0,0 +1,203 @@ +/* $NetBSD: cmp.c,v 1.14 1998/10/09 02:00:39 enami Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cmp.c 8.1 (Berkeley) 5/31/93"; +#else +__RCSID("$NetBSD: cmp.c,v 1.14 1998/10/09 02:00:39 enami Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include + +#include "ls.h" +#include "extern.h" + +#if defined(_POSIX_SOURCE) || defined(_POSIX_C_SOURCE) || \ + defined(_XOPEN_SOURCE) || defined(__NetBSD__) +#define ATIMENSEC_CMP(x, op, y) ((x)->st_atimensec op (y)->st_atimensec) +#define CTIMENSEC_CMP(x, op, y) ((x)->st_ctimensec op (y)->st_ctimensec) +#define MTIMENSEC_CMP(x, op, y) ((x)->st_mtimensec op (y)->st_mtimensec) +#else +#define ATIMENSEC_CMP(x, op, y) \ + ((x)->st_atimespec.tv_nsec op (y)->st_atimespec.tv_nsec) +#define CTIMENSEC_CMP(x, op, y) \ + ((x)->st_ctimespec.tv_nsec op (y)->st_ctimespec.tv_nsec) +#define MTIMENSEC_CMP(x, op, y) \ + ((x)->st_mtimespec.tv_nsec op (y)->st_mtimespec.tv_nsec) +#endif + +int +namecmp(a, b) + const FTSENT *a, *b; +{ + return (strcmp(a->fts_name, b->fts_name)); +} + +int +revnamecmp(a, b) + const FTSENT *a, *b; +{ + return (strcmp(b->fts_name, a->fts_name)); +} + +int +modcmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revmodcmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_mtime > a->fts_statp->st_mtime) + return (-1); + else if (b->fts_statp->st_mtime < a->fts_statp->st_mtime) + return (1); + else if (MTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (MTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +acccmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revacccmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_atime > a->fts_statp->st_atime) + return (-1); + else if (b->fts_statp->st_atime < a->fts_statp->st_atime) + return (1); + else if (ATIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (ATIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +statcmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (-1); + else + return (namecmp(a, b)); +} + +int +revstatcmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_ctime > a->fts_statp->st_ctime) + return (-1); + else if (b->fts_statp->st_ctime < a->fts_statp->st_ctime) + return (1); + else if (CTIMENSEC_CMP(b->fts_statp, >, a->fts_statp)) + return (-1); + else if (CTIMENSEC_CMP(b->fts_statp, <, a->fts_statp)) + return (1); + else + return (revnamecmp(a, b)); +} + +int +sizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (-1); + else + return (namecmp(a, b)); +} + +int +revsizecmp(a, b) + const FTSENT *a, *b; +{ + if (b->fts_statp->st_size > a->fts_statp->st_size) + return (-1); + if (b->fts_statp->st_size < a->fts_statp->st_size) + return (1); + else + return (revnamecmp(a, b)); +} diff --git a/ls/extern.h b/ls/extern.h new file mode 100644 index 0000000..a42d48f --- /dev/null +++ b/ls/extern.h @@ -0,0 +1,56 @@ +/* $NetBSD: extern.h,v 1.7 1998/01/18 13:30:03 lukem Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 5/31/93 + */ + +int acccmp __P((const FTSENT *, const FTSENT *)); +int revacccmp __P((const FTSENT *, const FTSENT *)); +int modcmp __P((const FTSENT *, const FTSENT *)); +int revmodcmp __P((const FTSENT *, const FTSENT *)); +int namecmp __P((const FTSENT *, const FTSENT *)); +int revnamecmp __P((const FTSENT *, const FTSENT *)); +int statcmp __P((const FTSENT *, const FTSENT *)); +int revstatcmp __P((const FTSENT *, const FTSENT *)); +int sizecmp __P((const FTSENT *, const FTSENT *)); +int revsizecmp __P((const FTSENT *, const FTSENT *)); + +char *flags_to_string __P((u_long, char *)); +int string_to_flags __P((char **, u_long *, u_long *)); +void prcopy __P((char *, char *, int)); +void printacol __P((DISPLAY *)); +void printcol __P((DISPLAY *)); +void printlong __P((DISPLAY *)); +void printscol __P((DISPLAY *)); +void usage __P((void)); diff --git a/ls/ls.1 b/ls/ls.1 new file mode 100644 index 0000000..6181998 --- /dev/null +++ b/ls/ls.1 @@ -0,0 +1,367 @@ +.\" $NetBSD: ls.1,v 1.19 1998/06/01 21:11:28 hubertf Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1991, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following 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. +.\" +.\" @(#)ls.1 8.7 (Berkeley) 7/29/94 +.\" +.Dd July 29, 1994 +.Dt LS 1 +.Os +.Sh NAME +.Nm ls +.Nd list directory contents +.Sh SYNOPSIS +.Nm +.Op Fl ACFLRSTWacdfgiklnoqrstux1 +.Op Ar file ... +.Sh DESCRIPTION +For each operand that names a +.Ar file +of a type other than +directory, +.Nm +displays its name as well as any requested, +associated information. +For each operand that names a +.Ar file +of type directory, +.Nm +displays the names of files contained +within that directory, as well as any requested, associated +information. +.Pp +If no operands are given, the contents of the current +directory are displayed. +If more than one operand is given, +non-directory operands are displayed first; directory +and non-directory operands are sorted separately and in +lexicographical order. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl A +List all entries except for +.Ql \&. +and +.Ql \&.. . +Always set for the super-user. +.It Fl C +Force multi-column output; this is the default when output is to a terminal. +.It Fl F +Display a slash (/) immediately after each pathname that is a directory, +an asterisk (*) after each that is executable, +an at sign (@) after each symbolic link, +a percent sign (%) after each whiteout, +an equal sign (=) after each socket, +and a vertical bar (|) after each that is a +.Tn FIFO . +.It Fl L +If argument is a symbolic link, list the file or directory the link references +rather than the link itself. +.It Fl R +Recursively list subdirectories encountered. +.It Fl S +Sort by size, largest file first. +.It Fl T +Display complete time information for the file, including +month, day, hour, minute, second, and year. +.It Fl W +Display whiteouts when scanning directories. +.It Fl a +Include directory entries whose names begin with a +dot (.). +.It Fl c +Use time when file status was last changed for sorting or printing. +.It Fl d +Directories are listed as plain files (not searched recursively) and +symbolic links in the argument list are not indirected through. +.It Fl f +Output is not sorted. +.It Fl g +Does nothing; kept for compatibility with older versions of +.Xr ls 1 . +.It Fl i +For each file, print the file's file serial number (inode number). +.It Fl k +Modifies the +.Fl s +option, causing the sizes to be reported in kilobytes. +.It Fl l +(The lowercase letter ``ell.'') List in long format. (See below.) +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the long listing. +.It Fl n +Display the user and group IDs numerically rather than converting +to a user or group name in a long +.Pq Fl l +output. +.It Fl o +Include the file flags in a long +.Pq Fl l +output. +.It Fl q +Force printing of non-graphic characters in file names as +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 smallest or oldest entries first. +.It Fl s +Display the number of file system blocks actually used by each file, in units +of 512 bytes, where partial units are rounded up to the next integer value. +If the output is to a terminal, a total sum for all the file +sizes is output on a line before the listing. +.It Fl t +Sort by time modified (most recently modified +first) before sorting the operands by lexicographical +order. +.It Fl u +Use time of last access, +instead of last modification +of the file for sorting +.Pq Fl t +or printing +.Pq Fl l . +.It Fl x +Multi-column output sorted across the page rather than down the page. +.It Fl v +Force unedited printing of non-graphic characters; this is the default when +output is not to a terminal. +.It Fl \&1 +(The numeric digit ``one.'') Force output to be +one entry per line. +This is the default when +output is not to a terminal. +.El +.Pp +The +.Fl 1 , +.Fl C , +.Fl l , +and +.Fl x +options all override each other; the last one specified determines +the format used. +.Pp +The +.Fl c , +and +.Fl u +options override each other; the last one specified determines +the file time used. +.Pp +By default, +.Nm +lists one entry per line to standard +output; the exceptions are to terminals or when the +.Fl C +option is specified. +.Pp +File information is displayed with one or more +s separating the information associated with the +.Fl i , +.Fl s , +and +.Fl l +options. +.Ss The Long Format +If the +.Fl l +option is given, the following information +is displayed for each file: +file mode, +number of links, owner name, group name, +number of bytes in the file, abbreviated +month, day-of-month file was last modified, +hour file last modified, minute file last +modified, and the pathname. +In addition, for each directory whose contents are displayed, the total +number of 512-byte blocks used by the files in the directory is displayed +on a line by itself immediately before the information for the files in the +directory. +.Pp +If the owner or group names are not a known user or group name, +or the +.Fl n +option is given, +the numeric ID's are displayed. +.Pp +If the file is a character special or block special file, +the major and minor device numbers for the file are displayed +in the size field. If the file is a symbolic link the pathname of the +linked-to file is preceded by +.Dq \-> . +.Pp +The file mode printed under the +.Fl l +option consists of the +entry type, owner permissions, and group permissions. +The entry type character describes the type of file, as +follows: +.Pp +.Bl -tag -width 4n -offset indent -compact +.It Sy b +Block special file. +.It Sy c +Character special file. +.It Sy d +Directory. +.It Sy l +Symbolic link. +.It Sy s +Socket link. +.\" .It Sy p +.\" .Tn FIFO . +.It Sy w +Whiteout. +.It Sy \- +Regular file. +.El +.Pp +The next three fields +are three characters each: +owner permissions, +group permissions, and +other permissions. +Each field has three character positions: +.Bl -enum -offset indent +.It +If +.Sy r , +the file is readable; if +.Sy \- , +it is not readable. +.It +If +.Sy w , +the file is writable; if +.Sy \- , +it is not writable. +.It +The first of the following that applies: +.Bl -tag -width 4n -offset indent +.It Sy S +If in the owner permissions, the file is not executable and +set-user-ID mode is set. +If in the group permissions, the file is not executable +and set-group-ID mode is set. +.It Sy s +If in the owner permissions, the file is executable +and set-user-ID mode is set. +If in the group permissions, the file is executable +and setgroup-ID mode is set. +.It Sy x +The file is executable or the directory is +searchable. +.It Sy \- +The file is neither readable, writable, executable, +nor set-user-ID nor set-group-ID mode, nor sticky. (See below.) +.El +.Pp +These next two apply only to the third character in the last group +(other permissions). +.Bl -tag -width 4n -offset indent +.It Sy T +The sticky bit is set +(mode +.Li 1000 ) , +but not execute or search permission. (See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.It Sy t +The sticky bit is set (mode +.Li 1000 ) , +and is searchable or executable. +(See +.Xr chmod 1 +or +.Xr sticky 8 . ) +.El +.El +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh ENVIRONMENT VARIABLES +The following environment variables affect the execution of +.Nm "" : +.Bl -tag -width BLOCKSIZE +.It Ev BLOCKSIZE +If the environment variable +.Ev BLOCKSIZE +is set, and the +.Fl k +option is not specified, the block counts +(see +.Fl s ) +will be displayed in units of that size block. +.It COLUMNS +If this variable contains a string representing a +decimal integer, it is used as the +column position width for displaying +multiple-text-column output. +The +.Nm +utility calculates how +many pathname text columns to display +based on the width provided. +(See +.Fl C . ) +.It Ev TZ +The timezone to use when displaying dates. +See +.Xr environ 7 +for more information. +.El +.Sh COMPATIBILITY +The group field is now automatically included in the long listing for +files in order to be compatible with the +.St -p1003.2 +specification. +.Sh SEE ALSO +.Xr chmod 1 , +.Xr symlink 7 , +.Xr sticky 8 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +specification. +.Sh HISTORY +An +.Nm +utility appeared in +.At v5 . diff --git a/ls/ls.c b/ls/ls.c new file mode 100644 index 0000000..c943b9c --- /dev/null +++ b/ls/ls.c @@ -0,0 +1,616 @@ +/* $NetBSD: ls.c,v 1.31 1998/08/19 01:44:19 thorpej Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ls.c 8.7 (Berkeley) 8/5/94"; +#else +__RCSID("$NetBSD: ls.c,v 1.31 1998/08/19 01:44:19 thorpej Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +int main __P((int, char *[])); + +static void display __P((FTSENT *, FTSENT *)); +static int mastercmp __P((const FTSENT **, const FTSENT **)); +static void traverse __P((int, char **, int)); + +static void (*printfcn) __P((DISPLAY *)); +static int (*sortfcn) __P((const FTSENT *, const FTSENT *)); + +#define BY_NAME 0 +#define BY_SIZE 1 +#define BY_TIME 2 + +long blocksize; /* block size units */ +int termwidth = 80; /* default terminal width */ +int sortkey = BY_NAME; + +/* flags */ +int f_accesstime; /* use time of last access */ +int f_column; /* columnated format */ +int f_columnacross; /* columnated format, sorted across */ +int f_flags; /* show flags associated with a file */ +int f_inode; /* print inode */ +int f_listdir; /* list actual directory, not contents */ +int f_listdot; /* list files beginning with . */ +int f_longform; /* long listing format */ +int f_nonprint; /* show unprintables as ? */ +int f_nosort; /* don't sort output */ +int f_numericonly; /* don't convert uid/gid to name */ +int f_recursive; /* ls subdirectories also */ +int f_reversesort; /* reverse whatever sort is used */ +int f_sectime; /* print the real time for all files */ +int f_singlecol; /* use single column output */ +int f_size; /* list size in short listing */ +int f_statustime; /* use time of last mode change */ +int f_type; /* add type character for non-regular files */ +int f_whiteout; /* show whiteout entries */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + static char dot[] = ".", *dotav[] = { dot, NULL }; + struct winsize win; + int ch, fts_options, notused; + int kflag = 0; + const char *p; + + /* Terminal defaults to -Cq, non-terminal defaults to -1. */ + if (isatty(STDOUT_FILENO)) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); + else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termwidth = win.ws_col; + f_column = f_nonprint = 1; + } else + f_singlecol = 1; + + /* Root is -A automatically. */ + if (!getuid()) + f_listdot = 1; + + fts_options = FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "1ACFLRSTWacdfgiklnoqrstuxv")) != -1) { + switch (ch) { + /* + * The -1, -C, -l and -x options all override each other so + * shell aliasing works correctly. + */ + case '1': + f_singlecol = 1; + f_column = f_columnacross = f_longform = 0; + break; + case 'C': + f_column = 1; + f_columnacross = f_longform = f_singlecol = 0; + break; + case 'l': + f_longform = 1; + f_column = f_columnacross = f_singlecol = 0; + break; + case 'x': + f_columnacross = 1; + f_column = f_longform = f_singlecol = 0; + break; + /* The -c and -u options override each other. */ + case 'c': + f_statustime = 1; + f_accesstime = 0; + break; + case 'u': + f_accesstime = 1; + f_statustime = 0; + break; + case 'F': + f_type = 1; + break; + case 'L': + fts_options &= ~FTS_PHYSICAL; + fts_options |= FTS_LOGICAL; + break; + case 'R': + f_recursive = 1; + break; + case 'a': + fts_options |= FTS_SEEDOT; + /* FALLTHROUGH */ + case 'A': + f_listdot = 1; + break; + /* The -d option turns off the -R option. */ + case 'd': + f_listdir = 1; + f_recursive = 0; + break; + case 'f': + f_nosort = 1; + break; + case 'g': /* Compatibility with 4.3BSD. */ + break; + case 'i': + f_inode = 1; + break; + case 'k': + blocksize = 1024; + kflag = 1; + break; + case 'n': + f_numericonly = 1; + break; + case 'o': + f_flags = 1; + break; + case 'q': + f_nonprint = 1; + break; + case 'r': + f_reversesort = 1; + break; + case 'S': + sortkey = BY_SIZE; + break; + case 's': + f_size = 1; + break; + case 'T': + f_sectime = 1; + break; + case 't': + sortkey = BY_TIME; + break; + case 'W': + f_whiteout = 1; + break; + case 'v': + f_nonprint = 0; + break; + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + /* + * If not -F, -i, -l, -S, -s or -t options, don't require stat + * information. + */ + if (!f_inode && !f_longform && !f_size && !f_type && + sortkey == BY_NAME) + fts_options |= FTS_NOSTAT; + + /* + * If not -F, -d or -l options, follow any symbolic links listed on + * the command line. + */ + if (!f_longform && !f_listdir && !f_type) + fts_options |= FTS_COMFOLLOW; + + /* + * If -W, show whiteout entries + */ +#ifdef FTS_WHITEOUT + if (f_whiteout) + fts_options |= FTS_WHITEOUT; +#endif + + /* If -l or -s, figure out block size. */ + if (f_longform || f_size) { + if (!kflag) + (void)getbsize(¬used, &blocksize); + blocksize /= 512; + } + + /* Select a sort function. */ + if (f_reversesort) { + switch (sortkey) { + case BY_NAME: + sortfcn = revnamecmp; + break; + case BY_SIZE: + sortfcn = revsizecmp; + break; + case BY_TIME: + if (f_accesstime) + sortfcn = revacccmp; + else if (f_statustime) + sortfcn = revstatcmp; + else /* Use modification time. */ + sortfcn = revmodcmp; + break; + } + } else { + switch (sortkey) { + case BY_NAME: + sortfcn = namecmp; + break; + case BY_SIZE: + sortfcn = sizecmp; + break; + case BY_TIME: + if (f_accesstime) + sortfcn = acccmp; + else if (f_statustime) + sortfcn = statcmp; + else /* Use modification time. */ + sortfcn = modcmp; + break; + } + } + + /* Select a print function. */ + if (f_singlecol) + printfcn = printscol; + else if (f_columnacross) + printfcn = printacol; + else if (f_longform) + printfcn = printlong; + else + printfcn = printcol; + + if (argc) + traverse(argc, argv, fts_options); + else + traverse(1, dotav, fts_options); + exit(0); + /* NOTREACHED */ +} + +static int output; /* If anything output. */ + +/* + * Traverse() walks the logical directory structure specified by the argv list + * in the order specified by the mastercmp() comparison function. During the + * traversal it passes linked lists of structures to display() which represent + * a superset (may be exact set) of the files to be displayed. + */ +static void +traverse(argc, argv, options) + int argc, options; + char *argv[]; +{ + FTS *ftsp; + FTSENT *p, *chp; + int ch_options; + char pbuf[MAXPATHLEN + 1], *path; + + if ((ftsp = + fts_open(argv, options, f_nosort ? NULL : mastercmp)) == NULL) + err(1, "%s", ""); + + display(NULL, fts_children(ftsp, 0)); + if (f_listdir) + return; + + /* + * If not recursing down this tree and don't need stat info, just get + * the names. + */ + ch_options = !f_recursive && options & FTS_NOSTAT ? FTS_NAMEONLY : 0; + + while ((p = fts_read(ftsp)) != NULL) + switch (p->fts_info) { + case FTS_DC: + warnx("%s: directory causes a cycle", p->fts_name); + break; + case FTS_DNR: + case FTS_ERR: + warnx("%s: %s", p->fts_name, strerror(p->fts_errno)); + break; + case FTS_D: + if (p->fts_level != FTS_ROOTLEVEL && + p->fts_name[0] == '.' && !f_listdot) + break; + + /* + * If already output something, put out a newline as + * a separator. If multiple arguments, precede each + * directory with its name. + */ + if (f_nonprint) { + prcopy(p->fts_path, pbuf, p->fts_pathlen+1); + path = pbuf; + } else + path = p->fts_path; + + if (output) + (void)printf("\n%s:\n", path); + else if (argc > 1) { + (void)printf("%s:\n", path); + output = 1; + } + + chp = fts_children(ftsp, ch_options); + display(p, chp); + + if (!f_recursive && chp != NULL) + (void)fts_set(ftsp, p, FTS_SKIP); + break; + } + if (errno) + err(1, "fts_read"); +} + +/* + * Display() takes a linked list of FTSENT structures and passes the list + * along with any other necessary information to the print function. P + * points to the parent directory of the display list. + */ +static void +display(p, list) + FTSENT *p, *list; +{ + struct stat *sp; + DISPLAY d; + FTSENT *cur; + NAMES *np; + u_int64_t btotal, maxblock, maxsize; + int maxinode, maxnlink, maxmajor, maxminor; + int bcfile, entries, flen, glen, ulen, maxflags, maxgroup, maxlen; + int maxuser, needstats; + const char *user, *group; + char ubuf[21]="", gbuf[21]="", buf[21]; /* 64 bits == 20 digits, +1 for NUL */ + char nuser[12], ngroup[12]; + char *flags = NULL; + +#ifdef __GNUC__ + /* This outrageous construct just to shut up a GCC warning. */ + (void) &maxsize; +#endif + + /* + * If list is NULL there are two possibilities: that the parent + * directory p has no children, or that fts_children() returned an + * error. We ignore the error case since it will be replicated + * on the next call to fts_read() on the post-order visit to the + * directory p, and will be signalled in traverse(). + */ + if (list == NULL) + return; + + needstats = f_inode || f_longform || f_size; + flen = 0; + maxinode = maxnlink = 0; + bcfile = 0; + maxuser = maxgroup = maxflags = maxlen = 0; + btotal = maxblock = maxsize = 0; + maxmajor = maxminor = 0; + for (cur = list, entries = 0; cur; cur = cur->fts_link) { + if (cur->fts_info == FTS_ERR || cur->fts_info == FTS_NS) { + warnx("%s: %s", + cur->fts_name, strerror(cur->fts_errno)); + cur->fts_number = NO_PRINT; + continue; + } + + /* + * P is NULL if list is the argv list, to which different rules + * apply. + */ + if (p == NULL) { + /* Directories will be displayed later. */ + if (cur->fts_info == FTS_D && !f_listdir) { + cur->fts_number = NO_PRINT; + continue; + } + } else { + /* Only display dot file if -a/-A set. */ + if (cur->fts_name[0] == '.' && !f_listdot) { + cur->fts_number = NO_PRINT; + continue; + } + } + if (cur->fts_namelen > maxlen) + maxlen = cur->fts_namelen; + if (needstats) { + sp = cur->fts_statp; + if (sp->st_blocks > maxblock) + maxblock = sp->st_blocks; + if (sp->st_ino > maxinode) + maxinode = sp->st_ino; + if (sp->st_nlink > maxnlink) + maxnlink = sp->st_nlink; + if (sp->st_size > maxsize) + maxsize = sp->st_size; + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) { + bcfile = 1; + if (major(sp->st_rdev) > maxmajor) + maxmajor = major(sp->st_rdev); + if (minor(sp->st_rdev) > maxminor) + maxminor = minor(sp->st_rdev); + } + + btotal += sp->st_blocks; + if (f_longform) { + if (f_numericonly) { + (void)snprintf(nuser, sizeof(nuser), + "%u", sp->st_uid); + (void)snprintf(ngroup, sizeof(ngroup), + "%u", sp->st_gid); + user = nuser; + group = ngroup; + } else { + if ((user = user_from_uid(sp->st_uid, 0)) == NULL) { + (void)snprintf(ubuf, sizeof(ubuf), "%d", (int)sp->st_uid); + user = ubuf; + } + if ((group = group_from_gid(sp->st_gid, 0)) == NULL) { + (void)snprintf(gbuf, sizeof(gbuf), "%d", (int)sp->st_gid); + group = gbuf; + } + } + if ((ulen = strlen(user)) > maxuser) + maxuser = ulen; + if ((glen = strlen(group)) > maxgroup) + maxgroup = glen; + if (f_flags) { + flags = + flags_to_string(sp->st_flags, "-"); + if ((flen = strlen(flags)) > maxflags) + maxflags = flen; + } else + flen = 0; + + if ((np = malloc(sizeof(NAMES) + + ulen + glen + flen + 3)) == NULL) + err(1, "%s", ""); + + np->user = &np->data[0]; + (void)strcpy(np->user, user); + np->group = &np->data[ulen + 1]; + (void)strcpy(np->group, group); + + if (f_flags) { + np->flags = &np->data[ulen + glen + 2]; + (void)strcpy(np->flags, flags); + } + cur->fts_pointer = np; + } + } + ++entries; + } + + if (!entries) + return; + + d.list = list; + d.entries = entries; + d.maxlen = maxlen; + if (needstats) { + d.btotal = btotal; + (void)snprintf(buf, sizeof(buf), "%qu", (long long)maxblock); + d.s_block = strlen(buf); + d.s_flags = maxflags; + d.s_group = maxgroup; + (void)snprintf(buf, sizeof(buf), "%u", maxinode); + d.s_inode = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%u", maxnlink); + d.s_nlink = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%qu", (long long)maxsize); + d.s_size = strlen(buf); + d.s_user = maxuser; + if (bcfile) { + (void)snprintf(buf, sizeof(buf), "%u", maxmajor); + d.s_major = strlen(buf); + (void)snprintf(buf, sizeof(buf), "%u", maxminor); + d.s_minor = strlen(buf); + if (d.s_major + d.s_minor + 2 > d.s_size) + d.s_size = d.s_major + d.s_minor + 2; + else if (d.s_size - d.s_minor - 2 > d.s_major) + d.s_major = d.s_size - d.s_minor - 2; + } else { + d.s_major = 0; + d.s_minor = 0; + } + } + + printfcn(&d); + output = 1; + + if (f_longform) + for (cur = list; cur; cur = cur->fts_link) + free(cur->fts_pointer); +} + +/* + * Ordering for mastercmp: + * If ordering the argv (fts_level = FTS_ROOTLEVEL) return non-directories + * as larger than directories. Within either group, use the sort function. + * All other levels use the sort function. Error entries remain unsorted. + */ +static int +mastercmp(a, b) + const FTSENT **a, **b; +{ + int a_info, b_info; + + a_info = (*a)->fts_info; + if (a_info == FTS_ERR) + return (0); + b_info = (*b)->fts_info; + if (b_info == FTS_ERR) + return (0); + + if (a_info == FTS_NS || b_info == FTS_NS) { + if (b_info != FTS_NS) + return (1); + else if (a_info != FTS_NS) + return (-1); + else + return (namecmp(*a, *b)); + } + + if (a_info != b_info && !f_listdir && + (*a)->fts_level == FTS_ROOTLEVEL) { + if (a_info == FTS_D) + return (1); + else if (b_info == FTS_D) + return (-1); + } + return (sortfcn(*a, *b)); +} diff --git a/ls/ls.h b/ls/ls.h new file mode 100644 index 0000000..733a7bb --- /dev/null +++ b/ls/ls.h @@ -0,0 +1,76 @@ +/* $NetBSD: ls.h,v 1.9 1998/05/16 15:12:26 lukem Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ls.h 8.1 (Berkeley) 5/31/93 + */ + +#define NO_PRINT 1 + +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_inode; /* print inode */ +extern int f_longform; /* long listing format */ +extern int f_nonprint; /* show unprintables as ? */ +extern int f_sectime; /* print the real time for all files */ +extern int f_size; /* list size in short listing */ +extern int f_statustime; /* use time of last mode change */ +extern int f_type; /* add type character for non-regular files */ + +typedef struct { + FTSENT *list; + u_int64_t btotal; + int entries; + int maxlen; + int s_block; + int s_flags; + int s_group; + int s_inode; + int s_nlink; + int s_size; + int s_user; + int s_major; + int s_minor; +} DISPLAY; + +typedef struct { + char *user; + char *group; + char *flags; + char data[1]; +} NAMES; diff --git a/ls/print.c b/ls/print.c new file mode 100644 index 0000000..240f221 --- /dev/null +++ b/ls/print.c @@ -0,0 +1,363 @@ +/* $NetBSD: print.c,v 1.22 1998/07/28 05:15:47 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)print.c 8.5 (Berkeley) 7/28/94"; +#else +__RCSID("$NetBSD: print.c,v 1.22 1998/07/28 05:15:47 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +static int printaname __P((FTSENT *, int, int)); +static void printlink __P((FTSENT *)); +static void printtime __P((time_t)); +static int printtype __P((u_int)); + +static time_t now; + +#define IS_NOPRINT(p) ((p)->fts_number == NO_PRINT) + +void +printscol(dp) + DISPLAY *dp; +{ + FTSENT *p; + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + (void)printaname(p, dp->s_inode, dp->s_block); + (void)putchar('\n'); + } +} + +void +printlong(dp) + DISPLAY *dp; +{ + struct stat *sp; + FTSENT *p; + NAMES *np; + char buf[20]; + char nbuf[MAXPATHLEN + 1], *name; + + now = time(NULL); + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %qu\n", + (long long)(howmany(dp->btotal, blocksize))); + + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + sp = p->fts_statp; + if (f_inode) + (void)printf("%*u ", dp->s_inode, sp->st_ino); + if (f_size) + (void)printf("%*qu ", dp->s_block, + (long long)howmany(sp->st_blocks, blocksize)); + (void)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); + if (f_flags) + (void)printf("%-*s ", dp->s_flags, np->flags); + if (S_ISCHR(sp->st_mode) || S_ISBLK(sp->st_mode)) + (void)printf("%*u, %*u ", + dp->s_major, major(sp->st_rdev), dp->s_minor, + minor(sp->st_rdev)); + else + (void)printf("%*qu ", dp->s_size, + (long long)sp->st_size); + if (f_accesstime) + printtime(sp->st_atime); + else if (f_statustime) + printtime(sp->st_ctime); + else + printtime(sp->st_mtime); + if (f_nonprint) { + prcopy(p->fts_name, nbuf, p->fts_namelen+1); + name = nbuf; + } else + name = p->fts_name; + (void)printf("%s", name); + if (f_type) + (void)printtype(sp->st_mode); + if (S_ISLNK(sp->st_mode)) + printlink(p); + (void)putchar('\n'); + } +} + +void +printcol(dp) + DISPLAY *dp; +{ + extern int termwidth; + static FTSENT **array; + static int lastentries = -1; + FTSENT *p; + int base, chcnt, col, colwidth, num; + int numcols, numrows, row; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth += 1; + + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + + /* + * Have to do random access in the linked list -- build a table + * of pointers. + */ + if (dp->entries > lastentries) { + lastentries = dp->entries; + if ((array = + realloc(array, dp->entries * sizeof(FTSENT *))) == NULL) { + warn("%s", ""); + printscol(dp); + } + } + for (p = dp->list, num = 0; p; p = p->fts_link) + if (p->fts_number != NO_PRINT) + array[num++] = p; + + numcols = termwidth / colwidth; + colwidth = termwidth / numcols; /* spread out if possible */ + numrows = num / numcols; + if (num % numcols) + ++numrows; + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %qu\n", + (long long)(howmany(dp->btotal, blocksize))); + for (row = 0; row < numrows; ++row) { + for (base = row, chcnt = col = 0; col < numcols; ++col) { + chcnt = printaname(array[base], dp->s_inode, + dp->s_block); + if ((base += numrows) >= num) + break; + while (chcnt++ < colwidth) + (void)putchar(' '); + } + (void)putchar('\n'); + } +} + +void +printacol(dp) + DISPLAY *dp; +{ + extern int termwidth; + FTSENT *p; + int chcnt, col, colwidth; + int numcols; + + colwidth = dp->maxlen; + if (f_inode) + colwidth += dp->s_inode + 1; + if (f_size) + colwidth += dp->s_block + 1; + if (f_type) + colwidth += 1; + + colwidth += 1; + + if (termwidth < 2 * colwidth) { + printscol(dp); + return; + } + + numcols = termwidth / colwidth; + colwidth = termwidth / numcols; /* spread out if possible */ + + if (dp->list->fts_level != FTS_ROOTLEVEL && (f_longform || f_size)) + (void)printf("total %qu\n", + (long long)(howmany(dp->btotal, blocksize))); + chcnt = col = 0; + for (p = dp->list; p; p = p->fts_link) { + if (IS_NOPRINT(p)) + continue; + if (col >= numcols) { + chcnt = col = 0; + (void)putchar('\n'); + } + chcnt = printaname(p, dp->s_inode, dp->s_block); + while (chcnt++ < colwidth) + (void)putchar(' '); + col++; + } + (void)putchar('\n'); +} + +/* + * print [inode] [size] name + * return # of characters printed, no trailing characters. + */ +static int +printaname(p, inodefield, sizefield) + FTSENT *p; + int sizefield, inodefield; +{ + struct stat *sp; + int chcnt; + char nbuf[MAXPATHLEN + 1], *name; + + sp = p->fts_statp; + chcnt = 0; + if (f_inode) + chcnt += printf("%*u ", inodefield, sp->st_ino); + if (f_size) + chcnt += printf("%*qu ", sizefield, + (long long)howmany(sp->st_blocks, blocksize)); + if (f_nonprint) { + prcopy(p->fts_name, nbuf, p->fts_namelen+1); + name = nbuf; + } else + name = p->fts_name; + chcnt += printf("%s", name); + if (f_type) + chcnt += printtype(sp->st_mode); + return (chcnt); +} + +static void +printtime(ftime) + time_t ftime; +{ + int i; + char *longstring; + + longstring = ctime(&ftime); + for (i = 4; i < 11; ++i) + (void)putchar(longstring[i]); + +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) + if (f_sectime) + for (i = 11; i < 24; i++) + (void)putchar(longstring[i]); + else if (ftime + SIXMONTHS > now && ftime - SIXMONTHS < now) + for (i = 11; i < 16; ++i) + (void)putchar(longstring[i]); + else { + (void)putchar(' '); + for (i = 20; i < 24; ++i) + (void)putchar(longstring[i]); + } + (void)putchar(' '); +} + +static int +printtype(mode) + u_int mode; +{ + switch (mode & S_IFMT) { + case S_IFDIR: + (void)putchar('/'); + return (1); + case S_IFIFO: + (void)putchar('|'); + return (1); + case S_IFLNK: + (void)putchar('@'); + return (1); + case S_IFSOCK: + (void)putchar('='); + return (1); + case S_IFWHT: + (void)putchar('%'); + return (1); + } + if (mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { + (void)putchar('*'); + return (1); + } + return (0); +} + +static void +printlink(p) + FTSENT *p; +{ + int lnklen; + char name[MAXPATHLEN + 1], path[MAXPATHLEN + 1]; + + if (p->fts_level == FTS_ROOTLEVEL) + (void)snprintf(name, sizeof(name), "%s", p->fts_name); + else + (void)snprintf(name, sizeof(name), + "%s/%s", p->fts_parent->fts_accpath, p->fts_name); + prcopy(name, name, strlen(name)); + if ((lnklen = readlink(name, path, sizeof(path) - 1)) == -1) { + (void)fprintf(stderr, "\nls: %s: %s\n", name, strerror(errno)); + return; + } + path[lnklen] = '\0'; + (void)printf(" -> %s", path); +} diff --git a/ls/stat_flags.c b/ls/stat_flags.c new file mode 100644 index 0000000..e46a3e8 --- /dev/null +++ b/ls/stat_flags.c @@ -0,0 +1,164 @@ +/* $NetBSD: stat_flags.c,v 1.6 1997/07/20 18:53:12 christos Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)stat_flags.c 8.2 (Berkeley) 7/28/94"; +#else +__RCSID("$NetBSD: stat_flags.c,v 1.6 1997/07/20 18:53:12 christos Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +#define SAPPEND(s) { \ + if (prefix != NULL) \ + (void)strcat(string, prefix); \ + (void)strcat(string, s); \ + prefix = ","; \ +} + +/* + * flags_to_string -- + * Convert stat flags to a comma-separated string. If no flags + * are set, return the default string. + */ +char * +flags_to_string(flags, def) + u_long flags; + char *def; +{ + static char string[128]; + char *prefix; + + string[0] = '\0'; + prefix = NULL; + if (flags & UF_APPEND) + SAPPEND("uappnd"); + if (flags & UF_IMMUTABLE) + SAPPEND("uchg"); + if (flags & UF_NODUMP) + SAPPEND("nodump"); + if (flags & UF_OPAQUE) + SAPPEND("opaque"); + if (flags & SF_APPEND) + SAPPEND("sappnd"); + if (flags & SF_ARCHIVED) + SAPPEND("arch"); + if (flags & SF_IMMUTABLE) + SAPPEND("schg"); + return (prefix == NULL && def != NULL ? def : string); +} + +#define TEST(a, b, f) { \ + if (!memcmp(a, b, sizeof(b))) { \ + if (clear) { \ + if (clrp) \ + *clrp |= (f); \ + } else if (setp) \ + *setp |= (f); \ + break; \ + } \ +} + +/* + * string_to_flags -- + * Take string of arguments and return stat flags. Return 0 on + * success, 1 on failure. On failure, stringp is set to point + * to the offending token. + */ +int +string_to_flags(stringp, setp, clrp) + char **stringp; + u_long *setp, *clrp; +{ + int clear; + char *string, *p; + + clear = 0; + if (setp) + *setp = 0; + if (clrp) + *clrp = 0; + string = *stringp; + while ((p = strsep(&string, "\t ,")) != NULL) { + *stringp = p; + if (*p == '\0') + continue; + if (p[0] == 'n' && p[1] == 'o') { + clear = 1; + p += 2; + } + switch (p[0]) { + case 'a': + TEST(p, "arch", SF_ARCHIVED); + TEST(p, "archived", SF_ARCHIVED); + return (1); + case 'd': + clear = !clear; + TEST(p, "dump", UF_NODUMP); + return (1); + case 'o': + TEST(p, "opaque", UF_OPAQUE); + return (1); + case 's': + TEST(p, "sappnd", SF_APPEND); + TEST(p, "sappend", SF_APPEND); + TEST(p, "schg", SF_IMMUTABLE); + TEST(p, "schange", SF_IMMUTABLE); + TEST(p, "simmutable", SF_IMMUTABLE); + return (1); + case 'u': + TEST(p, "uappnd", UF_APPEND); + TEST(p, "uappend", UF_APPEND); + TEST(p, "uchg", UF_IMMUTABLE); + TEST(p, "uchange", UF_IMMUTABLE); + TEST(p, "uimmutable", UF_IMMUTABLE); + /* FALLTHROUGH */ + default: + return (1); + } + } + return (0); +} diff --git a/ls/util.c b/ls/util.c new file mode 100644 index 0000000..fee0027 --- /dev/null +++ b/ls/util.c @@ -0,0 +1,80 @@ +/* $NetBSD: util.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)util.c 8.5 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: util.c,v 1.15 1998/07/28 05:31:25 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include + +#include "ls.h" +#include "extern.h" + +void +prcopy(src, dest, len) + char *src, *dest; + int len; +{ + int ch; + + while (len--) { + ch = *src++; + *dest++ = (isprint(ch) || ch == '\0') ? ch : '?'; + } +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: ls [-1ACFLRSTWacdfgiklnoqrstux] [file ...]\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/mkdir/Makefile b/mkdir/Makefile new file mode 100644 index 0000000..d26e5ad --- /dev/null +++ b/mkdir/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = mkdir + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = mkdir.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mkdir.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/mkdir/Makefile.postamble b/mkdir/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/mkdir/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/mkdir/Makefile.preamble b/mkdir/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/mkdir/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/mkdir/PB.project b/mkdir/PB.project new file mode 100644 index 0000000..0d6316a --- /dev/null +++ b/mkdir/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (mkdir.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mkdir.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = mkdir; + 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/mkdir/mkdir.1 b/mkdir/mkdir.1 new file mode 100644 index 0000000..83b87a4 --- /dev/null +++ b/mkdir/mkdir.1 @@ -0,0 +1,97 @@ +.\" $NetBSD: mkdir.1,v 1.10 1997/10/20 08:52:35 enami Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkdir.1 8.2 (Berkeley) 1/25/94 +.\" +.Dd January 25, 1994 +.Dt MKDIR 1 +.Os +.Sh NAME +.Nm mkdir +.Nd make directories +.Sh SYNOPSIS +.Nm +.Op Fl p +.Op Fl m Ar mode +.Ar directory_name ... +.Sh DESCRIPTION +.Nm +creates the directories named as operands, in the order specified, +using mode +.Li rwxrwxrwx (\&0777) +as modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Pp +.Bl -tag -width indent +.It Fl m +Set the file permission bits of the final created directory to +the specified mode. +The mode argument can be in any of the formats specified to the +.Xr chmod 1 +utility. +If a symbolic mode is specified, the operation characters +.Dq + +and +.Dq - +are interpreted relative to an initial mode of +.Dq a=rwx . +.It Fl p +Create intermediate directories as required. +If this option is not specified, the full path prefix of each +operand must already exist. +Intermediate directories are created with permission bits of +.Li rwxrwxrwx (\&0777) +as modified by the current umask, plus write and search +permission for the owner. Do not consider it an error if the +argument directory already exists. +.El +.Pp +The user must have write permission in the parent directory. +.Pp +.Nm +exits 0 if successful, and >0 if an error occurred. +.Sh SEE ALSO +.Xr chmod 1 , +.Xr rmdir 1 , +.Xr umask 2 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. diff --git a/mkdir/mkdir.c b/mkdir/mkdir.c new file mode 100644 index 0000000..2dcd1cf --- /dev/null +++ b/mkdir/mkdir.c @@ -0,0 +1,195 @@ +/* $NetBSD: mkdir.c,v 1.19 1998/07/28 05:31:25 mycroft Exp $ */ + +/* + * Copyright (c) 1983, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkdir.c 8.2 (Berkeley) 1/25/94"; +#else +__RCSID("$NetBSD: mkdir.c,v 1.19 1998/07/28 05:31:25 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +int mkpath __P((char *, mode_t, mode_t)); +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, exitval, pflag; + mode_t *set; + mode_t mode, dir_mode; + + (void)setlocale(LC_ALL, ""); + + /* + * The default file mode is a=rwx (0777) with selected permissions + * removed in accordance with the file mode creation mask. For + * intermediate path name components, the mode is the default modified + * by u+wx so that the subdirectories can always be created. + */ + mode = 0777 & ~umask(0); + dir_mode = mode | S_IWUSR | S_IXUSR; + + pflag = 0; + while ((ch = getopt(argc, argv, "m:p")) != -1) + switch(ch) { + case 'p': + pflag = 1; + break; + case 'm': + if ((set = setmode(optarg)) == NULL) + errx(1, "invalid file mode: %s", optarg); + mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (*argv == NULL) + usage(); + + for (exitval = 0; *argv != NULL; ++argv) { + char *slash; + + /* Remove trailing slashes, per POSIX. */ + slash = strrchr(*argv, '\0'); + while (--slash > *argv && *slash == '/') + *slash = '\0'; + + if (pflag) { + if (mkpath(*argv, mode, dir_mode) < 0) + exitval = 1; + } else { + if (mkdir(*argv, mode) < 0) { + warn("%s", *argv); + exitval = 1; + } + /* + * The mkdir() and umask() calls both honor only the low + * nine bits, so if you try to set a mode including the + * sticky, setuid, setgid bits you lose them. So chmod(). + */ + else if (chmod(*argv, mode) == -1) { + warn("%s", *argv); + exitval = 1; + } + } + } + exit(exitval); + /* NOTREACHED */ +} + +/* + * mkpath -- create directories. + * path - path + * mode - file mode of terminal directory + * dir_mode - file mode of intermediate directories + */ +int +mkpath(path, mode, dir_mode) + char *path; + mode_t mode; + mode_t dir_mode; +{ + struct stat sb; + char *slash; + int done = 0; + + slash = path; + + while (!done) { + slash += strspn(slash, "/"); + slash += strcspn(slash, "/"); + + done = (*slash == '\0'); + *slash = '\0'; + + if (stat(path, &sb)) { + if (errno != ENOENT + || mkdir(path, done ? mode : dir_mode)) { + warn("%s", path); + return (-1); + } + /* + * The mkdir() and umask() calls both honor only the low + * nine bits, so if you try to set a mode including the + * sticky, setuid, setgid bits you lose them. So chmod(). + */ + if (chmod(path, done ? mode : dir_mode) == -1) { + warn("%s", path); + return (-1); + } + } else if (!S_ISDIR(sb.st_mode)) { + warnx("%s: %s", path, strerror(ENOTDIR)); + return (-1); + } + + *slash = '/'; + } + + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: mkdir [-p] [-m mode] dirname ...\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/mkfifo/Makefile b/mkfifo/Makefile new file mode 100644 index 0000000..ed49dbe --- /dev/null +++ b/mkfifo/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = mkfifo + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = mkfifo.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mkfifo.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/mkfifo/Makefile.postamble b/mkfifo/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/mkfifo/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/mkfifo/Makefile.preamble b/mkfifo/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/mkfifo/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/mkfifo/PB.project b/mkfifo/PB.project new file mode 100644 index 0000000..b348276 --- /dev/null +++ b/mkfifo/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (mkfifo.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mkfifo.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 = mkfifo; + 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/mkfifo/mkfifo.1 b/mkfifo/mkfifo.1 new file mode 100644 index 0000000..f8a0e16 --- /dev/null +++ b/mkfifo/mkfifo.1 @@ -0,0 +1,91 @@ +.\" $NetBSD: mkfifo.1,v 1.6 1997/10/19 05:11:52 lukem Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mkfifo.1 8.2 (Berkeley) 1/5/94 +.\" +.Dd January 5, 1994 +.Dt MKFIFO 1 +.Os BSD 4.4 +.Sh NAME +.Nm mkfifo +.Nd make fifos +.Sh SYNOPSIS +.Nm +.Op Fl m Ar mode +.Ar fifo_name ... +.Sh DESCRIPTION +.Nm +creates the fifos requested, in the order specified, +using mode +.Li \&0666 +modified by the current +.Xr umask 2 . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl m +Set the file permission bits of newly-created directories to +.Ar mode . +The mode is specified as in +.Xr chmod 1 . +In symbolic mode strings, the +.Dq + +and +.Dq - +operators are interpreted relative to an assumed initial mode of +.Dq a=rw +.El +.Pp +.Nm +requires write permission in the parent directory. +.Pp +.Nm +exits 0 if successful, and >0 if an error occurred. +.Sh SEE ALSO +.Xr mkdir 1 , +.Xr rm 1 , +.Xr mkfifo 2 , +.Xr mknod 8 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2-92 +compliant. +.Sh HISTORY +.Nm +command appeared in +.Bx 4.4 . diff --git a/mkfifo/mkfifo.c b/mkfifo/mkfifo.c new file mode 100644 index 0000000..6bd45b0 --- /dev/null +++ b/mkfifo/mkfifo.c @@ -0,0 +1,113 @@ +/* $NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $ */ + +/* + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mkfifo.c 8.2 (Berkeley) 1/5/94"; +#endif +__RCSID("$NetBSD: mkfifo.c,v 1.8 1997/10/19 05:11:54 lukem Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char **)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, exitval; + void * set; + mode_t mode; + + setlocale (LC_ALL, ""); + + /* The default mode is the value of the bitwise inclusive or of + S_IRUSR, S_IWUSR, S_IRGRP, S_IWGRP, S_IROTH, and S_IWOTH + modified by the file creation mask */ + mode = 0666 & ~umask(0); + + while ((ch = getopt(argc, argv, "m:")) != -1) + switch(ch) { + case 'm': + if (!(set = setmode(optarg))) { + errx(1, "invalid file mode."); + /* NOTREACHED */ + } + /* In symbolic mode strings, the + and - operators are + interpreted relative to an assumed initial mode of + a=rw. */ + mode = getmode (set, 0666); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + if (argv[0] == NULL) + usage(); + + for (exitval = 0; *argv; ++argv) { + if (mkfifo(*argv, mode) < 0) { + warn("%s", *argv); + exitval = 1; + } + } + exit(exitval); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: mkfifo [-m mode] fifoname ...\n"); + exit(1); +} diff --git a/mknod/Makefile b/mknod/Makefile new file mode 100644 index 0000000..b91bcdc --- /dev/null +++ b/mknod/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = mknod + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = mknod.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mknod.8 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /sbin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/mknod/Makefile.postamble b/mknod/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/mknod/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/mknod/Makefile.preamble b/mknod/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/mknod/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/mknod/PB.project b/mknod/PB.project new file mode 100644 index 0000000..db5f15a --- /dev/null +++ b/mknod/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (mknod.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mknod.8); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /sbin; + 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 = mknod; + 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/mknod/mknod.8 b/mknod/mknod.8 new file mode 100644 index 0000000..7a1517d --- /dev/null +++ b/mknod/mknod.8 @@ -0,0 +1,149 @@ +.\" $NetBSD: mknod.8,v 1.15 1998/09/11 07:20:48 mycroft Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mknod.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd September 11, 1998 +.Dt MKNOD 8 +.Os NetBSD 1.4 +.Sh NAME +.Nm mknod +.Nd make device special file +.Sh SYNOPSIS +.Nm +.Op Fl F Ar format +.Ar name +.Op Cm c | Cm b +.Ar major minor +.Nm "" +.Op Fl F Ar format +.Ar name +.Op Cm c | Cm b +.Ar major unit subunit +.Nm "" +.Ar name +.Op Cm c | Cm b +.Ar number +.Sh DESCRIPTION +The +.Nm +command creates device special files. +Normally the shell script +.Pa /dev/MAKEDEV +is used to create special files for commonly known devices; it executes +.Nm +with the appropriate arguments and can make all the files required for the +device. +.Pp +To make nodes manually, the required arguments are: +.Pp +.Bl -tag -width majorx +.It Ar name +Device name, for example +.Dq sd +for a SCSI disk on an HP300 or a +.Dq pty +for pseudo-devices. +.It Cm b | Cm c +Type of device. If the +device is a block type device such as a tape or disk drive which needs +both cooked and raw special files, +the type is +.Cm b . +All other devices are character type devices, such as terminal +and pseudo devices, and are type +.Cm c . +.It Ar major +The major device number is an integer number which tells the kernel +which device driver entry point to use. To learn what +major device number to use for a particular device, check the file +.Pa /dev/MAKEDEV +to see if the device is known, or check +the system dependent device configuration file: +.Bd -filled -offset indent +.Dq Pa /usr/src/sys/conf/device. Ns Em architecture +.Ed +.Pp +(for example +.Pa device.hp300 ) . +.It Ar minor +The minor device number tells the kernel which one of several similar +devices the node corresponds to; for example, it may be a specific serial +port or pty. +.It Ar unit and subunit +The unit and subunit numbers select a subset of a device; for example, the +unit may specify a particular SCSI disk, and the subunit a partition on +that disk. (Currently this form of specification is only supported by the +.Ar bsdos +format, for compatibility with the +.Bsx +.Xr mknod 8 .) +.El +.Pp +Device numbers for different operating systems may be packed in a different +format. To create device nodes that may be used by such an operating system +(e.g. in an exported file system used for netbooting), the +.Fl F +option is used. The following formats are recognized: +native, +386bsd, +4bsd, +bsdos, +freebsd, +hpux, +isc, +linux, +netbsd, +osf1, +sco, +solaris, +sunos, +svr3, +svr4 and +ultrix. +.Pp +Alternatively, a single opaque device number may be specified. +.Sh SEE ALSO +.Xr mkfifo 1 , +.Xr mkfifo 2 , +.Xr mknod 2 , +.Xr MAKEDEV 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v6 . +The +.Fl F +option appeared in +.Nx 1.4 . diff --git a/mknod/mknod.c b/mknod/mknod.c new file mode 100644 index 0000000..c45abd5 --- /dev/null +++ b/mknod/mknod.c @@ -0,0 +1,396 @@ +/* $NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $ */ + +/*- + * Copyright (c) 1998 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Charles M. Hannum. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.\n"); +__RCSID("$NetBSD: mknod.c,v 1.15 1998/09/11 07:22:13 mycroft Exp $"); +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +int main __P((int, char *[])); +static void usage __P((void)); +typedef dev_t pack_t __P((int, u_long [])); + + +pack_t pack_native; + +dev_t +pack_native(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev(numbers[0], numbers[1]); + if (major(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_netbsd(x) ((int32_t)((((x) & 0x000fff00) >> 8))) +#define minor_netbsd(x) ((int32_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev_netbsd(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) + +pack_t pack_netbsd; + +dev_t +pack_netbsd(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_netbsd(numbers[0], numbers[1]); + if (major_netbsd(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_netbsd(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_freebsd(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_freebsd(x) ((int32_t)(((x) & 0xffff00ff) >> 0)) +#define makedev_freebsd(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0xffff00ff))) + +pack_t pack_freebsd; + +dev_t +pack_freebsd(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_freebsd(numbers[0], numbers[1]); + if (major_freebsd(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_freebsd(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_8_8(x) ((int32_t)(((x) & 0x0000ff00) >> 8)) +#define minor_8_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_8_8(x,y) ((dev_t)((((x) << 8) & 0x0000ff00) | \ + (((y) << 0) & 0x000000ff))) + +pack_t pack_8_8; + +dev_t +pack_8_8(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_8_8(numbers[0], numbers[1]); + if (major_8_8(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_8_8(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_12_20(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define minor_12_20(x) ((int32_t)(((x) & 0x000fffff) >> 0)) +#define makedev_12_20(x,y) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 0) & 0x000fffff))) + +pack_t pack_12_20; + +dev_t +pack_12_20(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_12_20(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_14_18(x) ((int32_t)(((x) & 0xfffc0000) >> 18)) +#define minor_14_18(x) ((int32_t)(((x) & 0x0003ffff) >> 0)) +#define makedev_14_18(x,y) ((dev_t)((((x) << 18) & 0xfffc0000) | \ + (((y) << 0) & 0x0003ffff))) + +pack_t pack_14_18; + +dev_t +pack_14_18(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_14_18(numbers[0], numbers[1]); + if (major_14_18(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_14_18(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_8_24(x) ((int32_t)(((x) & 0xff000000) >> 24)) +#define minor_8_24(x) ((int32_t)(((x) & 0x00ffffff) >> 0)) +#define makedev_8_24(x,y) ((dev_t)((((x) << 24) & 0xff000000) | \ + (((y) << 0) & 0x00ffffff))) + +pack_t pack_8_24; + +dev_t +pack_8_24(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_8_24(numbers[0], numbers[1]); + if (major_8_24(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_8_24(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +#define major_12_12_8(x) ((int32_t)(((x) & 0xfff00000) >> 20)) +#define unit_12_12_8(x) ((int32_t)(((x) & 0x000fff00) >> 8)) +#define subunit_12_12_8(x) ((int32_t)(((x) & 0x000000ff) >> 0)) +#define makedev_12_12_8(x,y,z) ((dev_t)((((x) << 20) & 0xfff00000) | \ + (((y) << 8) & 0x000fff00) | \ + (((z) << 0) & 0x000000ff))) + +pack_t pack_bsdos; + +dev_t +pack_bsdos(n, numbers) + int n; + u_long numbers[]; +{ + dev_t dev=0; /* Quiet -Wall */ + + if (n == 2) { + dev = makedev_12_20(numbers[0], numbers[1]); + if (major_12_20(dev) != numbers[0]) + errx(1, "invalid major number"); + if (minor_12_20(dev) != numbers[1]) + errx(1, "invalid minor number"); + } else if (n == 3) { + dev = makedev_12_12_8(numbers[0], numbers[1], numbers[2]); + if (major_12_12_8(dev) != numbers[0]) + errx(1, "invalid major number"); + if (unit_12_12_8(dev) != numbers[1]) + errx(1, "invalid unit number"); + if (subunit_12_12_8(dev) != numbers[2]) + errx(1, "invalid subunit number"); + } else + errx(1, "too many fields for format"); + return (dev); +} + + +struct format { + char *name; + pack_t *pack; +} formats[] = { + {"386bsd", pack_8_8}, + {"4bsd", pack_8_8}, + {"bsdos", pack_bsdos}, + {"freebsd", pack_freebsd}, + {"hpux", pack_8_24}, + {"isc", pack_8_8}, + {"linux", pack_8_8}, + {"native", pack_native}, + {"netbsd", pack_netbsd}, + {"osf1", pack_12_20}, + {"sco", pack_8_8}, + {"solaris", pack_14_18}, + {"sunos", pack_8_8}, + {"svr3", pack_8_8}, + {"svr4", pack_14_18}, + {"ultrix", pack_8_8}, +}; + +int compare_format __P((const void *, const void *)); + +int +compare_format(key, element) + const void *key; + const void *element; +{ + const char *name; + const struct format *format; + + name = key; + format = element; + + return (strcmp(name, format->name)); +} + + +int +main(argc, argv) + int argc; + char **argv; +{ + char *name; + mode_t mode; + dev_t dev; + pack_t *pack; + u_long numbers[8]; + struct format *format; + char *p; + int n; + int ch; + + pack = pack_native; + + while ((ch = getopt(argc, argv, "F:")) != -1) { + switch (ch) { + case 'F': + format = bsearch(optarg, formats, + sizeof(formats)/sizeof(formats[0]), + sizeof(formats[0]), compare_format); + if (format == 0) + errx(1, "invalid format: %s", optarg); + pack = format->pack; + break; + + default: + case '?': + usage(); + } + } + argc -= optind; + argv += optind; + + if (argc < 3 || argc > 10) + usage(); + + name = *argv; + argc--; + argv++; + + mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH; + if (*argv[0] == 'c') + mode |= S_IFCHR; + else if (*argv[0] == 'b') + mode |= S_IFBLK; + else + errx(1, "node type must be 'b' or 'c'."); + argc--; + argv++; + + for (n = 0; n < argc; n++) { + numbers[n] = strtoul(argv[n], &p, 0); + if ((p && *p != '\0') || (numbers[n] == ULONG_MAX && errno == ERANGE)) + errx(1, "invalid number: %s", argv[n]); + } + + if (argc == 1) + dev = numbers[0]; + else + dev = (*pack)(argc, numbers); + +#if 0 + printf("name: %s\nmode: %05o\ndev: %08x\n", name, mode, dev); +#else + if (mknod(name, mode, dev) < 0) + err(1, "%s", argv[0]); +#endif + + exit(0); +} + +void +usage() +{ + + fprintf(stderr, "usage: mknod [-F format] name [b | c] major minor\n"); + fprintf(stderr, " mknod [-F format] name [b | c] major unit subunit\n"); + fprintf(stderr, " mknod name [b | c] number\n"); + exit(1); +} diff --git a/mtree/Makefile b/mtree/Makefile new file mode 100644 index 0000000..01d374a --- /dev/null +++ b/mtree/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = mtree + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = extern.h mtree.h + +CFILES = compare.c create.c misc.c mtree.c spec.c verify.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mtree.8 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/sbin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/mtree/Makefile.postamble b/mtree/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/mtree/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/mtree/Makefile.preamble b/mtree/Makefile.preamble new file mode 100644 index 0000000..65868da --- /dev/null +++ b/mtree/Makefile.preamble @@ -0,0 +1,5 @@ +vpath crc.c ../cksum + +CFILES += crc.c + +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/mtree/PB.project b/mtree/PB.project new file mode 100644 index 0000000..22cc839 --- /dev/null +++ b/mtree/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (extern.h, mtree.h); + OTHER_LINKED = (compare.c, create.c, misc.c, mtree.c, spec.c, verify.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mtree.8); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /usr/sbin; + 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 = mtree; + 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/mtree/compare.c b/mtree/compare.c new file mode 100644 index 0000000..596f9de --- /dev/null +++ b/mtree/compare.c @@ -0,0 +1,298 @@ +/* $NetBSD: compare.c,v 1.15 1998/08/27 18:03:45 ross Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: compare.c,v 1.15 1998/08/27 18:03:45 ross Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +extern int tflag, uflag; + +static char *ftype __P((u_int)); + +#define INDENTNAMELEN 8 +#define LABEL \ + if (!label++) { \ + len = printf("%s: ", RP(p)); \ + if (len > INDENTNAMELEN) { \ + tab = "\t"; \ + (void)printf("\n"); \ + } else { \ + tab = ""; \ + (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \ + } \ + } + +int +compare(name, s, p) + char *name; + NODE *s; + FTSENT *p; +{ + u_int32_t len, val; + int fd, label; + char *cp, *tab; + + tab = NULL; + label = 0; + switch(s->type) { + case F_BLOCK: + if (!S_ISBLK(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_CHAR: + if (!S_ISCHR(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_DIR: + if (!S_ISDIR(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_FIFO: + if (!S_ISFIFO(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_FILE: + if (!S_ISREG(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_LINK: + if (!S_ISLNK(p->fts_statp->st_mode)) + goto typeerr; + break; + case F_SOCK: + if (!S_ISSOCK(p->fts_statp->st_mode)) { +typeerr: LABEL; + (void)printf("\ttype (%s, %s)\n", + ftype(s->type), inotype(p->fts_statp->st_mode)); + } + break; + } + /* Set the uid/gid first, then set the mode. */ + if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) { + LABEL; + (void)printf("%suser (%u, %u", + tab, s->st_uid, p->fts_statp->st_uid); + if (uflag) + if (chown(p->fts_accpath, s->st_uid, -1)) + (void)printf(", not modified: %s)\n", + strerror(errno)); + else + (void)printf(", modified)\n"); + else + (void)printf(")\n"); + tab = "\t"; + } + if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) { + LABEL; + (void)printf("%sgid (%u, %u", + tab, s->st_gid, p->fts_statp->st_gid); + if (uflag) + if (chown(p->fts_accpath, -1, s->st_gid)) + (void)printf(", not modified: %s)\n", + strerror(errno)); + else + (void)printf(", modified)\n"); + else + (void)printf(")\n"); + tab = "\t"; + } + if (s->flags & F_MODE && + s->st_mode != (p->fts_statp->st_mode & MBITS)) { + LABEL; + (void)printf("%spermissions (%#o, %#o", + tab, s->st_mode, p->fts_statp->st_mode & MBITS); + if (uflag) + if (chmod(p->fts_accpath, s->st_mode)) + (void)printf(", not modified: %s)\n", + strerror(errno)); + else + (void)printf(", modified)\n"); + else + (void)printf(")\n"); + tab = "\t"; + } + if (s->flags & F_NLINK && s->type != F_DIR && + s->st_nlink != p->fts_statp->st_nlink) { + LABEL; + (void)printf("%slink count (%u, %u)\n", + tab, s->st_nlink, p->fts_statp->st_nlink); + tab = "\t"; + } + if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) { + LABEL; + (void)printf("%ssize (%qd, %qd)\n", + tab, (long long)s->st_size, + (long long)p->fts_statp->st_size); + tab = "\t"; + } + /* + * XXX + * Since utimes(2) only takes a timeval, there's no point in + * comparing the low bits of the timespec nanosecond field. This + * will only result in mismatches that we can never fix. + * + * Doesn't display microsecond differences. + */ + if (s->flags & F_TIME) { + struct timeval tv[2]; + + TIMESPEC_TO_TIMEVAL(&tv[0], &s->st_mtimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &p->fts_statp->st_mtimespec); + if (tv[0].tv_sec != tv[1].tv_sec || + tv[0].tv_usec != tv[1].tv_usec) { + LABEL; + (void)printf("%smodification time (%.24s, ", + tab, ctime(&s->st_mtimespec.tv_sec)); + (void)printf("%.24s", + ctime(&p->fts_statp->st_mtimespec.tv_sec)); + if (tflag) { + tv[1] = tv[0]; + if (utimes(p->fts_accpath, tv)) + (void)printf(", not modified: %s)\n", + strerror(errno)); + else + (void)printf(", modified)\n"); + } else + (void)printf(")\n"); + tab = "\t"; + } + } + if (s->flags & F_CKSUM) { + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) { + LABEL; + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else if (crc(fd, &val, &len)) { + (void)close(fd); + LABEL; + (void)printf("%scksum: %s: %s\n", + tab, p->fts_accpath, strerror(errno)); + tab = "\t"; + } else { + (void)close(fd); + if (s->cksum != val) { + LABEL; + (void)printf("%scksum (%lu, %lu)\n", + tab, s->cksum, (unsigned long)val); + } + tab = "\t"; + } + } + if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) { + LABEL; + (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink); + } + return (label); +} + +char * +inotype(type) + u_int type; +{ + switch(type & S_IFMT) { + case S_IFBLK: + return ("block"); + case S_IFCHR: + return ("char"); + case S_IFDIR: + return ("dir"); + case S_IFIFO: + return ("fifo"); + case S_IFREG: + return ("file"); + case S_IFLNK: + return ("link"); + case S_IFSOCK: + return ("socket"); + default: + return ("unknown"); + } + /* NOTREACHED */ +} + +static char * +ftype(type) + u_int type; +{ + switch(type) { + case F_BLOCK: + return ("block"); + case F_CHAR: + return ("char"); + case F_DIR: + return ("dir"); + case F_FIFO: + return ("fifo"); + case F_FILE: + return ("file"); + case F_LINK: + return ("link"); + case F_SOCK: + return ("socket"); + default: + return ("unknown"); + } + /* NOTREACHED */ +} + +char * +rlink(name) + char *name; +{ + static char lbuf[MAXPATHLEN]; + int len; + + if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1) + mtree_err("%s: %s", name, strerror(errno)); + lbuf[len] = '\0'; + return (lbuf); +} diff --git a/mtree/create.c b/mtree/create.c new file mode 100644 index 0000000..7487de1 --- /dev/null +++ b/mtree/create.c @@ -0,0 +1,306 @@ +/* $NetBSD: create.c,v 1.16 1998/08/30 03:20:09 nathanw Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: create.c,v 1.16 1998/08/30 03:20:09 nathanw Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +#define INDENTNAMELEN 15 +#define MAXLINELEN 80 + +extern int crc_total, ftsoptions; +extern int dflag, sflag; +extern u_short keys; +extern char fullpath[MAXPATHLEN]; + +static gid_t gid; +static uid_t uid; +static mode_t mode; + +static int dsort __P((const FTSENT **, const FTSENT **)); +static void output __P((int *, const char *, ...)); +static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *)); +static void statf __P((FTSENT *)); + +void +cwalk() +{ + FTS *t; + FTSENT *p; + time_t clock; + char *argv[2], host[MAXHOSTNAMELEN + 1]; + + (void)time(&clock); + (void)gethostname(host, sizeof(host)); + host[sizeof(host) - 1] = '\0'; + (void)printf( + "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s", + getlogin(), host, fullpath, ctime(&clock)); + + argv[0] = "."; + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, dsort)) == NULL) + mtree_err("fts_open: %s", strerror(errno)); + while ((p = fts_read(t)) != NULL) + switch(p->fts_info) { + case FTS_D: + (void)printf("\n# %s\n", p->fts_path); + statd(t, p, &uid, &gid, &mode); + statf(p); + break; + case FTS_DP: + if (p->fts_level > 0) + (void)printf("# %s\n..\n\n", p->fts_path); + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + (void)fprintf(stderr, + "mtree: %s: %s\n", p->fts_path, strerror(errno)); + break; + default: + if (!dflag) + statf(p); + break; + + } + (void)fts_close(t); + if (sflag && keys & F_CKSUM) + (void)fprintf(stderr, + "mtree: %s checksum: %u\n", fullpath, crc_total); +} + +static void +statf(p) + FTSENT *p; +{ + struct group *gr; + struct passwd *pw; + u_int32_t len, val; + int fd, indent; + + if (S_ISDIR(p->fts_statp->st_mode)) + indent = printf("%s", p->fts_name); + else + indent = printf(" %s", p->fts_name); + + if (indent > INDENTNAMELEN) + indent = MAXLINELEN; + else + indent += printf("%*s", INDENTNAMELEN - indent, ""); + + if (!S_ISREG(p->fts_statp->st_mode)) + output(&indent, "type=%s", inotype(p->fts_statp->st_mode)); + if (keys & (F_UID | F_UNAME) && p->fts_statp->st_uid != uid) { + if (keys & F_UNAME && (pw = getpwuid(p->fts_statp->st_uid))) + output(&indent, "uname=%s", pw->pw_name); + else /* if (keys & F_UID) */ + output(&indent, "uid=%u", p->fts_statp->st_uid); + } + if (keys & (F_GID | F_GNAME) && p->fts_statp->st_gid != gid) { + if (keys & F_GNAME && (gr = getgrgid(p->fts_statp->st_gid))) + output(&indent, "gname=%s", gr->gr_name); + else /* if (keys & F_GID) */ + output(&indent, "gid=%u", p->fts_statp->st_gid); + } + if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode) + output(&indent, "mode=%#o", p->fts_statp->st_mode & MBITS); + if (keys & F_NLINK && p->fts_statp->st_nlink != 1) + output(&indent, "nlink=%u", p->fts_statp->st_nlink); + if (keys & F_SIZE && S_ISREG(p->fts_statp->st_mode)) + output(&indent, "size=%qd", p->fts_statp->st_size); + if (keys & F_TIME) + output(&indent, "time=%ld.%ld", + p->fts_statp->st_mtimespec.tv_sec, + p->fts_statp->st_mtimespec.tv_nsec); + if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) { + if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 || + crc(fd, &val, &len)) + mtree_err("%s: %s", p->fts_accpath, strerror(errno)); + (void)close(fd); + output(&indent, "cksum=%lu", val); + } + if (keys & F_SLINK && + (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE)) + output(&indent, "link=%s", rlink(p->fts_accpath)); + (void)putchar('\n'); +} + +#define MAXGID 5000 +#define MAXUID 5000 +#define MAXMODE MBITS + 1 + +static int +statd(t, parent, puid, pgid, pmode) + FTS *t; + FTSENT *parent; + uid_t *puid; + gid_t *pgid; + mode_t *pmode; +{ + FTSENT *p; + gid_t sgid; + uid_t suid; + mode_t smode; + struct group *gr; + struct passwd *pw; + gid_t savegid; + uid_t saveuid; + mode_t savemode; + u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE]; + + savegid = 0; + saveuid = 0; + savemode = 0; + if ((p = fts_children(t, 0)) == NULL) { + if (errno) + mtree_err("%s: %s", RP(parent), strerror(errno)); + return (1); + } + + memset(g, 0, sizeof(g)); + memset(u, 0, sizeof(u)); + memset(m, 0, sizeof(m)); + + maxuid = maxgid = maxmode = 0; + for (; p; p = p->fts_link) { + smode = p->fts_statp->st_mode & MBITS; + if (smode < MAXMODE && ++m[smode] > maxmode) { + savemode = smode; + maxmode = m[smode]; + } + sgid = p->fts_statp->st_gid; + if (sgid < MAXGID && ++g[sgid] > maxgid) { + savegid = sgid; + maxgid = g[sgid]; + } + suid = p->fts_statp->st_uid; + if (suid < MAXUID && ++u[suid] > maxuid) { + saveuid = suid; + maxuid = u[suid]; + } + } + (void)printf("/set type=file"); + if (keys & F_GID) + (void)printf(" gid=%u", savegid); + if (keys & F_GNAME) { + if ((gr = getgrgid(savegid)) != NULL) + (void)printf(" gname=%s", gr->gr_name); + else + (void)printf(" gid=%u", savegid); + } + if (keys & F_UNAME) { + if ((pw = getpwuid(saveuid)) != NULL) + (void)printf(" uname=%s", pw->pw_name); + else + (void)printf(" uid=%u", saveuid); + } + if (keys & F_UID) + (void)printf(" uid=%u", saveuid); + if (keys & F_MODE) + (void)printf(" mode=%#o", savemode); + if (keys & F_NLINK) + (void)printf(" nlink=1"); + (void)printf("\n"); + *puid = saveuid; + *pgid = savegid; + *pmode = savemode; + return (0); +} + +static int +dsort(a, b) + const FTSENT **a, **b; +{ + if (S_ISDIR((*a)->fts_statp->st_mode)) { + if (!S_ISDIR((*b)->fts_statp->st_mode)) + return (1); + } else if (S_ISDIR((*b)->fts_statp->st_mode)) + return (-1); + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +output(int *offset, const char *fmt, ...) +#else +output(offset, fmt, va_alist) + int *offset; + char *fmt; + va_dcl +#endif +{ + va_list ap; + char buf[1024]; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (*offset + strlen(buf) > MAXLINELEN - 3) { + (void)printf(" \\\n%*s", INDENTNAMELEN, ""); + *offset = INDENTNAMELEN; + } + *offset += printf(" %s", buf) + 1; +} diff --git a/mtree/extern.h b/mtree/extern.h new file mode 100644 index 0000000..4b09f62 --- /dev/null +++ b/mtree/extern.h @@ -0,0 +1,52 @@ +/* $NetBSD: extern.h,v 1.3 1995/03/07 21:12:07 cgd Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +#include "mtree.h" + +#ifdef __APPLE__ +#include +#endif + +int compare __P((char *, NODE *, FTSENT *)); +int crc __P((int, u_int32_t *, u_int32_t *)); +void cwalk __P((void)); +void mtree_err __P((const char *, ...)); +char *inotype __P((u_int)); +u_int parsekey __P((char *, int *)); +char *rlink __P((char *)); +NODE *spec __P((void)); +int verify __P((void)); diff --git a/mtree/misc.c b/mtree/misc.c new file mode 100644 index 0000000..4eeceef --- /dev/null +++ b/mtree/misc.c @@ -0,0 +1,134 @@ +/* $NetBSD: misc.c,v 1.5 1997/10/17 11:46:40 lukem Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)misc.c 8.1 (Berkeley) 6/6/93 + */ + +#include +#ifndef lint +__RCSID("$NetBSD: misc.c,v 1.5 1997/10/17 11:46:40 lukem Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +extern int lineno; + +typedef struct _key { + char *name; /* key name */ + u_int val; /* value */ + +#define NEEDVALUE 0x01 + u_int flags; +} KEY; + +/* NB: the following table must be sorted lexically. */ +static KEY keylist[] = { + {"cksum", F_CKSUM, NEEDVALUE}, + {"gid", F_GID, NEEDVALUE}, + {"gname", F_GNAME, NEEDVALUE}, + {"ignore", F_IGN, 0}, + {"link", F_SLINK, NEEDVALUE}, + {"mode", F_MODE, NEEDVALUE}, + {"nlink", F_NLINK, NEEDVALUE}, + {"optional", F_OPT, 0}, + {"size", F_SIZE, NEEDVALUE}, + {"time", F_TIME, NEEDVALUE}, + {"type", F_TYPE, NEEDVALUE}, + {"uid", F_UID, NEEDVALUE}, + {"uname", F_UNAME, NEEDVALUE} +}; + +int keycompare __P((const void *, const void *)); + +u_int +parsekey(name, needvaluep) + char *name; + int *needvaluep; +{ + KEY *k, tmp; + + tmp.name = name; + k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY), + sizeof(KEY), keycompare); + if (k == NULL) + mtree_err("unknown keyword %s", name); + + if (needvaluep) + *needvaluep = k->flags & NEEDVALUE ? 1 : 0; + return (k->val); +} + +int +keycompare(a, b) + const void *a, *b; +{ + return (strcmp(((KEY *)a)->name, ((KEY *)b)->name)); +} + +#if __STDC__ +#include +#else +#include +#endif + +void +#if __STDC__ +mtree_err(const char *fmt, ...) +#else +mtree_err(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "mtree: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (lineno) + (void)fprintf(stderr, + "mtree: failed at line %d of the specification\n", lineno); + exit(1); + /* NOTREACHED */ +} diff --git a/mtree/mtree.8 b/mtree/mtree.8 new file mode 100644 index 0000000..cb89812 --- /dev/null +++ b/mtree/mtree.8 @@ -0,0 +1,261 @@ +.\" $NetBSD: mtree.8,v 1.7 1997/10/17 11:46:46 lukem Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mtree.8 8.2 (Berkeley) 12/11/93 +.\" +.Dd December 11, 1993 +.Dt MTREE 8 +.Os +.Sh NAME +.Nm mtree +.Nd map a directory hierarchy +.Sh SYNOPSIS +.Nm +.Op Fl cderux +.Op Fl f Ar spec +.Op Fl K Ar keywords +.Op Fl k Ar keywords +.Op Fl p Ar path +.Op Fl s Ar seed +.Sh DESCRIPTION +The utility +.Nm +compares the file hierarchy rooted in the current directory against a +specification read from the standard input. +Messages are written to the standard output for any files whose +characteristics do not match the specification, or which are +missing from either the file hierarchy or the specification. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl c +Print a specification for the file hierarchy to the standard output. +.It Fl d +Ignore everything except directory type files. +.It Fl e +Don't complain about files that are in the file hierarchy, but not in the +specification. +.It Fl f +Read the specification from +.Ar file , +instead of from the standard input. +.It Fl K +Add the specified (whitespace or comma separated) keywords to the current +set of keywords. +.It Fl k +Use the ``type'' keyword plus the specified (whitespace or comma separated) +keywords instead of the current set of keywords. +.It Fl p +Use the file hierarchy rooted in +.Ar path , +instead of the current directory. +.It Fl r +Remove any files in the file hierarchy that are not described in the +specification. +.It Fl s +Display a single checksum to the standard error output that represents all +of the files for which the keyword +.Cm cksum +was specified. +The checksum is seeded with the specified value. +.It Fl U +Modify the owner, group, and permissions of existing files to match +the specification and create any missing directories. +User, group, and permissions must all be specified for missing directories +to be created. +Exit with a status of 0 on success, 1 if any error occurred; +a mismatch is not considered to be an error if it was corrected. +.It Fl u +Same as +.Fl U +except a status of 2 is returned if the file hierarchy did not match +the specification. +.It Fl x +Don't descend below mount points in the file hierarchy. +.El +.Pp +Specifications are mostly composed of ``keywords'', i.e. strings that +that specify values relating to files. +No keywords have default values, and if a keyword has no value set, no +checks based on it are performed. +.Pp +Currently supported keywords are as follows: +.Bl -tag -width Cm +.It Cm cksum +The checksum of the file using the default algorithm specified by +the +.Xr cksum 1 +utility. +.It Cm ignore +Ignore any file hierarchy below this file. +.It Cm gid +The file group as a numeric value. +.It Cm gname +The file group as a symbolic name. +.It Cm link +The file the symbolic link is expected to reference. +.It Cm mode +The current file's permissions as a numeric (octal) or symbolic +value. +.It Cm nlink +The number of hard links the file is expected to have. +.It Cm optional +The file is optional; don't complain about the file if it's +not in the file hierarchy. +.It Cm uid +The file owner as a numeric value. +.It Cm uname +The file owner as a symbolic name. +.It Cm size +The size, in bytes, of the file. +.It Cm time +The last modification time of the file. +.It Cm type +The type of the file; may be set to any one of the following: +.sp +.Bl -tag -width Cm -compact +.It Cm block +block special device +.It Cm char +character special device +.It Cm dir +directory +.It Cm fifo +fifo +.It Cm file +regular file +.It Cm link +symbolic link +.It Cm socket +socket +.El +.El +.Pp +The default set of keywords are +.Cm gid , +.Cm link , +.Cm mode , +.Cm nlink , +.Cm size , +.Cm time , +and +.Cm uid . +.Pp +There are four types of lines in a specification. +.Pp +The first type of line sets a global value for a keyword, and consists of +the string ``/set'' followed by whitespace, followed by sets of keyword/value +pairs, separated by whitespace. +Keyword/value pairs consist of a keyword, followed by an equals sign +(``=''), followed by a value, without whitespace characters. +Once a keyword has been set, its value remains unchanged until either +reset or unset. +.Pp +The second type of line unsets keywords and consists of the string +``/unset'', followed by whitespace, followed by one or more keywords, +separated by whitespace. +.Pp +The third type of line is a file specification and consists of a file +name, followed by whitespace, followed by zero or more whitespace +separated keyword/value pairs. +The file name may be preceded by whitespace characters. +The file name may contain any of the standard file name matching +characters (``['', ``]'', ``?'' or ``*''), in which case files +in the hierarchy will be associated with the first pattern that +they match. +.Pp +Each of the keyword/value pairs consist of a keyword, followed by an +equals sign (``=''), followed by the keyword's value, without +whitespace characters. +These values override, without changing, the global value of the +corresponding keyword. +.Pp +All paths are relative. +Specifying a directory will cause subsequent files to be searched +for in that directory hierarchy. +Which brings us to the last type of line in a specification: a line +containing only the string +.Dq Nm \&.. +causes the current directory +path to ascend one level. +.Pp +Empty lines and lines whose first non-whitespace character is a hash +mark (``#'') are ignored. +.Pp +The +.Nm +utility exits with a status of 0 on success, 1 if any error occurred, +and 2 if the file hierarchy did not match the specification. +.Sh EXAMPLES +To detect system binaries that have been ``trojan horsed'', it is recommended +that +.Nm +be run on the file systems, and a copy of the results stored on a different +machine, or, at least, in encrypted form. +The seed for the +.Fl s +option should not be an obvious value and the final checksum should not be +stored on-line under any circumstances! +Then, periodically, +.Nm +should be run against the on-line specifications and the final checksum +compared with the previous value. +While it is possible for the bad guys to change the on-line specifications +to conform to their modified binaries, it shouldn't be possible for them +to make it produce the same final checksum value. +If the final checksum value changes, the off-line copies of the specification +can be used to detect which of the binaries have actually been modified. +.Pp +The +.Fl d +and +.Fl u +options can be used in combination to create directory hierarchies +for distributions and other such things. +.Sh FILES +.Bl -tag -width /etc/mtree -compact +.It Pa /etc/mtree +system specification directory +.El +.Sh SEE ALSO +.Xr chmod 1 , +.Xr chgrp 1 , +.Xr cksum 1 , +.Xr stat 2 , +.Xr fts 3 , +.Xr chown 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.3 Reno . diff --git a/mtree/mtree.c b/mtree/mtree.c new file mode 100644 index 0000000..f51b372 --- /dev/null +++ b/mtree/mtree.c @@ -0,0 +1,162 @@ +/* $NetBSD: mtree.c,v 1.9 1997/10/17 11:46:51 lukem Exp $ */ + +/*- + * Copyright (c) 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: mtree.c,v 1.9 1997/10/17 11:46:51 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +extern int crc_total; + +int ftsoptions = FTS_PHYSICAL; +int cflag, dflag, eflag, rflag, sflag, tflag, uflag, Uflag; +u_short keys; +char fullpath[MAXPATHLEN]; + + int main __P((int, char **)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + char *dir, *p; + int status; + + dir = NULL; + keys = KEYDEFAULT; + while ((ch = getopt(argc, argv, "cdef:K:k:p:rs:tUux")) != -1) + switch((char)ch) { + case 'c': + cflag = 1; + break; + case 'd': + dflag = 1; + break; + case 'e': + eflag = 1; + break; + case 'f': + if (!(freopen(optarg, "r", stdin))) + mtree_err("%s: %s", optarg, strerror(errno)); + break; + case 'K': + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); + break; + case 'k': + keys = F_TYPE; + while ((p = strsep(&optarg, " \t,")) != NULL) + if (*p != '\0') + keys |= parsekey(p, NULL); + break; + case 'p': + dir = optarg; + break; + case 'r': + rflag = 1; + break; + case 's': + sflag = 1; + crc_total = ~strtol(optarg, &p, 0); + if (*p) + mtree_err("illegal seed value -- %s", optarg); + break; + case 't': + tflag = 1; + break; + case 'U': + Uflag = uflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc) + usage(); + + if (dir && chdir(dir)) + mtree_err("%s: %s", dir, strerror(errno)); + + if ((cflag || sflag) && !getwd(fullpath)) + mtree_err("%s", fullpath); + + if (cflag) { + cwalk(); + exit(0); + } + status = verify(); + if (Uflag & (status == MISMATCHEXIT)) + status = 0; + exit(status); +} + +static void +usage() +{ + (void)fprintf(stderr, +"usage: mtree [-cderUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"); + exit(1); +} diff --git a/mtree/mtree.h b/mtree/mtree.h new file mode 100644 index 0000000..431fdd8 --- /dev/null +++ b/mtree/mtree.h @@ -0,0 +1,97 @@ +/* $NetBSD: mtree.h,v 1.7 1995/03/07 21:26:27 cgd Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mtree.h 8.1 (Berkeley) 6/6/93 + */ + +#ifndef _MTREE_H_ +#define _MTREE_H_ + +#include +#include +#include + +#define KEYDEFAULT \ + (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID) + +#define MISMATCHEXIT 2 + +typedef struct _node { + struct _node *parent, *child; /* up, down */ + struct _node *prev, *next; /* left, right */ + off_t st_size; /* size */ + struct timespec st_mtimespec; /* last modification time */ + u_long cksum; /* check sum */ + char *slink; /* symbolic link reference */ + uid_t st_uid; /* uid */ + gid_t st_gid; /* gid */ +#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO) + mode_t st_mode; /* mode */ + nlink_t st_nlink; /* link count */ + +#define F_CKSUM 0x0001 /* check sum */ +#define F_DONE 0x0002 /* directory done */ +#define F_GID 0x0004 /* gid */ +#define F_GNAME 0x0008 /* group name */ +#define F_IGN 0x0010 /* ignore */ +#define F_MAGIC 0x0020 /* name has magic chars */ +#define F_MODE 0x0040 /* mode */ +#define F_NLINK 0x0080 /* number of links */ +#define F_OPT 0x0100 /* existence optional */ +#define F_SIZE 0x0200 /* size */ +#define F_SLINK 0x0400 /* link count */ +#define F_TIME 0x0800 /* modification time */ +#define F_TYPE 0x1000 /* file type */ +#define F_UID 0x2000 /* uid */ +#define F_UNAME 0x4000 /* user name */ +#define F_VISIT 0x8000 /* file visited */ + u_short flags; /* items set */ + +#define F_BLOCK 0x001 /* block special */ +#define F_CHAR 0x002 /* char special */ +#define F_DIR 0x004 /* directory */ +#define F_FIFO 0x008 /* fifo */ +#define F_FILE 0x010 /* regular file */ +#define F_LINK 0x020 /* symbolic link */ +#define F_SOCK 0x040 /* socket */ + u_char type; /* file type */ + + char name[1]; /* file name (must be last) */ +} NODE; + +#define RP(p) \ + ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \ + (p)->fts_path + 2 : (p)->fts_path) + +#endif /* _MTREE_H_ */ diff --git a/mtree/spec.c b/mtree/spec.c new file mode 100644 index 0000000..88045ee --- /dev/null +++ b/mtree/spec.c @@ -0,0 +1,293 @@ +/* $NetBSD: spec.c,v 1.12 1998/09/23 19:46:00 itohy Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)spec.c 8.2 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: spec.c,v 1.12 1998/09/23 19:46:00 itohy Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mtree.h" +#include "extern.h" + +int lineno; /* Current spec line number. */ + +static void set __P((char *, NODE *)); +static void unset __P((char *, NODE *)); + +NODE * +spec() +{ + NODE *centry, *last; + char *p; + NODE ginfo, *root; + int c_cur, c_next; + char buf[2048]; + + root = NULL; + centry = last = NULL; + memset(&ginfo, 0, sizeof(ginfo)); + c_cur = c_next = 0; + for (lineno = 1; fgets(buf, sizeof(buf), stdin); + ++lineno, c_cur = c_next, c_next = 0) { + /* Skip empty lines. */ + if (buf[0] == '\n') + continue; + + /* Find end of line. */ + if ((p = strchr(buf, '\n')) == NULL) + mtree_err("line %d too long", lineno); + + /* See if next line is continuation line. */ + if (p[-1] == '\\') { + --p; + c_next = 1; + } + + /* Null-terminate the line. */ + *p = '\0'; + + /* Skip leading whitespace. */ + for (p = buf; *p && isspace(*p); ++p); + + /* If nothing but whitespace or comment char, continue. */ + if (!*p || *p == '#') + continue; + +#ifdef DEBUG + (void)fprintf(stderr, "line %d: {%s}\n", lineno, p); +#endif + if (c_cur) { + set(p, centry); + continue; + } + + /* Grab file name, "$", "set", or "unset". */ + if ((p = strtok(p, "\n\t ")) == NULL) + mtree_err("missing field"); + + if (p[0] == '/') + switch(p[1]) { + case 's': + if (strcmp(p + 1, "set")) + break; + set(NULL, &ginfo); + continue; + case 'u': + if (strcmp(p + 1, "unset")) + break; + unset(NULL, &ginfo); + continue; + } + + if (strchr(p, '/')) + mtree_err("slash character in file name"); + + if (!strcmp(p, "..")) { + /* Don't go up, if haven't gone down. */ + if (!root) + goto noparent; + if (last->type != F_DIR || last->flags & F_DONE) { + if (last == root) + goto noparent; + last = last->parent; + } + last->flags |= F_DONE; + continue; + +noparent: mtree_err("no parent node"); + } + + if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL) + mtree_err("%s", strerror(errno)); + *centry = ginfo; + (void)strcpy(centry->name, p); +#define MAGIC "?*[" + if (strpbrk(p, MAGIC)) + centry->flags |= F_MAGIC; + set(NULL, centry); + + if (!root) { + last = root = centry; + root->parent = root; + } else if (last->type == F_DIR && !(last->flags & F_DONE)) { + centry->parent = last; + last = last->child = centry; + } else { + centry->parent = last->parent; + centry->prev = last; + last = last->next = centry; + } + } + return (root); +} + +static void +set(t, ip) + char *t; + NODE *ip; +{ + int type; + char *kw, *val; + struct group *gr; + struct passwd *pw; + mode_t *m; + int value; + char *ep; + + val = NULL; + for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) { + ip->flags |= type = parsekey(kw, &value); + if (value && (val = strtok(NULL, " \t\n")) == NULL) + mtree_err("missing value"); + switch(type) { + case F_CKSUM: + ip->cksum = strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid checksum %s", val); + break; + case F_GID: + ip->st_gid = (gid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid gid %s", val); + break; + case F_GNAME: + if ((gr = getgrnam(val)) == NULL) + mtree_err("unknown group %s", val); + ip->st_gid = gr->gr_gid; + break; + case F_IGN: + /* just set flag bit */ + break; + case F_MODE: + if ((m = setmode(val)) == NULL) + mtree_err("invalid file mode %s", val); + ip->st_mode = getmode(m, 0); + free(m); + break; + case F_NLINK: + ip->st_nlink = (nlink_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid link count %s", val); + break; + case F_SIZE: + ip->st_size = (off_t)strtoq(val, &ep, 10); + if (*ep) + mtree_err("invalid size %s", val); + break; + case F_SLINK: + if ((ip->slink = strdup(val)) == NULL) + mtree_err("%s", strerror(errno)); + break; + case F_TIME: + ip->st_mtimespec.tv_sec = + (time_t)strtoul(val, &ep, 10); + if (*ep != '.') + mtree_err("invalid time %s", val); + val = ep + 1; + ip->st_mtimespec.tv_nsec = strtol(val, &ep, 10); + if (*ep) + mtree_err("invalid time %s", val); + break; + case F_TYPE: + switch(*val) { + case 'b': + if (!strcmp(val, "block")) + ip->type = F_BLOCK; + break; + case 'c': + if (!strcmp(val, "char")) + ip->type = F_CHAR; + break; + case 'd': + if (!strcmp(val, "dir")) + ip->type = F_DIR; + break; + case 'f': + if (!strcmp(val, "file")) + ip->type = F_FILE; + if (!strcmp(val, "fifo")) + ip->type = F_FIFO; + break; + case 'l': + if (!strcmp(val, "link")) + ip->type = F_LINK; + break; + case 's': + if (!strcmp(val, "socket")) + ip->type = F_SOCK; + break; + default: + mtree_err("unknown file type %s", val); + } + break; + case F_UID: + ip->st_uid = (uid_t)strtoul(val, &ep, 10); + if (*ep) + mtree_err("invalid uid %s", val); + break; + case F_UNAME: + if ((pw = getpwnam(val)) == NULL) + mtree_err("unknown user %s", val); + ip->st_uid = pw->pw_uid; + break; + } + } +} + +static void +unset(t, ip) + char *t; + NODE *ip; +{ + char *p; + + while ((p = strtok(t, "\n\t ")) != NULL) + ip->flags &= ~parsekey(p, NULL); +} diff --git a/mtree/verify.c b/mtree/verify.c new file mode 100644 index 0000000..057f240 --- /dev/null +++ b/mtree/verify.c @@ -0,0 +1,211 @@ +/* $NetBSD: verify.c,v 1.14 1998/08/27 18:03:45 ross Exp $ */ + +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: verify.c,v 1.14 1998/08/27 18:03:45 ross Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "mtree.h" +#include "extern.h" + +extern int crc_total, ftsoptions; +extern int dflag, eflag, rflag, sflag, uflag; +extern char fullpath[MAXPATHLEN]; + +static NODE *root; +static char path[MAXPATHLEN]; + +static void miss __P((NODE *, char *)); +static int vwalk __P((void)); + +int +verify() +{ + int rval; + + root = spec(); + rval = vwalk(); + miss(root, path); + return (rval); +} + +static int +vwalk() +{ + FTS *t; + FTSENT *p; + NODE *ep, *level; + int ftsdepth, specdepth, rval; + char *argv[2]; + + argv[0] = "."; + argv[1] = NULL; + if ((t = fts_open(argv, ftsoptions, NULL)) == NULL) + mtree_err("fts_open: %s", strerror(errno)); + level = root; + ftsdepth = specdepth = rval = 0; + while ((p = fts_read(t)) != NULL) { + switch(p->fts_info) { + case FTS_D: + ++ftsdepth; + break; + case FTS_DP: + --ftsdepth; + if (specdepth > ftsdepth) { + for (level = level->parent; level->prev; + level = level->prev); + --specdepth; + } + continue; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + (void)fprintf(stderr, "mtree: %s: %s\n", + RP(p), strerror(errno)); + continue; + default: + if (dflag) + continue; + } + + for (ep = level; ep; ep = ep->next) + if ((ep->flags & F_MAGIC && + !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) || + !strcmp(ep->name, p->fts_name)) { + ep->flags |= F_VISIT; + if (compare(ep->name, ep, p)) + rval = MISMATCHEXIT; + if (!(ep->flags & F_IGN) && + ep->child && ep->type == F_DIR && + p->fts_info == FTS_D) { + level = ep->child; + ++specdepth; + } else + (void)fts_set(t, p, FTS_SKIP); + break; + } + + if (ep) + continue; + if (!eflag) { + (void)printf("extra: %s", RP(p)); + if (rflag) { + if (unlink(p->fts_accpath)) { + (void)printf(", not removed: %s", + strerror(errno)); + } else + (void)printf(", removed"); + } + (void)putchar('\n'); + } + (void)fts_set(t, p, FTS_SKIP); + } + (void)fts_close(t); + if (sflag) + (void)fprintf(stderr, + "mtree: %s checksum: %u\n", fullpath, crc_total); + return (rval); +} + +static void +miss(p, tail) + NODE *p; + char *tail; +{ + int create; + char *tp; + + for (; p; p = p->next) { + if (p->flags & F_OPT && !(p->flags & F_VISIT)) + continue; + if (p->type != F_DIR && (dflag || p->flags & F_VISIT)) + continue; + (void)strcpy(tail, p->name); + if (!(p->flags & F_VISIT)) + (void)printf("missing: %s", path); + if (p->type != F_DIR) { + putchar('\n'); + continue; + } + + create = 0; + if (!(p->flags & F_VISIT) && uflag) { + if (!(p->flags & (F_UID | F_UNAME))) + (void)printf(" (not created: user not specified)"); + else if (!(p->flags & (F_GID | F_GNAME))) + (void)printf(" (not created: group not specified)"); + else if (!(p->flags & F_MODE)) + (void)printf(" (not created: mode not specified)"); + else if (mkdir(path, S_IRWXU)) + (void)printf(" (not created: %s)", + strerror(errno)); + else { + create = 1; + (void)printf(" (created)"); + } + } + if (!(p->flags & F_VISIT)) + (void)putchar('\n'); + + for (tp = tail; *tp; ++tp); + *tp = '/'; + miss(p->child, tp + 1); + *tp = '\0'; + + if (!create) + continue; + if (chown(path, p->st_uid, p->st_gid)) { + (void)printf("%s: user/group/mode not modified: %s\n", + path, strerror(errno)); + continue; + } + if (chmod(path, p->st_mode)) + (void)printf("%s: permissions not set: %s\n", + path, strerror(errno)); + } +} diff --git a/mv/Makefile b/mv/Makefile new file mode 100644 index 0000000..52e16a4 --- /dev/null +++ b/mv/Makefile @@ -0,0 +1,50 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = mv + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = pathnames.h + +CFILES = mv.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble mv.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/mv/Makefile.postamble b/mv/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/mv/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/mv/Makefile.preamble b/mv/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/mv/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/mv/PB.project b/mv/PB.project new file mode 100644 index 0000000..626c69c --- /dev/null +++ b/mv/PB.project @@ -0,0 +1,26 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = (pathnames.h); + OTHER_LINKED = (mv.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, mv.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = mv; + 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/mv/mv.1 b/mv/mv.1 new file mode 100644 index 0000000..f831399 --- /dev/null +++ b/mv/mv.1 @@ -0,0 +1,133 @@ +.\" $NetBSD: mv.1,v 1.11 1998/04/20 05:43:30 ross Exp $ +.\" +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)mv.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt MV 1 +.Os +.Sh NAME +.Nm mv +.Nd move files +.Sh SYNOPSIS +.Nm +.Op Fl fi +.Ar source target +.Nm "" +.Op Fl fi +.Ar source ... directory +.Sh DESCRIPTION +.Pp +In its first form, the +.Nm +utility renames the file named by the +.Ar source +operand to the destination path named by the +.Ar target +operand. +This form is assumed when the last operand does not name an already +existing directory. +.Pp +In its second form, +.Nm +moves each file named by a +.Ar source +operand to a destination file in the existing directory named by the +.Ar directory +operand. +The destination path for each operand is the pathname produced by the +concatenation of the last operand, a slash, and the final pathname +component of the named file. +.Pp +The following options are available: +.Bl -tag -width flag +.It Fl f +Do not prompt for confirmation before overwriting the destination +path. +.It Fl i +Causes +.Nm +to write a prompt to standard error before moving a file that would +overwrite an existing file. +If the response from the standard input begins with the character ``y'', +the move is attempted. +.El +The last of any +.Fl f +or +.Fl i +options is the one which affects +.Nm Ns 's +behavior. +.Pp +It is an error for either the +.Ar source +operand or the destination path to specify a directory unless both do. +.Pp +If the destination path does not have a mode which permits writing, +.Nm +prompts the user for confirmation as specified for the +.Fl i +option. +.Pp +As the +.Xr rename 2 +call does not work across file systems, +.Nm +uses +.Xr cp 1 +and +.Xr rm 1 +to accomplish the move. +The effect is equivalent to: +.Bd -literal -offset indent +rm -f destination_path && \e +\tcp -PRp source_file destination && \e +\trm -rf source_file +.Ed +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr cp 1 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. diff --git a/mv/mv.c b/mv/mv.c new file mode 100644 index 0000000..a8c08d0 --- /dev/null +++ b/mv/mv.c @@ -0,0 +1,361 @@ +/* $NetBSD: mv.c,v 1.19 1998/07/28 11:41:49 mycroft Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ken Smith of The State University of New York at Buffalo. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: mv.c,v 1.19 1998/07/28 11:41:49 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pathnames.h" + +int fflg, iflg; +int stdin_ok; + +int copy __P((char *, char *)); +int do_move __P((char *, char *)); +int fastcopy __P((char *, char *, struct stat *)); +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int baselen, len, rval; + char *p, *endp; + struct stat sb; + int ch; + char path[MAXPATHLEN + 1]; + + (void)setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "if")) != -1) + switch (ch) { + case 'i': + fflg = 0; + iflg = 1; + break; + case 'f': + iflg = 0; + fflg = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + stdin_ok = isatty(STDIN_FILENO); + + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); + } + + /* It's a directory, move each file into it. */ + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + *endp++ = '/'; + ++baselen; + for (rval = 0; --argc; ++argv) { + p = *argv + strlen(*argv) - 1; + while (*p == '/' && p != *argv) + *p-- = '\0'; + if ((p = strrchr(*argv, '/')) == NULL) + p = *argv; + else + ++p; + + if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, len + 1); + if (do_move(*argv, path)) + rval = 1; + } + } + exit(rval); + /* NOTREACHED */ +} + +int +do_move(from, to) + char *from, *to; +{ + struct stat sb; + char modep[15]; + + /* + * (1) If the destination path exists, the -f option is not specified + * and either of the following conditions are true: + * + * (a) The perimissions of the destination path do not permit + * writing and the standard input is a terminal. + * (b) The -i option is specified. + * + * the mv utility shall write a prompt to standard error and + * read a line from standard input. If the response is not + * affirmative, mv shall do nothing more with the current + * source file... + */ + if (!fflg && !access(to, F_OK)) { + int ask = 1; + int ch; + + if (iflg) { + (void)fprintf(stderr, "overwrite %s? ", to); + } else if (stdin_ok && access(to, W_OK) && !stat(to, &sb)) { + strmode(sb.st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to); + } else + ask = 0; + if (ask) { + if ((ch = getchar()) != EOF && ch != '\n') + while (getchar() != '\n'); + if (ch != 'y' && ch != 'Y') + return (0); + } + } + + /* + * (2) If rename() succeeds, mv shall do nothing more with the + * current source file. If it fails for any other reason than + * EXDEV, mv shall write a diagnostic message to the standard + * error and do nothing more with the current source file. + * + * (3) If the destination path exists, and it is a file of type + * directory and source_file is not a file of type directory, + * or it is a file not of type directory, and source file is + * a file of type directory, mv shall write a diagnostic + * message to standard error, and do nothing more with the + * current source file... + */ + if (!rename(from, to)) + return (0); + + if (errno != EXDEV) { + warn("rename %s to %s", from, to); + return (1); + } + + /* + * (4) If the destination path exists, mv shall attempt to remove it. + * If this fails for any reason, mv shall write a diagnostic + * message to the standard error and do nothing more with the + * current source file... + */ + if (!lstat(to, &sb)) { + if ((S_ISDIR(sb.st_mode)) ? rmdir(to) : unlink(to)) { + warn("can't remove %s", to); + return (1); + } + } + + /* + * (5) The file hierarchy rooted in source_file shall be duplicated + * as a file hierarchy rooted in the destination path... + */ + if (lstat(from, &sb)) { + warn("%s", from); + return (1); + } + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb) : copy(from, to)); +} + +int +fastcopy(from, to, sbp) + char *from, *to; + struct stat *sbp; +{ + struct timeval tval[2]; + static u_int blen; + static char *bp; + int nread, from_fd, to_fd; + + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); + } + if ((to_fd = + open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { + warn("%s", to); + (void)close(from_fd); + return (1); + } + if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { + warn("%s", ""); + return (1); + } + while ((nread = read(from_fd, bp, blen)) > 0) + if (write(to_fd, bp, nread) != nread) { + warn("%s", to); + goto err; + } + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)close(from_fd); + (void)close(to_fd); + return (1); + } + (void)close(from_fd); + + TIMESPEC_TO_TIMEVAL(&tval[0], &sbp->st_atimespec); + TIMESPEC_TO_TIMEVAL(&tval[1], &sbp->st_mtimespec); +#ifndef __APPLE__ + if (futimes(to_fd, tval)) +#else + if (utimes(to, tval)) +#endif + warn("%s: set times", to); + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { + if (errno != EPERM) + warn("%s: set owner/group", to); + sbp->st_mode &= ~(S_ISUID | S_ISGID); + } + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode", to); + + if (close(to_fd)) { + warn("%s", to); + return (1); + } + + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + return (0); +} + +int +copy(from, to) + char *from, *to; +{ + int pid, status; + + if ((pid = vfork()) == 0) { + execl(_PATH_CP, "mv", "-PRp", from, to, NULL); + warn("%s", _PATH_CP); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_CP); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_CP); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_CP, WEXITSTATUS(status)); + return (1); + } + if (!(pid = vfork())) { + execl(_PATH_RM, "mv", "-rf", from, NULL); + warn("%s", _PATH_RM); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_RM); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_RM); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_RM, WEXITSTATUS(status)); + return (1); + } + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: mv [-fi] source target\n"); + (void)fprintf(stderr, " mv [-fi] source ... directory\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/mv/pathnames.h b/mv/pathnames.h new file mode 100644 index 0000000..c612fd0 --- /dev/null +++ b/mv/pathnames.h @@ -0,0 +1,39 @@ +/* $NetBSD: pathnames.h,v 1.6 1995/03/21 09:06:55 cgd Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 5/31/93 + */ + +#define _PATH_RM "/bin/rm" +#define _PATH_CP "/bin/cp" diff --git a/pax/Makefile b/pax/Makefile new file mode 100644 index 0000000..ebe65d9 --- /dev/null +++ b/pax/Makefile @@ -0,0 +1,54 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = pax + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +HFILES = cache.h cpio.h extern.h ftree.h options.h pat_rep.h pax.h\ + sel_subs.h tables.h tar.h + +CFILES = ar_io.c ar_subs.c buf_subs.c cache.c cpio.c file_subs.c\ + ftree.c gen_subs.c getoldopt.c options.c pat_rep.c pax.c\ + sel_subs.c tables.c tar.c tty_subs.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble pax.1 cpio.1\ + tar.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/pax/Makefile.postamble b/pax/Makefile.postamble new file mode 100644 index 0000000..3879a87 --- /dev/null +++ b/pax/Makefile.postamble @@ -0,0 +1,5 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common + +after_install:: + $(LINKPRODUCT) $(DSTROOT)$(USRBINDIR)/cpio + $(LINKPRODUCT) $(DSTROOT)$(USRBINDIR)/tar diff --git a/pax/Makefile.preamble b/pax/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/pax/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/pax/PB.project b/pax/PB.project new file mode 100644 index 0000000..11107f6 --- /dev/null +++ b/pax/PB.project @@ -0,0 +1,54 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + H_FILES = ( + cache.h, + cpio.h, + extern.h, + ftree.h, + options.h, + pat_rep.h, + pax.h, + sel_subs.h, + tables.h, + tar.h + ); + OTHER_LINKED = ( + ar_io.c, + ar_subs.c, + buf_subs.c, + cache.c, + cpio.c, + file_subs.c, + ftree.c, + gen_subs.c, + getoldopt.c, + options.c, + pat_rep.c, + pax.c, + sel_subs.c, + tables.c, + tar.c, + tty_subs.c + ); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, pax.1, cpio.1, tar.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = pax; + 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/pax/ar_io.c b/pax/ar_io.c new file mode 100644 index 0000000..9fd7346 --- /dev/null +++ b/pax/ar_io.c @@ -0,0 +1,1372 @@ +/* $OpenBSD: ar_io.c,v 1.17 1997/09/01 18:29:42 deraadt Exp $ */ +/* $NetBSD: ar_io.c,v 1.5 1996/03/26 23:54:13 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ar_io.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_io.c,v 1.17 1997/09/01 18:29:42 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "extern.h" + +/* + * Routines which deal directly with the archive I/O device/file. + */ + +#define DMOD 0666 /* default mode of created archives */ +#define EXT_MODE O_RDONLY /* open mode for list/extract */ +#define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */ +#define APP_MODE O_RDWR /* mode for append */ +#define STDO "" /* psuedo name for stdout */ +#define STDN "" /* psuedo name for stdin */ +static int arfd = -1; /* archive file descriptor */ +static int artyp = ISREG; /* archive type: file/FIFO/tape */ +static int arvol = 1; /* archive volume number */ +static int lstrval = -1; /* return value from last i/o */ +static int io_ok; /* i/o worked on volume after resync */ +static int did_io; /* did i/o ever occur on volume? */ +static int done; /* set via tty termination */ +static struct stat arsb; /* stat of archive device at open */ +static int invld_rec; /* tape has out of spec record size */ +static int wr_trail = 1; /* trailer was rewritten in append */ +static int can_unlnk = 0; /* do we unlink null archives? */ +char *arcname; /* printable name of archive */ +char *gzip_program; /* name of gzip program */ + +static int get_phys __P((void)); +extern sigset_t s_mask; +static void ar_start_gzip __P((int)); + +/* + * ar_open() + * Opens the next archive volume. Determines the type of the device and + * sets up block sizes as required by the archive device and the format. + * Note: we may be called with name == NULL on the first open only. + * Return: + * -1 on failure, 0 otherwise + */ + +#ifdef __STDC__ +int +ar_open(char *name) +#else +int +ar_open(name) + char *name; +#endif +{ + struct mtget mb; + + if (arfd != -1) + (void)close(arfd); + arfd = -1; + can_unlnk = did_io = io_ok = invld_rec = 0; + artyp = ISREG; + flcnt = 0; + + /* + * open based on overall operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + if (name == NULL) { + arfd = STDIN_FILENO; + arcname = STDN; + } else if ((arfd = open(name, EXT_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to read on %s", name); + if (zflag) + ar_start_gzip(arfd); + break; + case ARCHIVE: + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, AR_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to write on %s", name); + else + can_unlnk = 1; + if (zflag) + ar_start_gzip(arfd); + break; + case APPND: + if (zflag) + err(1, "can not gzip while appending"); + if (name == NULL) { + arfd = STDOUT_FILENO; + arcname = STDO; + } else if ((arfd = open(name, APP_MODE, DMOD)) < 0) + syswarn(0, errno, "Failed open to read/write on %s", + name); + break; + case COPY: + /* + * arfd not used in COPY mode + */ + arcname = ""; + lstrval = 1; + return(0); + } + if (arfd < 0) + return(-1); + + if (chdname != NULL) + if (chdir(chdname) != 0) + syswarn(1, errno, "Failed chdir to %s", chdname); + /* + * set up is based on device type + */ + if (fstat(arfd, &arsb) < 0) { + syswarn(0, errno, "Failed stat on %s", arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + if (S_ISDIR(arsb.st_mode)) { + paxwarn(0, "Cannot write an archive on top of a directory %s", + arcname); + (void)close(arfd); + arfd = -1; + can_unlnk = 0; + return(-1); + } + + if (S_ISCHR(arsb.st_mode)) + artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; + else if (S_ISBLK(arsb.st_mode)) + artyp = ISBLK; + else if ((lseek(arfd, (off_t)0L, SEEK_CUR) == -1) && (errno == ESPIPE)) + artyp = ISPIPE; + else + artyp = ISREG; + + /* + * make sure we beyond any doubt that we only can unlink regular files + * we created + */ + if (artyp != ISREG) + can_unlnk = 0; + /* + * if we are writing, we are done + */ + if (act == ARCHIVE) { + blksz = rdblksz = wrblksz; + lstrval = 1; + return(0); + } + + /* + * set default blksz on read. APPNDs writes rdblksz on the last volume + * On all new archive volumes, we shift to wrblksz (if the user + * specified one, otherwize we will continue to use rdblksz). We + * must to set blocksize based on what kind of device the archive is + * stored. + */ + switch(artyp) { + case ISTAPE: + /* + * Tape drives come in at least two flavors. Those that support + * variable sized records and those that have fixed sized + * records. They must be treated differently. For tape drives + * that support variable sized records, we must make large + * reads to make sure we get the entire record, otherwise we + * will just get the first part of the record (up to size we + * asked). Tapes with fixed sized records may or may not return + * multiple records in a single read. We really do not care + * what the physical record size is UNLESS we are going to + * append. (We will need the physical block size to rewrite + * the trailer). Only when we are appending do we go to the + * effort to figure out the true PHYSICAL record size. + */ + blksz = rdblksz = MAXBLK; + break; + case ISPIPE: + case ISBLK: + case ISCHR: + /* + * Blocksize is not a major issue with these devices (but must + * be kept a multiple of 512). If the user specified a write + * block size, we use that to read. Under append, we must + * always keep blksz == rdblksz. Otherwise we go ahead and use + * the device optimal blocksize as (and if) returned by stat + * and if it is within pax specs. + */ + if ((act == APPND) && wrblksz) { + blksz = rdblksz = wrblksz; + break; + } + + if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && + ((arsb.st_blksize % BLKMULT) == 0)) + rdblksz = arsb.st_blksize; + else + rdblksz = DEVBLK; + /* + * For performance go for large reads when we can without harm + */ + if ((act == APPND) || (artyp == ISCHR)) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + case ISREG: + /* + * if the user specified wrblksz works, use it. Under appends + * we must always keep blksz == rdblksz + */ + if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ + blksz = rdblksz = wrblksz; + break; + } + /* + * See if we can find the blocking factor from the file size + */ + for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) + if ((arsb.st_size % rdblksz) == 0) + break; + /* + * When we cannont find a match, we may have a flawed archive. + */ + if (rdblksz <= 0) + rdblksz = FILEBLK; + /* + * for performance go for large reads when we can + */ + if (act == APPND) + blksz = rdblksz; + else + blksz = MAXBLK; + break; + default: + /* + * should never happen, worse case, slow... + */ + blksz = rdblksz = BLKMULT; + break; + } + lstrval = 1; + return(0); +} + +/* + * ar_close() + * closes archive device, increments volume number, and prints i/o summary + */ +#ifdef __STDC__ +void +ar_close(void) +#else +void +ar_close() +#endif +{ + FILE *outf; + + if (arfd < 0) { + did_io = io_ok = flcnt = 0; + return; + } + + if (act == LIST) + outf = stdout; + else + outf = stderr; + + /* + * Close archive file. This may take a LONG while on tapes (we may be + * forced to wait for the rewind to complete) so tell the user what is + * going on (this avoids the user hitting control-c thinking pax is + * broken). + */ + if (vflag && (artyp == ISTAPE)) { + if (vfpart) + (void)putc('\n', outf); + (void)fprintf(outf, + "%s: Waiting for tape drive close to complete...", + argv0); + (void)fflush(outf); + } + + /* + * if nothing was written to the archive (and we created it), we remove + * it + */ + if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) && + (arsb.st_size == 0)) { + (void)unlink(arcname); + can_unlnk = 0; + } + + (void)close(arfd); + + if (vflag && (artyp == ISTAPE)) { + (void)fputs("done.\n", outf); + vfpart = 0; + (void)fflush(outf); + } + arfd = -1; + + if (!io_ok && !did_io) { + flcnt = 0; + return; + } + did_io = io_ok = 0; + + /* + * The volume number is only increased when the last device has data + * and we have already determined the archive format. + */ + if (frmt != NULL) + ++arvol; + + if (!vflag) { + flcnt = 0; + return; + } + + /* + * Print out a summary of I/O for this archive volume. + */ + if (vfpart) { + (void)putc('\n', outf); + vfpart = 0; + } + + /* + * If we have not determined the format yet, we just say how many bytes + * we have skipped over looking for a header to id. there is no way we + * could have written anything yet. + */ + if (frmt == NULL) { +# ifdef NET2_STAT + (void)fprintf(outf, "%s: unknown format, %lu bytes skipped.\n", +# else + (void)fprintf(outf, "%s: unknown format, %qu bytes skipped.\n", +# endif + argv0, rdcnt); + (void)fflush(outf); + flcnt = 0; + return; + } + + if (strcmp(NM_CPIO, argv0) == 0) + (void)fprintf(outf, "%qu blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120); + else if (strcmp(NM_TAR, argv0) != 0) + (void)fprintf(outf, +# ifdef NET2_STAT + "%s: %s vol %d, %lu files, %lu bytes read, %lu bytes written.\n", +# else + "%s: %s vol %d, %lu files, %qu bytes read, %qu bytes written.\n", +# endif + argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt); + (void)fflush(outf); + flcnt = 0; +} + +/* + * ar_drain() + * drain any archive format independent padding from an archive read + * from a socket or a pipe. This is to prevent the process on the + * other side of the pipe from getting a SIGPIPE (pax will stop + * reading an archive once a format dependent trailer is detected). + */ +#ifdef __STDC__ +void +ar_drain(void) +#else +void +ar_drain() +#endif +{ + register int res; + char drbuf[MAXBLK]; + + /* + * we only drain from a pipe/socket. Other devices can be closed + * without reading up to end of file. We sure hope that pipe is closed + * on the other side so we will get an EOF. + */ + if ((artyp != ISPIPE) || (lstrval <= 0)) + return; + + /* + * keep reading until pipe is drained + */ + while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0) + ; + lstrval = res; +} + +/* + * ar_set_wr() + * Set up device right before switching from read to write in an append. + * device dependent code (if required) to do this should be added here. + * For all archive devices we are already positioned at the place we want + * to start writing when this routine is called. + * Return: + * 0 if all ready to write, -1 otherwise + */ + +#ifdef __STDC__ +int +ar_set_wr(void) +#else +int +ar_set_wr() +#endif +{ + off_t cpos; + + /* + * we must make sure the trailer is rewritten on append, ar_next() + * will stop us if the archive containing the trailer was not written + */ + wr_trail = 0; + + /* + * Add any device dependent code as required here + */ + if (artyp != ISREG) + return(0); + /* + * Ok we have an archive in a regular file. If we were rewriting a + * file, we must get rid of all the stuff after the current offset + * (it was not written by pax). + */ + if (((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) || + (ftruncate(arfd, cpos) < 0)) { + syswarn(1, errno, "Unable to truncate archive file"); + return(-1); + } + return(0); +} + +/* + * ar_app_ok() + * check if the last volume in the archive allows appends. We cannot check + * this until we are ready to write since there is no spec that says all + * volumes in a single archive have to be of the same type... + * Return: + * 0 if we can append, -1 otherwise. + */ + +#ifdef __STDC__ +int +ar_app_ok(void) +#else +int +ar_app_ok() +#endif +{ + if (artyp == ISPIPE) { + paxwarn(1, "Cannot append to an archive obtained from a pipe."); + return(-1); + } + + if (!invld_rec) + return(0); + paxwarn(1,"Cannot append, device record size %d does not support %s spec", + rdblksz, argv0); + return(-1); +} + +/* + * ar_read() + * read up to a specified number of bytes from the archive into the + * supplied buffer. When dealing with tapes we may not always be able to + * read what we want. + * Return: + * Number of bytes in buffer. 0 for end of file, -1 for a read error. + */ + +#ifdef __STDC__ +int +ar_read(register char *buf, register int cnt) +#else +int +ar_read(buf, cnt) + register char *buf; + register int cnt; +#endif +{ + register int res = 0; + + /* + * if last i/o was in error, no more reads until reset or new volume + */ + if (lstrval <= 0) + return(lstrval); + + /* + * how we read must be based on device type + */ + switch (artyp) { + case ISTAPE: + if ((res = read(arfd, buf, cnt)) > 0) { + /* + * CAUTION: tape systems may not always return the same + * sized records so we leave blksz == MAXBLK. The + * physical record size that a tape drive supports is + * very hard to determine in a uniform and portable + * manner. + */ + io_ok = 1; + if (res != rdblksz) { + /* + * Record size changed. If this is happens on + * any record after the first, we probably have + * a tape drive which has a fixed record size + * we are getting multiple records in a single + * read). Watch out for record blocking that + * violates pax spec (must be a multiple of + * BLKMULT). + */ + rdblksz = res; + if (rdblksz % BLKMULT) + invld_rec = 1; + } + return(res); + } + break; + case ISREG: + case ISBLK: + case ISCHR: + case ISPIPE: + default: + /* + * Files are so easy to deal with. These other things cannot + * be trusted at all. So when we are dealing with character + * devices and pipes we just take what they have ready for us + * and return. Trying to do anything else with them runs the + * risk of failure. + */ + if ((res = read(arfd, buf, cnt)) > 0) { + io_ok = 1; + return(res); + } + break; + } + + /* + * We are in trouble at this point, something is broken... + */ + lstrval = res; + if (res < 0) + syswarn(1, errno, "Failed read on archive volume %d", arvol); + else + paxwarn(0, "End of archive volume %d reached", arvol); + return(res); +} + +/* + * ar_write() + * Write a specified number of bytes in supplied buffer to the archive + * device so it appears as a single "block". Deals with errors and tries + * to recover when faced with short writes. + * Return: + * Number of bytes written. 0 indicates end of volume reached and with no + * flaws (as best that can be detected). A -1 indicates an unrecoverable + * error in the archive occured. + */ + +#ifdef __STDC__ +int +ar_write(register char *buf, register int bsz) +#else +int +ar_write(buf, bsz) + register char *buf; + register int bsz; +#endif +{ + register int res; + off_t cpos; + + /* + * do not allow pax to create a "bad" archive. Once a write fails on + * an archive volume prevent further writes to it. + */ + if (lstrval <= 0) + return(lstrval); + + if ((res = write(arfd, buf, bsz)) == bsz) { + wr_trail = 1; + io_ok = 1; + return(bsz); + } + /* + * write broke, see what we can do with it. We try to send any partial + * writes that may violate pax spec to the next archive volume. + */ + if (res < 0) + lstrval = res; + else + lstrval = 0; + + switch (artyp) { + case ISREG: + if ((res > 0) && (res % BLKMULT)) { + /* + * try to fix up partial writes which are not BLKMULT + * in size by forcing the runt record to next archive + * volume + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + cpos -= (off_t)res; + if (ftruncate(arfd, cpos) < 0) + break; + res = lstrval = 0; + break; + } + if (res >= 0) + break; + /* + * if file is out of space, handle it like a return of 0 + */ + if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) + res = lstrval = 0; + break; + case ISTAPE: + case ISCHR: + case ISBLK: + if (res >= 0) + break; + if (errno == EACCES) { + paxwarn(0, "Write failed, archive is write protected."); + res = lstrval = 0; + return(0); + } + /* + * see if we reached the end of media, if so force a change to + * the next volume + */ + if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) + res = lstrval = 0; + break; + case ISPIPE: + default: + /* + * we cannot fix errors to these devices + */ + break; + } + + /* + * Better tell the user the bad news... + * if this is a block aligned archive format, we may have a bad archive + * if the format wants the header to start at a BLKMULT boundry. While + * we can deal with the mis-aligned data, it violates spec and other + * archive readers will likely fail. if the format is not block + * aligned, the user may be lucky (and the archive is ok). + */ + if (res >= 0) { + if (res > 0) + wr_trail = 1; + io_ok = 1; + } + + /* + * If we were trying to rewrite the trailer and it didn't work, we + * must quit right away. + */ + if (!wr_trail && (res <= 0)) { + paxwarn(1,"Unable to append, trailer re-write failed. Quitting."); + return(res); + } + + if (res == 0) + paxwarn(0, "End of archive volume %d reached", arvol); + else if (res < 0) + syswarn(1, errno, "Failed write to archive volume: %d", arvol); + else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) + paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); + else + paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED"); + return(res); +} + +/* + * ar_rdsync() + * Try to move past a bad spot on a flawed archive as needed to continue + * I/O. Clears error flags to allow I/O to continue. + * Return: + * 0 when ok to try i/o again, -1 otherwise. + */ + +#ifdef __STDC__ +int +ar_rdsync(void) +#else +int +ar_rdsync() +#endif +{ + long fsbz; + off_t cpos; + off_t mpos; + struct mtop mb; + + /* + * Fail resync attempts at user request (done) or this is going to be + * an update/append to a existing archive. if last i/o hit media end, + * we need to go to the next volume not try a resync + */ + if ((done > 0) || (lstrval == 0)) + return(-1); + + if ((act == APPND) || (act == ARCHIVE)) { + paxwarn(1, "Cannot allow updates to an archive with flaws."); + return(-1); + } + if (io_ok) + did_io = 1; + + switch(artyp) { + case ISTAPE: + /* + * if the last i/o was a successful data transfer, we assume + * the fault is just a bad record on the tape that we are now + * past. If we did not get any data since the last resync try + * to move the tape foward one PHYSICAL record past any + * damaged tape section. Some tape drives are stubborn and need + * to be pushed. + */ + if (io_ok) { + io_ok = 0; + lstrval = 1; + break; + } + mb.mt_op = MTFSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) + break; + lstrval = 1; + break; + case ISREG: + case ISCHR: + case ISBLK: + /* + * try to step over the bad part of the device. + */ + io_ok = 0; + if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG)) + fsbz = BLKMULT; + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) + break; + mpos = fsbz - (cpos % (off_t)fsbz); + if (lseek(arfd, mpos, SEEK_CUR) < 0) + break; + lstrval = 1; + break; + case ISPIPE: + default: + /* + * cannot recover on these archive device types + */ + io_ok = 0; + break; + } + if (lstrval <= 0) { + paxwarn(1, "Unable to recover from an archive read failure."); + return(-1); + } + paxwarn(0, "Attempting to recover from an archive read failure."); + return(0); +} + +/* + * ar_fow() + * Move the I/O position within the archive foward the specified number of + * bytes as supported by the device. If we cannot move the requested + * number of bytes, return the actual number of bytes moved in skipped. + * Return: + * 0 if moved the requested distance, -1 on complete failure, 1 on + * partial move (the amount moved is in skipped) + */ + +#ifdef __STDC__ +int +ar_fow(off_t sksz, off_t *skipped) +#else +int +ar_fow(sksz, skipped) + off_t sksz; + off_t *skipped; +#endif +{ + off_t cpos; + off_t mpos; + + *skipped = 0; + if (sksz <= 0) + return(0); + + /* + * we cannot move foward at EOF or error + */ + if (lstrval <= 0) + return(lstrval); + + /* + * Safer to read forward on devices where it is hard to find the end of + * the media without reading to it. With tapes we cannot be sure of the + * number of physical blocks to skip (we do not know physical block + * size at this point), so we must only read foward on tapes! + */ + if (artyp != ISREG) + return(0); + + /* + * figure out where we are in the archive + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) >= 0) { + /* + * we can be asked to move farther than there are bytes in this + * volume, if so, just go to file end and let normal buf_fill() + * deal with the end of file (it will go to next volume by + * itself) + */ + if ((mpos = cpos + sksz) > arsb.st_size) { + *skipped = arsb.st_size - cpos; + mpos = arsb.st_size; + } else + *skipped = sksz; + if (lseek(arfd, mpos, SEEK_SET) >= 0) + return(0); + } + syswarn(1, errno, "Foward positioning operation on archive failed"); + lstrval = -1; + return(-1); +} + +/* + * ar_rev() + * move the i/o position within the archive backwards the specified byte + * count as supported by the device. With tapes drives we RESET rdblksz to + * the PHYSICAL blocksize. + * NOTE: We should only be called to move backwards so we can rewrite the + * last records (the trailer) of an archive (APPEND). + * Return: + * 0 if moved the requested distance, -1 on complete failure + */ + +#ifdef __STDC__ +int +ar_rev(off_t sksz) +#else +int +ar_rev(sksz) + off_t sksz; +#endif +{ + off_t cpos; + struct mtop mb; + register int phyblk; + + /* + * make sure we do not have try to reverse on a flawed archive + */ + if (lstrval < 0) + return(lstrval); + + switch(artyp) { + case ISPIPE: + if (sksz <= 0) + break; + /* + * cannot go backwards on these critters + */ + paxwarn(1, "Reverse positioning on pipes is not supported."); + lstrval = -1; + return(-1); + case ISREG: + case ISBLK: + case ISCHR: + default: + if (sksz <= 0) + break; + + /* + * For things other than files, backwards movement has a very + * high probability of failure as we really do not know the + * true attributes of the device we are talking to (the device + * may not even have the ability to lseek() in any direction). + * First we figure out where we are in the archive. + */ + if ((cpos = lseek(arfd, (off_t)0L, SEEK_CUR)) < 0) { + syswarn(1, errno, + "Unable to obtain current archive byte offset"); + lstrval = -1; + return(-1); + } + + /* + * we may try to go backwards past the start when the archive + * is only a single record. If this hapens and we are on a + * multi volume archive, we need to go to the end of the + * previous volume and continue our movement backwards from + * there. + */ + if ((cpos -= sksz) < (off_t)0L) { + if (arvol > 1) { + /* + * this should never happen + */ + paxwarn(1,"Reverse position on previous volume."); + lstrval = -1; + return(-1); + } + cpos = (off_t)0L; + } + if (lseek(arfd, cpos, SEEK_SET) < 0) { + syswarn(1, errno, "Unable to seek archive backwards"); + lstrval = -1; + return(-1); + } + break; + case ISTAPE: + /* + * Calculate and move the proper number of PHYSICAL tape + * blocks. If the sksz is not an even multiple of the physical + * tape size, we cannot do the move (this should never happen). + * (We also cannot handler trailers spread over two vols). + * get_phys() also makes sure we are in front of the filemark. + */ + if ((phyblk = get_phys()) <= 0) { + lstrval = -1; + return(-1); + } + + /* + * make sure future tape reads only go by physical tape block + * size (set rdblksz to the real size). + */ + rdblksz = phyblk; + + /* + * if no movement is required, just return (we must be after + * get_phys() so the physical blocksize is properly set) + */ + if (sksz <= 0) + break; + + /* + * ok we have to move. Make sure the tape drive can do it. + */ + if (sksz % phyblk) { + paxwarn(1, + "Tape drive unable to backspace requested amount"); + lstrval = -1; + return(-1); + } + + /* + * move backwards the requested number of bytes + */ + mb.mt_op = MTBSR; + mb.mt_count = sksz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno, "Unable to backspace tape %d blocks.", + mb.mt_count); + lstrval = -1; + return(-1); + } + break; + } + lstrval = 1; + return(0); +} + +/* + * get_phys() + * Determine the physical block size on a tape drive. We need the physical + * block size so we know how many bytes we skip over when we move with + * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when + * return. + * This is one really SLOW routine... + * Return: + * physical block size if ok (ok > 0), -1 otherwise + */ + +#ifdef __STDC__ +static int +get_phys(void) +#else +static int +get_phys() +#endif +{ + register int padsz = 0; + register int res; + register int phyblk; + struct mtop mb; + char scbuf[MAXBLK]; + + /* + * move to the file mark, and then back up one record and read it. + * this should tell us the physical record size the tape is using. + */ + if (lstrval == 1) { + /* + * we know we are at file mark when we get back a 0 from + * read() + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + padsz += res; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + } + + /* + * move backwards over the file mark so we are at the end of the + * last record. + */ + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * move backwards so we are in front of the last record and read it to + * get physical tape blocksize. + */ + mb.mt_op = MTBSR; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over last tape block."); + return(-1); + } + if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { + syswarn(1, errno, "Cannot determine archive tape blocksize."); + return(-1); + } + + /* + * read foward to the file mark, then back up in front of the filemark + * (this is a bit paranoid, but should be safe to do). + */ + while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) + ; + if (res < 0) { + syswarn(1, errno, "Unable to locate tape filemark."); + return(-1); + } + mb.mt_op = MTBSF; + mb.mt_count = 1; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1, errno, "Unable to backspace over tape filemark."); + return(-1); + } + + /* + * set lstrval so we know that the filemark has not been seen + */ + lstrval = 1; + + /* + * return if there was no padding + */ + if (padsz == 0) + return(phyblk); + + /* + * make sure we can move backwards over the padding. (this should + * never fail). + */ + if (padsz % phyblk) { + paxwarn(1, "Tape drive unable to backspace requested amount"); + return(-1); + } + + /* + * move backwards over the padding so the head is where it was when + * we were first called (if required). + */ + mb.mt_op = MTBSR; + mb.mt_count = padsz/phyblk; + if (ioctl(arfd, MTIOCTOP, &mb) < 0) { + syswarn(1,errno,"Unable to backspace tape over %d pad blocks", + mb.mt_count); + return(-1); + } + return(phyblk); +} + +/* + * ar_next() + * prompts the user for the next volume in this archive. For some devices + * we may allow the media to be changed. Otherwise a new archive is + * prompted for. By pax spec, if there is no controlling tty or an eof is + * read on tty input, we must quit pax. + * Return: + * 0 when ready to continue, -1 when all done + */ + +#ifdef __STDC__ +int +ar_next(void) +#else +int +ar_next() +#endif +{ + char buf[PAXPATHLEN+2]; + static int freeit = 0; + sigset_t o_mask; + + /* + * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so + * things like writing EOF etc will be done) (Watch out ar_close() can + * also be called via a signal handler, so we must prevent a race. + */ + if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) + syswarn(0, errno, "Unable to set signal mask"); + ar_close(); + if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) + syswarn(0, errno, "Unable to restore signal mask"); + + if (done || !wr_trail || strcmp(NM_TAR, argv0) == 0) + return(-1); + + tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); + + /* + * if i/o is on stdin or stdout, we cannot reopen it (we do not know + * the name), the user will be forced to type it in. + */ + if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG) + && (artyp != ISPIPE)) { + if (artyp == ISTAPE) { + tty_prnt("%s ready for archive tape volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT TAPE on the tape drive"); + } else { + tty_prnt("%s ready for archive volume: %d\n", + arcname, arvol); + tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); + } + + if ((act == ARCHIVE) || (act == APPND)) + tty_prnt(" and make sure it is WRITE ENABLED.\n"); + else + tty_prnt("\n"); + + for(;;) { + tty_prnt("Type \"y\" to continue, \".\" to quit %s,", + argv0); + tty_prnt(" or \"s\" to switch to new device.\nIf you"); + tty_prnt(" cannot change storage media, type \"s\"\n"); + tty_prnt("Is the device ready and online? > "); + + if ((tty_read(buf,sizeof(buf))<0) || !strcmp(buf,".")){ + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + + if ((buf[0] == '\0') || (buf[1] != '\0')) { + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + + switch (buf[0]) { + case 'y': + case 'Y': + /* + * we are to continue with the same device + */ + if (ar_open(arcname) >= 0) + return(0); + tty_prnt("Cannot re-open %s, try again\n", + arcname); + continue; + case 's': + case 'S': + /* + * user wants to open a different device + */ + tty_prnt("Switching to a different archive\n"); + break; + default: + tty_prnt("%s unknown command, try again\n",buf); + continue; + } + break; + } + } else + tty_prnt("Ready for archive volume: %d\n", arvol); + + /* + * have to go to a different archive + */ + for (;;) { + tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); + tty_prnt("Archive name > "); + + if ((tty_read(buf, sizeof(buf)) < 0) || !strcmp(buf, ".")) { + done = 1; + lstrval = -1; + tty_prnt("Quitting %s!\n", argv0); + vfpart = 0; + return(-1); + } + if (buf[0] == '\0') { + tty_prnt("Empty file name, try again\n"); + continue; + } + if (!strcmp(buf, "..")) { + tty_prnt("Illegal file name: .. try again\n"); + continue; + } + if (strlen(buf) > PAXPATHLEN) { + tty_prnt("File name too long, try again\n"); + continue; + } + + /* + * try to open new archive + */ + if (ar_open(buf) >= 0) { + if (freeit) { + (void)free(arcname); + freeit = 0; + } + if ((arcname = strdup(buf)) == NULL) { + done = 1; + lstrval = -1; + paxwarn(0, "Cannot save archive name."); + return(-1); + } + freeit = 1; + break; + } + tty_prnt("Cannot open %s, try again\n", buf); + continue; + } + return(0); +} + +/* + * ar_start_gzip() + * starts the gzip compression/decompression process as a child, using magic + * to keep the fd the same in the calling function (parent). + */ +void +#ifdef __STDC__ +ar_start_gzip(int fd) +#else +ar_start_gzip(fd) + int fd; +#endif +{ + pid_t pid; + int fds[2]; + char *gzip_flags=NULL; + + if (pipe(fds) < 0) + err(1, "could not pipe"); + pid = fork(); + if (pid < 0) + err(1, "could not fork"); + + /* parent */ + if (pid) { + switch (act) { + case ARCHIVE: + dup2(fds[1], fd); + break; + case LIST: + case EXTRACT: + dup2(fds[0], fd); + break; + default: + errx(1, "ar_start_gzip: impossible"); + } + close(fds[0]); + close(fds[1]); + } else { + switch (act) { + case ARCHIVE: + dup2(fds[0], STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + gzip_flags = "-c"; + break; + case LIST: + case EXTRACT: + dup2(fds[1], STDOUT_FILENO); + dup2(fd, STDIN_FILENO); + gzip_flags = "-dc"; + break; + default: + errx(1, "ar_start_gzip: impossible"); + } + close(fds[0]); + close(fds[1]); + if (execlp(gzip_program, gzip_program, gzip_flags, NULL) < 0) + err(1, "could not exec"); + /* NOTREACHED */ + } +} diff --git a/pax/ar_subs.c b/pax/ar_subs.c new file mode 100644 index 0000000..95bd7d5 --- /dev/null +++ b/pax/ar_subs.c @@ -0,0 +1,1288 @@ +/* $OpenBSD: ar_subs.c,v 1.13 1997/09/16 21:20:35 niklas Exp $ */ +/* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ar_subs.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ar_subs.c,v 1.13 1997/09/16 21:20:35 niklas Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +static void wr_archive __P((register ARCHD *, int is_app)); +static int get_arc __P((void)); +static int next_head __P((register ARCHD *)); +extern sigset_t s_mask; + +char *chdname; + +/* + * Routines which control the overall operation modes of pax as specified by + * the user: list, append, read ... + */ + +static char hdbuf[BLKMULT]; /* space for archive header on read */ +u_long flcnt; /* number of files processed */ + +/* + * list() + * list the contents of an archive which match user supplied pattern(s) + * (no pattern matches all). + */ + +#ifdef __STDC__ +void +list(void) +#else +void +list() +#endif +{ + register ARCHD *arcn; + register int res; + ARCHD archd; + time_t now; + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine. We + * also save current time for ls_list() so we do not make a system + * call for each file we need to print. If verbose (vflag) start up + * the name and group caches. + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0)) + return; + + if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0))) + return; + + now = time(NULL); + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + /* + * check for pattern, and user specified options match. + * When all patterns are matched we are done. + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res == 0) && (sel_chk(arcn) == 0)) { + /* + * pattern resulted in a selected file + */ + if (pat_sel(arcn) < 0) + break; + + /* + * modify the name as requested by the user if name + * survives modification, do a listing of the file + */ + if ((res = mod_name(arcn)) < 0) + break; + if (res == 0) + ls_list(arcn, now, stdout); + } + + /* + * skip to next archive format header using values calculated + * by the format header read routine + */ + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + } + + /* + * all done, let format have a chance to cleanup, and make sure that + * the patterns supplied by the user were all matched + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + pat_chk(); +} + +/* + * extract() + * extract the member(s) of an archive as specified by user supplied + * pattern(s) (no patterns extracts all members) + */ + +#ifdef __STDC__ +void +extract(void) +#else +void +extract() +#endif +{ + register ARCHD *arcn; + register int res; + off_t cnt; + ARCHD archd; + struct stat sb; + int fd; + time_t now; + + arcn = &archd; + /* + * figure out archive type; pass any format specific options to the + * archive option processing routine; call the format init routine; + * start up the directory modification time and access mode database + */ + if ((get_arc() < 0) || ((*frmt->options)() < 0) || + ((*frmt->st_rd)() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + now = time(NULL); + + /* + * step through each entry on the archive until the format read routine + * says it is done + */ + while (next_head(arcn) == 0) { + + /* + * check for pattern, and user specified options match. When + * all the patterns are matched we are done + */ + if ((res = pat_match(arcn)) < 0) + break; + + if ((res > 0) || (sel_chk(arcn) != 0)) { + /* + * file is not selected. skip past any file data and + * padding and go back for the next archive member + */ + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * with -u or -D only extract when the archive member is newer + * than the file with the same name in the file system (nos + * test of being the same type is required). + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this operation can be confusing to the + * user who might expect the test to be done on an existing + * file AFTER the name mod. In honesty the pax spec is probably + * flawed in this respect. + */ + if ((uflag || Dflag) && ((lstat(arcn->name, &sb) == 0))) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + /* + * this archive member is now been selected. modify the name. + */ + if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if (res > 0) { + /* + * a bad name mod, skip and purge name from link table + */ + purg_lnk(arcn); + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + + /* + * Non standard -Y and -Z flag. When the exisiting file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } else if (arcn->sb.st_mtime <= sb.st_mtime) { + (void)rd_skip(arcn->skip + arcn->pad); + continue; + } + } + + if (vflag) { + if (vflag > 1) + ls_list(arcn, now, stderr); + else { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + } + + /* + * if required, chdir around. + */ + if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) + if (chdir(arcn->pat->chdname) != 0) + syswarn(1, errno, "Cannot chdir to %s", + arcn->pat->chdname); + + /* + * all ok, extract this member based on type + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * process archive members that are not regular files. + * throw out padding and any data that might follow the + * header (as determined by the format). + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + + (void)rd_skip(arcn->skip + arcn->pad); + if (res < 0) + purg_lnk(arcn); + + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + /* + * we have a file with data here. If we can not create it, skip + * over the data and purge the name from hard link table + */ + if ((fd = file_creat(arcn)) < 0) { + (void)rd_skip(arcn->skip + arcn->pad); + purg_lnk(arcn); + continue; + } + /* + * extract the file from the archive and skip over padding and + * any unprocessed data + */ + res = (*frmt->rd_data)(arcn, fd, &cnt); + file_close(arcn, fd); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + if (!res) + (void)rd_skip(cnt + arcn->pad); + + /* + * if required, chdir around. + */ + if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) + if (fchdir(cwdfd) != 0) + syswarn(1, errno, + "Can't fchdir to starting directory"); + } + + /* + * all done, restore directory modes and times as required; make sure + * all patterns supplied by the user were matched; block off signals + * to avoid chance for multiple entry into the cleanup code. + */ + (void)(*frmt->end_rd)(); + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + proc_dir(); + pat_chk(); +} + +/* + * wr_archive() + * Write an archive. used in both creating a new archive and appends on + * previously written archive. + */ + +#ifdef __STDC__ +static void +wr_archive(register ARCHD *arcn, int is_app) +#else +static void +wr_archive(arcn, is_app) + register ARCHD *arcn; + int is_app; +#endif +{ + register int res; + register int hlk; + register int wr_one; + off_t cnt; + int (*wrf)(); + int fd = -1; + time_t now; + + /* + * if this format supports hard link storage, start up the database + * that detects them. + */ + if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0)) + return; + + /* + * start up the file traversal code and format specific write + */ + if ((ftree_start() < 0) || ((*frmt->st_wr)() < 0)) + return; + wrf = frmt->wr; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * if this not append, and there are no files, we do no write a trailer + */ + wr_one = is_app; + + now = time(NULL); + + /* + * while there are files to archive, process them one at at time + */ + while (next_file(arcn) == 0) { + /* + * check if this file meets user specified options match. + */ + if (sel_chk(arcn) != 0) + continue; + fd = -1; + if (uflag) { + /* + * only archive if this file is newer than a file with + * the same name that is already stored on the archive + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) + continue; + } + + /* + * this file is considered selected now. see if this is a hard + * link to a file already stored + */ + ftree_sel(arcn); + if (hlk && (chk_lnk(arcn) < 0)) + break; + + if ((arcn->type == PAX_REG) || (arcn->type == PAX_HRG) || + (arcn->type == PAX_CTG)) { + /* + * we will have to read this file. by opening it now we + * can avoid writing a header to the archive for a file + * we were later unable to read (we also purge it from + * the link table). + */ + if ((fd = open(arcn->org_name, O_RDONLY, 0)) < 0) { + syswarn(1,errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + } + + /* + * Now modify the name as requested by the user + */ + if ((res = mod_name(arcn)) < 0) { + /* + * name modification says to skip this file, close the + * file and purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + break; + } + + if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) { + /* + * unable to obtain the crc we need, close the file, + * purge link table entry + */ + rdfile_close(arcn, &fd); + purg_lnk(arcn); + continue; + } + + if (vflag) { + if (vflag > 1) + ls_list(arcn, now, stderr); + else { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + } + ++flcnt; + + /* + * looks safe to store the file, have the format specific + * routine write routine store the file header on the archive + */ + if ((res = (*wrf)(arcn)) < 0) { + rdfile_close(arcn, &fd); + break; + } + wr_one = 1; + if (res > 0) { + /* + * format write says no file data needs to be stored + * so we are done messing with this file + */ + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + rdfile_close(arcn, &fd); + continue; + } + + /* + * Add file data to the archive, quit on write error. if we + * cannot write the entire file contents to the archive we + * must pad the archive to replace the missing file data + * (otherwise during an extract the file header for the file + * which FOLLOWS this one will not be where we expect it to + * be). + */ + res = (*frmt->wr_data)(arcn, fd, &cnt); + rdfile_close(arcn, &fd); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + if (res < 0) + break; + + /* + * pad as required, cnt is number of bytes not written + */ + if (((cnt > 0) && (wr_skip(cnt) < 0)) || + ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) + break; + } + + /* + * tell format to write trailer; pad to block boundry; reset directory + * mode/access times, and check if all patterns supplied by the user + * were matched. block off signals to avoid chance for multiple entry + * into the cleanup code + */ + if (wr_one) { + (*frmt->end_wr)(); + wr_fin(); + } + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + if (tflag) + proc_dir(); + ftree_chk(); +} + +/* + * append() + * Add file to previously written archive. Archive format specified by the + * user must agree with archive. The archive is read first to collect + * modification times (if -u) and locate the archive trailer. The archive + * is positioned in front of the record with the trailer and wr_archive() + * is called to add the new members. + * PAX IMPLEMENTATION DETAIL NOTE: + * -u is implemented by adding the new members to the end of the archive. + * Care is taken so that these do not end up as links to the older + * version of the same file already stored in the archive. It is expected + * when extraction occurs these newer versions will over-write the older + * ones stored "earlier" in the archive (this may be a bad assumption as + * it depends on the implementation of the program doing the extraction). + * It is really difficult to splice in members without either re-writing + * the entire archive (from the point were the old version was), or having + * assistance of the format specification in terms of a special update + * header that invalidates a previous archive record. The posix spec left + * the method used to implement -u unspecified. This pax is able to + * over write existing files that it creates. + */ + +#ifdef __STDC__ +void +append(void) +#else +void +append() +#endif +{ + register ARCHD *arcn; + register int res; + ARCHD archd; + FSUB *orgfrmt; + int udev; + off_t tlen; + + arcn = &archd; + orgfrmt = frmt; + + /* + * Do not allow an append operation if the actual archive is of a + * different format than the user specified foramt. + */ + if (get_arc() < 0) + return; + if ((orgfrmt != NULL) && (orgfrmt != frmt)) { + paxwarn(1, "Cannot mix current archive format %s with %s", + frmt->name, orgfrmt->name); + return; + } + + /* + * pass the format any options and start up format + */ + if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) + return; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files we see. + */ + if (uflag && (ftime_start() < 0)) + return; + + /* + * some archive formats encode hard links by recording the device and + * file serial number (inode) but copy the file anyway (multiple times) + * to the archive. When we append, we run the risk that newly added + * files may have the same device and inode numbers as those recorded + * on the archive but during a previous run. If this happens, when the + * archive is extracted we get INCORRECT hard links. We avoid this by + * remapping the device numbers so that newly added files will never + * use the same device number as one found on the archive. remapping + * allows new members to safely have links among themselves. remapping + * also avoids problems with file inode (serial number) truncations + * when the inode number is larger than storage space in the archive + * header. See the remap routines for more details. + */ + if ((udev = frmt->udev) && (dev_start() < 0)) + return; + + /* + * reading the archive may take a long time. If verbose tell the user + */ + if (vflag) { + (void)fprintf(stderr, + "%s: Reading archive to position at the end...", argv0); + vfpart = 1; + } + + /* + * step through the archive until the format says it is done + */ + while (next_head(arcn) == 0) { + /* + * check if this file meets user specified options. + */ + if (sel_chk(arcn) != 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + + if (uflag) { + /* + * see if this is the newest version of this file has + * already been seen, if so skip. + */ + if ((res = chk_ftime(arcn)) < 0) + break; + if (res > 0) { + if (rd_skip(arcn->skip + arcn->pad) == 1) + break; + continue; + } + } + + /* + * Store this device number. Device numbers seen during the + * read phase of append will cause newly appended files with a + * device number seen in the old part of the archive to be + * remapped to an unused device number. + */ + if ((udev && (add_dev(arcn) < 0)) || + (rd_skip(arcn->skip + arcn->pad) == 1)) + break; + } + + /* + * done, finish up read and get the number of bytes to back up so we + * can add new members. The format might have used the hard link table, + * purge it. + */ + tlen = (*frmt->end_rd)(); + lnk_end(); + + /* + * try to postion for write, if this fails quit. if any error occurs, + * we will refuse to write + */ + if (appnd_start(tlen) < 0) + return; + + /* + * tell the user we are done reading. + */ + if (vflag && vfpart) { + (void)fputs("done.\n", stderr); + vfpart = 0; + } + + /* + * go to the writing phase to add the new members + */ + wr_archive(arcn, 1); +} + +/* + * archive() + * write a new archive + */ + +#ifdef __STDC__ +void +archive(void) +#else +void +archive() +#endif +{ + ARCHD archd; + + /* + * if we only are adding members that are newer, we need to save the + * mod times for all files; set up for writing; pass the format any + * options write the archive + */ + if ((uflag && (ftime_start() < 0)) || (wr_start() < 0)) + return; + if ((*frmt->options)() < 0) + return; + + wr_archive(&archd, 0); +} + +/* + * copy() + * copy files from one part of the file system to another. this does not + * use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an + * archive was written and then extracted in the destination directory + * (except the files are forced to be under the destination directory). + */ + +#ifdef __STDC__ +void +copy(void) +#else +void +copy() +#endif +{ + register ARCHD *arcn; + register int res; + register int fddest; + register char *dest_pt; + register int dlen; + register int drem; + int fdsrc = -1; + struct stat sb; + ARCHD archd; + char dirbuf[PAXPATHLEN+1]; + + arcn = &archd; + /* + * set up the destination dir path and make sure it is a directory. We + * make sure we have a trailing / on the destination + */ + dlen = l_strncpy(dirbuf, dirptr, sizeof(dirbuf) - 1); + dest_pt = dirbuf + dlen; + if (*(dest_pt-1) != '/') { + *dest_pt++ = '/'; + ++dlen; + } + *dest_pt = '\0'; + drem = PAXPATHLEN - dlen; + + if (stat(dirptr, &sb) < 0) { + syswarn(1, errno, "Cannot access destination directory %s", + dirptr); + return; + } + if (!S_ISDIR(sb.st_mode)) { + paxwarn(1, "Destination is not a directory %s", dirptr); + return; + } + + /* + * start up the hard link table; file traversal routines and the + * modification time and access mode database + */ + if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0)) + return; + + /* + * When we are doing interactive rename, we store the mapping of names + * so we can fix up hard links files later in the archive. + */ + if (iflag && (name_start() < 0)) + return; + + /* + * set up to cp file trees + */ + cp_start(); + + /* + * while there are files to archive, process them + */ + while (next_file(arcn) == 0) { + fdsrc = -1; + + /* + * check if this file meets user specified options + */ + if (sel_chk(arcn) != 0) + continue; + + /* + * if there is already a file in the destination directory with + * the same name and it is newer, skip the one stored on the + * archive. + * NOTE: this test is done BEFORE name modifications as + * specified by pax. this can be confusing to the user who + * might expect the test to be done on an existing file AFTER + * the name mod. In honesty the pax spec is probably flawed in + * this respect + */ + if (uflag || Dflag) { + /* + * create the destination name + */ + if (*(arcn->name) == '/') + res = 1; + else + res = 0; + if ((arcn->nlen - res) > drem) { + paxwarn(1, "Destination pathname too long %s", + arcn->name); + continue; + } + (void)strncpy(dest_pt, arcn->name + res, drem); + dirbuf[PAXPATHLEN] = '\0'; + + /* + * if existing file is same age or newer skip + */ + res = lstat(dirbuf, &sb); + *dest_pt = '\0'; + + if (res == 0) { + if (uflag && Dflag) { + if ((arcn->sb.st_mtime<=sb.st_mtime) && + (arcn->sb.st_ctime<=sb.st_ctime)) + continue; + } else if (Dflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + } + + /* + * this file is considered selected. See if this is a hard link + * to a previous file; modify the name as requested by the + * user; set the final destination. + */ + ftree_sel(arcn); + if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0)) + break; + if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) { + /* + * skip file, purge from link table + */ + purg_lnk(arcn); + continue; + } + + /* + * Non standard -Y and -Z flag. When the exisiting file is + * same age or newer skip + */ + if ((Yflag || Zflag) && ((lstat(arcn->name, &sb) == 0))) { + if (Yflag && Zflag) { + if ((arcn->sb.st_mtime <= sb.st_mtime) && + (arcn->sb.st_ctime <= sb.st_ctime)) + continue; + } else if (Yflag) { + if (arcn->sb.st_ctime <= sb.st_ctime) + continue; + } else if (arcn->sb.st_mtime <= sb.st_mtime) + continue; + } + + if (vflag) { + (void)fputs(arcn->name, stderr); + vfpart = 1; + } + ++flcnt; + + /* + * try to create a hard link to the src file if requested + * but make sure we are not trying to overwrite ourselves. + */ + if (lflag) + res = cross_lnk(arcn); + else + res = chk_same(arcn); + if (res <= 0) { + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + + /* + * have to create a new file + */ + if ((arcn->type != PAX_REG) && (arcn->type != PAX_CTG)) { + /* + * create a link or special file + */ + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + res = lnk_creat(arcn); + else + res = node_creat(arcn); + if (res < 0) + purg_lnk(arcn); + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + continue; + } + + /* + * have to copy a regular file to the destination directory. + * first open source file and then create the destination file + */ + if ((fdsrc = open(arcn->org_name, O_RDONLY, 0)) < 0) { + syswarn(1, errno, "Unable to open %s to read", + arcn->org_name); + purg_lnk(arcn); + continue; + } + if ((fddest = file_creat(arcn)) < 0) { + rdfile_close(arcn, &fdsrc); + purg_lnk(arcn); + continue; + } + + /* + * copy source file data to the destination file + */ + cp_file(arcn, fdsrc, fddest); + file_close(arcn, fddest); + rdfile_close(arcn, &fdsrc); + + if (vflag && vfpart) { + (void)putc('\n', stderr); + vfpart = 0; + } + } + + /* + * restore directory modes and times as required; make sure all + * patterns were selected block off signals to avoid chance for + * multiple entry into the cleanup code. + */ + (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); + ar_close(); + proc_dir(); + ftree_chk(); +} + +/* + * next_head() + * try to find a valid header in the archive. Uses format specific + * routines to extract the header and id the trailer. Trailers may be + * located within a valid header or in an invalid header (the location + * is format specific. The inhead field from the option table tells us + * where to look for the trailer). + * We keep reading (and resyncing) until we get enough contiguous data + * to check for a header. If we cannot find one, we shift by a byte + * add a new byte from the archive to the end of the buffer and try again. + * If we get a read error, we throw out what we have (as we must have + * contiguous data) and start over again. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if we got a header, -1 if we are unable to ever find another one + * (we reached the end of input, or we reached the limit on retries. see + * the specs for rd_wrbuf() for more details) + */ + +#ifdef __STDC__ +static int +next_head(register ARCHD *arcn) +#else +static int +next_head(arcn) + register ARCHD *arcn; +#endif +{ + register int ret; + register char *hdend; + register int res; + register int shftsz; + register int hsz; + register int in_resync = 0; /* set when we are in resync mode */ + int cnt = 0; /* counter for trailer function */ + int first = 1; /* on 1st read, EOF isn't premature. */ + + /* + * set up initial conditions, we want a whole frmt->hsz block as we + * have no data yet. + */ + res = hsz = frmt->hsz; + hdend = hdbuf; + shftsz = hsz - 1; + for(;;) { + /* + * keep looping until we get a contiguous FULL buffer + * (frmt->hsz is the proper size) + */ + for (;;) { + if ((ret = rd_wrbuf(hdend, res)) == res) + break; + + /* + * If we read 0 bytes (EOF) from an archive when we + * expect to find a header, we have stepped upon + * an archive without the customary block of zeroes + * end marker. It's just stupid to error out on + * them, so exit gracefully. + */ + if (first && ret == 0) + return(-1); + first = 0; + + /* + * some kind of archive read problem, try to resync the + * storage device, better give the user the bad news. + */ + if ((ret == 0) || (rd_sync() < 0)) { + paxwarn(1,"Premature end of file on archive read"); + return(-1); + } + if (!in_resync) { + if (act == APPND) { + paxwarn(1, + "Archive I/O error, cannot continue"); + return(-1); + } + paxwarn(1,"Archive I/O error. Trying to recover."); + ++in_resync; + } + + /* + * oh well, throw it all out and start over + */ + res = hsz; + hdend = hdbuf; + } + + /* + * ok we have a contiguous buffer of the right size. Call the + * format read routine. If this was not a valid header and this + * format stores trailers outside of the header, call the + * format specific trailer routine to check for a trailer. We + * have to watch out that we do not mis-identify file data or + * block padding as a header or trailer. Format specific + * trailer functions must NOT check for the trailer while we + * are running in resync mode. Some trailer functions may tell + * us that this block cannot contain a valid header either, so + * we then throw out the entire block and start over. + */ + if ((*frmt->rd)(arcn, hdbuf) == 0) + break; + + if (!frmt->inhead) { + /* + * this format has trailers outside of valid headers + */ + if ((ret = (*frmt->trail)(hdbuf,in_resync,&cnt)) == 0){ + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + if (ret == 1) { + /* + * we are in resync and we were told to throw + * the whole block out because none of the + * bytes in this block can be used to form a + * valid header + */ + res = hsz; + hdend = hdbuf; + continue; + } + } + + /* + * Brute force section. + * not a valid header. We may be able to find a header yet. So + * we shift over by one byte, and set up to read one byte at a + * time from the archive and place it at the end of the buffer. + * We will keep moving byte at a time until we find a header or + * get a read error and have to start over. + */ + if (!in_resync) { + if (act == APPND) { + paxwarn(1,"Unable to append, archive header flaw"); + return(-1); + } + paxwarn(1,"Invalid header, starting valid header search."); + ++in_resync; + } + memmove(hdbuf, hdbuf+1, shftsz); + res = 1; + hdend = hdbuf + shftsz; + } + + /* + * ok got a valid header, check for trailer if format encodes it in the + * the header. NOTE: the parameters are different than trailer routines + * which encode trailers outside of the header! + */ + if (frmt->inhead && ((*frmt->trail)(arcn) == 0)) { + /* + * valid trailer found, drain input as required + */ + ar_drain(); + return(-1); + } + + ++flcnt; + return(0); +} + +/* + * get_arc() + * Figure out what format an archive is. Handles archive with flaws by + * brute force searches for a legal header in any supported format. The + * format id routines have to be careful to NOT mis-identify a format. + * ASSUMED: headers fit within a BLKMULT header. + * Return: + * 0 if archive found -1 otherwise + */ + +#ifdef __STDC__ +static int +get_arc(void) +#else +static int +get_arc() +#endif +{ + register int i; + register int hdsz = 0; + register int res; + register int minhd = BLKMULT; + char *hdend; + int notice = 0; + + /* + * find the smallest header size in all archive formats and then set up + * to read the archive. + */ + for (i = 0; ford[i] >= 0; ++i) { + if (fsub[ford[i]].hsz < minhd) + minhd = fsub[ford[i]].hsz; + } + if (rd_start() < 0) + return(-1); + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + for(;;) { + for (;;) { + /* + * fill the buffer with at least the smallest header + */ + i = rd_wrbuf(hdend, res); + if (i > 0) + hdsz += i; + if (hdsz >= minhd) + break; + + /* + * if we cannot recover from a read error quit + */ + if ((i == 0) || (rd_sync() < 0)) + goto out; + + /* + * when we get an error none of the data we already + * have can be used to create a legal header (we just + * got an error in the middle), so we throw it all out + * and refill the buffer with fresh data. + */ + res = BLKMULT; + hdsz = 0; + hdend = hdbuf; + if (!notice) { + if (act == APPND) + return(-1); + paxwarn(1,"Cannot identify format. Searching..."); + ++notice; + } + } + + /* + * we have at least the size of the smallest header in any + * archive format. Look to see if we have a match. The array + * ford[] is used to specify the header id order to reduce the + * chance of incorrectly id'ing a valid header (some formats + * may be subsets of each other and the order would then be + * important). + */ + for (i = 0; ford[i] >= 0; ++i) { + if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0) + continue; + frmt = &(fsub[ford[i]]); + /* + * yuck, to avoid slow special case code in the extract + * routines, just push this header back as if it was + * not seen. We have left extra space at start of the + * buffer for this purpose. This is a bit ugly, but + * adding all the special case code is far worse. + */ + pback(hdbuf, hdsz); + return(0); + } + + /* + * We have a flawed archive, no match. we start searching, but + * we never allow additions to flawed archives + */ + if (!notice) { + if (act == APPND) + return(-1); + paxwarn(1, "Cannot identify format. Searching..."); + ++notice; + } + + /* + * brute force search for a header that we can id. + * we shift through byte at a time. this is slow, but we cannot + * determine the nature of the flaw in the archive in a + * portable manner + */ + if (--hdsz > 0) { + memmove(hdbuf, hdbuf+1, hdsz); + res = BLKMULT - hdsz; + hdend = hdbuf + hdsz; + } else { + res = BLKMULT; + hdend = hdbuf; + hdsz = 0; + } + } + + out: + /* + * we cannot find a header, bow, apologize and quit + */ + paxwarn(1, "Sorry, unable to determine archive format."); + return(-1); +} diff --git a/pax/buf_subs.c b/pax/buf_subs.c new file mode 100644 index 0000000..6e13e8f --- /dev/null +++ b/pax/buf_subs.c @@ -0,0 +1,1094 @@ +/* $OpenBSD: buf_subs.c,v 1.7 1997/09/01 18:29:46 deraadt Exp $ */ +/* $NetBSD: buf_subs.c,v 1.5 1995/03/21 09:07:08 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)buf_subs.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: buf_subs.c,v 1.7 1997/09/01 18:29:46 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +/* + * routines which implement archive and file buffering + */ + +#define MINFBSZ 512 /* default block size for hole detect */ +#define MAXFLT 10 /* default media read error limit */ + +/* + * Need to change bufmem to dynamic allocation when the upper + * limit on blocking size is removed (though that will violate pax spec) + * MAXBLK define and tests will also need to be updated. + */ +static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */ +static char *buf; /* normal start of i/o buffer */ +static char *bufend; /* end or last char in i/o buffer */ +static char *bufpt; /* read/write point in i/o buffer */ +int blksz = MAXBLK; /* block input/output size in bytes */ +int wrblksz; /* user spec output size in bytes */ +int maxflt = MAXFLT; /* MAX consecutive media errors */ +int rdblksz; /* first read blksize (tapes only) */ +off_t wrlimit; /* # of bytes written per archive vol */ +off_t wrcnt; /* # of bytes written on current vol */ +off_t rdcnt; /* # of bytes read on current vol */ + +/* + * wr_start() + * set up the buffering system to operate in a write mode + * Return: + * 0 if ok, -1 if the user specified write block size violates pax spec + */ + +#ifdef __STDC__ +int +wr_start(void) +#else +int +wr_start() +#endif +{ + buf = &(bufmem[BLKMULT]); + /* + * Check to make sure the write block size meets pax specs. If the user + * does not specify a blocksize, we use the format default blocksize. + * We must be picky on writes, so we do not allow the user to create an + * archive that might be hard to read elsewhere. If all ok, we then + * open the first archive volume + */ + if (!wrblksz) + wrblksz = frmt->bsz; + if (wrblksz > MAXBLK) { + paxwarn(1, "Write block size of %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + paxwarn(1, "Write block size of %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + if (wrblksz > MAXBLK_POSIX) { + paxwarn(0, "Write block size of %d larger than POSIX max %d, archive may not be portable", + wrblksz, MAXBLK_POSIX); + return(-1); + } + + /* + * we only allow wrblksz to be used with all archive operations + */ + blksz = rdblksz = wrblksz; + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + wrcnt = 0; + bufend = buf + wrblksz; + bufpt = buf; + return(0); +} + +/* + * rd_start() + * set up buffering system to read an archive + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +rd_start(void) +#else +int +rd_start() +#endif +{ + /* + * leave space for the header pushback (see get_arc()). If we are + * going to append and user specified a write block size, check it + * right away + */ + buf = &(bufmem[BLKMULT]); + if ((act == APPND) && wrblksz) { + if (wrblksz > MAXBLK) { + paxwarn(1,"Write block size %d too large, maximium is: %d", + wrblksz, MAXBLK); + return(-1); + } + if (wrblksz % BLKMULT) { + paxwarn(1, "Write block size %d is not a %d byte multiple", + wrblksz, BLKMULT); + return(-1); + } + } + + /* + * open the archive + */ + if ((ar_open(arcname) < 0) && (ar_next() < 0)) + return(-1); + bufend = buf + rdblksz; + bufpt = bufend; + rdcnt = 0; + return(0); +} + +/* + * cp_start() + * set up buffer system for copying within the file system + */ + +#ifdef __STDC__ +void +cp_start(void) +#else +void +cp_start() +#endif +{ + buf = &(bufmem[BLKMULT]); + rdblksz = blksz = MAXBLK; +} + +/* + * appnd_start() + * Set up the buffering system to append new members to an archive that + * was just read. The last block(s) of an archive may contain a format + * specific trailer. To append a new member, this trailer has to be + * removed from the archive. The first byte of the trailer is replaced by + * the start of the header of the first file added to the archive. The + * format specific end read function tells us how many bytes to move + * backwards in the archive to be positioned BEFORE the trailer. Two + * different postions have to be adjusted, the O.S. file offset (e.g. the + * position of the tape head) and the write point within the data we have + * stored in the read (soon to become write) buffer. We may have to move + * back several records (the number depends on the size of the archive + * record and the size of the format trailer) to read up the record where + * the first byte of the trailer is recorded. Trailers may span (and + * overlap) record boundries. + * We first calculate which record has the first byte of the trailer. We + * move the OS file offset back to the start of this record and read it + * up. We set the buffer write pointer to be at this byte (the byte where + * the trailer starts). We then move the OS file pointer back to the + * start of this record so a flush of this buffer will replace the record + * in the archive. + * A major problem is rewriting this last record. For archives stored + * on disk files, this is trival. However, many devices are really picky + * about the conditions under which they will allow a write to occur. + * Often devices restrict the conditions where writes can be made writes, + * so it may not be feasable to append archives stored on all types of + * devices. + * Return: + * 0 for success, -1 for failure + */ + +#ifdef __STDC__ +int +appnd_start(off_t skcnt) +#else +int +appnd_start(skcnt) + off_t skcnt; +#endif +{ + register int res; + off_t cnt; + + if (exit_val != 0) { + paxwarn(0, "Cannot append to an archive that may have flaws."); + return(-1); + } + /* + * if the user did not specify a write blocksize, inherit the size used + * in the last archive volume read. (If a is set we still use rdblksz + * until next volume, cannot shift sizes within a single volume). + */ + if (!wrblksz) + wrblksz = blksz = rdblksz; + else + blksz = rdblksz; + + /* + * make sure that this volume allows appends + */ + if (ar_app_ok() < 0) + return(-1); + + /* + * Calculate bytes to move back and move in front of record where we + * need to start writing from. Remember we have to add in any padding + * that might be in the buffer after the trailer in the last block. We + * travel skcnt + padding ROUNDED UP to blksize. + */ + skcnt += bufend - bufpt; + if ((cnt = (skcnt/blksz) * blksz) < skcnt) + cnt += blksz; + if (ar_rev((off_t)cnt) < 0) + goto out; + + /* + * We may have gone too far if there is valid data in the block we are + * now in front of, read up the block and position the pointer after + * the valid data. + */ + if ((cnt -= skcnt) > 0) { + /* + * watch out for stupid tape drives. ar_rev() will set rdblksz + * to be real physical blocksize so we must loop until we get + * the old rdblksz (now in blksz). If ar_rev() fouls up the + * determination of the physical block size, we will fail. + */ + bufpt = buf; + bufend = buf + blksz; + while (bufpt < bufend) { + if ((res = ar_read(bufpt, rdblksz)) <= 0) + goto out; + bufpt += res; + } + if (ar_rev((off_t)(bufpt - buf)) < 0) + goto out; + bufpt = buf + cnt; + bufend = buf + blksz; + } else { + /* + * buffer is empty + */ + bufend = buf + blksz; + bufpt = buf; + } + rdblksz = blksz; + rdcnt -= skcnt; + wrcnt = 0; + + /* + * At this point we are ready to write. If the device requires special + * handling to write at a point were previously recorded data resides, + * that is handled in ar_set_wr(). From now on we operate under normal + * ARCHIVE mode (write) conditions + */ + if (ar_set_wr() < 0) + return(-1); + act = ARCHIVE; + return(0); + + out: + paxwarn(1, "Unable to rewrite archive trailer, cannot append."); + return(-1); +} + +/* + * rd_sync() + * A read error occurred on this archive volume. Resync the buffer and + * try to reset the device (if possible) so we can continue to read. Keep + * trying to do this until we get a valid read, or we reach the limit on + * consecutive read faults (at which point we give up). The user can + * adjust the read error limit through a command line option. + * Returns: + * 0 on success, and -1 on failure + */ + +#ifdef __STDC__ +int +rd_sync(void) +#else +int +rd_sync() +#endif +{ + register int errcnt = 0; + register int res; + + /* + * if the user says bail out on first fault, we are out of here... + */ + if (maxflt == 0) + return(-1); + if (act == APPND) { + paxwarn(1, "Unable to append when there are archive read errors."); + return(-1); + } + + /* + * poke at device and try to get past media error + */ + if (ar_rdsync() < 0) { + if (ar_next() < 0) + return(-1); + else + rdcnt = 0; + } + + for (;;) { + if ((res = ar_read(buf, blksz)) > 0) { + /* + * All right! got some data, fill that buffer + */ + bufpt = buf; + bufend = buf + res; + rdcnt += res; + return(0); + } + + /* + * Oh well, yet another failed read... + * if error limit reached, ditch. o.w. poke device to move past + * bad media and try again. if media is badly damaged, we ask + * the poor (and upset user at this point) for the next archive + * volume. remember the goal on reads is to get the most we + * can extract out of the archive. + */ + if ((maxflt > 0) && (++errcnt > maxflt)) + paxwarn(0,"Archive read error limit (%d) reached",maxflt); + else if (ar_rdsync() == 0) + continue; + if (ar_next() < 0) + break; + rdcnt = 0; + errcnt = 0; + } + return(-1); +} + +/* + * pback() + * push the data used during the archive id phase back into the I/O + * buffer. This is required as we cannot be sure that the header does NOT + * overlap a block boundry (as in the case we are trying to recover a + * flawed archived). This was not designed to be used for any other + * purpose. (What software engineering, HA!) + * WARNING: do not even THINK of pback greater than BLKMULT, unless the + * pback space is increased. + */ + +#ifdef __STDC__ +void +pback(char *pt, int cnt) +#else +void +pback(pt, cnt) + char *pt; + int cnt; +#endif +{ + bufpt -= cnt; + memcpy(bufpt, pt, cnt); + return; +} + +/* + * rd_skip() + * skip foward in the archive during a archive read. Used to get quickly + * past file data and padding for files the user did NOT select. + * Return: + * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected. + */ + +#ifdef __STDC__ +int +rd_skip(off_t skcnt) +#else +int +rd_skip(skcnt) + off_t skcnt; +#endif +{ + off_t res; + off_t cnt; + off_t skipped = 0; + + /* + * consume what data we have in the buffer. If we have to move foward + * whole records, we call the low level skip function to see if we can + * move within the archive without doing the expensive reads on data we + * do not want. + */ + if (skcnt == 0) + return(0); + res = MIN((bufend - bufpt), skcnt); + bufpt += res; + skcnt -= res; + + /* + * if skcnt is now 0, then no additional i/o is needed + */ + if (skcnt == 0) + return(0); + + /* + * We have to read more, calculate complete and partial record reads + * based on rdblksz. we skip over "cnt" complete records + */ + res = skcnt%rdblksz; + cnt = (skcnt/rdblksz) * rdblksz; + + /* + * if the skip fails, we will have to resync. ar_fow will tell us + * how much it can skip over. We will have to read the rest. + */ + if (ar_fow(cnt, &skipped) < 0) + return(-1); + res += cnt - skipped; + rdcnt += skipped; + + /* + * what is left we have to read (which may be the whole thing if + * ar_fow() told us the device can only read to skip records); + */ + while (res > 0L) { + cnt = bufend - bufpt; + /* + * if the read fails, we will have to resync + */ + if ((cnt <= 0) && ((cnt = buf_fill()) < 0)) + return(-1); + if (cnt == 0) + return(1); + cnt = MIN(cnt, res); + bufpt += cnt; + res -= cnt; + } + return(0); +} + +/* + * wr_fin() + * flush out any data (and pad if required) the last block. We always pad + * with zero (even though we do not have to). Padding with 0 makes it a + * lot easier to recover if the archive is damaged. zero paddding SHOULD + * BE a requirement.... + */ + +#ifdef __STDC__ +void +wr_fin(void) +#else +void +wr_fin() +#endif +{ + if (bufpt > buf) { + memset(bufpt, 0, bufend - bufpt); + bufpt = bufend; + (void)buf_flush(blksz); + } +} + +/* + * wr_rdbuf() + * fill the write buffer from data passed to it in a buffer (usually used + * by format specific write routines to pass a file header). On failure we + * punt. We do not allow the user to continue to write flawed archives. + * We assume these headers are not very large (the memory copy we use is + * a bit expensive). + * Return: + * 0 if buffer was filled ok, -1 o.w. (buffer flush failure) + */ + +#ifdef __STDC__ +int +wr_rdbuf(register char *out, register int outcnt) +#else +int +wr_rdbuf(out, outcnt) + register char *out; + register int outcnt; +#endif +{ + register int cnt; + + /* + * while there is data to copy copy into the write buffer. when the + * write buffer fills, flush it to the archive and continue + */ + while (outcnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + /* + * only move what we have space for + */ + cnt = MIN(cnt, outcnt); + memcpy(bufpt, out, cnt); + bufpt += cnt; + out += cnt; + outcnt -= cnt; + } + return(0); +} + +/* + * rd_wrbuf() + * copy from the read buffer into a supplied buffer a specified number of + * bytes. If the read buffer is empty fill it and continue to copy. + * usually used to obtain a file header for processing by a format + * specific read routine. + * Return + * number of bytes copied to the buffer, 0 indicates EOF on archive volume, + * -1 is a read error + */ + +#ifdef __STDC__ +int +rd_wrbuf(register char *in, register int cpcnt) +#else +int +rd_wrbuf(in, cpcnt) + register char *in; + register int cpcnt; +#endif +{ + register int res; + register int cnt; + register int incnt = cpcnt; + + /* + * loop until we fill the buffer with the requested number of bytes + */ + while (incnt > 0) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) { + /* + * read error, return what we got (or the error if + * no data was copied). The caller must know that an + * error occured and has the best knowledge what to + * do with it + */ + if ((res = cpcnt - incnt) > 0) + return(res); + return(cnt); + } + + /* + * calculate how much data to copy based on whats left and + * state of buffer + */ + cnt = MIN(cnt, incnt); + memcpy(in, bufpt, cnt); + bufpt += cnt; + incnt -= cnt; + in += cnt; + } + return(cpcnt); +} + +/* + * wr_skip() + * skip foward during a write. In other words add padding to the file. + * we add zero filled padding as it makes flawed archives much easier to + * recover from. the caller tells us how many bytes of padding to add + * This routine was not designed to add HUGE amount of padding, just small + * amounts (a few 512 byte blocks at most) + * Return: + * 0 if ok, -1 if there was a buf_flush failure + */ + +#ifdef __STDC__ +int +wr_skip(off_t skcnt) +#else +int +wr_skip(skcnt) + off_t skcnt; +#endif +{ + register int cnt; + + /* + * loop while there is more padding to add + */ + while (skcnt > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) + return(-1); + cnt = MIN(cnt, skcnt); + memset(bufpt, 0, cnt); + bufpt += cnt; + skcnt -= cnt; + } + return(0); +} + +/* + * wr_rdfile() + * fill write buffer with the contents of a file. We are passed an open + * file descriptor to the file an the archive structure that describes the + * file we are storing. The variable "left" is modified to contain the + * number of bytes of the file we were NOT able to write to the archive. + * it is important that we always write EXACTLY the number of bytes that + * the format specific write routine told us to. The file can also get + * bigger, so reading to the end of file would create an improper archive, + * we just detect this case and warn the user. We never create a bad + * archive if we can avoid it. Of course trying to archive files that are + * active is asking for trouble. It we fail, we pass back how much we + * could NOT copy and let the caller deal with it. + * Return: + * 0 ok, -1 if archive write failure. a short read of the file returns a + * 0, but "left" is set to be greater than zero. + */ + +#ifdef __STDC__ +int +wr_rdfile(ARCHD *arcn, int ifd, off_t *left) +#else +int +wr_rdfile(arcn, ifd, left) + ARCHD *arcn; + int ifd; + off_t *left; +#endif +{ + register int cnt; + register int res = 0; + register off_t size = arcn->sb.st_size; + struct stat sb; + + /* + * while there are more bytes to write + */ + while (size > 0L) { + cnt = bufend - bufpt; + if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) { + *left = size; + return(-1); + } + cnt = MIN(cnt, size); + if ((res = read(ifd, bufpt, cnt)) <= 0) + break; + size -= res; + bufpt += res; + } + + /* + * better check the file did not change during this operation + * or the file read failed. + */ + if (res < 0) + syswarn(1, errno, "Read fault on %s", arcn->org_name); + else if (size != 0L) + paxwarn(1, "File changed size during read %s", arcn->org_name); + else if (fstat(ifd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during copy to archive", + arcn->org_name); + *left = size; + return(0); +} + +/* + * rd_wrfile() + * extract the contents of a file from the archive. If we are unable to + * extract the entire file (due to failure to write the file) we return + * the numbers of bytes we did NOT process. This way the caller knows how + * many bytes to skip past to find the next archive header. If the failure + * was due to an archive read, we will catch that when we try to skip. If + * the format supplies a file data crc value, we calculate the actual crc + * so that it can be compared to the value stored in the header + * NOTE: + * We call a special function to write the file. This function attempts to + * restore file holes (blocks of zeros) into the file. When files are + * sparse this saves space, and is a LOT faster. For non sparse files + * the performance hit is small. As of this writing, no archive supports + * information on where the file holes are. + * Return: + * 0 ok, -1 if archive read failure. if we cannot write the entire file, + * we return a 0 but "left" is set to be the amount unwritten + */ + +#ifdef __STDC__ +int +rd_wrfile(ARCHD *arcn, int ofd, off_t *left) +#else +int +rd_wrfile(arcn, ofd, left) + ARCHD *arcn; + int ofd; + off_t *left; +#endif +{ + register int cnt = 0; + register off_t size = arcn->sb.st_size; + register int res = 0; + register char *fnm = arcn->name; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + u_long crc = 0L; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (fstat(ofd, &sb) == 0) { + if (sb.st_blksize > 0) + sz = (int)sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + *left = 0L; + + /* + * Copy the archive to the file the number of bytes specified. We have + * to assume that we want to recover file holes as none of the archive + * formats can record the location of file holes. + */ + while (size > 0L) { + cnt = bufend - bufpt; + /* + * if we get a read error, we do not want to skip, as we may + * miss a header, so we do not set left, but if we get a write + * error, we do want to skip over the unprocessed data. + */ + if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) + break; + cnt = MIN(cnt, size); + if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) { + *left = size; + break; + } + + if (docrc) { + /* + * update the actual crc value + */ + cnt = res; + while (--cnt >= 0) + crc += *bufpt++ & 0xff; + } else + bufpt += res; + size -= res; + } + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved foward may not put + * a hole at the end of the file. + */ + if (isem && (arcn->sb.st_size > 0L)) + file_flush(ofd, fnm, isem); + + /* + * if we failed from archive read, we do not want to skip + */ + if ((size > 0L) && (*left == 0L)) + return(-1); + + /* + * some formats record a crc on file data. If so, then we compare the + * calculated crc to the crc stored in the archive + */ + if (docrc && (size == 0L) && (arcn->crc != crc)) + paxwarn(1,"Actual crc does not match expected crc %s",arcn->name); + return(0); +} + +/* + * cp_file() + * copy the contents of one file to another. used during -rw phase of pax + * just as in rd_wrfile() we use a special write function to write the + * destination file so we can properly copy files with holes. + */ + +#ifdef __STDC__ +void +cp_file(ARCHD *arcn, int fd1, int fd2) +#else +void +cp_file(arcn, fd1, fd2) + ARCHD *arcn; + int fd1; + int fd2; +#endif +{ + register int cnt; + register off_t cpcnt = 0L; + register int res = 0; + register char *fnm = arcn->name; + register int no_hole = 0; + int isem = 1; + int rem; + int sz = MINFBSZ; + struct stat sb; + + /* + * check for holes in the source file. If none, we will use regular + * write instead of file write. + */ + if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= arcn->sb.st_size) + ++no_hole; + + /* + * pass the blocksize of the file being written to the write routine, + * if the size is zero, use the default MINFBSZ + */ + if (fstat(fd2, &sb) == 0) { + if (sb.st_blksize > 0) + sz = sb.st_blksize; + } else + syswarn(0,errno,"Unable to obtain block size for file %s",fnm); + rem = sz; + + /* + * read the source file and copy to destination file until EOF + */ + for(;;) { + if ((cnt = read(fd1, buf, blksz)) <= 0) + break; + if (no_hole) + res = write(fd2, buf, cnt); + else + res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm); + if (res != cnt) + break; + cpcnt += cnt; + } + + /* + * check to make sure the copy is valid. + */ + if (res < 0) + syswarn(1, errno, "Failed write during copy of %s to %s", + arcn->org_name, arcn->name); + else if (cpcnt != arcn->sb.st_size) + paxwarn(1, "File %s changed size during copy to %s", + arcn->org_name, arcn->name); + else if (fstat(fd1, &sb) < 0) + syswarn(1, errno, "Failed stat of %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during copy to %s", + arcn->org_name, arcn->name); + + /* + * if the last block has a file hole (all zero), we must make sure this + * gets updated in the file. We force the last block of zeros to be + * written. just closing with the file offset moved foward may not put + * a hole at the end of the file. + */ + if (!no_hole && isem && (arcn->sb.st_size > 0L)) + file_flush(fd2, fnm, isem); + return; +} + +/* + * buf_fill() + * fill the read buffer with the next record (or what we can get) from + * the archive volume. + * Return: + * Number of bytes of data in the read buffer, -1 for read error, and + * 0 when finished (user specified termination in ar_next()). + */ + +#ifdef __STDC__ +int +buf_fill(void) +#else +int +buf_fill() +#endif +{ + register int cnt; + static int fini = 0; + + if (fini) + return(0); + + for(;;) { + /* + * try to fill the buffer. on error the next archive volume is + * opened and we try again. + */ + if ((cnt = ar_read(buf, blksz)) > 0) { + bufpt = buf; + bufend = buf + cnt; + rdcnt += cnt; + return(cnt); + } + + /* + * errors require resync, EOF goes to next archive + */ + if (cnt < 0) + break; + if (ar_next() < 0) { + fini = 1; + return(0); + } + rdcnt = 0; + } + exit_val = 1; + return(-1); +} + +/* + * buf_flush() + * force the write buffer to the archive. We are passed the number of + * bytes in the buffer at the point of the flush. When we change archives + * the record size might change. (either larger or smaller). + * Return: + * 0 if all is ok, -1 when a write error occurs. + */ + +#ifdef __STDC__ +int +buf_flush(register int bufcnt) +#else +int +buf_flush(bufcnt) + register int bufcnt; +#endif +{ + register int cnt; + register int push = 0; + register int totcnt = 0; + + /* + * if we have reached the user specified byte count for each archive + * volume, prompt for the next volume. (The non-standrad -R flag). + * NOTE: If the wrlimit is smaller than wrcnt, we will always write + * at least one record. We always round limit UP to next blocksize. + */ + if ((wrlimit > 0) && (wrcnt > wrlimit)) { + paxwarn(0, "User specified archive volume byte limit reached."); + if (ar_next() < 0) { + wrcnt = 0; + exit_val = 1; + return(-1); + } + wrcnt = 0; + + /* + * The new archive volume might have changed the size of the + * write blocksize. if so we figure out if we need to write + * (one or more times), or if there is now free space left in + * the buffer (it is no longer full). bufcnt has the number of + * bytes in the buffer, (the blocksize, at the point we were + * CALLED). Push has the amount of "extra" data in the buffer + * if the block size has shrunk from a volume change. + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * We have enough data to write at least one archive block + */ + for (;;) { + /* + * write a block and check if it all went out ok + */ + cnt = ar_write(buf, blksz); + if (cnt == blksz) { + /* + * the write went ok + */ + wrcnt += cnt; + totcnt += cnt; + if (push > 0) { + /* we have extra data to push to the front. + * check for more than 1 block of push, and if + * so we loop back to write again + */ + memcpy(buf, bufend, push); + bufpt = buf + push; + if (push >= blksz) { + push -= blksz; + continue; + } + } else + bufpt = buf; + return(totcnt); + } else if (cnt > 0) { + /* + * Oh drat we got a partial write! + * if format doesnt care about alignment let it go, + * we warned the user in ar_write().... but this means + * the last record on this volume violates pax spec.... + */ + totcnt += cnt; + wrcnt += cnt; + bufpt = buf + cnt; + cnt = bufcnt - cnt; + memcpy(buf, bufpt, cnt); + bufpt = buf + cnt; + if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0)) + return(totcnt); + break; + } + + /* + * All done, go to next archive + */ + wrcnt = 0; + if (ar_next() < 0) + break; + + /* + * The new archive volume might also have changed the block + * size. if so, figure out if we have too much or too little + * data for using the new block size + */ + bufend = buf + blksz; + if (blksz > bufcnt) + return(0); + if (blksz < bufcnt) + push = bufcnt - blksz; + } + + /* + * write failed, stop pax. we must not create a bad archive! + */ + exit_val = 1; + return(-1); +} diff --git a/pax/cache.c b/pax/cache.c new file mode 100644 index 0000000..df58ec4 --- /dev/null +++ b/pax/cache.c @@ -0,0 +1,500 @@ +/* $OpenBSD: cache.c,v 1.6 1997/07/25 18:58:27 mickey Exp $ */ +/* $NetBSD: cache.c,v 1.4 1995/03/21 09:07:10 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cache.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: cache.c,v 1.6 1997/07/25 18:58:27 mickey Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "cache.h" +#include "extern.h" + +/* + * routines that control user, group, uid and gid caches (for the archive + * member print routine). + * IMPORTANT: + * these routines cache BOTH hits and misses, a major performance improvement + */ + +static int pwopn = 0; /* is password file open */ +static int gropn = 0; /* is group file open */ +static UIDC **uidtb = NULL; /* uid to name cache */ +static GIDC **gidtb = NULL; /* gid to name cache */ +static UIDC **usrtb = NULL; /* user name to uid cache */ +static GIDC **grptb = NULL; /* group name to gid cache */ + +/* + * uidtb_start + * creates an an empty uidtb + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +uidtb_start(void) +#else +int +uidtb_start() +#endif +{ + static int fail = 0; + + if (uidtb != NULL) + return(0); + if (fail) + return(-1); + if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for user id cache table"); + return(-1); + } + return(0); +} + +/* + * gidtb_start + * creates an an empty gidtb + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +gidtb_start(void) +#else +int +gidtb_start() +#endif +{ + static int fail = 0; + + if (gidtb != NULL) + return(0); + if (fail) + return(-1); + if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for group id cache table"); + return(-1); + } + return(0); +} + +/* + * usrtb_start + * creates an an empty usrtb + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +usrtb_start(void) +#else +int +usrtb_start() +#endif +{ + static int fail = 0; + + if (usrtb != NULL) + return(0); + if (fail) + return(-1); + if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { + ++fail; + paxwarn(1, "Unable to allocate memory for user name cache table"); + return(-1); + } + return(0); +} + +/* + * grptb_start + * creates an an empty grptb + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +grptb_start(void) +#else +int +grptb_start() +#endif +{ + static int fail = 0; + + if (grptb != NULL) + return(0); + if (fail) + return(-1); + if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { + ++fail; + paxwarn(1,"Unable to allocate memory for group name cache table"); + return(-1); + } + return(0); +} + +/* + * name_uid() + * caches the name (if any) for the uid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +#ifdef __STDC__ +char * +name_uid(uid_t uid, int frc) +#else +char * +name_uid(uid, frc) + uid_t uid; + int frc; +#endif +{ + register struct passwd *pw; + register UIDC *ptr; + register int hash; + if ((uidtb == NULL) && (uidtb_start() < 0)) + return(""); + + /* + * see if we have this uid cached + */ + hash = uid % UID_SZ; + ptr = uidtb[hash]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { + /* + * have an entry for this uid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this uid, we will add it + */ + if (!pwopn) { + setpassent(1); + ++pwopn; + } + if (ptr == NULL) + ptr = (UIDC *)malloc(sizeof(UIDC)); + + if ((pw = getpwuid(uid)) == NULL) { + /* + * no match for this uid in the local password file + * a string that is the uid in numberic format + */ + if (ptr == NULL) + return(""); + ptr->uid = uid; + ptr->valid = INVALID; +# ifdef NET2_STAT + (void)snprintf(ptr->name, sizeof(ptr->name), "%u", uid); +# else + (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", + (unsigned long)uid); +# endif + uidtb[hash] = ptr; + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this uid in the password file + */ + if (ptr == NULL) + return(pw->pw_name); + ptr->uid = uid; + (void)strncpy(ptr->name, pw->pw_name, UNMLEN-1); + ptr->name[UNMLEN-1] = '\0'; + ptr->valid = VALID; + uidtb[hash] = ptr; + } + return(ptr->name); +} + +/* + * name_gid() + * caches the name (if any) for the gid. If frc set, we always return the + * the stored name (if valid or invalid match). We use a simple hash table. + * Return + * Pointer to stored name (or a empty string) + */ + +#ifdef __STDC__ +char * +name_gid(gid_t gid, int frc) +#else +char * +name_gid(gid, frc) + gid_t gid; + int frc; +#endif +{ + register struct group *gr; + register GIDC *ptr; + register int hash; + + if ((gidtb == NULL) && (gidtb_start() < 0)) + return(""); + + /* + * see if we have this gid cached + */ + hash = gid % GID_SZ; + ptr = gidtb[hash]; + if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { + /* + * have an entry for this gid + */ + if (frc || (ptr->valid == VALID)) + return(ptr->name); + return(""); + } + + /* + * No entry for this gid, we will add it + */ + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = (GIDC *)malloc(sizeof(GIDC)); + + if ((gr = getgrgid(gid)) == NULL) { + /* + * no match for this gid in the local group file, put in + * a string that is the gid in numberic format + */ + if (ptr == NULL) + return(""); + ptr->gid = gid; + ptr->valid = INVALID; +# ifdef NET2_STAT + (void)snprintf(ptr->name, sizeof(ptr->name), "%u", gid); +# else + (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", + (unsigned long)gid); +# endif + gidtb[hash] = ptr; + if (frc == 0) + return(""); + } else { + /* + * there is an entry for this group in the group file + */ + if (ptr == NULL) + return(gr->gr_name); + ptr->gid = gid; + (void)strncpy(ptr->name, gr->gr_name, GNMLEN-1); + ptr->name[GNMLEN-1] = '\0'; + ptr->valid = VALID; + gidtb[hash] = ptr; + } + return(ptr->name); +} + +/* + * uid_name() + * caches the uid for a given user name. We use a simple hash table. + * Return + * the uid (if any) for a user name, or a -1 if no match can be found + */ + +#ifdef __STDC__ +int +uid_name(char *name, uid_t *uid) +#else +int +uid_name(name, uid) + char *name; + uid_t *uid; +#endif +{ + register struct passwd *pw; + register UIDC *ptr; + register int namelen; + register int hash; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((usrtb == NULL) && (usrtb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + hash = st_hash(name, namelen, UNM_SZ); + ptr = usrtb[hash]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *uid = ptr->uid; + return(0); + } + + if (!pwopn) { + setpassent(1); + ++pwopn; + } + + if (ptr == NULL) + ptr = (UIDC *)malloc(sizeof(UIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching uid + */ + if (ptr == NULL) { + if ((pw = getpwnam(name)) == NULL) + return(-1); + *uid = pw->pw_uid; + return(0); + } + usrtb[hash] = ptr; + (void)strncpy(ptr->name, name, UNMLEN-1); + ptr->name[UNMLEN-1] = '\0'; + if ((pw = getpwnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *uid = ptr->uid = pw->pw_uid; + return(0); +} + +/* + * gid_name() + * caches the gid for a given group name. We use a simple hash table. + * Return + * the gid (if any) for a group name, or a -1 if no match can be found + */ + +#ifdef __STDC__ +int +gid_name(char *name, gid_t *gid) +#else +int +gid_name(name, gid) + char *name; + gid_t *gid; +#endif +{ + register struct group *gr; + register GIDC *ptr; + register int namelen; + register int hash; + + /* + * return -1 for mangled names + */ + if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) + return(-1); + if ((grptb == NULL) && (grptb_start() < 0)) + return(-1); + + /* + * look up in hash table, if found and valid return the uid, + * if found and invalid, return a -1 + */ + hash = st_hash(name, namelen, GID_SZ); + ptr = grptb[hash]; + if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { + if (ptr->valid == INVALID) + return(-1); + *gid = ptr->gid; + return(0); + } + + if (!gropn) { + setgroupent(1); + ++gropn; + } + if (ptr == NULL) + ptr = (GIDC *)malloc(sizeof(GIDC)); + + /* + * no match, look it up, if no match store it as an invalid entry, + * or store the matching gid + */ + if (ptr == NULL) { + if ((gr = getgrnam(name)) == NULL) + return(-1); + *gid = gr->gr_gid; + return(0); + } + + grptb[hash] = ptr; + (void)strncpy(ptr->name, name, GNMLEN-1); + ptr->name[GNMLEN-1] = '\0'; + if ((gr = getgrnam(name)) == NULL) { + ptr->valid = INVALID; + return(-1); + } + ptr->valid = VALID; + *gid = ptr->gid = gr->gr_gid; + return(0); +} diff --git a/pax/cache.h b/pax/cache.h new file mode 100644 index 0000000..01283ef --- /dev/null +++ b/pax/cache.h @@ -0,0 +1,77 @@ +/* $OpenBSD: cache.h,v 1.2 1996/06/23 14:20:31 deraadt Exp $ */ +/* $NetBSD: cache.h,v 1.3 1995/03/21 09:07:12 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)cache.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Constants and data structures used to implement group and password file + * caches. Traditional passwd/group cache routines perform quite poorly with + * archives. The chances of hitting a valid lookup with an archive is quite a + * bit worse than with files already resident on the file system. These misses + * create a MAJOR performance cost. To adress this problem, these routines + * cache both hits and misses. + * + * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and + * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME + */ +#define UNMLEN 32 /* >= user name found in any protocol */ +#define GNMLEN 32 /* >= group name found in any protocol */ +#define UID_SZ 317 /* size of user_name/uid cache */ +#define UNM_SZ 317 /* size of user_name/uid cache */ +#define GID_SZ 251 /* size of gid cache */ +#define GNM_SZ 317 /* size of group name cache */ +#define VALID 1 /* entry and name are valid */ +#define INVALID 2 /* entry valid, name NOT valid */ + +/* + * Node structures used in the user, group, uid, and gid caches. + */ + +typedef struct uidc { + int valid; /* is this a valid or a miss entry */ + char name[UNMLEN]; /* uid name */ + uid_t uid; /* cached uid */ +} UIDC; + +typedef struct gidc { + int valid; /* is this a valid or a miss entry */ + char name[GNMLEN]; /* gid name */ + gid_t gid; /* cached gid */ +} GIDC; diff --git a/pax/cpio.1 b/pax/cpio.1 new file mode 100644 index 0000000..639bc07 --- /dev/null +++ b/pax/cpio.1 @@ -0,0 +1,270 @@ +.\" +.\" Copyright (c) 1997 SigmaSoft, Th. Lockert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 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. +.\" 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 THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: cpio.1,v 1.3 1997/04/06 06:11:11 millert Exp $ +.\" +.Dd February 16, 1997 +.Dt CPIO 1 +.Os +.Sh NAME +.Nm cpio +.Nd copy file archives in and out +.Sh SYNOPSIS +.Nm +.Fl o +.Op Fl aABcLvzZ +.Op Fl C Ar bytes +.Op Fl F Ar archive +.Op Fl H Ar format +.Op Fl O Ar archive +.Ar "< name-list" +.Op Ar "> archive" +.Nm +.Fl i +.Op Fl bBcdfmrsStuvzZ6 +.Op Fl C Ar bytes +.Op Fl E Ar file +.Op Fl F Ar archive +.Op Fl H Ar format +.Op Fl I Ar archive +.Op Ar "pattern ..." +.Op Ar "< archive" +.Nm +.Fl p +.Op Fl adlLmuv +.Ar destination-directory +.Ar "< name-list" +.Sh DESCRIPTION +The +.Nm +command copies files to and from a +.Nm +archive. +.Pp +The following options are supported: +.Bl -tag -width Fl +.It Fl o +Create an archive. Reads the list of files to store in the +archive from standard input, and writes the archive on standard +output. +.Bl -tag -width Fl +.It Fl a +Reset the access times on files that has been copied to the +archive. +.It Fl A +Append to the specified archive. +.It Fl B +Set block size of output to 5120 bytes. +.It Fl c +Use ASCII format for +.Nm +header for portability. +.It Fl C Ar bytes +Set the block size of output to +.Ar bytes . +.It Fl F Ar archive +.It Fl O Ar archive +Use the specified file name as the archive to write to. +.It Fl H Ar format +Write the archive in the specified format. Recognized +formats are: +.Bl -tag -width Ds +.It Ar bcpio +Old binary cpio format. +.It Ar cpio +Old octal character cpio format. +.It Ar sv4cpio +SVR4 hex cpio format. +.It Ar tar +Old tar format. +.It Ar ustar +POSIX ustar format. +.El +.It Fl L +Follow symbolic links. +.It Fl v +Be verbose about operations. List filenames as they are +written to the archive. +.It Fl z +Compress archive using +.Xr gzip 1 +format. +.It Fl Z +Compress archive using +.Xr compress 1 +format. +.El +.It Fl i +Restore files from an archive. Reads the archive file from +standard input and extracts files matching the +.Ar patterns +that were specified on the command line. +.Bl -tag -width Fl +.It Fl b +Do byte- and word swapping after reading in data from the +archive, for restoring archives created on systems with +different byte order. +.It Fl B +Set the block size of the archive being read to 5120 bytes. +.It Fl c +Expect the archive headers to be in ASCII format. +.It Fl C Ar bytes +Read archive written with a blocksize of +.Ar bytes . +.It Fl d +Create any intermediate directories as needed during +restore. +.It Fl E Ar file +Read list of file name patters to extract or list from +.Ar file . +.It Fl f +Restore all files except those matching the +.Ar patterns +given on the command line. +.It Fl F Ar archive +.It Fl I Ar archive +Use the specified file as the input for the archive. +.It Fl H Ar format +Read an archive of the specified format. Recognized +formats are: +.Bl -tag -width Ds +.It Ar bcpio +Old binary cpio format. +.It Ar cpio +Old octal character cpio format. +.It Ar sv4cpio +SVR4 hex cpio format. +.It Ar tar +Old tar format. +.It Ar ustar +POSIX ustar format. +.El +.It Fl m +Restore modification times on files. +.It Fl r +Rename restored files interactively. +.It Fl s +Swap bytes after reading data from the archive. +.It Fl S +Swap words after reading data from the archive. +.It Fl t +Only list the contents of the archive, no files or +directories will be created. +.It Fl u +Overwrite files even when the file in the archive is +older than the one that will be overwritten. +.It Fl v +Be verbose about operations. List filenames as they are +copied in from the archive. +.It Fl z +Uncompress archive using +.Xr gzip 1 +format. +.It Fl Z +Uncompress archive using +.Xr compress 1 +format. +.It Fl 6 +Process old-style \*Qcpio\*U format archives. +.El +.It Fl p +Copy files from one location to another in a single pass. +The list of files to copy are read from standard in and +written out to a directory relative to the specified +.Ar directory +argument. +.Bl -tag -width Fl +.It Fl a +Reset the access times on files that has been copied. +.It Fl d +Create any intermediate directories as needed to write +the files at the new location. +.It Fl l +When possible, link files rather than creating an +extra copy. +.It Fl L +Follow symbolic links. +.It Fl m +Restore modification times on files. +.It Fl u +Overwrite files even when the original file being copied is +older than the one that will be overwritten. +.It Fl v +Be verbose about operations. List filenames as they are +copied. +.El +.El +.Sh ERRORS +.Nm +will exit with one of the following values: +.Bl -tag -width 2n +.It 0 +All files were processed successfully. +.It 1 +An error occured. +.El +.Pp +Whenever +.Nm +cannot create a file or a link when extracting an archive or cannot +find a file while writing an archive, or cannot preserve the user +ID, group ID, file mode or access and modification times when the +.Fl p +options is specified, a diagnostic message is written to standard +error and a non-zero exit value will be returned, but processing +will continue. In the case where +.Nm +cannot create a link to a file, +.Nm +will not create a second copy of the file. +.Pp +If the extraction of a file from an archive is prematurely terminated +by a signal or error, +.Nm +may have only partially extracted the file the user wanted. +Additionally, the file modes of extracted files and directories may +have incorrect file bits, and the modification and access times may +be wrong. +.Pp +If the creation of an archive is prematurely terminated by a signal +or error, +.Nm +may have only partially created the archive which may violate the +specific archive format specification. +.Sh SEE ALSO +.Xr pax 1 , +.Xr tar 1 +.Sh BUGS +The +.Fl s +and +.Fl S +options are currently not implemented. +.Sh AUTHOR +Keith Muller at the University of California, San Diego diff --git a/pax/cpio.c b/pax/cpio.c new file mode 100644 index 0000000..43feb64 --- /dev/null +++ b/pax/cpio.c @@ -0,0 +1,1284 @@ +/* $OpenBSD: cpio.c,v 1.5 1997/07/25 18:58:28 mickey Exp $ */ +/* $NetBSD: cpio.c,v 1.5 1995/03/21 09:07:13 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cpio.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: cpio.c,v 1.5 1997/07/25 18:58:28 mickey Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "cpio.h" +#include "extern.h" + +static int rd_nm __P((register ARCHD *, int)); +static int rd_ln_nm __P((register ARCHD *)); +static int com_rd __P((register ARCHD *)); + +/* + * Routines which support the different cpio versions + */ + +static int swp_head; /* binary cpio header byte swap */ + +/* + * Routines common to all versions of cpio + */ + +/* + * cpio_strd() + * Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +#ifdef __STDC__ +int +cpio_strd(void) +#else +int +cpio_strd() +#endif +{ + return(lnk_start()); +} + +/* + * cpio_trail() + * Called to determine if a header block is a valid trailer. We are + * passed the block, the in_sync flag (which tells us we are in resync + * mode; looking for a valid header), and cnt (which starts at zero) + * which is used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, + */ + +#ifdef __STDC__ +int +cpio_trail(register ARCHD *arcn) +#else +int +cpio_trail(arcn) + register ARCHD *arcn; +#endif +{ + /* + * look for trailer id in file we are about to process + */ + if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0)) + return(0); + return(-1); +} + +/* + * com_rd() + * operations common to all cpio read functions. + * Return: + * 0 + */ + +#ifdef __STDC__ +static int +com_rd(register ARCHD *arcn) +#else +static int +com_rd(arcn) + register ARCHD *arcn; +#endif +{ + arcn->skip = 0; + arcn->pat = NULL; + arcn->org_name = arcn->name; + switch(arcn->sb.st_mode & C_IFMT) { + case C_ISFIFO: + arcn->type = PAX_FIF; + break; + case C_ISDIR: + arcn->type = PAX_DIR; + break; + case C_ISBLK: + arcn->type = PAX_BLK; + break; + case C_ISCHR: + arcn->type = PAX_CHR; + break; + case C_ISLNK: + arcn->type = PAX_SLK; + break; + case C_ISOCK: + arcn->type = PAX_SCK; + break; + case C_ISCTG: + case C_ISREG: + default: + /* + * we have file data, set up skip (pad is set in the format + * specific sections) + */ + arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG; + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + } + if (chk_lnk(arcn) < 0) + return(-1); + return(0); +} + +/* + * cpio_end_wr() + * write the special file with the name trailer in the proper format + * Return: + * result of the write of the trailer from the cpio specific write func + */ + +#ifdef __STDC__ +int +cpio_endwr(void) +#else +int +cpio_endwr() +#endif +{ + ARCHD last; + + /* + * create a trailer request and call the proper format write function + */ + memset(&last, 0, sizeof(last)); + last.nlen = sizeof(TRAILER) - 1; + last.type = PAX_REG; + last.sb.st_nlink = 1; + (void)strcpy(last.name, TRAILER); + return((*frmt->wr)(&last)); +} + +/* + * rd_nam() + * read in the file name which follows the cpio header + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +static int +rd_nm(register ARCHD *arcn, int nsz) +#else +static int +rd_nm(arcn, nsz) + register ARCHD *arcn; + int nsz; +#endif +{ + /* + * do not even try bogus values + */ + if ((nsz == 0) || (nsz > sizeof(arcn->name))) { + paxwarn(1, "Cpio file name length %d is out of range", nsz); + return(-1); + } + + /* + * read the name and make sure it is not empty and is \0 terminated + */ + if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || + (arcn->name[0] == '\0')) { + paxwarn(1, "Cpio file name in header is corrupted"); + return(-1); + } + return(0); +} + +/* + * rd_ln_nm() + * read in the link name for a file with links. The link name is stored + * like file data (and is NOT \0 terminated!) + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +static int +rd_ln_nm(register ARCHD *arcn) +#else +static int +rd_ln_nm(arcn) + register ARCHD *arcn; +#endif +{ + /* + * check the length specified for bogus values + */ + if ((arcn->sb.st_size == 0) || + (arcn->sb.st_size >= sizeof(arcn->ln_name))) { +# ifdef NET2_STAT + paxwarn(1, "Cpio link name length is invalid: %lu", + arcn->sb.st_size); +# else + paxwarn(1, "Cpio link name length is invalid: %qu", + arcn->sb.st_size); +# endif + return(-1); + } + + /* + * read in the link name and \0 terminate it + */ + if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != + (int)arcn->sb.st_size) { + paxwarn(1, "Cpio link name read error"); + return(-1); + } + arcn->ln_nlen = arcn->sb.st_size; + arcn->ln_name[arcn->ln_nlen] = '\0'; + + /* + * watch out for those empty link names + */ + if (arcn->ln_name[0] == '\0') { + paxwarn(1, "Cpio link name is corrupt"); + return(-1); + } + return(0); +} + +/* + * Routines common to the extended byte oriented cpio format + */ + +/* + * cpio_id() + * determine if a block given to us is a valid extended byte oriented + * cpio header + * Return: + * 0 if a valid header, -1 otherwise + */ + +#ifdef __STDC__ +int +cpio_id(char *blk, int size) +#else +int +cpio_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_CPIO)) || + (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * cpio_rd() + * determine if a buffer is a byte oriented extended cpio archive entry. + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#ifdef __STDC__ +int +cpio_rd(register ARCHD *arcn, register char *buf) +#else +int +cpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register int nsz; + register HD_CPIO *hd; + + /* + * check that this is a valid header, if not return -1 + */ + if (cpio_id(buf, sizeof(HD_CPIO)) < 0) + return(-1); + hd = (HD_CPIO *)buf; + + /* + * byte oriented cpio (posix) does not have padding! extract the octal + * ascii fields from the header + */ + arcn->pad = 0L; + arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT); + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT); + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + OCT); + arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime, sizeof(hd->c_mtime), + OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef NET2_STAT + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize,sizeof(hd->c_filesize), + OCT); +# endif + + /* + * check name size and if valid, read in the name of this entry (name + * follows header in the archive) + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * no link name to read for this file + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + return(com_rd(arcn)); + } + + /* + * check link name size and read in the link name. Link names are + * stored like file data. + */ + if (rd_ln_nm(arcn) < 0) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * cpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#ifdef __STDC__ +off_t +cpio_endrd(void) +#else +off_t +cpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER))); +} + +/* + * cpio_stwr() + * start up the device mapping table + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +#ifdef __STDC__ +int +cpio_stwr(void) +#else +int +cpio_stwr() +#endif +{ + return(dev_start()); +} + +/* + * cpio_wr() + * copy the data in the ARCHD to buffer in extended byte oriented cpio + * format. + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#ifdef __STDC__ +int +cpio_wr(register ARCHD *arcn) +#else +int +cpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_CPIO *hd; + register int nsz; + char hdblk[sizeof(HD_CPIO)]; + + /* + * check and repair truncated device and inode fields in the header + */ + if (map_dev(arcn, (u_long)CPIO_MASK, (u_long)CPIO_MASK) < 0) + return(-1); + + arcn->pad = 0L; + nsz = arcn->nlen + 1; + hd = (HD_CPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * set data size for file data + */ +# ifdef NET2_STAT + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) { +# endif + paxwarn(1,"File is too large for cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * set data size to hold link name + */ + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), OCT)) + goto out; + break; + default: + /* + * all other file types have no file data + */ + if (ul_asc((u_long)0, hd->c_filesize, sizeof(hd->c_filesize), + OCT)) + goto out; + break; + } + + /* + * copy the values to the header using octal ascii + */ + if (ul_asc((u_long)MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || + ul_asc((u_long)arcn->sb.st_dev, hd->c_dev, sizeof(hd->c_dev), + OCT) || + ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + OCT) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + OCT) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + OCT) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + OCT) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + OCT) || + ul_asc((u_long)arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), + OCT) || + ul_asc((u_long)arcn->sb.st_mtime,hd->c_mtime,sizeof(hd->c_mtime), + OCT) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) + goto out; + + /* + * write the file name to the archive + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0)) { + paxwarn(1, "Unable to write cpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if this file has data, we are done. The caller will write the file + * data, if we are link tell caller we are done, go to next file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name to the archive, tell the caller to go to the + * next file as we are done. + */ + if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { + paxwarn(1,"Unable to write cpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Cpio header field is too small to store file %s", + arcn->org_name); + return(1); +} + +/* + * Routines common to the system VR4 version of cpio (with/without file CRC) + */ + +/* + * vcpio_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header + * uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +#ifdef __STDC__ +int +vcpio_id(char *blk, int size) +#else +int +vcpio_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_id() + * determine if a block given to us is a valid system VR4 cpio header + * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX + * Return: + * 0 if a valid header, -1 otherwise + */ + +#ifdef __STDC__ +int +crc_id(char *blk, int size) +#else +int +crc_id(blk, size) + char *blk; + int size; +#endif +{ + if ((size < sizeof(HD_VCPIO)) || + (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0)) + return(-1); + return(0); +} + +/* + * crc_strd() + w set file data CRC calculations. Fire up the hard link detection code + * Return: + * 0 if ok -1 otherwise (the return values of lnk_start()) + */ + +#ifdef __STDC__ +int +crc_strd(void) +#else +int +crc_strd() +#endif +{ + docrc = 1; + return(lnk_start()); +} + +/* + * vcpio_rd() + * determine if a buffer is a system VR4 archive entry. (with/without CRC) + * convert and store the values in the ARCHD parameter. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#ifdef __STDC__ +int +vcpio_rd(register ARCHD *arcn, register char *buf) +#else +int +vcpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_VCPIO *hd; + dev_t devminor; + dev_t devmajor; + register int nsz; + + /* + * during the id phase it was determined if we were using CRC, use the + * proper id routine. + */ + if (docrc) { + if (crc_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } else { + if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0) + return(-1); + } + + hd = (HD_VCPIO *)buf; + arcn->pad = 0L; + + /* + * extract the hex ascii fields from the header + */ + arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX); + arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX); + arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX); + arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX); + arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; +# ifdef NET2_STAT + arcn->sb.st_size = (off_t)asc_ul(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# else + arcn->sb.st_size = (off_t)asc_uqd(hd->c_filesize, + sizeof(hd->c_filesize), HEX); +# endif + arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), + HEX); + devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX); + arcn->sb.st_dev = TODEV(devmajor, devminor); + devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_maj), HEX); + devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_min), HEX); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX); + + /* + * check the length of the file name, if ok read it in, return -1 if + * bogus + */ + if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * skip padding. header + filename is aligned to 4 byte boundries + */ + if (rd_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = VCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + /* + * read in the link name and skip over the padding + */ + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(VCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * vcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#ifdef __STDC__ +off_t +vcpio_endrd(void) +#else +off_t +vcpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_VCPIO) + sizeof(TRAILER) + + (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER))))); +} + +/* + * crc_stwr() + * start up the device mapping table, enable crc file calculation + * Return: + * 0 if ok, -1 otherwise (what dev_start() returns) + */ + +#ifdef __STDC__ +int +crc_stwr(void) +#else +int +crc_stwr() +#endif +{ + docrc = 1; + return(dev_start()); +} + +/* + * vcpio_wr() + * copy the data in the ARCHD to buffer in system VR4 cpio + * (with/without crc) format. + * Return + * 0 if file has data to be written after the header, 1 if file has + * NO data to write after the header, -1 if archive write failed + */ + +#ifdef __STDC__ +int +vcpio_wr(register ARCHD *arcn) +#else +int +vcpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_VCPIO *hd; + unsigned int nsz; + char hdblk[sizeof(HD_VCPIO)]; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)VCPIO_MASK, (u_long)VCPIO_MASK) < 0) + return(-1); + nsz = arcn->nlen + 1; + hd = (HD_VCPIO *)hdblk; + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + + /* + * add the proper magic value depending whether we were asked for + * file data crc's, and the crc if needed. + */ + if (docrc) { + if (ul_asc((u_long)VCMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), + HEX)) + goto out; + } else { + if (ul_asc((u_long)VMAGIC, hd->c_magic, sizeof(hd->c_magic), + OCT) || + ul_asc((u_long)0L, hd->c_chksum, sizeof(hd->c_chksum),HEX)) + goto out; + } + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = VCPIO_PAD(arcn->sb.st_size); +# ifdef NET2_STAT + if (ul_asc((u_long)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# else + if (uqd_asc((u_quad_t)arcn->sb.st_size, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) { +# endif + paxwarn(1,"File is too large for sv4cpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + if (ul_asc((u_long)arcn->ln_nlen, hd->c_filesize, + sizeof(hd->c_filesize), HEX)) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + if (ul_asc((u_long)0L, hd->c_filesize, sizeof(hd->c_filesize), + HEX)) + goto out; + break; + } + + /* + * set the other fields in the header + */ + if (ul_asc((u_long)arcn->sb.st_ino, hd->c_ino, sizeof(hd->c_ino), + HEX) || + ul_asc((u_long)arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), + HEX) || + ul_asc((u_long)arcn->sb.st_uid, hd->c_uid, sizeof(hd->c_uid), + HEX) || + ul_asc((u_long)arcn->sb.st_gid, hd->c_gid, sizeof(hd->c_gid), + HEX) || + ul_asc((u_long)arcn->sb.st_mtime, hd->c_mtime, sizeof(hd->c_mtime), + HEX) || + ul_asc((u_long)arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_dev),hd->c_maj, sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_dev),hd->c_min, sizeof(hd->c_min), + HEX) || + ul_asc((u_long)MAJOR(arcn->sb.st_rdev),hd->c_rmaj,sizeof(hd->c_maj), + HEX) || + ul_asc((u_long)MINOR(arcn->sb.st_rdev),hd->c_rmin,sizeof(hd->c_min), + HEX) || + ul_asc((u_long)nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) || + (wr_rdbuf(arcn->name, (int)nsz) < 0) || + (wr_skip((off_t)(VCPIO_PAD(sizeof(HD_VCPIO) + nsz))) < 0)) { + paxwarn(1,"Could not write sv4cpio header for %s",arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done, copy the file + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(VCPIO_PAD(arcn->ln_nlen))) < 0)) { + paxwarn(1,"Could not write sv4cpio link name for %s", + arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1,"Sv4cpio header field is too small for file %s",arcn->org_name); + return(1); +} + +/* + * Routines common to the old binary header cpio + */ + +/* + * bcpio_id() + * determine if a block given to us is a old binary cpio header + * (with/without header byte swapping) + * Return: + * 0 if a valid header, -1 otherwise + */ + +#ifdef __STDC__ +int +bcpio_id(char *blk, int size) +#else +int +bcpio_id(blk, size) + char *blk; + int size; +#endif +{ + if (size < sizeof(HD_BCPIO)) + return(-1); + + /* + * check both normal and byte swapped magic cookies + */ + if (((u_short)SHRT_EXT(blk)) == MAGIC) + return(0); + if (((u_short)RSHRT_EXT(blk)) == MAGIC) { + if (!swp_head) + ++swp_head; + return(0); + } + return(-1); +} + +/* + * bcpio_rd() + * determine if a buffer is a old binary archive entry. (it may have byte + * swapped header) convert and store the values in the ARCHD parameter. + * This is a very old header format and should not really be used. + * Return: + * 0 if a valid header, -1 otherwise. + */ + +#ifdef __STDC__ +int +bcpio_rd(register ARCHD *arcn, register char *buf) +#else +int +bcpio_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_BCPIO *hd; + register int nsz; + + /* + * check the header + */ + if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) + return(-1); + + arcn->pad = 0L; + hd = (HD_BCPIO *)buf; + if (swp_head) { + /* + * header has swapped bytes on 16 bit boundries + */ + arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(RSHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(RSHRT_EXT(hd->h_filesize_2))); + nsz = (int)(RSHRT_EXT(hd->h_namesize)); + } else { + arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); + arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); + arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); + arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); + arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); + arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); + arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); + arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); + arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | + ((time_t)(SHRT_EXT(hd->h_mtime_2))); + arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); + arcn->sb.st_size = (arcn->sb.st_size << 16) | + ((off_t)(SHRT_EXT(hd->h_filesize_2))); + nsz = (int)(SHRT_EXT(hd->h_namesize)); + } + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * check the file name size, if bogus give up. otherwise read the file + * name + */ + if (nsz < 2) + return(-1); + arcn->nlen = nsz - 1; + if (rd_nm(arcn, nsz) < 0) + return(-1); + + /* + * header + file name are aligned to 2 byte boundries, skip if needed + */ + if (rd_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0) + return(-1); + + /* + * if not a link (or a file with no data), calculate pad size (for + * padding which follows the file data), clear the link name and return + */ + if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ + /* + * we have a valid header (not a link) + */ + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + return(com_rd(arcn)); + } + + if ((rd_ln_nm(arcn) < 0) || + (rd_skip((off_t)(BCPIO_PAD(arcn->sb.st_size))) < 0)) + return(-1); + + /* + * we have a valid header (with a link) + */ + return(com_rd(arcn)); +} + +/* + * bcpio_endrd() + * no cleanup needed here, just return size of the trailer (for append) + * Return: + * size of trailer header in this format + */ + +#ifdef __STDC__ +off_t +bcpio_endrd(void) +#else +off_t +bcpio_endrd() +#endif +{ + return((off_t)(sizeof(HD_BCPIO) + sizeof(TRAILER) + + (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER))))); +} + +/* + * bcpio_wr() + * copy the data in the ARCHD to buffer in old binary cpio format + * There is a real chance of field overflow with this critter. So we + * always check the conversion is ok. nobody in his their right mind + * should write an achive in this format... + * Return + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#ifdef __STDC__ +int +bcpio_wr(register ARCHD *arcn) +#else +int +bcpio_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_BCPIO *hd; + register int nsz; + char hdblk[sizeof(HD_BCPIO)]; + off_t t_offt; + int t_int; + time_t t_timet; + + /* + * check and repair truncated device and inode fields in the cpio + * header + */ + if (map_dev(arcn, (u_long)BCPIO_MASK, (u_long)BCPIO_MASK) < 0) + return(-1); + + if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) + arcn->sb.st_rdev = 0; + hd = (HD_BCPIO *)hdblk; + + switch(arcn->type) { + case PAX_CTG: + case PAX_REG: + case PAX_HRG: + /* + * caller will copy file data to the archive. tell him how + * much to pad. + */ + arcn->pad = BCPIO_PAD(arcn->sb.st_size); + hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); + hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); + hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); + hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); + t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); + t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->sb.st_size != t_offt) { + paxwarn(1,"File is too large for bcpio format %s", + arcn->org_name); + return(1); + } + break; + case PAX_SLK: + /* + * no file data for the caller to process, the file data has + * the size of the link + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); + hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); + hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); + hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); + t_int = (int)(SHRT_EXT(hd->h_filesize_1)); + t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); + if (arcn->ln_nlen != t_int) + goto out; + break; + default: + /* + * no file data for the caller to process + */ + arcn->pad = 0L; + hd->h_filesize_1[0] = (char)0; + hd->h_filesize_1[1] = (char)0; + hd->h_filesize_2[0] = (char)0; + hd->h_filesize_2[1] = (char)0; + break; + } + + /* + * build up the rest of the fields + */ + hd->h_magic[0] = CHR_WR_2(MAGIC); + hd->h_magic[1] = CHR_WR_3(MAGIC); + hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); + hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); + if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) + goto out; + hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); + hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); + if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) + goto out; + hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); + hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); + if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) + goto out; + hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); + hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); + if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) + goto out; + hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); + hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); + if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) + goto out; + hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); + hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); + if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) + goto out; + hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); + hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); + if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) + goto out; + hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); + hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); + hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); + hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); + t_timet = (time_t)(SHRT_EXT(hd->h_mtime_1)); + t_timet = (t_timet << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); + if (arcn->sb.st_mtime != t_timet) + goto out; + nsz = arcn->nlen + 1; + hd->h_namesize[0] = CHR_WR_2(nsz); + hd->h_namesize[1] = CHR_WR_3(nsz); + if (nsz != (int)(SHRT_EXT(hd->h_namesize))) + goto out; + + /* + * write the header, the file name and padding as required. + */ + if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) || + (wr_rdbuf(arcn->name, nsz) < 0) || + (wr_skip((off_t)(BCPIO_PAD(sizeof(HD_BCPIO) + nsz))) < 0)) { + paxwarn(1, "Could not write bcpio header for %s", arcn->org_name); + return(-1); + } + + /* + * if we have file data, tell the caller we are done + */ + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG) || + (arcn->type == PAX_HRG)) + return(0); + + /* + * if we are not a link, tell the caller we are done, go to next file + */ + if (arcn->type != PAX_SLK) + return(1); + + /* + * write the link name, tell the caller we are done. + */ + if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || + (wr_skip((off_t)(BCPIO_PAD(arcn->ln_nlen))) < 0)) { + paxwarn(1,"Could not write bcpio link name for %s",arcn->org_name); + return(-1); + } + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1,"Bcpio header field is too small for file %s", arcn->org_name); + return(1); +} diff --git a/pax/cpio.h b/pax/cpio.h new file mode 100644 index 0000000..811f8f3 --- /dev/null +++ b/pax/cpio.h @@ -0,0 +1,154 @@ +/* $OpenBSD: cpio.h,v 1.2 1996/06/23 14:20:32 deraadt Exp $ */ +/* $NetBSD: cpio.h,v 1.3 1995/03/21 09:07:15 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)cpio.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Defines common to all versions of cpio + */ +#define TRAILER "TRAILER!!!" /* name in last archive record */ + +/* + * Header encoding of the different file types + */ +#define C_ISDIR 040000 /* Directory */ +#define C_ISFIFO 010000 /* FIFO */ +#define C_ISREG 0100000 /* Regular file */ +#define C_ISBLK 060000 /* Block special file */ +#define C_ISCHR 020000 /* Character special file */ +#define C_ISCTG 0110000 /* Reserved for contiguous files */ +#define C_ISLNK 0120000 /* Reserved for symbolic links */ +#define C_ISOCK 0140000 /* Reserved for sockets */ +#define C_IFMT 0170000 /* type of file */ + +/* + * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990 + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_dev[6]; /* device number */ + char c_ino[6]; /* inode number */ + char c_mode[6]; /* file type/access */ + char c_uid[6]; /* owners uid */ + char c_gid[6]; /* owners gid */ + char c_nlink[6]; /* # of links at archive creation */ + char c_rdev[6]; /* block/char major/minor # */ + char c_mtime[11]; /* modification time */ + char c_namesize[6]; /* length of pathname */ + char c_filesize[11]; /* length of file in bytes */ +} HD_CPIO; + +#define MAGIC 070707 /* transportable archive id */ + +#ifdef _PAX_ +#define AMAGIC "070707" /* ascii equivalent string of MAGIC */ +#define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */ + /* used for dev/inode remaps */ +#endif /* _PAX_ */ + +/* + * Binary cpio header structure + * + * CAUTION! CAUTION! CAUTION! + * Each field really represents a 16 bit short (NOT ASCII). Described as + * an array of chars in an attempt to improve portability!! + */ +typedef struct { + u_char h_magic[2]; + u_char h_dev[2]; + u_char h_ino[2]; + u_char h_mode[2]; + u_char h_uid[2]; + u_char h_gid[2]; + u_char h_nlink[2]; + u_char h_rdev[2]; + u_char h_mtime_1[2]; + u_char h_mtime_2[2]; + u_char h_namesize[2]; + u_char h_filesize_1[2]; + u_char h_filesize_2[2]; +} HD_BCPIO; + +#ifdef _PAX_ +/* + * extraction and creation macros for binary cpio + */ +#define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff)) +#define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff)) +#define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff)) +#define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff)) +#define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff)) +#define CHR_WR_3(val) ((char)((val) & 0xff)) + +/* + * binary cpio masks and pads + */ +#define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */ +#define BCPIO_MASK 0xffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ + +/* + * System VR4 cpio header structure (with/without file data crc) + */ +typedef struct { + char c_magic[6]; /* magic cookie */ + char c_ino[8]; /* inode number */ + char c_mode[8]; /* file type/access */ + char c_uid[8]; /* owners uid */ + char c_gid[8]; /* owners gid */ + char c_nlink[8]; /* # of links at archive creation */ + char c_mtime[8]; /* modification time */ + char c_filesize[8]; /* length of file in bytes */ + char c_maj[8]; /* block/char major # */ + char c_min[8]; /* block/char minor # */ + char c_rmaj[8]; /* special file major # */ + char c_rmin[8]; /* special file minor # */ + char c_namesize[8]; /* length of pathname */ + char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */ +} HD_VCPIO; + +#define VMAGIC 070701 /* sVr4 new portable archive id */ +#define VCMAGIC 070702 /* sVr4 new portable archive id CRC */ +#ifdef _PAX_ +#define AVMAGIC "070701" /* ascii string of above */ +#define AVCMAGIC "070702" /* ascii string of above */ +#define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */ +#define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */ +#endif /* _PAX_ */ diff --git a/pax/extern.h b/pax/extern.h new file mode 100644 index 0000000..478152c --- /dev/null +++ b/pax/extern.h @@ -0,0 +1,299 @@ +/* $OpenBSD: extern.h,v 1.14 1997/07/24 23:19:18 millert Exp $ */ +/* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * External references from each source file + */ + +#include + +/* + * ar_io.c + */ +extern char *arcname; +extern char *gzip_program; +int ar_open __P((char *)); +void ar_close __P((void)); +void ar_drain __P((void)); +int ar_set_wr __P((void)); +int ar_app_ok __P((void)); +int ar_read __P((register char *, register int)); +int ar_write __P((register char *, register int)); +int ar_rdsync __P((void)); +int ar_fow __P((off_t, off_t *)); +int ar_rev __P((off_t )); +int ar_next __P((void)); + +/* + * ar_subs.c + */ +extern u_long flcnt; +void list __P((void)); +void extract __P((void)); +void append __P((void)); +void archive __P((void)); +void copy __P((void)); + +/* + * buf_subs.c + */ +extern int blksz; +extern int wrblksz; +extern int maxflt; +extern int rdblksz; +extern off_t wrlimit; +extern off_t rdcnt; +extern off_t wrcnt; +int wr_start __P((void)); +int rd_start __P((void)); +void cp_start __P((void)); +int appnd_start __P((off_t)); +int rd_sync __P((void)); +void pback __P((char *, int)); +int rd_skip __P((off_t)); +void wr_fin __P((void)); +int wr_rdbuf __P((register char *, register int)); +int rd_wrbuf __P((register char *, register int)); +int wr_skip __P((off_t)); +int wr_rdfile __P((ARCHD *, int, off_t *)); +int rd_wrfile __P((ARCHD *, int, off_t *)); +void cp_file __P((ARCHD *, int, int)); +int buf_fill __P((void)); +int buf_flush __P((register int)); + +/* + * cache.c + */ +int uidtb_start __P((void)); +int gidtb_start __P((void)); +int usrtb_start __P((void)); +int grptb_start __P((void)); +char * name_uid __P((uid_t, int)); +char * name_gid __P((gid_t, int)); +int uid_name __P((char *, uid_t *)); +int gid_name __P((char *, gid_t *)); + +/* + * cpio.c + */ +int cpio_strd __P((void)); +int cpio_trail __P((register ARCHD *)); +int cpio_endwr __P((void)); +int cpio_id __P((char *, int)); +int cpio_rd __P((register ARCHD *, register char *)); +off_t cpio_endrd __P((void)); +int cpio_stwr __P((void)); +int cpio_wr __P((register ARCHD *)); +int vcpio_id __P((char *, int)); +int crc_id __P((char *, int)); +int crc_strd __P((void)); +int vcpio_rd __P((register ARCHD *, register char *)); +off_t vcpio_endrd __P((void)); +int crc_stwr __P((void)); +int vcpio_wr __P((register ARCHD *)); +int bcpio_id __P((char *, int)); +int bcpio_rd __P((register ARCHD *, register char *)); +off_t bcpio_endrd __P((void)); +int bcpio_wr __P((register ARCHD *)); + +/* + * file_subs.c + */ +int file_creat __P((register ARCHD *)); +void file_close __P((register ARCHD *, int)); +int lnk_creat __P((register ARCHD *)); +int cross_lnk __P((register ARCHD *)); +int chk_same __P((register ARCHD *)); +int node_creat __P((register ARCHD *)); +int unlnk_exist __P((register char *, register int)); +int chk_path __P((register char *, uid_t, gid_t)); +void set_ftime __P((char *fnm, time_t mtime, time_t atime, int frc)); +int set_ids __P((char *, uid_t, gid_t)); +int set_lids __P((char *, uid_t, gid_t)); +void set_pmode __P((char *, mode_t)); +int file_write __P((int, char *, register int, int *, int *, int, char *)); +void file_flush __P((int, char *, int)); +void rdfile_close __P((register ARCHD *, register int *)); +int set_crc __P((register ARCHD *, register int)); + +/* + * ftree.c + */ +int ftree_start __P((void)); +int ftree_add __P((register char *, int)); +void ftree_sel __P((register ARCHD *)); +void ftree_chk __P((void)); +int next_file __P((register ARCHD *)); + +/* + * gen_subs.c + */ +void ls_list __P((register ARCHD *, time_t, FILE *)); +void ls_tty __P((register ARCHD *)); +int l_strncpy __P((register char *, register char *, int)); +u_long asc_ul __P((register char *, int, register int)); +int ul_asc __P((u_long, register char *, register int, register int)); +#ifndef NET2_STAT +u_quad_t asc_uqd __P((register char *, int, register int)); +int uqd_asc __P((u_quad_t, register char *, register int, register int)); +#endif + +/* + * getoldopt.c + */ +int getoldopt __P((int, char **, char *)); + +/* + * options.c + */ +extern FSUB fsub[]; +extern int ford[]; +void options __P((register int, register char **)); +OPLIST * opt_next __P((void)); +int opt_add __P((register char *)); +int bad_opt __P((void)); +extern char *chdname; + +/* + * pat_rep.c + */ +int rep_add __P((register char *)); +int pat_add __P((char *, char *)); +void pat_chk __P((void)); +int pat_sel __P((register ARCHD *)); +int pat_match __P((register ARCHD *)); +int mod_name __P((register ARCHD *)); +int set_dest __P((register ARCHD *, char *, int)); + +/* + * pax.c + */ +extern int act; +extern FSUB *frmt; +extern int cflag; +extern int cwdfd; +extern int dflag; +extern int iflag; +extern int kflag; +extern int lflag; +extern int nflag; +extern int tflag; +extern int uflag; +extern int vflag; +extern int zflag; +extern int Dflag; +extern int Hflag; +extern int Lflag; +extern int Xflag; +extern int Yflag; +extern int Zflag; +extern int vfpart; +extern int patime; +extern int pmtime; +extern int nodirs; +extern int pmode; +extern int pids; +extern int rmleadslash; +extern int exit_val; +extern int docrc; +extern char *dirptr; +extern char *ltmfrmt; +extern char *argv0; +int main __P((int, char **)); +void sig_cleanup __P((int)); + +/* + * sel_subs.c + */ +int sel_chk __P((register ARCHD *)); +int grp_add __P((register char *)); +int usr_add __P((register char *)); +int trng_add __P((register char *)); + +/* + * tables.c + */ +int lnk_start __P((void)); +int chk_lnk __P((register ARCHD *)); +void purg_lnk __P((register ARCHD *)); +void lnk_end __P((void)); +int ftime_start __P((void)); +int chk_ftime __P((register ARCHD *)); +int name_start __P((void)); +int add_name __P((register char *, int, char *)); +void sub_name __P((register char *, int *, size_t)); +int dev_start __P((void)); +int add_dev __P((register ARCHD *)); +int map_dev __P((register ARCHD *, u_long, u_long)); +int atdir_start __P((void)); +void atdir_end __P((void)); +void add_atdir __P((char *, dev_t, ino_t, time_t, time_t)); +int get_atdir __P((dev_t, ino_t, time_t *, time_t *)); +int dir_start __P((void)); +void add_dir __P((char *, int, struct stat *, int)); +void proc_dir __P((void)); +u_int st_hash __P((char *, int, int)); + +/* + * tar.c + */ +int tar_endwr __P((void)); +off_t tar_endrd __P((void)); +int tar_trail __P((register char *, register int, register int *)); +int tar_id __P((register char *, int)); +int tar_opt __P((void)); +int tar_rd __P((register ARCHD *, register char *)); +int tar_wr __P((register ARCHD *)); +int ustar_strd __P((void)); +int ustar_stwr __P((void)); +int ustar_id __P((char *, int)); +int ustar_rd __P((register ARCHD *, register char *)); +int ustar_wr __P((register ARCHD *)); + +/* + * tty_subs.c + */ +int tty_init __P((void)); +void tty_prnt __P((char *, ...)); +int tty_read __P((char *, int)); +void paxwarn __P((int, char *, ...)); +void syswarn __P((int, int, char *, ...)); diff --git a/pax/file_subs.c b/pax/file_subs.c new file mode 100644 index 0000000..2aba0fb --- /dev/null +++ b/pax/file_subs.c @@ -0,0 +1,1112 @@ +/* $OpenBSD: file_subs.c,v 1.13 1997/09/01 18:29:48 deraadt Exp $ */ +/* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)file_subs.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: file_subs.c,v 1.13 1997/09/01 18:29:48 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "extern.h" + +static int +mk_link __P((register char *,register struct stat *,register char *, int)); + +/* + * routines that deal with file operations such as: creating, removing; + * and setting access modes, uid/gid and times of files + */ + +#define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) +#define SETBITS (S_ISUID | S_ISGID) +#define ABITS (FILEBITS | SETBITS) + +/* + * file_creat() + * Create and open a file. + * Return: + * file descriptor or -1 for failure + */ + +#ifdef __STDC__ +int +file_creat(register ARCHD *arcn) +#else +int +file_creat(arcn) + register ARCHD *arcn; +#endif +{ + int fd = -1; + mode_t file_mode; + int oerrno; + + /* + * assume file doesn't exist, so just try to create it, most times this + * works. We have to take special handling when the file does exist. To + * detect this, we use O_EXCL. For example when trying to create a + * file and a character device or fifo exists with the same name, we + * can accidently open the device by mistake (or block waiting to open) + * If we find that the open has failed, then figure spend the effore to + * figure out why. This strategy was found to have better average + * performance in common use than checking the file (and the path) + * first with lstat. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, + file_mode)) >= 0) + return(fd); + + /* + * the file seems to exist. First we try to get rid of it (found to be + * the second most common failure when traced). If this fails, only + * then we go to the expense to check and create the path to the file + */ + if (unlnk_exist(arcn->name, arcn->type) != 0) + return(-1); + + for (;;) { + /* + * try to open it again, if this fails, check all the nodes in + * the path and give it a final try. if chk_path() finds that + * it cannot fix anything, we will skip the last attempt + */ + if ((fd = open(arcn->name, O_WRONLY | O_CREAT | O_TRUNC, + file_mode)) >= 0) + break; + oerrno = errno; + if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { + syswarn(1, oerrno, "Unable to create %s", arcn->name); + return(-1); + } + } + return(fd); +} + +/* + * file_close() + * Close file descriptor to a file just created by pax. Sets modes, + * ownership and times as required. + * Return: + * 0 for success, -1 for failure + */ + +#ifdef __STDC__ +void +file_close(register ARCHD *arcn, int fd) +#else +void +file_close(arcn, fd) + register ARCHD *arcn; + int fd; +#endif +{ + int res = 0; + + if (fd < 0) + return; + if (close(fd) < 0) + syswarn(0, errno, "Unable to close file descriptor on %s", + arcn->name); + + /* + * set owner/groups first as this may strip off mode bits we want + * then set file permission modes. Then set file access and + * modification times. + */ + if (pids) + res = set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid); + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(arcn->name, arcn->sb.st_mode); + if (patime || pmtime) + set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); +} + +/* + * lnk_creat() + * Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name + * must exist; + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +lnk_creat(register ARCHD *arcn) +#else +int +lnk_creat(arcn) + register ARCHD *arcn; +#endif +{ + struct stat sb; + + /* + * we may be running as root, so we have to be sure that link target + * is not a directory, so we lstat and check + */ + if (lstat(arcn->ln_name, &sb) < 0) { + syswarn(1,errno,"Unable to link to %s from %s", arcn->ln_name, + arcn->name); + return(-1); + } + + if (S_ISDIR(sb.st_mode)) { + paxwarn(1, "A hard link to the directory %s is not allowed", + arcn->ln_name); + return(-1); + } + + return(mk_link(arcn->ln_name, &sb, arcn->name, 0)); +} + +/* + * cross_lnk() + * Create a hard link to arcn->org_name from arcn->name. Only used in copy + * with the -l flag. No warning or error if this does not succeed (we will + * then just create the file) + * Return: + * 1 if copy() should try to create this file node + * 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self). + */ + +#ifdef __STDC__ +int +cross_lnk(register ARCHD *arcn) +#else +int +cross_lnk(arcn) + register ARCHD *arcn; +#endif +{ + /* + * try to make a link to orginal file (-l flag in copy mode). make sure + * we do not try to link to directories in case we are running as root + * (and it might succeed). + */ + if (arcn->type == PAX_DIR) + return(1); + return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1)); +} + +/* + * chk_same() + * In copy mode if we are not trying to make hard links between the src + * and destinations, make sure we are not going to overwrite ourselves by + * accident. This slows things down a little, but we have to protect all + * those people who make typing errors. + * Return: + * 1 the target does not exist, go ahead and copy + * 0 skip it file exists (-k) or may be the same as source file + */ + +#ifdef __STDC__ +int +chk_same(register ARCHD *arcn) +#else +int +chk_same(arcn) + register ARCHD *arcn; +#endif +{ + struct stat sb; + + /* + * if file does not exist, return. if file exists and -k, skip it + * quietly + */ + if (lstat(arcn->name, &sb) < 0) + return(1); + if (kflag) + return(0); + + /* + * better make sure the user does not have src == dest by mistake + */ + if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) { + paxwarn(1, "Unable to copy %s, file would overwrite itself", + arcn->name); + return(0); + } + return(1); +} + +/* + * mk_link() + * try to make a hard link between two files. if ign set, we do not + * complain. + * Return: + * 0 if successful (or we are done with this file but no error, such as + * finding the from file exists and the user has set -k). + * 1 when ign was set to indicates we could not make the link but we + * should try to copy/extract the file as that might work (and is an + * allowed option). -1 an error occurred. + */ + +#ifdef __STDC__ +static int +mk_link(register char *to, register struct stat *to_sb, register char *from, + int ign) +#else +static int +mk_link(to, to_sb, from, ign) + register char *to; + register struct stat *to_sb; + register char *from; + int ign; +#endif +{ + struct stat sb; + int oerrno; + + /* + * if from file exists, it has to be unlinked to make the link. If the + * file exists and -k is set, skip it quietly + */ + if (lstat(from, &sb) == 0) { + if (kflag) + return(0); + + /* + * make sure it is not the same file, protect the user + */ + if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) { + paxwarn(1, "Unable to link file %s to itself", to); + return(-1);; + } + + /* + * try to get rid of the file, based on the type + */ + if (S_ISDIR(sb.st_mode)) { + if (rmdir(from) < 0) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + } else if (unlink(from) < 0) { + if (!ign) { + syswarn(1, errno, "Unable to remove %s", from); + return(-1); + } + return(1); + } + } + + /* + * from file is gone (or did not exist), try to make the hard link. + * if it fails, check the path and try it again (if chk_path() says to + * try again) + */ + for (;;) { + if (link(to, from) == 0) + break; + oerrno = errno; + if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0) + continue; + if (!ign) { + syswarn(1, oerrno, "Could not link to %s from %s", to, + from); + return(-1); + } + return(1); + } + + /* + * all right the link was made + */ + return(0); +} + +/* + * node_creat() + * create an entry in the file system (other than a file or hard link). + * If successful, sets uid/gid modes and times as required. + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +node_creat(register ARCHD *arcn) +#else +int +node_creat(arcn) + register ARCHD *arcn; +#endif +{ + register int res; + register int ign = 0; + register int oerrno; + register int pass = 0; + mode_t file_mode; + struct stat sb; + + /* + * create node based on type, if that fails try to unlink the node and + * try again. finally check the path and try again. As noted in the + * file and link creation routines, this method seems to exhibit the + * best performance in general use workloads. + */ + file_mode = arcn->sb.st_mode & FILEBITS; + + for (;;) { + switch(arcn->type) { + case PAX_DIR: + res = mkdir(arcn->name, file_mode); + if (ign) + res = 0; + break; + case PAX_CHR: + file_mode |= S_IFCHR; + res = mknod(arcn->name, file_mode, arcn->sb.st_rdev); + break; + case PAX_BLK: + file_mode |= S_IFBLK; + res = mknod(arcn->name, file_mode, arcn->sb.st_rdev); + break; + case PAX_FIF: + res = mkfifo(arcn->name, file_mode); + break; + case PAX_SCK: + /* + * Skip sockets, operation has no meaning under BSD + */ + paxwarn(0, + "%s skipped. Sockets cannot be copied or extracted", + arcn->name); + return(-1); + case PAX_SLK: + res = symlink(arcn->ln_name, arcn->name); + break; + case PAX_CTG: + case PAX_HLK: + case PAX_HRG: + case PAX_REG: + default: + /* + * we should never get here + */ + paxwarn(0, "%s has an unknown file type, skipping", + arcn->name); + return(-1); + } + + /* + * if we were able to create the node break out of the loop, + * otherwise try to unlink the node and try again. if that + * fails check the full path and try a final time. + */ + if (res == 0) + break; + + /* + * we failed to make the node + */ + oerrno = errno; + if ((ign = unlnk_exist(arcn->name, arcn->type)) < 0) + return(-1); + + if (++pass <= 1) + continue; + + if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { + syswarn(1, oerrno, "Could not create: %s", arcn->name); + return(-1); + } + } + + /* + * we were able to create the node. set uid/gid, modes and times + */ + if (pids) + res = ((arcn->type == PAX_SLK) ? + set_lids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid) : + set_ids(arcn->name, arcn->sb.st_uid, arcn->sb.st_gid)); + else + res = 0; + + /* + * symlinks are done now. + */ + if (arcn->type == PAX_SLK) + return(0); + + /* + * IMPORTANT SECURITY NOTE: + * if not preserving mode or we cannot set uid/gid, then PROHIBIT any + * set uid/gid bits + */ + if (!pmode || res) + arcn->sb.st_mode &= ~(SETBITS); + if (pmode) + set_pmode(arcn->name, arcn->sb.st_mode); + + if (arcn->type == PAX_DIR && strcmp(NM_CPIO, argv0) != 0) { + /* + * Dirs must be processed again at end of extract to set times + * and modes to agree with those stored in the archive. However + * to allow extract to continue, we may have to also set owner + * rights. This allows nodes in the archive that are children + * of this directory to be extracted without failure. Both time + * and modes will be fixed after the entire archive is read and + * before pax exits. + */ + if (access(arcn->name, R_OK | W_OK | X_OK) < 0) { + if (lstat(arcn->name, &sb) < 0) { + syswarn(0, errno,"Could not access %s (stat)", + arcn->name); + set_pmode(arcn->name,file_mode | S_IRWXU); + } else { + /* + * We have to add rights to the dir, so we make + * sure to restore the mode. The mode must be + * restored AS CREATED and not as stored if + * pmode is not set. + */ + set_pmode(arcn->name, + ((sb.st_mode & FILEBITS) | S_IRWXU)); + if (!pmode) + arcn->sb.st_mode = sb.st_mode; + } + + /* + * we have to force the mode to what was set here, + * since we changed it from the default as created. + */ + add_dir(arcn->name, arcn->nlen, &(arcn->sb), 1); + } else if (pmode || patime || pmtime) + add_dir(arcn->name, arcn->nlen, &(arcn->sb), 0); + } + + if (patime || pmtime) + set_ftime(arcn->name, arcn->sb.st_mtime, arcn->sb.st_atime, 0); + return(0); +} + +/* + * unlnk_exist() + * Remove node from file system with the specified name. We pass the type + * of the node that is going to replace it. When we try to create a + * directory and find that it already exists, we allow processing to + * continue as proper modes etc will always be set for it later on. + * Return: + * 0 is ok to proceed, no file with the specified name exists + * -1 we were unable to remove the node, or we should not remove it (-k) + * 1 we found a directory and we were going to create a directory. + */ + +#ifdef __STDC__ +int +unlnk_exist(register char *name, register int type) +#else +int +unlnk_exist(name, type) + register char *name; + register int type; +#endif +{ + struct stat sb; + + /* + * the file does not exist, or -k we are done + */ + if (lstat(name, &sb) < 0) + return(0); + if (kflag) + return(-1); + + if (S_ISDIR(sb.st_mode)) { + /* + * try to remove a directory, if it fails and we were going to + * create a directory anyway, tell the caller (return a 1) + */ + if (rmdir(name) < 0) { + if (type == PAX_DIR) + return(1); + syswarn(1,errno,"Unable to remove directory %s", name); + return(-1); + } + return(0); + } + + /* + * try to get rid of all non-directory type nodes + */ + if (unlink(name) < 0) { + syswarn(1, errno, "Could not unlink %s", name); + return(-1); + } + return(0); +} + +/* + * chk_path() + * We were trying to create some kind of node in the file system and it + * failed. chk_path() makes sure the path up to the node exists and is + * writeable. When we have to create a directory that is missing along the + * path somewhere, the directory we create will be set to the same + * uid/gid as the file has (when uid and gid are being preserved). + * NOTE: this routine is a real performance loss. It is only used as a + * last resort when trying to create entries in the file system. + * Return: + * -1 when it could find nothing it is allowed to fix. + * 0 otherwise + */ + +#ifdef __STDC__ +int +chk_path( register char *name, uid_t st_uid, gid_t st_gid) +#else +int +chk_path(name, st_uid, st_gid) + register char *name; + uid_t st_uid; + gid_t st_gid; +#endif +{ + register char *spt = name; + struct stat sb; + int retval = -1; + + /* + * watch out for paths with nodes stored directly in / (e.g. /bozo) + */ + if (*spt == '/') + ++spt; + + for(;;) { + /* + * work foward from the first / and check each part of the path + */ + spt = strchr(spt, '/'); + if (spt == NULL) + break; + *spt = '\0'; + + /* + * if it exists we assume it is a directory, it is not within + * the spec (at least it seems to read that way) to alter the + * file system for nodes NOT EXPLICITLY stored on the archive. + * If that assumption is changed, you would test the node here + * and figure out how to get rid of it (probably like some + * recursive unlink()) or fix up the directory permissions if + * required (do an access()). + */ + if (lstat(name, &sb) == 0) { + *(spt++) = '/'; + continue; + } + + /* + * the path fails at this point, see if we can create the + * needed directory and continue on + */ + if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { + *spt = '/'; + retval = -1; + break; + } + + /* + * we were able to create the directory. We will tell the + * caller that we found something to fix, and it is ok to try + * and create the node again. + */ + retval = 0; + if (pids) + (void)set_ids(name, st_uid, st_gid); + + /* + * make sure the user doen't have some strange umask that + * causes this newly created directory to be unusable. We fix + * the modes and restore them back to the creation default at + * the end of pax + */ + if ((access(name, R_OK | W_OK | X_OK) < 0) && + (lstat(name, &sb) == 0)) { + set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU)); + add_dir(name, spt - name, &sb, 1); + } + *(spt++) = '/'; + continue; + } + return(retval); +} + +/* + * set_ftime() + * Set the access time and modification time for a named file. If frc is + * non-zero we force these times to be set even if the user did not + * request access and/or modification time preservation (this is also + * used by -t to reset access times). + * When ign is zero, only those times the user has asked for are set, the + * other ones are left alone. We do not assume the un-documented feature + * of many utimes() implementations that consider a 0 time value as a do + * not set request. + */ + +#ifdef __STDC__ +void +set_ftime(char *fnm, time_t mtime, time_t atime, int frc) +#else +void +set_ftime(fnm, mtime, atime, frc) + char *fnm; + time_t mtime; + time_t atime; + int frc; +#endif +{ + static struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; + struct stat sb; + + tv[0].tv_sec = (long)atime; + tv[1].tv_sec = (long)mtime; + if (!frc && (!patime || !pmtime)) { + /* + * if we are not forcing, only set those times the user wants + * set. We get the current values of the times if we need them. + */ + if (lstat(fnm, &sb) == 0) { + if (!patime) + tv[0].tv_sec = (long)sb.st_atime; + if (!pmtime) + tv[1].tv_sec = (long)sb.st_mtime; + } else + syswarn(0,errno,"Unable to obtain file stats %s", fnm); + } + + /* + * set the times + */ + if (utimes(fnm, tv) < 0) + syswarn(1, errno, "Access/modification time set failed on: %s", + fnm); + return; +} + +/* + * set_ids() + * set the uid and gid of a file system node + * Return: + * 0 when set, -1 on failure + */ + +#ifdef __STDC__ +int +set_ids(char *fnm, uid_t uid, gid_t gid) +#else +int +set_ids(fnm, uid, gid) + char *fnm; + uid_t uid; + gid_t gid; +#endif +{ + if (chown(fnm, uid, gid) < 0) { + /* + * ignore EPERM unless in verbose mode or being run by root. + * if running as pax, POSIX requires a warning. + */ + if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag || + geteuid() == 0) + syswarn(1, errno, "Unable to set file uid/gid of %s", + fnm); + return(-1); + } + return(0); +} + +/* + * set_lids() + * set the uid and gid of a file system node + * Return: + * 0 when set, -1 on failure + */ + +#ifdef __STDC__ +int +set_lids(char *fnm, uid_t uid, gid_t gid) +#else +int +set_lids(fnm, uid, gid) + char *fnm; + uid_t uid; + gid_t gid; +#endif +{ +#ifdef __APPLE__ + if (chown(fnm, uid, gid) < 0) { +#else + if (lchown(fnm, uid, gid) < 0) { +#endif + /* + * ignore EPERM unless in verbose mode or being run by root. + * if running as pax, POSIX requires a warning. + */ + if (strcmp(NM_PAX, argv0) == 0 || errno != EPERM || vflag || + geteuid() == 0) + syswarn(1, errno, "Unable to set file uid/gid of %s", + fnm); + return(-1); + } + return(0); +} + +/* + * set_pmode() + * Set file access mode + */ + +#ifdef __STDC__ +void +set_pmode(char *fnm, mode_t mode) +#else +void +set_pmode(fnm, mode) + char *fnm; + mode_t mode; +#endif +{ + mode &= ABITS; + if (chmod(fnm, mode) < 0) + syswarn(1, errno, "Could not set permissions on %s", fnm); + return; +} + +/* + * file_write() + * Write/copy a file (during copy or archive extract). This routine knows + * how to copy files with lseek holes in it. (Which are read as file + * blocks containing all 0's but do not have any file blocks associated + * with the data). Typical examples of these are files created by dbm + * variants (.pag files). While the file size of these files are huge, the + * actual storage is quite small (the files are sparse). The problem is + * the holes read as all zeros so are probably stored on the archive that + * way (there is no way to determine if the file block is really a hole, + * we only know that a file block of all zero's can be a hole). + * At this writing, no major archive format knows how to archive files + * with holes. However, on extraction (or during copy, -rw) we have to + * deal with these files. Without detecting the holes, the files can + * consume a lot of file space if just written to disk. This replacement + * for write when passed the basic allocation size of a file system block, + * uses lseek whenever it detects the input data is all 0 within that + * file block. In more detail, the strategy is as follows: + * While the input is all zero keep doing an lseek. Keep track of when we + * pass over file block boundries. Only write when we hit a non zero + * input. once we have written a file block, we continue to write it to + * the end (we stop looking at the input). When we reach the start of the + * next file block, start checking for zero blocks again. Working on file + * block boundries significantly reduces the overhead when copying files + * that are NOT very sparse. This overhead (when compared to a write) is + * almost below the measurement resolution on many systems. Without it, + * files with holes cannot be safely copied. It does has a side effect as + * it can put holes into files that did not have them before, but that is + * not a problem since the file contents are unchanged (in fact it saves + * file space). (Except on paging files for diskless clients. But since we + * cannot determine one of those file from here, we ignore them). If this + * ever ends up on a system where CTG files are supported and the holes + * are not desired, just do a conditional test in those routines that + * call file_write() and have it call write() instead. BEFORE CLOSING THE + * FILE, make sure to call file_flush() when the last write finishes with + * an empty block. A lot of file systems will not create an lseek hole at + * the end. In this case we drop a single 0 at the end to force the + * trailing 0's in the file. + * ---Parameters--- + * rem: how many bytes left in this file system block + * isempt: have we written to the file block yet (is it empty) + * sz: basic file block allocation size + * cnt: number of bytes on this write + * str: buffer to write + * Return: + * number of bytes written, -1 on write (or lseek) error. + */ + +#ifdef __STDC__ +int +file_write(int fd, char *str, register int cnt, int *rem, int *isempt, int sz, + char *name) +#else +int +file_write(fd, str, cnt, rem, isempt, sz, name) + int fd; + char *str; + register int cnt; + int *rem; + int *isempt; + int sz; + char *name; +#endif +{ + register char *pt; + register char *end; + register int wcnt; + register char *st = str; + + /* + * while we have data to process + */ + while (cnt) { + if (!*rem) { + /* + * We are now at the start of file system block again + * (or what we think one is...). start looking for + * empty blocks again + */ + *isempt = 1; + *rem = sz; + } + + /* + * only examine up to the end of the current file block or + * remaining characters to write, whatever is smaller + */ + wcnt = MIN(cnt, *rem); + cnt -= wcnt; + *rem -= wcnt; + if (*isempt) { + /* + * have not written to this block yet, so we keep + * looking for zero's + */ + pt = st; + end = st + wcnt; + + /* + * look for a zero filled buffer + */ + while ((pt < end) && (*pt == '\0')) + ++pt; + + if (pt == end) { + /* + * skip, buf is empty so far + */ + if (lseek(fd, (off_t)wcnt, SEEK_CUR) < 0) { + syswarn(1,errno,"File seek on %s", + name); + return(-1); + } + st = pt; + continue; + } + /* + * drat, the buf is not zero filled + */ + *isempt = 0; + } + + /* + * have non-zero data in this file system block, have to write + */ + if (write(fd, st, wcnt) != wcnt) { + syswarn(1, errno, "Failed write to file %s", name); + return(-1); + } + st += wcnt; + } + return(st - str); +} + +/* + * file_flush() + * when the last file block in a file is zero, many file systems will not + * let us create a hole at the end. To get the last block with zeros, we + * write the last BYTE with a zero (back up one byte and write a zero). + */ + +#ifdef __STDC__ +void +file_flush(int fd, char *fname, int isempt) +#else +void +file_flush(fd, fname, isempt) + int fd; + char *fname; + int isempt; +#endif +{ + static char blnk[] = "\0"; + + /* + * silly test, but make sure we are only called when the last block is + * filled with all zeros. + */ + if (!isempt) + return; + + /* + * move back one byte and write a zero + */ + if (lseek(fd, (off_t)-1, SEEK_CUR) < 0) { + syswarn(1, errno, "Failed seek on file %s", fname); + return; + } + + if (write(fd, blnk, 1) < 0) + syswarn(1, errno, "Failed write to file %s", fname); + return; +} + +/* + * rdfile_close() + * close a file we have beed reading (to copy or archive). If we have to + * reset access time (tflag) do so (the times are stored in arcn). + */ + +#ifdef __STDC__ +void +rdfile_close(register ARCHD *arcn, register int *fd) +#else +void +rdfile_close(arcn, fd) + register ARCHD *arcn; + register int *fd; +#endif +{ + /* + * make sure the file is open + */ + if (*fd < 0) + return; + + (void)close(*fd); + *fd = -1; + if (!tflag) + return; + + /* + * user wants last access time reset + */ + set_ftime(arcn->org_name, arcn->sb.st_mtime, arcn->sb.st_atime, 1); + return; +} + +/* + * set_crc() + * read a file to calculate its crc. This is a real drag. Archive formats + * that have this, end up reading the file twice (we have to write the + * header WITH the crc before writing the file contents. Oh well... + * Return: + * 0 if was able to calculate the crc, -1 otherwise + */ + +#ifdef __STDC__ +int +set_crc(register ARCHD *arcn, register int fd) +#else +int +set_crc(arcn, fd) + register ARCHD *arcn; + register int fd; +#endif +{ + register int i; + register int res; + off_t cpcnt = 0L; + u_long size; + unsigned long crc = 0L; + char tbuf[FILEBLK]; + struct stat sb; + + if (fd < 0) { + /* + * hmm, no fd, should never happen. well no crc then. + */ + arcn->crc = 0L; + return(0); + } + + if ((size = (u_long)arcn->sb.st_blksize) > (u_long)sizeof(tbuf)) + size = (u_long)sizeof(tbuf); + + /* + * read all the bytes we think that there are in the file. If the user + * is trying to archive an active file, forget this file. + */ + for(;;) { + if ((res = read(fd, tbuf, size)) <= 0) + break; + cpcnt += res; + for (i = 0; i < res; ++i) + crc += (tbuf[i] & 0xff); + } + + /* + * safety check. we want to avoid archiving files that are active as + * they can create inconsistant archive copies. + */ + if (cpcnt != arcn->sb.st_size) + paxwarn(1, "File changed size %s", arcn->org_name); + else if (fstat(fd, &sb) < 0) + syswarn(1, errno, "Failed stat on %s", arcn->org_name); + else if (arcn->sb.st_mtime != sb.st_mtime) + paxwarn(1, "File %s was modified during read", arcn->org_name); + else if (lseek(fd, (off_t)0L, SEEK_SET) < 0) + syswarn(1, errno, "File rewind failed on: %s", arcn->org_name); + else { + arcn->crc = crc; + return(0); + } + return(-1); +} diff --git a/pax/ftree.c b/pax/ftree.c new file mode 100644 index 0000000..c5fcc44 --- /dev/null +++ b/pax/ftree.c @@ -0,0 +1,565 @@ +/* $OpenBSD: ftree.c,v 1.8 1997/09/01 18:29:49 deraadt Exp $ */ +/* $NetBSD: ftree.c,v 1.4 1995/03/21 09:07:21 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ftree.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: ftree.c,v 1.8 1997/09/01 18:29:49 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "ftree.h" +#include "extern.h" + +/* + * routines to interface with the fts library function. + * + * file args supplied to pax are stored on a single linked list (of type FTREE) + * and given to fts to be processed one at a time. pax "selects" files from + * the expansion of each arg into the corresponding file tree (if the arg is a + * directory, otherwise the node itself is just passed to pax). The selection + * is modified by the -n and -u flags. The user is informed when a specific + * file arg does not generate any selected files. -n keeps expanding the file + * tree arg until one of its files is selected, then skips to the next file + * arg. when the user does not supply the file trees as command line args to + * pax, they are read from stdin + */ + +static FTS *ftsp = NULL; /* curent FTS handle */ +static int ftsopts; /* options to be used on fts_open */ +static char *farray[2]; /* array for passing each arg to fts */ +static FTREE *fthead = NULL; /* head of linked list of file args */ +static FTREE *fttail = NULL; /* tail of linked list of file args */ +static FTREE *ftcur = NULL; /* current file arg being processed */ +static FTSENT *ftent = NULL; /* current file tree entry */ +static int ftree_skip; /* when set skip to next file arg */ + +static int ftree_arg __P((void)); + +/* + * ftree_start() + * initialize the options passed to fts_open() during this run of pax + * options are based on the selection of pax options by the user + * fts_start() also calls fts_arg() to open the first valid file arg. We + * also attempt to reset directory access times when -t (tflag) is set. + * Return: + * 0 if there is at least one valid file arg to process, -1 otherwise + */ + +#ifdef __STDC__ +int +ftree_start(void) +#else +int +ftree_start() +#endif +{ + /* + * set up the operation mode of fts, open the first file arg. We must + * use FTS_NOCHDIR, as the user may have to open multiple archives and + * if fts did a chdir off into the boondocks, we may create an archive + * volume in an place where the user did not expect to. + */ + ftsopts = FTS_NOCHDIR; + + /* + * optional user flags that effect file traversal + * -H command line symlink follow only (half follow) + * -L follow sylinks (logical) + * -P do not follow sylinks (physical). This is the default. + * -X do not cross over mount points + * -t preserve access times on files read. + * -n select only the first member of a file tree when a match is found + * -d do not extract subtrees rooted at a directory arg. + */ + if (Lflag) + ftsopts |= FTS_LOGICAL; + else + ftsopts |= FTS_PHYSICAL; + if (Hflag) +# ifdef NET2_FTS + paxwarn(0, "The -H flag is not supported on this version"); +# else + ftsopts |= FTS_COMFOLLOW; +# endif + if (Xflag) + ftsopts |= FTS_XDEV; + + if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { + paxwarn(1, "Unable to allocate memory for file name buffer"); + return(-1); + } + + if (ftree_arg() < 0) + return(-1); + if (tflag && (atdir_start() < 0)) + return(-1); + return(0); +} + +/* + * ftree_add() + * add the arg to the linked list of files to process. Each will be + * processed by fts one at a time + * Return: + * 0 if added to the linked list, -1 if failed + */ + +#ifdef __STDC__ +int +ftree_add(register char *str, int chflg) +#else +int +ftree_add(str, chflg) + register char *str; + int chflg; +#endif +{ + register FTREE *ft; + register int len; + + /* + * simple check for bad args + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(0, "Invalid file name arguement"); + return(-1); + } + + /* + * allocate FTREE node and add to the end of the linked list (args are + * processed in the same order they were passed to pax). Get rid of any + * trailing / the user may pass us. (watch out for / by itself). + */ + if ((ft = (FTREE *)malloc(sizeof(FTREE))) == NULL) { + paxwarn(0, "Unable to allocate memory for filename"); + return(-1); + } + + if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) + str[len] = '\0'; + ft->fname = str; + ft->refcnt = 0; + ft->chflg = chflg; + ft->fow = NULL; + if (fthead == NULL) { + fttail = fthead = ft; + return(0); + } + fttail->fow = ft; + fttail = ft; + return(0); +} + +/* + * ftree_sel() + * this entry has been selected by pax. bump up reference count and handle + * -n and -d processing. + */ + +#ifdef __STDC__ +void +ftree_sel(register ARCHD *arcn) +#else +void +ftree_sel(arcn) + register ARCHD *arcn; +#endif +{ + /* + * set reference bit for this pattern. This linked list is only used + * when file trees are supplied pax as args. The list is not used when + * the trees are read from stdin. + */ + if (ftcur != NULL) + ftcur->refcnt = 1; + + /* + * if -n we are done with this arg, force a skip to the next arg when + * pax asks for the next file in next_file(). + * if -d we tell fts only to match the directory (if the arg is a dir) + * and not the entire file tree rooted at that point. + */ + if (nflag) + ftree_skip = 1; + + if (!dflag || (arcn->type != PAX_DIR)) + return; + + if (ftent != NULL) + (void)fts_set(ftsp, ftent, FTS_SKIP); +} + +/* + * ftree_chk() + * called at end on pax execution. Prints all those file args that did not + * have a selected member (reference count still 0) + */ + +#ifdef __STDC__ +void +ftree_chk(void) +#else +void +ftree_chk() +#endif +{ + register FTREE *ft; + register int wban = 0; + + /* + * make sure all dir access times were reset. + */ + if (tflag) + atdir_end(); + + /* + * walk down list and check reference count. Print out those members + * that never had a match + */ + for (ft = fthead; ft != NULL; ft = ft->fow) { + if ((ft->refcnt > 0) || ft->chflg) + continue; + if (wban == 0) { + paxwarn(1,"WARNING! These file names were not selected:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", ft->fname); + } +} + +/* + * ftree_arg() + * Get the next file arg for fts to process. Can be from either the linked + * list or read from stdin when the user did not them as args to pax. Each + * arg is processed until the first successful fts_open(). + * Return: + * 0 when the next arg is ready to go, -1 if out of file args (or EOF on + * stdin). + */ + +#ifdef __STDC__ +static int +ftree_arg(void) +#else +static int +ftree_arg() +#endif +{ + register char *pt; + + /* + * close off the current file tree + */ + if (ftsp != NULL) { + (void)fts_close(ftsp); + ftsp = NULL; + } + + /* + * keep looping until we get a valid file tree to process. Stop when we + * reach the end of the list (or get an eof on stdin) + */ + for(;;) { + if (fthead == NULL) { + /* + * the user didn't supply any args, get the file trees + * to process from stdin; + */ + if (fgets(farray[0], PAXPATHLEN+1, stdin) == NULL) + return(-1); + if ((pt = strchr(farray[0], '\n')) != NULL) + *pt = '\0'; + } else { + /* + * the user supplied the file args as arguements to pax + */ + if (ftcur == NULL) + ftcur = fthead; + else if ((ftcur = ftcur->fow) == NULL) + return(-1); + if (ftcur->chflg) { + /* First fchdir() back... */ + if (fchdir(cwdfd) < 0) { + syswarn(1, errno, + "Can't fchdir to starting directory"); + return(-1); + } + if (chdir(ftcur->fname) < 0) { + syswarn(1, errno, "Can't chdir to %s", + ftcur->fname); + return(-1); + } + continue; + } else + farray[0] = ftcur->fname; + } + + /* + * watch it, fts wants the file arg stored in a array of char + * ptrs, with the last one a null. we use a two element array + * and set farray[0] to point at the buffer with the file name + * in it. We cannnot pass all the file args to fts at one shot + * as we need to keep a handle on which file arg generates what + * files (the -n and -d flags need this). If the open is + * successful, return a 0. + */ + if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) + break; + } + return(0); +} + +/* + * next_file() + * supplies the next file to process in the supplied archd structure. + * Return: + * 0 when contents of arcn have been set with the next file, -1 when done. + */ + +#ifdef __STDC__ +int +next_file(register ARCHD *arcn) +#else +int +next_file(arcn) + register ARCHD *arcn; +#endif +{ + register int cnt; + time_t atime; + time_t mtime; + + /* + * ftree_sel() might have set the ftree_skip flag if the user has the + * -n option and a file was selected from this file arg tree. (-n says + * only one member is matched for each pattern) ftree_skip being 1 + * forces us to go to the next arg now. + */ + if (ftree_skip) { + /* + * clear and go to next arg + */ + ftree_skip = 0; + if (ftree_arg() < 0) + return(-1); + } + + /* + * loop until we get a valid file to process + */ + for(;;) { + if ((ftent = fts_read(ftsp)) == NULL) { + /* + * out of files in this tree, go to next arg, if none + * we are done + */ + if (ftree_arg() < 0) + return(-1); + continue; + } + + /* + * handle each type of fts_read() flag + */ + switch(ftent->fts_info) { + case FTS_D: + case FTS_DEFAULT: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + /* + * these are all ok + */ + break; + case FTS_DP: + /* + * already saw this directory. If the user wants file + * access times reset, we use this to restore the + * access time for this directory since this is the + * last time we will see it in this file subtree + * remember to force the time (this is -t on a read + * directory, not a created directory). + */ +# ifdef NET2_FTS + if (!tflag || (get_atdir(ftent->fts_statb.st_dev, + ftent->fts_statb.st_ino, &mtime, &atime) < 0)) +# else + if (!tflag || (get_atdir(ftent->fts_statp->st_dev, + ftent->fts_statp->st_ino, &mtime, &atime) < 0)) +# endif + continue; + set_ftime(ftent->fts_path, mtime, atime, 1); + continue; + case FTS_DC: + /* + * fts claims a file system cycle + */ + paxwarn(1,"File system cycle found at %s",ftent->fts_path); + continue; + case FTS_DNR: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "Unable to read directory %s", ftent->fts_path); + continue; + case FTS_ERR: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "File system traversal error"); + continue; + case FTS_NS: + case FTS_NSOK: +# ifdef NET2_FTS + syswarn(1, errno, +# else + syswarn(1, ftent->fts_errno, +# endif + "Unable to access %s", ftent->fts_path); + continue; + } + + /* + * ok got a file tree node to process. copy info into arcn + * structure (initialize as required) + */ + arcn->skip = 0; + arcn->pad = 0; + arcn->ln_nlen = 0; + arcn->ln_name[0] = '\0'; +# ifdef NET2_FTS + arcn->sb = ftent->fts_statb; +# else + arcn->sb = *(ftent->fts_statp); +# endif + + /* + * file type based set up and copy into the arcn struct + * SIDE NOTE: + * we try to reset the access time on all files and directories + * we may read when the -t flag is specified. files are reset + * when we close them after copying. we reset the directories + * when we are done with their file tree (we also clean up at + * end in case we cut short a file tree traversal). However + * there is no way to reset access times on symlinks. + */ + switch(S_IFMT & arcn->sb.st_mode) { + case S_IFDIR: + arcn->type = PAX_DIR; + if (!tflag) + break; + add_atdir(ftent->fts_path, arcn->sb.st_dev, + arcn->sb.st_ino, arcn->sb.st_mtime, + arcn->sb.st_atime); + break; + case S_IFCHR: + arcn->type = PAX_CHR; + break; + case S_IFBLK: + arcn->type = PAX_BLK; + break; + case S_IFREG: + /* + * only regular files with have data to store on the + * archive. all others will store a zero length skip. + * the skip field is used by pax for actual data it has + * to read (or skip over). + */ + arcn->type = PAX_REG; + arcn->skip = arcn->sb.st_size; + break; + case S_IFLNK: + arcn->type = PAX_SLK; + /* + * have to read the symlink path from the file + */ + if ((cnt = readlink(ftent->fts_path, arcn->ln_name, + PAXPATHLEN)) < 0) { + syswarn(1, errno, "Unable to read symlink %s", + ftent->fts_path); + continue; + } + /* + * set link name length, watch out readlink does not + * allways null terminate the link path + */ + arcn->ln_name[cnt] = '\0'; + arcn->ln_nlen = cnt; + break; + case S_IFSOCK: + /* + * under BSD storing a socket is senseless but we will + * let the format specific write function make the + * decision of what to do with it. + */ + arcn->type = PAX_SCK; + break; + case S_IFIFO: + arcn->type = PAX_FIF; + break; + } + break; + } + + /* + * copy file name, set file name length + */ + arcn->nlen = l_strncpy(arcn->name, ftent->fts_path, sizeof(arcn->name) - 1); + arcn->name[arcn->nlen] = '\0'; + arcn->org_name = ftent->fts_path; + return(0); +} diff --git a/pax/ftree.h b/pax/ftree.h new file mode 100644 index 0000000..5ac8ff9 --- /dev/null +++ b/pax/ftree.h @@ -0,0 +1,54 @@ +/* $OpenBSD: ftree.h,v 1.3 1996/10/27 06:45:11 downsj Exp $ */ +/* $NetBSD: ftree.h,v 1.3 1995/03/21 09:07:23 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)ftree.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * Data structure used by the ftree.c routines to store the file args to be + * handed to fts(). It keeps a reference count of which args generated a + * "selected" member + */ + +typedef struct ftree { + char *fname; /* file tree name */ + int refcnt; /* has tree had a selected file? */ + int chflg; /* change directory flag */ + struct ftree *fow; /* pointer to next entry on list */ +} FTREE; diff --git a/pax/gen_subs.c b/pax/gen_subs.c new file mode 100644 index 0000000..4f5f898 --- /dev/null +++ b/pax/gen_subs.c @@ -0,0 +1,467 @@ +/* $OpenBSD: gen_subs.c,v 1.8 1997/09/01 18:29:51 deraadt Exp $ */ +/* $NetBSD: gen_subs.c,v 1.5 1995/03/21 09:07:26 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)gen_subs.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: gen_subs.c,v 1.8 1997/09/01 18:29:51 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" + +/* + * a collection of general purpose subroutines used by pax + */ + +/* + * constants used by ls_list() when printing out archive members + */ +#define MODELEN 20 +#define DATELEN 64 +#define SIXMONTHS ((DAYSPERNYEAR / 2) * SECSPERDAY) +#define CURFRMT "%b %e %H:%M" +#define OLDFRMT "%b %e %Y" +#ifndef UT_NAMESIZE +#define UT_NAMESIZE 8 +#endif +#define UT_GRPSIZE 6 + +/* + * ls_list() + * list the members of an archive in ls format + */ + +#ifdef __STDC__ +void +ls_list(register ARCHD *arcn, time_t now, FILE *fp) +#else +void +ls_list(arcn, now, fp) + register ARCHD *arcn; + time_t now; + FILE *fp; +#endif +{ + register struct stat *sbp; + char f_mode[MODELEN]; + char f_date[DATELEN]; + char *timefrmt; + + /* + * if not verbose, just print the file name + */ + if (!vflag) { + (void)fprintf(fp, "%s\n", arcn->name); + (void)fflush(fp); + return; + } + + /* + * user wants long mode + */ + sbp = &(arcn->sb); + strmode(sbp->st_mode, f_mode); + + if (ltmfrmt == NULL) { + /* + * no locale specified format. time format based on age + * compared to the time pax was started. + */ + if ((sbp->st_mtime + SIXMONTHS) <= now) + timefrmt = OLDFRMT; + else + timefrmt = CURFRMT; + } else + timefrmt = ltmfrmt; + + /* + * print file mode, link count, uid, gid and time + */ + if (strftime(f_date,DATELEN,timefrmt,localtime((const time_t *)&(sbp->st_mtime))) == 0) + f_date[0] = '\0'; + (void)fprintf(fp, "%s%2u %-*s %-*s ", f_mode, sbp->st_nlink, + UT_NAMESIZE, name_uid(sbp->st_uid, 1), UT_GRPSIZE, + name_gid(sbp->st_gid, 1)); + + /* + * print device id's for devices, or sizes for other nodes + */ + if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK)) +# ifdef NET2_STAT + (void)fprintf(fp, "%4u,%4u ", MAJOR(sbp->st_rdev), +# else + (void)fprintf(fp, "%4lu,%4lu ", (unsigned long)MAJOR(sbp->st_rdev), +# endif + (unsigned long)MINOR(sbp->st_rdev)); + else { +# ifdef NET2_STAT + (void)fprintf(fp, "%9lu ", sbp->st_size); +# else + (void)fprintf(fp, "%9qu ", sbp->st_size); +# endif + } + + /* + * print name and link info for hard and soft links + */ + (void)fprintf(fp, "%s %s", f_date, arcn->name); + if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + (void)fprintf(fp, " == %s\n", arcn->ln_name); + else if (arcn->type == PAX_SLK) + (void)fprintf(fp, " => %s\n", arcn->ln_name); + else + (void)putc('\n', fp); + (void)fflush(fp); + return; +} + +/* + * tty_ls() + * print a short summary of file to tty. + */ + +#ifdef __STDC__ +void +ls_tty(register ARCHD *arcn) +#else +void +ls_tty(arcn) + register ARCHD *arcn; +#endif +{ + char f_date[DATELEN]; + char f_mode[MODELEN]; + char *timefrmt; + + if (ltmfrmt == NULL) { + /* + * no locale specified format + */ + if ((arcn->sb.st_mtime + SIXMONTHS) <= time(NULL)) + timefrmt = OLDFRMT; + else + timefrmt = CURFRMT; + } else + timefrmt = ltmfrmt; + + /* + * convert time to string, and print + */ + if (strftime(f_date, DATELEN, timefrmt, + localtime((const time_t *)&(arcn->sb.st_mtime))) == 0) + f_date[0] = '\0'; + strmode(arcn->sb.st_mode, f_mode); + tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name); + return; +} + +/* + * l_strncpy() + * copy src to dest up to len chars (stopping at first '\0'). + * when src is shorter than len, pads to len with '\0'. + * Return: + * number of chars copied. (Note this is a real performance win over + * doing a strncpy(), a strlen(), and then a possible memset()) + */ + +#ifdef __STDC__ +int +l_strncpy(register char *dest, register char *src, int len) +#else +int +l_strncpy(dest, src, len) + register char *dest; + register char *src; + int len; +#endif +{ + register char *stop; + register char *start; + + stop = dest + len; + start = dest; + while ((dest < stop) && (*src != '\0')) + *dest++ = *src++; + len = dest - start; + while (dest < stop) + *dest++ = '\0'; + return(len); +} + +/* + * asc_ul() + * convert hex/octal character string into a u_long. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * unsigned long value + */ + +#ifdef __STDC__ +u_long +asc_ul(register char *str, int len, register int base) +#else +u_long +asc_ul(str, len, base) + register char *str; + int len; + register int base; +#endif +{ + register char *stop; + u_long tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * ul_asc() + * convert an unsigned long into an hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +#ifdef __STDC__ +int +ul_asc(u_long val, register char *str, register int len, register int base) +#else +int +ul_asc(val, str, len, base) + u_long val; + register char *str; + register int len; + register int base; +#endif +{ + register char *pt; + u_long digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_long)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_long)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef NET2_STAT +/* + * asc_uqd() + * convert hex/octal character string into a u_quad_t. We do not have to + * check for overflow! (the headers in all supported formats are not large + * enough to create an overflow). + * NOTE: strings passed to us are NOT TERMINATED. + * Return: + * u_quad_t value + */ + +#ifdef __STDC__ +u_quad_t +asc_uqd(register char *str, int len, register int base) +#else +u_quad_t +asc_uqd(str, len, base) + register char *str; + int len; + register int base; +#endif +{ + register char *stop; + u_quad_t tval = 0; + + stop = str + len; + + /* + * skip over leading blanks and zeros + */ + while ((str < stop) && ((*str == ' ') || (*str == '0'))) + ++str; + + /* + * for each valid digit, shift running value (tval) over to next digit + * and add next digit + */ + if (base == HEX) { + while (str < stop) { + if ((*str >= '0') && (*str <= '9')) + tval = (tval << 4) + (*str++ - '0'); + else if ((*str >= 'A') && (*str <= 'F')) + tval = (tval << 4) + 10 + (*str++ - 'A'); + else if ((*str >= 'a') && (*str <= 'f')) + tval = (tval << 4) + 10 + (*str++ - 'a'); + else + break; + } + } else { + while ((str < stop) && (*str >= '0') && (*str <= '7')) + tval = (tval << 3) + (*str++ - '0'); + } + return(tval); +} + +/* + * uqd_asc() + * convert an u_quad_t into a hex/oct ascii string. pads with LEADING + * ascii 0's to fill string completely + * NOTE: the string created is NOT TERMINATED. + */ + +#ifdef __STDC__ +int +uqd_asc(u_quad_t val, register char *str, register int len, register int base) +#else +int +uqd_asc(val, str, len, base) + u_quad_t val; + register char *str; + register int len; + register int base; +#endif +{ + register char *pt; + u_quad_t digit; + + /* + * WARNING str is not '\0' terminated by this routine + */ + pt = str + len - 1; + + /* + * do a tailwise conversion (start at right most end of string to place + * least significant digit). Keep shifting until conversion value goes + * to zero (all digits were converted) + */ + if (base == HEX) { + while (pt >= str) { + if ((digit = (val & 0xf)) < 10) + *pt-- = '0' + (char)digit; + else + *pt-- = 'a' + (char)(digit - 10); + if ((val = (val >> 4)) == (u_quad_t)0) + break; + } + } else { + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = (val >> 3)) == (u_quad_t)0) + break; + } + } + + /* + * pad with leading ascii ZEROS. We return -1 if we ran out of space. + */ + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif diff --git a/pax/getoldopt.c b/pax/getoldopt.c new file mode 100644 index 0000000..8a15a2f --- /dev/null +++ b/pax/getoldopt.c @@ -0,0 +1,73 @@ +/* $OpenBSD: getoldopt.c,v 1.3 1997/09/01 18:29:52 deraadt Exp $ */ +/* $NetBSD: getoldopt.c,v 1.3 1995/03/21 09:07:28 cgd Exp $ */ + +/* + * Plug-compatible replacement for getopt() for parsing tar-like + * arguments. If the first argument begins with "-", it uses getopt; + * otherwise, it uses the old rules used by tar, dump, and ps. + * + * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and placed + * in the Pubic Domain for your edification and enjoyment. + */ + +#ifndef lint +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: getoldopt.c,v 1.3 1997/09/01 18:29:52 deraadt Exp $"; +#endif /* not lint */ + +#include +#include +#include + +int +getoldopt(argc, argv, optstring) + int argc; + char **argv; + char *optstring; +{ + extern char *optarg; /* Points to next arg */ + extern int optind; /* Global argv index */ + static char *key; /* Points to next keyletter */ + static char use_getopt; /* !=0 if argv[1][0] was '-' */ + char c; + char *place; + + optarg = NULL; + + if (key == NULL) { /* First time */ + if (argc < 2) return EOF; + key = argv[1]; + if (*key == '-') + use_getopt++; + else + optind = 2; + } + + if (use_getopt) + return getopt(argc, argv, optstring); + + c = *key++; + if (c == '\0') { + key--; + return EOF; + } + place = strchr(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option %c\n", argv[0], c); + return('?'); + } + + place++; + if (*place == ':') { + if (optind < argc) { + optarg = argv[optind]; + optind++; + } else { + fprintf(stderr, "%s: %c argument missing\n", + argv[0], c); + return('?'); + } + } + + return(c); +} diff --git a/pax/options.c b/pax/options.c new file mode 100644 index 0000000..9a64e1f --- /dev/null +++ b/pax/options.c @@ -0,0 +1,1515 @@ +/* $OpenBSD: options.c,v 1.31 1998/01/22 06:21:29 millert Exp $ */ +/* $NetBSD: options.c,v 1.6 1996/03/26 23:54:18 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: options.c,v 1.31 1998/01/22 06:21:29 millert Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "options.h" +#include "cpio.h" +#include "tar.h" +#include "extern.h" + +/* + * Routines which handle command line options + */ + +static char flgch[] = FLGCH; /* list of all possible flags */ +static OPLIST *ophead = NULL; /* head for format specific options -x */ +static OPLIST *optail = NULL; /* option tail */ + +static int no_op __P((void)); +static void printflg __P((unsigned int)); +static int c_frmt __P((const void *, const void *)); +static off_t str_offt __P((char *)); +static void pax_options __P((register int, register char **)); +static void pax_usage __P((void)); +static void tar_options __P((register int, register char **)); +static void tar_usage __P((void)); +static void cpio_options __P((register int, register char **)); +static void cpio_usage __P((void)); + +#define GZIP_CMD "gzip" /* command to run as gzip */ +#define COMPRESS_CMD "compress" /* command to run as compress */ + +/* + * Format specific routine table - MUST BE IN SORTED ORDER BY NAME + * (see pax.h for description of each function) + * + * name, blksz, hdsz, udev, hlk, blkagn, inhead, id, st_read, + * read, end_read, st_write, write, end_write, trail, + * rd_data, wr_data, options + */ + +FSUB fsub[] = { +/* 0: OLD BINARY CPIO */ + {"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, 1, bcpio_id, cpio_strd, + bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* 1: OLD OCTAL CHARACTER CPIO */ + {"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, 1, cpio_id, cpio_strd, + cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* 2: SVR4 HEX CPIO */ + {"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, vcpio_id, cpio_strd, + vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* 3: SVR4 HEX CPIO WITH CRC */ + {"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, 1, crc_id, crc_strd, + vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, + rd_wrfile, wr_rdfile, bad_opt}, + +/* 4: OLD TAR */ + {"tar", 10240, BLKMULT, 0, 1, BLKMULT, 0, tar_id, no_op, + tar_rd, tar_endrd, no_op, tar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, tar_opt}, + +/* 5: POSIX USTAR */ + {"ustar", 10240, BLKMULT, 0, 1, BLKMULT, 0, ustar_id, ustar_strd, + ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail, + rd_wrfile, wr_rdfile, bad_opt}, +}; +#define F_OCPIO 0 /* format when called as cpio -6 */ +#define F_ACPIO 1 /* format when called as cpio -c */ +#define F_CPIO 3 /* format when called as cpio */ +#define F_OTAR 4 /* format when called as tar -o */ +#define F_TAR 5 /* format when called as tar */ +#define DEFLT 5 /* default write format from list above */ + +/* + * ford is the archive search order used by get_arc() to determine what kind + * of archive we are dealing with. This helps to properly id archive formats + * some formats may be subsets of others.... + */ +int ford[] = {5, 4, 3, 2, 1, 0, -1 }; + +/* + * options() + * figure out if we are pax, tar or cpio. Call the appropriate options + * parser + */ + +#ifdef __STDC__ +void +options(register int argc, register char **argv) +#else +void +options(argc, argv) + register int argc; + register char **argv; +#endif +{ + + /* + * Are we acting like pax, tar or cpio (based on argv[0]) + */ + if ((argv0 = strrchr(argv[0], '/')) != NULL) + argv0++; + else + argv0 = argv[0]; + + if (strcmp(NM_TAR, argv0) == 0) + return(tar_options(argc, argv)); + else if (strcmp(NM_CPIO, argv0) == 0) + return(cpio_options(argc, argv)); + /* + * assume pax as the default + */ + argv0 = NM_PAX; + return(pax_options(argc, argv)); +} + +/* + * pax_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#ifdef __STDC__ +static void +pax_options(register int argc, register char **argv) +#else +static void +pax_options(argc, argv) + register int argc; + register char **argv; +#endif +{ + register int c; + register int i; + unsigned int flg = 0; + unsigned int bflg = 0; + register char *pt; + FSUB tmp; + extern char *optarg; + extern int optind; + + /* + * process option flags + */ + while ((c=getopt(argc,argv,"ab:cdf:iklno:p:rs:tuvwx:zB:DE:G:HLPT:U:XYZ")) + != EOF) { + switch (c) { + case 'a': + /* + * append + */ + flg |= AF; + break; + case 'b': + /* + * specify blocksize + */ + flg |= BF; + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid block size %s", optarg); + pax_usage(); + } + break; + case 'c': + /* + * inverse match on patterns + */ + cflag = 1; + flg |= CF; + break; + case 'd': + /* + * match only dir on extract, not the subtree at dir + */ + dflag = 1; + flg |= DF; + break; + case 'f': + /* + * filename where the archive is stored + */ + arcname = optarg; + flg |= FF; + break; + case 'i': + /* + * interactive file rename + */ + iflag = 1; + flg |= IF; + break; + case 'k': + /* + * do not clobber files that exist + */ + kflag = 1; + flg |= KF; + break; + case 'l': + /* + * try to link src to dest with copy (-rw) + */ + lflag = 1; + flg |= LF; + break; + case 'n': + /* + * select first match for a pattern only + */ + nflag = 1; + flg |= NF; + break; + case 'o': + /* + * pass format specific options + */ + flg |= OF; + if (opt_add(optarg) < 0) + pax_usage(); + break; + case 'p': + /* + * specify file characteristic options + */ + for (pt = optarg; *pt != '\0'; ++pt) { + switch(*pt) { + case 'a': + /* + * do not preserve access time + */ + patime = 0; + break; + case 'e': + /* + * preserve user id, group id, file + * mode, access/modification times + */ + pids = 1; + pmode = 1; + patime = 1; + pmtime = 1; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'o': + /* + * preserve uid/gid + */ + pids = 1; + break; + case 'p': + /* + * preserver file mode bits + */ + pmode = 1; + break; + default: + paxwarn(1, "Invalid -p string: %c", *pt); + pax_usage(); + break; + } + } + flg |= PF; + break; + case 'r': + /* + * read the archive + */ + flg |= RF; + break; + case 's': + /* + * file name substitution name pattern + */ + if (rep_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= SF; + break; + case 't': + /* + * preserve access time on filesystem nodes we read + */ + tflag = 1; + flg |= TF; + break; + case 'u': + /* + * ignore those older files + */ + uflag = 1; + flg |= UF; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + flg |= VF; + break; + case 'w': + /* + * write an archive + */ + flg |= WF; + break; + case 'x': + /* + * specify an archive format on write + */ + tmp.name = optarg; + if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, + sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) { + flg |= XF; + break; + } + paxwarn(1, "Unknown -x format: %s", optarg); + (void)fputs("pax: Known -x formats are:", stderr); + for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) + (void)fprintf(stderr, " %s", fsub[i].name); + (void)fputs("\n\n", stderr); + pax_usage(); + break; + case 'z': + /* + * use gzip. Non standard option. + */ + zflag = 1; + gzip_program = GZIP_CMD; + break; + case 'B': + /* + * non-standard option on number of bytes written on a + * single archive volume. + */ + if ((wrlimit = str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid write limit %s", optarg); + pax_usage(); + } + if (wrlimit % BLKMULT) { + paxwarn(1, "Write limit is not a %d byte multiple", + BLKMULT); + pax_usage(); + } + flg |= CBF; + break; + case 'D': + /* + * On extraction check file inode change time before the + * modification of the file name. Non standard option. + */ + Dflag = 1; + flg |= CDF; + break; + case 'E': + /* + * non-standard limit on read faults + * 0 indicates stop after first error, values + * indicate a limit, "NONE" try forever + */ + flg |= CEF; + if (strcmp(NONE, optarg) == 0) + maxflt = -1; + else if ((maxflt = atoi(optarg)) < 0) { + paxwarn(1, "Error count value must be positive"); + pax_usage(); + } + break; + case 'G': + /* + * non-standard option for selecting files within an + * archive by group (gid or name) + */ + if (grp_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CGF; + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + flg |= CHF; + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + flg |= CLF; + break; + case 'P': + /* + * do NOT follow symlinks (default) + */ + Lflag = 0; + flg |= CPF; + break; + case 'T': + /* + * non-standard option for selecting files within an + * archive by modification time range (lower,upper) + */ + if (trng_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CTF; + break; + case 'U': + /* + * non-standard option for selecting files within an + * archive by user (uid or name) + */ + if (usr_add(optarg) < 0) { + pax_usage(); + break; + } + flg |= CUF; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + flg |= CXF; + break; + case 'Y': + /* + * On extraction check file inode change time after the + * modification of the file name. Non standard option. + */ + Yflag = 1; + flg |= CYF; + break; + case 'Z': + /* + * On extraction check modification time after the + * modification of the file name. Non standard option. + */ + Zflag = 1; + flg |= CZF; + break; + default: + pax_usage(); + break; + } + } + + /* + * figure out the operation mode of pax read,write,extract,copy,append + * or list. check that we have not been given a bogus set of flags + * for the operation mode. + */ + if (ISLIST(flg)) { + act = LIST; + bflg = flg & BDLIST; + } else if (ISEXTRACT(flg)) { + act = EXTRACT; + bflg = flg & BDEXTR; + } else if (ISARCHIVE(flg)) { + act = ARCHIVE; + bflg = flg & BDARCH; + } else if (ISAPPND(flg)) { + act = APPND; + bflg = flg & BDARCH; + } else if (ISCOPY(flg)) { + act = COPY; + bflg = flg & BDCOPY; + } else + pax_usage(); + if (bflg) { + printflg(flg); + pax_usage(); + } + + /* + * if we are writing (ARCHIVE) we use the default format if the user + * did not specify a format. when we write during an APPEND, we will + * adopt the format of the existing archive if none was supplied. + */ + if (!(flg & XF) && (act == ARCHIVE)) + frmt = &(fsub[DEFLT]); + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + for (; optind < argc; optind++) + if (pat_add(argv[optind], NULL) < 0) + pax_usage(); + break; + case COPY: + if (optind >= argc) { + paxwarn(0, "Destination directory was not supplied"); + pax_usage(); + } + --argc; + dirptr = argv[argc]; + /* FALL THROUGH */ + case ARCHIVE: + case APPND: + for (; optind < argc; optind++) + if (ftree_add(argv[optind], 0) < 0) + pax_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } +} + + +/* + * tar_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#ifdef __STDC__ +static void +tar_options(register int argc, register char **argv) +#else +static void +tar_options(argc, argv) + register int argc; + register char **argv; +#endif +{ + register int c; + int fstdin = 0; + int Oflag = 0; + + /* + * Set default values. + */ + rmleadslash = 1; + + /* + * process option flags + */ + while ((c = getoldopt(argc, argv, + "b:cef:hmopruts:vwxzBC:HLOPXZ014578")) + != EOF) { + switch(c) { + case 'b': + /* + * specify blocksize in 512-byte blocks + */ + if ((wrblksz = (int)str_offt(optarg)) <= 0) { + paxwarn(1, "Invalid block size %s", optarg); + tar_usage(); + } + wrblksz *= 512; /* XXX - check for int oflow */ + break; + case 'c': + /* + * create an archive + */ + act = ARCHIVE; + break; + case 'e': + /* + * stop after first error + */ + maxflt = 0; + break; + case 'f': + /* + * filename where the archive is stored + */ + if ((optarg[0] == '-') && (optarg[1]== '\0')) { + /* + * treat a - as stdin + */ + fstdin = 1; + arcname = NULL; + break; + } + fstdin = 0; + arcname = optarg; + break; + case 'h': + /* + * follow symlinks + */ + Lflag = 1; + break; + case 'm': + /* + * do not preserve modification time + */ + pmtime = 0; + break; + case 'o': + if (opt_add("write_opt=nodir") < 0) + tar_usage(); + case 'O': + Oflag = 1; + break; + case 'p': + /* + * preserve user id, group id, file + * mode, access/modification times + */ + pids = 1; + pmode = 1; + patime = 1; + pmtime = 1; + break; + case 'r': + case 'u': + /* + * append to the archive + */ + act = APPND; + break; + case 's': + /* + * file name substitution name pattern + */ + if (rep_add(optarg) < 0) { + tar_usage(); + break; + } + break; + case 't': + /* + * list contents of the tape + */ + act = LIST; + break; + case 'v': + /* + * verbose operation mode + */ + vflag++; + break; + case 'w': + /* + * interactive file rename + */ + iflag = 1; + break; + case 'x': + /* + * write an archive, preserve ids if root + */ + act = EXTRACT; + if (geteuid() == 0) + pids = 1; + break; + case 'z': + /* + * use gzip. Non standard option. + */ + zflag = 1; + gzip_program = GZIP_CMD; + break; + case 'B': + /* + * Nothing to do here, this is pax default + */ + break; + case 'C': + chdname = optarg; + break; + case 'H': + /* + * follow command line symlinks only + */ + Hflag = 1; + break; + case 'L': + /* + * follow symlinks + */ + Lflag = 1; + break; + case 'P': + /* + * do not remove leading '/' from pathnames + */ + rmleadslash = 0; + break; + case 'X': + /* + * do not pass over mount points in the file system + */ + Xflag = 1; + break; + case 'Z': + /* + * use compress. + */ + zflag = 1; + gzip_program = COMPRESS_CMD; + break; + case '0': + arcname = DEV_0; + break; + case '1': + arcname = DEV_1; + break; + case '4': + arcname = DEV_4; + break; + case '5': + arcname = DEV_5; + break; + case '7': + arcname = DEV_7; + break; + case '8': + arcname = DEV_8; + break; + default: + tar_usage(); + break; + } + } + argc -= optind; + argv += optind; + + /* Traditional tar behaviour (pax wants to read filelist from stdin) */ + if ((act == ARCHIVE || act == APPND) && argc == 0) + exit(0); + + /* + * if we are writing (ARCHIVE) specify tar, otherwise run like pax + * (unless -o specified) + */ + if (act == ARCHIVE || act == APPND) + frmt = &(fsub[Oflag ? F_OTAR : F_TAR]); + else if (Oflag) { + paxwarn(1, "The -O/-o options are only valid when writing an archive"); + tar_usage(); /* only valid when writing */ + } + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + default: + { + int sawpat = 0; + + while (*argv != NULL) { + if (strcmp(*argv, "-C") == 0) { + if(*++argv == NULL) + break; + chdname = *argv++; + + continue; + } + if (pat_add(*argv++, chdname) < 0) + tar_usage(); + sawpat++; + } + /* + * if patterns were added, we are doing chdir() + * on a file-by-file basis, else, just one + * global chdir (if any) after opening input. + */ + if (sawpat > 0) + chdname = NULL; + } + break; + case ARCHIVE: + case APPND: + if (chdname != NULL) { /* initial chdir() */ + if (ftree_add(chdname, 1) < 0) + tar_usage(); + } + + while (*argv != NULL) { + if (!strcmp(*argv, "-C")) { + if (*++argv == NULL) + break; + if (ftree_add(*argv++, 1) < 0) + tar_usage(); + } else { + if (ftree_add(*argv++, 0) < 0) + tar_usage(); + } + } + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + break; + } + if (!fstdin && ((arcname == NULL) || (*arcname == '\0'))) { + arcname = getenv("TAPE"); + if ((arcname == NULL) || (*arcname == '\0')) + arcname = _PATH_DEFTAPE; + } +} + +int +mkpath(path) + char *path; +{ + struct stat sb; + register char *slash; + int done = 0; + + slash = path; + + while (!done) { + slash += strspn(slash, "/"); + slash += strcspn(slash, "/"); + + done = (*slash == '\0'); + *slash = '\0'; + + if (stat(path, &sb)) { + if (errno != ENOENT || mkdir(path, 0777)) { + paxwarn(1, "%s", path); + return (-1); + } + } else if (!S_ISDIR(sb.st_mode)) { + syswarn(1, ENOTDIR, "%s", path); + return (-1); + } + + if (!done) + *slash = '/'; + } + + return (0); +} +/* + * cpio_options() + * look at the user specified flags. set globals as required and check if + * the user specified a legal set of flags. If not, complain and exit + */ + +#ifdef __STDC__ +static void +cpio_options(register int argc, register char **argv) +#else +static void +cpio_options(argc, argv) + register int argc; + register char **argv; +#endif +{ + register int c, i; + size_t len; + char *str; + FSUB tmp; + FILE *fp; + + kflag = 1; + pids = 1; + pmode = 1; + pmtime = 0; + arcname = NULL; + dflag = 1; + act = -1; + nodirs = 1; + while ((c=getopt(argc,argv,"abcdfiklmoprstuvzABC:E:F:H:I:LO:SZ6")) != EOF) + switch (c) { + case 'a': + /* + * preserve access time on files read + */ + tflag = 1; + break; + case 'b': + /* + * swap bytes and half-words when reading data + */ + break; + case 'c': + /* + * ASCII cpio header + */ + frmt = &(fsub[F_ACPIO]); + break; + case 'd': + /* + * create directories as needed + */ + nodirs = 0; + break; + case 'f': + /* + * invert meaning of pattern list + */ + cflag = 1; + break; + case 'i': + /* + * restore an archive + */ + act = EXTRACT; + break; + case 'k': + break; + case 'l': + /* + * use links instead of copies when possible + */ + lflag = 1; + break; + case 'm': + /* + * preserve modification time + */ + pmtime = 1; + break; + case 'o': + /* + * create an archive + */ + act = ARCHIVE; + frmt = &(fsub[F_CPIO]); + break; + case 'p': + /* + * copy-pass mode + */ + act = COPY; + break; + case 'r': + /* + * interactively rename files + */ + iflag = 1; + break; + case 's': + /* + * swap bytes after reading data + */ + break; + case 't': + /* + * list contents of archive + */ + act = LIST; + break; + case 'u': + /* + * replace newer files + */ + kflag = 0; + break; + case 'v': + /* + * verbose operation mode + */ + vflag = 1; + break; + case 'z': + /* + * use gzip. Non standard option. + */ + zflag = 1; + gzip_program = GZIP_CMD; + break; + case 'A': + /* + * append mode + */ + act = APPND; + break; + case 'B': + /* + * Use 5120 byte block size + */ + wrblksz = 5120; + break; + case 'C': + /* + * set block size in bytes + */ + wrblksz = atoi(optarg); + break; + case 'E': + /* + * file with patterns to extract or list + */ + if ((fp = fopen(optarg, "r")) == NULL) { + paxwarn(1, "Unable to open file '%s' for read", optarg); + cpio_usage(); + } + while ((str = fgetln(fp, &len)) != NULL) { + str[len - 1] = '\0'; + pat_add(str, NULL); + } + fclose(fp); + break; + case 'F': + case 'I': + case 'O': + /* + * filename where the archive is stored + */ + if ((optarg[0] == '-') && (optarg[1]== '\0')) { + /* + * treat a - as stdin + */ + arcname = NULL; + break; + } + arcname = optarg; + break; + case 'H': + /* + * specify an archive format on write + */ + tmp.name = optarg; + if ((frmt = (FSUB *)bsearch((void *)&tmp, (void *)fsub, + sizeof(fsub)/sizeof(FSUB), sizeof(FSUB), c_frmt)) != NULL) + break; + paxwarn(1, "Unknown -H format: %s", optarg); + (void)fputs("cpio: Known -H formats are:", stderr); + for (i = 0; i < (sizeof(fsub)/sizeof(FSUB)); ++i) + (void)fprintf(stderr, " %s", fsub[i].name); + (void)fputs("\n\n", stderr); + cpio_usage(); + break; + case 'L': + /* + * follow symbolic links + */ + Lflag = 1; + break; + case 'S': + /* + * swap halfwords after reading data + */ + break; + case 'Z': + /* + * use compress. Non standard option. + */ + zflag = 1; + gzip_program = COMPRESS_CMD; + break; + case '6': + /* + * process Version 6 cpio format + */ + frmt = &(fsub[F_OCPIO]); + break; + case '?': + default: + cpio_usage(); + break; + } + argc -= optind; + argv += optind; + + /* + * process the args as they are interpreted by the operation mode + */ + switch (act) { + case LIST: + case EXTRACT: + while (*argv != NULL) + if (pat_add(*argv++, NULL) < 0) + cpio_usage(); + break; + case COPY: + if (*argv == NULL) { + paxwarn(0, "Destination directory was not supplied"); + cpio_usage(); + } + dirptr = *argv; + if (mkpath(dirptr) < 0) + cpio_usage(); + --argc; + ++argv; + /* FALL THROUGH */ + case ARCHIVE: + case APPND: + if (*argv != NULL) + cpio_usage(); + /* + * no read errors allowed on updates/append operation! + */ + maxflt = 0; + while ((str = fgetln(stdin, &len)) != NULL) { + str[len - 1] = '\0'; + ftree_add(strdup(str), NULL); + } + break; + default: + cpio_usage(); + break; + } +} + +/* + * printflg() + * print out those invalid flag sets found to the user + */ + +#ifdef __STDC__ +static void +printflg(unsigned int flg) +#else +static void +printflg(flg) + unsigned int flg; +#endif +{ + int nxt; + int pos = 0; + + (void)fprintf(stderr,"%s: Invalid combination of options:", argv0); + while ((nxt = ffs(flg)) != 0) { + flg = flg >> nxt; + pos += nxt; + (void)fprintf(stderr, " -%c", flgch[pos-1]); + } + (void)putc('\n', stderr); +} + +/* + * c_frmt() + * comparison routine used by bsearch to find the format specified + * by the user + */ + +#ifdef __STDC__ +static int +c_frmt(const void *a, const void *b) +#else +static int +c_frmt(a, b) + void *a; + void *b; +#endif +{ + return(strcmp(((FSUB *)a)->name, ((FSUB *)b)->name)); +} + +/* + * opt_next() + * called by format specific options routines to get each format specific + * flag and value specified with -o + * Return: + * pointer to next OPLIST entry or NULL (end of list). + */ + +#ifdef __STDC__ +OPLIST * +opt_next(void) +#else +OPLIST * +opt_next() +#endif +{ + OPLIST *opt; + + if ((opt = ophead) != NULL) + ophead = ophead->fow; + return(opt); +} + +/* + * bad_opt() + * generic routine used to complain about a format specific options + * when the format does not support options. + */ + +#ifdef __STDC__ +int +bad_opt(void) +#else +int +bad_opt() +#endif +{ + register OPLIST *opt; + + if (ophead == NULL) + return(0); + /* + * print all we were given + */ + paxwarn(1,"These format options are not supported"); + while ((opt = opt_next()) != NULL) + (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); + pax_usage(); + return(0); +} + +/* + * opt_add() + * breaks the value supplied to -o into a option name and value. options + * are given to -o in the form -o name-value,name=value + * mulltiple -o may be specified. + * Return: + * 0 if format in name=value format, -1 if -o is passed junk + */ + +#ifdef __STDC__ +int +opt_add(register char *str) +#else +int +opt_add(str) + register char *str; +#endif +{ + register OPLIST *opt; + register char *frpt; + register char *pt; + register char *endpt; + + if ((str == NULL) || (*str == '\0')) { + paxwarn(0, "Invalid option name"); + return(-1); + } + if ((str = strdup(str)) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + return(-1); + } + frpt = endpt = str; + + /* + * break into name and values pieces and stuff each one into a + * OPLIST structure. When we know the format, the format specific + * option function will go through this list + */ + while ((frpt != NULL) && (*frpt != '\0')) { + if ((endpt = strchr(frpt, ',')) != NULL) + *endpt = '\0'; + if ((pt = strchr(frpt, '=')) == NULL) { + paxwarn(0, "Invalid options format"); + free(str); + return(-1); + } + if ((opt = (OPLIST *)malloc(sizeof(OPLIST))) == NULL) { + paxwarn(0, "Unable to allocate space for option list"); + free(str); + return(-1); + } + *pt++ = '\0'; + opt->name = frpt; + opt->value = pt; + opt->fow = NULL; + if (endpt != NULL) + frpt = endpt + 1; + else + frpt = NULL; + if (ophead == NULL) { + optail = ophead = opt; + continue; + } + optail->fow = opt; + optail = opt; + } + return(0); +} + +/* + * str_offt() + * Convert an expression of the following forms to an off_t > 0. + * 1) A positive decimal number. + * 2) A positive decimal number followed by a b (mult by 512). + * 3) A positive decimal number followed by a k (mult by 1024). + * 4) A positive decimal number followed by a m (mult by 512). + * 5) A positive decimal number followed by a w (mult by sizeof int) + * 6) Two or more positive decimal numbers (with/without k,b or w). + * seperated by x (also * for backwards compatibility), specifying + * the product of the indicated values. + * Return: + * 0 for an error, a positive value o.w. + */ + +#ifdef __STDC__ +static off_t +str_offt(char *val) +#else +static off_t +str_offt(val) + char *val; +#endif +{ + char *expr; + off_t num, t; + +# ifdef NET2_STAT + num = strtol(val, &expr, 0); + if ((num == LONG_MAX) || (num <= 0) || (expr == val)) +# else + num = strtoq(val, &expr, 0); + if ((num == QUAD_MAX) || (num <= 0) || (expr == val)) +# endif + return(0); + + switch(*expr) { + case 'b': + t = num; + num *= 512; + if (t > num) + return(0); + ++expr; + break; + case 'k': + t = num; + num *= 1024; + if (t > num) + return(0); + ++expr; + break; + case 'm': + t = num; + num *= 1048576; + if (t > num) + return(0); + ++expr; + break; + case 'w': + t = num; + num *= sizeof(int); + if (t > num) + return(0); + ++expr; + break; + } + + switch(*expr) { + case '\0': + break; + case '*': + case 'x': + t = num; + num *= str_offt(expr + 1); + if (t > num) + return(0); + break; + default: + return(0); + } + return(num); +} + +/* + * no_op() + * for those option functions where the archive format has nothing to do. + * Return: + * 0 + */ + +#ifdef __STDC__ +static int +no_op(void) +#else +static int +no_op() +#endif +{ + return(0); +} + +/* + * pax_usage() + * print the usage summary to the user + */ + +#ifdef __STDC__ +void +pax_usage(void) +#else +void +pax_usage() +#endif +{ + (void)fputs("usage: pax [-cdnv] [-E limit] [-f archive] ", stderr); + (void)fputs("[-s replstr] ... [-U user] ...", stderr); + (void)fputs("\n [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date]] ... ", stderr); + (void)fputs("[pattern ...]\n", stderr); + (void)fputs(" pax -r [-cdiknuvDYZ] [-E limit] ", stderr); + (void)fputs("[-f archive] [-o options] ... \n", stderr); + (void)fputs(" [-p string] ... [-s replstr] ... ", stderr); + (void)fputs("[-U user] ... [-G group] ...\n ", stderr); + (void)fputs("[-T [from_date][,to_date]] ... ", stderr); + (void)fputs(" [pattern ...]\n", stderr); + (void)fputs(" pax -w [-dituvHLPX] [-b blocksize] ", stderr); + (void)fputs("[ [-a] [-f archive] ] [-x format] \n", stderr); + (void)fputs(" [-B bytes] [-s replstr] ... ", stderr); + (void)fputs("[-o options] ... [-U user] ...", stderr); + (void)fputs("\n [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); + (void)fputs("[file ...]\n", stderr); + (void)fputs(" pax -r -w [-diklntuvDHLPXYZ] ", stderr); + (void)fputs("[-p string] ... [-s replstr] ...", stderr); + (void)fputs("\n [-U user] ... [-G group] ... ", stderr); + (void)fputs("[-T [from_date][,to_date][/[c][m]]] ... ", stderr); + (void)fputs("\n [file ...] directory\n", stderr); + exit(1); +} + +/* + * tar_usage() + * print the usage summary to the user + */ + +#ifdef __STDC__ +void +tar_usage(void) +#else +void +tar_usage() +#endif +{ + (void)fputs("usage: tar -{txru}[cevfbmopswzBHLPXZ014578] [tapefile] ", + stderr); + (void)fputs("[blocksize] [replstr] [-C directory] file1 file2...\n", + stderr); + exit(1); +} + +/* + * cpio_usage() + * print the usage summary to the user + */ + +#ifdef __STDC__ +void +cpio_usage(void) +#else +void +cpio_usage() +#endif +{ + (void)fputs("usage: cpio -o [-aABcLvVzZ] [-C bytes] [-H format] [-O archive]\n", stderr); + (void)fputs(" [-F archive] < name-list [> archive]\n", stderr); + (void)fputs(" cpio -i [-bBcdfmnrsStuvVzZ6] [-C bytes] [-E file] [-H format]\n", stderr); + (void)fputs(" [-I archive] [-F archive] [pattern...] [< archive]\n", stderr); + (void)fputs(" cpio -p [-adlLmuvV] destination-directory < name-list\n", stderr); + exit(1); +} diff --git a/pax/options.h b/pax/options.h new file mode 100644 index 0000000..d45f54a --- /dev/null +++ b/pax/options.h @@ -0,0 +1,116 @@ +/* $OpenBSD: options.h,v 1.2 1996/06/23 14:20:37 deraadt Exp $ */ +/* $NetBSD: options.h,v 1.3 1995/03/21 09:07:32 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)options.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * argv[0] names. Used for tar and cpio emulation + */ + +#define NM_TAR "tar" +#define NM_CPIO "cpio" +#define NM_PAX "pax" + +/* + * Constants used to specify the legal sets of flags in pax. For each major + * operation mode of pax, a set of illegal flags is defined. If any one of + * those illegal flags are found set, we scream and exit + */ +#define NONE "none" + +/* + * flags (one for each option). + */ +#define AF 0x00000001 +#define BF 0x00000002 +#define CF 0x00000004 +#define DF 0x00000008 +#define FF 0x00000010 +#define IF 0x00000020 +#define KF 0x00000040 +#define LF 0x00000080 +#define NF 0x00000100 +#define OF 0x00000200 +#define PF 0x00000400 +#define RF 0x00000800 +#define SF 0x00001000 +#define TF 0x00002000 +#define UF 0x00004000 +#define VF 0x00008000 +#define WF 0x00010000 +#define XF 0x00020000 +#define CBF 0x00040000 /* nonstandard extension */ +#define CDF 0x00080000 /* nonstandard extension */ +#define CEF 0x00100000 /* nonstandard extension */ +#define CGF 0x00200000 /* nonstandard extension */ +#define CHF 0x00400000 /* nonstandard extension */ +#define CLF 0x00800000 /* nonstandard extension */ +#define CPF 0x01000000 /* nonstandard extension */ +#define CTF 0x02000000 /* nonstandard extension */ +#define CUF 0x04000000 /* nonstandard extension */ +#define CXF 0x08000000 +#define CYF 0x10000000 /* nonstandard extension */ +#define CZF 0x20000000 /* nonstandard extension */ + +/* + * ascii string indexed by bit position above (alter the above and you must + * alter this string) used to tell the user what flags caused us to complain + */ +#define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ" + +/* + * legal pax operation bit patterns + */ + +#define ISLIST(x) (((x) & (RF|WF)) == 0) +#define ISEXTRACT(x) (((x) & (RF|WF)) == RF) +#define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF) +#define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF)) +#define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF)) +#define ISWRITE(x) (((x) & (RF|WF)) == WF) + +/* + * Illegal option flag subsets based on pax operation + */ + +#define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF) +#define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF) +#define BDCOPY (AF|BF|FF|OF|XF|CBF|CEF) +#define BDLIST (AF|BF|IF|KF|LF|OF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF) diff --git a/pax/pat_rep.c b/pax/pat_rep.c new file mode 100644 index 0000000..9ff1dca --- /dev/null +++ b/pax/pat_rep.c @@ -0,0 +1,1240 @@ +/* $OpenBSD: pat_rep.c,v 1.11 1997/09/01 18:29:56 deraadt Exp $ */ +/* $NetBSD: pat_rep.c,v 1.4 1995/03/21 09:07:33 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pat_rep.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: pat_rep.c,v 1.11 1997/09/01 18:29:56 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef NET2_REGEX +#include +#else +#include +#endif +#include "pax.h" +#include "pat_rep.h" +#include "extern.h" + +/* + * routines to handle pattern matching, name modification (regular expression + * substitution and interactive renames), and destination name modification for + * copy (-rw). Both file name and link names are adjusted as required in these + * routines. + */ + +#define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ +static PATTERN *pathead = NULL; /* file pattern match list head */ +static PATTERN *pattail = NULL; /* file pattern match list tail */ +static REPLACE *rephead = NULL; /* replacement string list head */ +static REPLACE *reptail = NULL; /* replacement string list tail */ + +static int rep_name __P((char *, int *, int)); +static int tty_rename __P((register ARCHD *)); +static int fix_path __P((char *, int *, char *, int)); +static int fn_match __P((register char *, register char *, char **)); +static char * range_match __P((register char *, register int)); +#ifdef NET2_REGEX +static int resub __P((regexp *, char *, char *, register char *)); +#else +static int resub __P((regex_t *, regmatch_t *, char *, char *, char *)); +#endif + +/* + * rep_add() + * parses the -s replacement string; compiles the regular expression + * and stores the compiled value and it's replacement string together in + * replacement string list. Input to this function is of the form: + * /old/new/pg + * The first char in the string specifies the delimiter used by this + * replacement string. "Old" is a regular expression in "ed" format which + * is compiled by regcomp() and is applied to filenames. "new" is the + * substitution string; p and g are options flags for printing and global + * replacement (over the single filename) + * Return: + * 0 if a proper replacement string and regular expression was added to + * the list of replacement patterns; -1 otherwise. + */ + +#ifdef __STDC__ +int +rep_add(register char *str) +#else +int +rep_add(str) + register char *str; +#endif +{ + register char *pt1; + register char *pt2; + register REPLACE *rep; +# ifndef NET2_REGEX + register int res; + char rebuf[BUFSIZ]; +# endif + + /* + * throw out the bad parameters + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty replacement string"); + return(-1); + } + + /* + * first character in the string specifies what the delimiter is for + * this expression + */ + if ((pt1 = strchr(str+1, *str)) == NULL) { + paxwarn(1, "Invalid replacement string %s", str); + return(-1); + } + + /* + * allocate space for the node that handles this replacement pattern + * and split out the regular expression and try to compile it + */ + if ((rep = (REPLACE *)malloc(sizeof(REPLACE))) == NULL) { + paxwarn(1, "Unable to allocate memory for replacement string"); + return(-1); + } + + *pt1 = '\0'; +# ifdef NET2_REGEX + if ((rep->rcmp = regcomp(str+1)) == NULL) { +# else + if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { + regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); + paxwarn(1, "%s while compiling regular expression %s", rebuf, str); +# endif + (void)free((char *)rep); + return(-1); + } + + /* + * put the delimiter back in case we need an error message and + * locate the delimiter at the end of the replacement string + * we then point the node at the new substitution string + */ + *pt1++ = *str; + if ((pt2 = strchr(pt1, *str)) == NULL) { +# ifdef NET2_REGEX + (void)free((char *)rep->rcmp); +# else + regfree(&(rep->rcmp)); +# endif + (void)free((char *)rep); + paxwarn(1, "Invalid replacement string %s", str); + return(-1); + } + + *pt2 = '\0'; + rep->nstr = pt1; + pt1 = pt2++; + rep->flgs = 0; + + /* + * set the options if any + */ + while (*pt2 != '\0') { + switch(*pt2) { + case 'g': + case 'G': + rep->flgs |= GLOB; + break; + case 'p': + case 'P': + rep->flgs |= PRNT; + break; + default: +# ifdef NET2_REGEX + (void)free((char *)rep->rcmp); +# else + regfree(&(rep->rcmp)); +# endif + (void)free((char *)rep); + *pt1 = *str; + paxwarn(1, "Invalid replacement string option %s", str); + return(-1); + } + ++pt2; + } + + /* + * all done, link it in at the end + */ + rep->fow = NULL; + if (rephead == NULL) { + reptail = rephead = rep; + return(0); + } + reptail->fow = rep; + reptail = rep; + return(0); +} + +/* + * pat_add() + * add a pattern match to the pattern match list. Pattern matches are used + * to select which archive members are extracted. (They appear as + * arguments to pax in the list and read modes). If no patterns are + * supplied to pax, all members in the archive will be selected (and the + * pattern match list is empty). + * Return: + * 0 if the pattern was added to the list, -1 otherwise + */ + +#ifdef __STDC__ +int +pat_add(char *str, char *chdname) +#else +int +pat_add(str, chdname) + char *str; + char *chdname; +#endif +{ + register PATTERN *pt; + + /* + * throw out the junk + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty pattern string"); + return(-1); + } + + /* + * allocate space for the pattern and store the pattern. the pattern is + * part of argv so do not bother to copy it, just point at it. Add the + * node to the end of the pattern list + */ + if ((pt = (PATTERN *)malloc(sizeof(PATTERN))) == NULL) { + paxwarn(1, "Unable to allocate memory for pattern string"); + return(-1); + } + + pt->pstr = str; + pt->pend = NULL; + pt->plen = strlen(str); + pt->fow = NULL; + pt->flgs = 0; + pt->chdname = chdname; + + if (pathead == NULL) { + pattail = pathead = pt; + return(0); + } + pattail->fow = pt; + pattail = pt; + return(0); +} + +/* + * pat_chk() + * complain if any the user supplied pattern did not result in a match to + * a selected archive member. + */ + +#ifdef __STDC__ +void +pat_chk(void) +#else +void +pat_chk() +#endif +{ + register PATTERN *pt; + register int wban = 0; + + /* + * walk down the list checking the flags to make sure MTCH was set, + * if not complain + */ + for (pt = pathead; pt != NULL; pt = pt->fow) { + if (pt->flgs & MTCH) + continue; + if (!wban) { + paxwarn(1, "WARNING! These patterns were not matched:"); + ++wban; + } + (void)fprintf(stderr, "%s\n", pt->pstr); + } +} + +/* + * pat_sel() + * the archive member which matches a pattern was selected. Mark the + * pattern as having selected an archive member. arcn->pat points at the + * pattern that was matched. arcn->pat is set in pat_match() + * + * NOTE: When the -c option is used, we are called when there was no match + * by pat_match() (that means we did match before the inverted sense of + * the logic). Now this seems really strange at first, but with -c we + * need to keep track of those patterns that cause a archive member to NOT + * be selected (it found an archive member with a specified pattern) + * Return: + * 0 if the pattern pointed at by arcn->pat was tagged as creating a + * match, -1 otherwise. + */ + +#ifdef __STDC__ +int +pat_sel(register ARCHD *arcn) +#else +int +pat_sel(arcn) + register ARCHD *arcn; +#endif +{ + register PATTERN *pt; + register PATTERN **ppt; + register int len; + + /* + * if no patterns just return + */ + if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) + return(0); + + /* + * when we are NOT limited to a single match per pattern mark the + * pattern and return + */ + if (!nflag) { + pt->flgs |= MTCH; + return(0); + } + + /* + * we reach this point only when we allow a single selected match per + * pattern, if the pattern matches a directory and we do not have -d + * (dflag) we are done with this pattern. We may also be handed a file + * in the subtree of a directory. in that case when we are operating + * with -d, this pattern was already selected and we are done + */ + if (pt->flgs & DIR_MTCH) + return(0); + + if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { + /* + * ok we matched a directory and we are allowing + * subtree matches but because of the -n only its children will + * match. This is tagged as a DIR_MTCH type. + * WATCH IT, the code assumes that pt->pend points + * into arcn->name and arcn->name has not been modified. + * If not we will have a big mess. Yup this is another kludge + */ + + /* + * if this was a prefix match, remove trailing part of path + * so we can copy it. Future matches will be exact prefix match + */ + if (pt->pend != NULL) + *pt->pend = '\0'; + + if ((pt->pstr = strdup(arcn->name)) == NULL) { + paxwarn(1, "Pattern select out of memory"); + if (pt->pend != NULL) + *pt->pend = '/'; + pt->pend = NULL; + return(-1); + } + + /* + * put the trailing / back in the source string + */ + if (pt->pend != NULL) { + *pt->pend = '/'; + pt->pend = NULL; + } + pt->plen = strlen(pt->pstr); + + /* + * strip off any trailing /, this should really never happen + */ + len = pt->plen - 1; + if (*(pt->pstr + len) == '/') { + *(pt->pstr + len) = '\0'; + pt->plen = len; + } + pt->flgs = DIR_MTCH | MTCH; + arcn->pat = pt; + return(0); + } + + /* + * we are then done with this pattern, so we delete it from the list + * because it can never be used for another match. + * Seems kind of strange to do for a -c, but the pax spec is really + * vague on the interaction of -c -n and -d. We assume that when -c + * and the pattern rejects a member (i.e. it matched it) it is done. + * In effect we place the order of the flags as having -c last. + */ + pt = pathead; + ppt = &pathead; + while ((pt != NULL) && (pt != arcn->pat)) { + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt == NULL) { + /* + * should never happen.... + */ + paxwarn(1, "Pattern list inconsistant"); + return(-1); + } + *ppt = pt->fow; + (void)free((char *)pt); + arcn->pat = NULL; + return(0); +} + +/* + * pat_match() + * see if this archive member matches any supplied pattern, if a match + * is found, arcn->pat is set to point at the potential pattern. Later if + * this archive member is "selected" we process and mark the pattern as + * one which matched a selected archive member (see pat_sel()) + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + */ + +#ifdef __STDC__ +int +pat_match(register ARCHD *arcn) +#else +int +pat_match(arcn) + register ARCHD *arcn; +#endif +{ + register PATTERN *pt; + + arcn->pat = NULL; + + /* + * if there are no more patterns and we have -n (and not -c) we are + * done. otherwise with no patterns to match, matches all + */ + if (pathead == NULL) { + if (nflag && !cflag) + return(-1); + return(0); + } + + /* + * have to search down the list one at a time looking for a match. + */ + pt = pathead; + while (pt != NULL) { + /* + * check for a file name match unless we have DIR_MTCH set in + * this pattern then we want a prefix match + */ + if (pt->flgs & DIR_MTCH) { + /* + * this pattern was matched before to a directory + * as we must have -n set for this (but not -d). We can + * only match CHILDREN of that directory so we must use + * an exact prefix match (no wildcards). + */ + if ((arcn->name[pt->plen] == '/') && + (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) + break; + } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) + break; + pt = pt->fow; + } + + /* + * return the result, remember that cflag (-c) inverts the sense of a + * match + */ + if (pt == NULL) + return(cflag ? 0 : 1); + + /* + * we had a match, now when we invert the sense (-c) we reject this + * member. However we have to tag the pattern a being successful, (in a + * match, not in selecting a archive member) so we call pat_sel() here. + */ + arcn->pat = pt; + if (!cflag) + return(0); + + if (pat_sel(arcn) < 0) + return(-1); + arcn->pat = NULL; + return(1); +} + +/* + * fn_match() + * Return: + * 0 if this archive member should be processed, 1 if it should be + * skipped and -1 if we are done with all patterns (and pax should quit + * looking for more members) + * Note: *pend may be changed to show where the prefix ends. + */ + +#ifdef __STDC__ +static int +fn_match(register char *pattern, register char *string, char **pend) +#else +static int +fn_match(pattern, string, pend) + register char *pattern; + register char *string; + char **pend; +#endif +{ + register char c; + char test; + + *pend = NULL; + for (;;) { + switch (c = *pattern++) { + case '\0': + /* + * Ok we found an exact match + */ + if (*string == '\0') + return(0); + + /* + * Check if it is a prefix match + */ + if ((dflag == 1) || (*string != '/')) + return(-1); + + /* + * It is a prefix match, remember where the trailing + * / is located + */ + *pend = string; + return(0); + case '?': + if ((test = *string++) == '\0') + return (-1); + break; + case '*': + c = *pattern; + /* + * Collapse multiple *'s. + */ + while (c == '*') + c = *++pattern; + + /* + * Optimized hack for pattern with a * at the end + */ + if (c == '\0') + return (0); + + /* + * General case, use recursion. + */ + while ((test = *string) != '\0') { + if (!fn_match(pattern, string, pend)) + return (0); + ++string; + } + return (-1); + case '[': + /* + * range match + */ + if (((test = *string++) == '\0') || + ((pattern = range_match(pattern, test)) == NULL)) + return (-1); + break; + case '\\': + default: + if (c != *string++) + return (-1); + break; + } + } + /* NOTREACHED */ +} + +#ifdef __STDC__ +static char * +range_match(register char *pattern, register int test) +#else +static char * +range_match(pattern, test) + register char *pattern; + register int test; +#endif +{ + register char c; + register char c2; + int negate; + int ok = 0; + + if ((negate = (*pattern == '!')) != 0) + ++pattern; + + while ((c = *pattern++) != ']') { + /* + * Illegal pattern + */ + if (c == '\0') + return (NULL); + + if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && + (c2 != ']')) { + if ((c <= test) && (test <= c2)) + ok = 1; + pattern += 2; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} + +/* + * mod_name() + * modify a selected file name. first attempt to apply replacement string + * expressions, then apply interactive file rename. We apply replacement + * string expressions to both filenames and file links (if we didn't the + * links would point to the wrong place, and we could never be able to + * move an archive that has a file link in it). When we rename files + * interactively, we store that mapping (old name to user input name) so + * if we spot any file links to the old file name in the future, we will + * know exactly how to fix the file link. + * Return: + * 0 continue to process file, 1 skip this file, -1 pax is finished + */ + +#ifdef __STDC__ +int +mod_name(register ARCHD *arcn) +#else +int +mod_name(arcn) + register ARCHD *arcn; +#endif +{ + register int res = 0; + + /* + * Strip off leading '/' if appropriate. + * Currently, this option is only set for the tar format. + */ + if (rmleadslash && arcn->name[0] == '/') { + if (arcn->name[1] == '\0') { + arcn->name[0] = '.'; + } else { + (void)memmove(arcn->name, &arcn->name[1], + strlen(arcn->name)); + arcn->nlen--; + } + if (rmleadslash < 2) { + rmleadslash = 2; + paxwarn(0, "Removing leading / from absolute path names in the archive"); + } + } + if (rmleadslash && arcn->ln_name[0] == '/' && + (arcn->type == PAX_HLK || arcn->type == PAX_HRG)) { + if (arcn->ln_name[1] == '\0') { + arcn->ln_name[0] = '.'; + } else { + (void)memmove(arcn->ln_name, &arcn->ln_name[1], + strlen(arcn->ln_name)); + arcn->ln_nlen--; + } + if (rmleadslash < 2) { + rmleadslash = 2; + paxwarn(0, "Removing leading / from absolute path names in the archive"); + } + } + + /* + * IMPORTANT: We have a problem. what do we do with symlinks? + * Modifying a hard link name makes sense, as we know the file it + * points at should have been seen already in the archive (and if it + * wasn't seen because of a read error or a bad archive, we lose + * anyway). But there are no such requirements for symlinks. On one + * hand the symlink that refers to a file in the archive will have to + * be modified to so it will still work at its new location in the + * file system. On the other hand a symlink that points elsewhere (and + * should continue to do so) should not be modified. There is clearly + * no perfect solution here. So we handle them like hardlinks. Clearly + * a replacement made by the interactive rename mapping is very likely + * to be correct since it applies to a single file and is an exact + * match. The regular expression replacements are a little harder to + * justify though. We claim that the symlink name is only likely + * to be replaced when it points within the file tree being moved and + * in that case it should be modified. what we really need to do is to + * call an oracle here. :) + */ + if (rephead != NULL) { + /* + * we have replacement strings, modify the name and the link + * name if any. + */ + if ((res = rep_name(arcn->name, &(arcn->nlen), 1)) != 0) + return(res); + + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && + ((res = rep_name(arcn->ln_name, &(arcn->ln_nlen), 0)) != 0)) + return(res); + } + + if (iflag) { + /* + * perform interactive file rename, then map the link if any + */ + if ((res = tty_rename(arcn)) != 0) + return(res); + if ((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) + sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); + } + return(res); +} + +/* + * tty_rename() + * Prompt the user for a replacement file name. A "." keeps the old name, + * a empty line skips the file, and an EOF on reading the tty, will cause + * pax to stop processing and exit. Otherwise the file name input, replaces + * the old one. + * Return: + * 0 process this file, 1 skip this file, -1 we need to exit pax + */ + +#ifdef __STDC__ +static int +tty_rename(register ARCHD *arcn) +#else +static int +tty_rename(arcn) + register ARCHD *arcn; +#endif +{ + char tmpname[PAXPATHLEN+2]; + int res; + + /* + * prompt user for the replacement name for a file, keep trying until + * we get some reasonable input. Archives may have more than one file + * on them with the same name (from updates etc). We print verbose info + * on the file so the user knows what is up. + */ + tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); + + for (;;) { + ls_tty(arcn); + tty_prnt("Input new name, or a \".\" to keep the old name, "); + tty_prnt("or a \"return\" to skip this file.\n"); + tty_prnt("Input > "); + if (tty_read(tmpname, sizeof(tmpname)) < 0) + return(-1); + if (strcmp(tmpname, "..") == 0) { + tty_prnt("Try again, illegal file name: ..\n"); + continue; + } + if (strlen(tmpname) > PAXPATHLEN) { + tty_prnt("Try again, file name too long\n"); + continue; + } + break; + } + + /* + * empty file name, skips this file. a "." leaves it alone + */ + if (tmpname[0] == '\0') { + tty_prnt("Skipping file.\n"); + return(1); + } + if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { + tty_prnt("Processing continues, name unchanged.\n"); + return(0); + } + + /* + * ok the name changed. We may run into links that point at this + * file later. we have to remember where the user sent the file + * in order to repair any links. + */ + tty_prnt("Processing continues, name changed to: %s\n", tmpname); + res = add_name(arcn->name, arcn->nlen, tmpname); + arcn->nlen = l_strncpy(arcn->name, tmpname, sizeof(arcn->name) - 1); + arcn->name[arcn->nlen] = '\0'; + if (res < 0) + return(-1); + return(0); +} + +/* + * set_dest() + * fix up the file name and the link name (if any) so this file will land + * in the destination directory (used during copy() -rw). + * Return: + * 0 if ok, -1 if failure (name too long) + */ + +#ifdef __STDC__ +int +set_dest(register ARCHD *arcn, char *dest_dir, int dir_len) +#else +int +set_dest(arcn, dest_dir, dir_len) + register ARCHD *arcn; + char *dest_dir; + int dir_len; +#endif +{ + if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) + return(-1); + + /* + * It is really hard to deal with symlinks here, we cannot be sure + * if the name they point was moved (or will be moved). It is best to + * leave them alone. + */ + if ((arcn->type != PAX_HLK) && (arcn->type != PAX_HRG)) + return(0); + + if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) + return(-1); + return(0); +} + +/* + * fix_path + * concatenate dir_name and or_name and store the result in or_name (if + * it fits). This is one ugly function. + * Return: + * 0 if ok, -1 if the final name is too long + */ + +#ifdef __STDC__ +static int +fix_path( char *or_name, int *or_len, char *dir_name, int dir_len) +#else +static int +fix_path(or_name, or_len, dir_name, dir_len) + char *or_name; + int *or_len; + char *dir_name; + int dir_len; +#endif +{ + register char *src; + register char *dest; + register char *start; + int len; + + /* + * we shift the or_name to the right enough to tack in the dir_name + * at the front. We make sure we have enough space for it all before + * we start. since dest always ends in a slash, we skip of or_name + * if it also starts with one. + */ + start = or_name; + src = start + *or_len; + dest = src + dir_len; + if (*start == '/') { + ++start; + --dest; + } + if ((len = dest - or_name) > PAXPATHLEN) { + paxwarn(1, "File name %s/%s, too long", dir_name, start); + return(-1); + } + *or_len = len; + + /* + * enough space, shift + */ + while (src >= start) + *dest-- = *src--; + src = dir_name + dir_len - 1; + + /* + * splice in the destination directory name + */ + while (src >= dir_name) + *dest-- = *src--; + + *(or_name + len) = '\0'; + return(0); +} + +/* + * rep_name() + * walk down the list of replacement strings applying each one in order. + * when we find one with a successful substitution, we modify the name + * as specified. if required, we print the results. if the resulting name + * is empty, we will skip this archive member. We use the regexp(3) + * routines (regexp() ought to win a prize as having the most cryptic + * library function manual page). + * --Parameters-- + * name is the file name we are going to apply the regular expressions to + * (and may be modified) + * nlen is the length of this name (and is modified to hold the length of + * the final string). + * prnt is a flag that says whether to print the final result. + * Return: + * 0 if substitution was successful, 1 if we are to skip the file (the name + * ended up empty) + */ + +#ifdef __STDC__ +static int +rep_name(char *name, int *nlen, int prnt) +#else +static int +rep_name(name, nlen, prnt) + char *name; + int *nlen; + int prnt; +#endif +{ + register REPLACE *pt; + register char *inpt; + register char *outpt; + register char *endpt; + register char *rpt; + register int found = 0; + register int res; +# ifndef NET2_REGEX + regmatch_t pm[MAXSUBEXP]; +# endif + char nname[PAXPATHLEN+1]; /* final result of all replacements */ + char buf1[PAXPATHLEN+1]; /* where we work on the name */ + + /* + * copy the name into buf1, where we will work on it. We need to keep + * the orig string around so we can print out the result of the final + * replacement. We build up the final result in nname. inpt points at + * the string we apply the regular expression to. prnt is used to + * suppress printing when we handle replacements on the link field + * (the user already saw that substitution go by) + */ + pt = rephead; + (void)strcpy(buf1, name); + inpt = buf1; + outpt = nname; + endpt = outpt + PAXPATHLEN; + + /* + * try each replacement string in order + */ + while (pt != NULL) { + do { + /* + * check for a successful substitution, if not go to + * the next pattern, or cleanup if we were global + */ +# ifdef NET2_REGEX + if (regexec(pt->rcmp, inpt) == 0) +# else + if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) +# endif + break; + + /* + * ok we found one. We have three parts, the prefix + * which did not match, the section that did and the + * tail (that also did not match). Copy the prefix to + * the final output buffer (watching to make sure we + * do not create a string too long). + */ + found = 1; +# ifdef NET2_REGEX + rpt = pt->rcmp->startp[0]; +# else + rpt = inpt + pm[0].rm_so; +# endif + + while ((inpt < rpt) && (outpt < endpt)) + *outpt++ = *inpt++; + if (outpt == endpt) + break; + + /* + * for the second part (which matched the regular + * expression) apply the substitution using the + * replacement string and place it the prefix in the + * final output. If we have problems, skip it. + */ +# ifdef NET2_REGEX + if ((res = resub(pt->rcmp,pt->nstr,outpt,endpt)) < 0) { +# else + if ((res = resub(&(pt->rcmp),pm,pt->nstr,outpt,endpt)) + < 0) { +# endif + if (prnt) + paxwarn(1, "Replacement name error %s", + name); + return(1); + } + outpt += res; + + /* + * we set up to look again starting at the first + * character in the tail (of the input string right + * after the last character matched by the regular + * expression (inpt always points at the first char in + * the string to process). If we are not doing a global + * substitution, we will use inpt to copy the tail to + * the final result. Make sure we do not overrun the + * output buffer + */ +# ifdef NET2_REGEX + inpt = pt->rcmp->endp[0]; +# else + inpt += pm[0].rm_eo - pm[0].rm_so; +# endif + + if ((outpt == endpt) || (*inpt == '\0')) + break; + + /* + * if the user wants global we keep trying to + * substitute until it fails, then we are done. + */ + } while (pt->flgs & GLOB); + + if (found) + break; + + /* + * a successful substitution did NOT occur, try the next one + */ + pt = pt->fow; + } + + if (found) { + /* + * we had a substitution, copy the last tail piece (if there is + * room) to the final result + */ + while ((outpt < endpt) && (*inpt != '\0')) + *outpt++ = *inpt++; + + *outpt = '\0'; + if ((outpt == endpt) && (*inpt != '\0')) { + if (prnt) + paxwarn(1,"Replacement name too long %s >> %s", + name, nname); + return(1); + } + + /* + * inform the user of the result if wanted + */ + if (prnt && (pt->flgs & PRNT)) { + if (*nname == '\0') + (void)fprintf(stderr,"%s >> \n", + name); + else + (void)fprintf(stderr,"%s >> %s\n", name, nname); + } + + /* + * if empty inform the caller this file is to be skipped + * otherwise copy the new name over the orig name and return + */ + if (*nname == '\0') + return(1); + *nlen = l_strncpy(name, nname, PAXPATHLEN + 1); + name[PAXPATHLEN] = '\0'; + } + return(0); +} + +#ifdef NET2_REGEX +/* + * resub() + * apply the replacement to the matched expression. expand out the old + * style ed(1) subexpression expansion. + * Return: + * -1 if error, or the number of characters added to the destination. + */ + +#ifdef __STDC__ +static int +resub(regexp *prog, char *src, char *dest, register char *destend) +#else +static int +resub(prog, src, dest, destend) + regexp *prog; + char *src; + char *dest; + register char *destend; +#endif +{ + register char *spt; + register char *dpt; + register char c; + register int no; + register int len; + + spt = src; + dpt = dest; + while ((dpt < destend) && ((c = *spt++) != '\0')) { + if (c == '&') + no = 0; + else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) + no = *spt++ - '0'; + else { + if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) + c = *spt++; + *dpt++ = c; + continue; + } + if ((prog->startp[no] == NULL) || (prog->endp[no] == NULL) || + ((len = prog->endp[no] - prog->startp[no]) <= 0)) + continue; + + /* + * copy the subexpression to the destination. + * fail if we run out of space or the match string is damaged + */ + if (len > (destend - dpt)) + len = destend - dpt; + if (l_strncpy(dpt, prog->startp[no], len) != len) + return(-1); + dpt += len; + } + return(dpt - dest); +} + +#else + +/* + * resub() + * apply the replacement to the matched expression. expand out the old + * style ed(1) subexpression expansion. + * Return: + * -1 if error, or the number of characters added to the destination. + */ + +#ifdef __STDC__ +static int +resub(regex_t *rp, register regmatch_t *pm, char *src, char *dest, + register char *destend) +#else +static int +resub(rp, pm, src, dest, destend) + regex_t *rp; + register regmatch_t *pm; + char *src; + char *dest; + register char *destend; +#endif +{ + register char *spt; + register char *dpt; + register char c; + register regmatch_t *pmpt; + register int len; + int subexcnt; + + spt = src; + dpt = dest; + subexcnt = rp->re_nsub; + while ((dpt < destend) && ((c = *spt++) != '\0')) { + /* + * see if we just have an ordinary replacement character + * or we refer to a subexpression. + */ + if (c == '&') { + pmpt = pm; + } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { + /* + * make sure there is a subexpression as specified + */ + if ((len = *spt++ - '0') > subexcnt) + return(-1); + pmpt = pm + len; + } else { + /* + * Ordinary character, just copy it + */ + if ((c == '\\') && ((*spt == '\\') || (*spt == '&'))) + c = *spt++; + *dpt++ = c; + continue; + } + + /* + * continue if the subexpression is bogus + */ + if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || + ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) + continue; + + /* + * copy the subexpression to the destination. + * fail if we run out of space or the match string is damaged + */ + if (len > (destend - dpt)) + len = destend - dpt; + if (l_strncpy(dpt, src + pmpt->rm_so, len) != len) + return(-1); + dpt += len; + } + return(dpt - dest); +} +#endif diff --git a/pax/pat_rep.h b/pax/pat_rep.h new file mode 100644 index 0000000..b41f1e1 --- /dev/null +++ b/pax/pat_rep.h @@ -0,0 +1,57 @@ +/* $OpenBSD: pat_rep.h,v 1.2 1996/06/23 14:20:38 deraadt Exp $ */ +/* $NetBSD: pat_rep.h,v 1.3 1995/03/21 09:07:35 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)pat_rep.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing user supplied replacement strings (-s) + */ +typedef struct replace { + char *nstr; /* the new string we will substitute with */ +# ifdef NET2_REGEX + regexp *rcmp; /* compiled regular expression used to match */ +# else + regex_t rcmp; /* compiled regular expression used to match */ +# endif + int flgs; /* print conversions? global in operation? */ +#define PRNT 0x1 +#define GLOB 0x2 + struct replace *fow; /* pointer to next pattern */ +} REPLACE; diff --git a/pax/pax.1 b/pax/pax.1 new file mode 100644 index 0000000..590c9df --- /dev/null +++ b/pax/pax.1 @@ -0,0 +1,1178 @@ +.\" $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. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Keith Muller of the University of California, San Diego. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pax.1 8.4 (Berkeley) 4/18/94 +.\" +.Dd April 18, 1994 +.Dt PAX 1 +.Os BSD 4.4 +.Sh NAME +.Nm pax +.Nd read and write file archives and copy directory hierarchies +.Sh SYNOPSIS +.Nm pax +.Op Fl cdnv +.Bk -words +.Op Fl f Ar archive +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Oc +.Ar ... +.Ek +.Op Ar pattern ... +.Nm pax +.Fl r +.Op Fl cdiknuvDYZ +.Bk -words +.Op Fl f Ar archive +.Ek +.Bk -words +.Op Fl o Ar options +.Ar ... +.Ek +.Bk -words +.Op Fl p Ar string +.Ar ... +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Op Fl E Ar limit +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Oc +.Ar ... +.Ek +.Op Ar pattern ... +.Nm pax +.Fl w +.Op Fl dituvHLPX +.Bk -words +.Op Fl b Ar blocksize +.Ek +.Oo +.Op Fl a +.Op Fl f Ar archive +.Oc +.Bk -words +.Op Fl x Ar format +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl o Ar options +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Op Fl B Ar bytes +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Op Ar /[c][m] +.Oc +.Ar ... +.Ek +.Op Ar file ... +.Nm pax +.Fl r +.Fl w +.Op Fl diklntuvDHLPXYZ +.Bk -words +.Op Fl p Ar string +.Ar ... +.Ek +.Bk -words +.Op Fl s Ar replstr +.Ar ... +.Ek +.Bk -words +.Op Fl U Ar user +.Ar ... +.Ek +.Bk -words +.Op Fl G Ar group +.Ar ... +.Ek +.Bk -words +.Oo +.Fl T +.Op Ar from_date +.Op Ar ,to_date +.Op Ar /[c][m] +.Oc +.Ar ... +.Ek +.Op Ar file ... +.Ar directory +.Sh DESCRIPTION +.Nm Pax +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. +A list of supported archive formats can be found under the description of the +.Fl x +option. +.Pp +The presence of the +.Fl r +and the +.Fl w +options specifies which of the following functional modes +.Nm pax +will operate under: +.Em list , read , write , +and +.Em copy. +.Bl -tag -width 6n +.It +.Em List . +.Nm Pax +will 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. +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 +.Dv standard input , +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. +All extracted files are created relative to the current file hierarchy. +The setting of ownership, access and modification times, and file mode of +the extracted files are discussed in more detail under the +.Fl p +option. +.It Fl w +.Em Write . +.Nm Pax +writes an archive containing the +.Ar file +operands to +.Dv standard output +using the specified archive format. +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +.Dv standard input . +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 +.Ar file +operands to the destination +.Ar directory . +When no +.Ar file +operands are specified, a list of files to copy with one per line is read from +the +.Dv standard input . +When a +.Ar file +operand is also a directory the entire file +hierarchy rooted at that directory will be included. +The effect of the +.Em copy +is as if the copied files were written to an archive file and then +subsequently extracted, except that there may be hard links between +the original and the copied files (see the +.Fl l +option below). +.Pp +.Em Warning : +The destination +.Ar directory +must not be one of the +.Ar file +operands or a member of a file hierarchy rooted at one of the +.Ar file +operands. +The result of a +.Em copy +under these conditions is unpredictable. +.El +.Pp +While processing a damaged archive during a +.Em read +or +.Em list +operation, +.Nm pax +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. +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 +will exit with a non-zero exit status. +.Pp +The +.Ar pattern +operand is used to select one or more pathnames of archive members. +Archive members are selected using the pattern matching notation described +by +.Xr fnmatch 3 . +When the +.Ar pattern +operand is not supplied, all members of the archive will be selected. +When a +.Ar pattern +matches a directory, the entire file hierarchy rooted at that directory will +be selected. +When a +.Ar pattern +operand does not select at least one archive member, +.Nm pax +will write these +.Ar pattern +operands in a diagnostic message to +.Dv standard error +and then exit with a non-zero exit status. +.Pp +The +.Ar file +operand specifies the pathname of a file to be copied or archived. +When a +.Ar file +operand does not select at least one archive member, +.Nm pax +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 +Read an archive file from +.Dv standard input +and extract the specified +.Ar files . +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 +of +.Dv S_IRWXU , S_IRWXG , +and +.Dv S_IRWXO +as the mode argument. +When the selected archive format supports the specification of linked +files and these files cannot be linked while the archive is being extracted, +.Nm pax +will write a diagnostic message to +.Dv standard error +and exit with a non-zero exit status at the completion of operation. +.It Fl w +Write files to the +.Dv standard output +in the specified archive format. +When no +.Ar file +operands are specified, +.Dv standard input +is read for a list of pathnames with one per line without any leading or +trailing +.Aq blanks . +.It Fl a +Append +.Ar files +to the end of an archive that was previously written. +If an archive format is not specified with a +.Fl x +option, the format currently being used in the archive will be selected. +Any attempt to append to an archive in a format different from the +format already used in the archive will cause +.Nm pax +to exit immediately +with a non-zero exit status. +The blocking size used in the archive volume where writing starts +will continue to be used for the remainder of that archive volume. +.Pp +.Em Warning : +Many storage devices are not able to support the operations necessary +to perform an append operation. +Any attempt to append to an archive stored on such a device may damage the +archive or have other unpredictable results. +Tape drives in particular are more likely to not support an append operation. +An archive stored in a regular file system file or on a disk device will +usually support an append operation. +.It Fl b Ar blocksize +When +.Em writing +an archive, +block the output at a positive decimal integer number of +bytes per write to the archive file. +The +.Ar blocksize +must be a multiple of 512 bytes with a maximum of 64512 bytes. +Archives larger than 32256 bytes violate the +.Tn POSIX +standard and will not be portable to all systems. +A +.Ar blocksize +can end with +.Li k +or +.Li b +to specify multiplication by 1024 (1K) or 512, respectively. +A pair of +.Ar blocksizes +can be separated by +.Li x +to indicate a product. +A specific archive device may impose additional restrictions on the size +of blocking it will support. +When blocking is not specified, the default +.Ar blocksize +is dependent on the specific archive format being used (see the +.Fl x +option). +.It Fl c +Match all file or archive members +.Em except +those specified by the +.Ar pattern +and +.Ar file +operands. +.It Fl d +Cause files of type directory being copied or archived, or archive members of +type directory being extracted, to match only the directory file or archive +member and not the file hierarchy rooted at the directory. +.It Fl f Ar archive +Specify +.Ar archive +as the pathname of the input or output archive, overriding the default +.Dv standard input +(for +.Em list +and +.Em read ) +or +.Dv standard output +(for +.Em write ) . +A single archive may span multiple files and different archive devices. +When required, +.Nm pax +will prompt for the pathname of the file or device of the next volume in the +archive. +.It Fl i +Interactively rename files or archive members. +For each archive member matching a +.Ar pattern +operand or each file matching a +.Ar file +operand, +.Nm pax +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 +.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 +.Dv +is encountered when reading a response or if +.Pa /dev/tty +cannot be opened for reading and writing. +.It Fl k +Do not overwrite existing files. +.It Fl l +Link files. (The letter ell). +In the +.Em copy +mode ( +.Fl r +.Fl w ) , +hard links are made between the source and destination file hierarchies +whenever possible. +.It Fl n +Select the first archive member that matches each +.Ar pattern +operand. +No more than one archive member is matched for each +.Ar pattern . +When members of type directory are matched, the file hierarchy rooted at that +directory is also matched (unless +.Fl d +is also specified). +.It Fl o Ar options +Information to modify the algorithm for extracting or writing archive files +which is specific to the archive format specified by +.Fl x . +In general, +.Ar options +take the form: +.Cm name=value +.It Fl p Ar string +Specify one or more file characteristic options (privileges). +The +.Ar string +option-argument is a string specifying file characteristics to be retained or +discarded on extraction. +The string consists of the specification characters +.Cm a , e , m , o , +and +.Cm p . +Multiple characteristics can be concatenated within the same string +and multiple +.Fl p +options can be specified. +The meaning of the specification characters are as follows: +.Bl -tag -width 2n +.It Cm a +Do not preserve file access times. +By default, file access times are preserved whenever possible. +.It Cm e +.Sq Preserve everything , +the user ID, group ID, file mode bits, +file access time, and file modification time. +This is intended to be used by +.Em root , +someone with all the appropriate privileges, in order to preserve all +aspects of the files as they are recorded in the archive. +The +.Cm e +flag is the sum of the +.Cm o +and +.Cm p +flags. +.It Cm m +Do not preserve file modification times. +By default, file modification times are preserved whenever possible. +.It Cm o +Preserve the user ID and group ID. +.It Cm p +.Sq Preserve +the file mode bits. +This intended to be used by a +.Em user +with regular privileges who wants to preserve all aspects of the file other +than the ownership. +The file times are preserved by default, but two other flags are offered to +disable this and use the time of extraction instead. +.El +.Pp +In the preceding list, +.Sq preserve +indicates that an attribute stored in the archive is given to the +extracted file, subject to the permissions of the invoking +process. +Otherwise the attribute of the extracted file is determined as +part of the normal file creation action. +If neither the +.Cm e +nor the +.Cm o +specification character is specified, or the user ID and group ID are not +preserved for any reason, +.Nm pax +will not set the +.Dv S_ISUID +.Em ( setuid ) +and +.Dv S_ISGID +.Em ( setgid ) +bits of the file mode. +If the preservation of any of these items fails for any reason, +.Nm pax +will write a diagnostic message to +.Dv standard error . +Failure to preserve these items will affect the final exit status, +but will not cause the extracted file to be deleted. +If the file characteristic letters in any of the string option-arguments are +duplicated or conflict with each other, the one(s) given last will take +precedence. +For example, if +.Dl Fl p Ar eme +is specified, file modification times are still preserved. +.It Fl s Ar replstr +Modify the file or archive member names specified by the +.Ar pattern +or +.Ar file +operands according to the substitution expression +.Ar replstr , +using the syntax of the +.Xr ed 1 +utility regular expressions. +The format of these regular expressions are: +.Dl /old/new/[gp] +As in +.Xr ed 1 , +.Cm old +is a basic regular expression and +.Cm new +can contain an ampersand (&), \\n (where n is a digit) back-references, +or subexpression matching. +The +.Cm old +string may also contain +.Dv +characters. +Any non-null character can be used as a delimiter (/ is shown here). +Multiple +.Fl s +expressions can be specified. +The expressions are applied in the order they are specified on the +command line, terminating with the first successful substitution. +The optional trailing +.Cm g +continues to apply the substitution expression to the pathname substring +which starts with the first character following the end of the last successful +substitution. The first unsuccessful substitution stops the operation of the +.Cm g +option. +The optional trailing +.Cm p +will cause the final result of a successful substitution to be written to +.Dv standard error +in the following format: +.Dl >> +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 +to be the same as they were before being read or accessed by +.Nm pax . +.It Fl u +Ignore files that are older (having a less recent file modification time) +than a pre-existing file or archive member with the same name. +During +.Em read , +an archive member with the same name as a file in the file system will be +extracted if the archive member is newer than the file. +During +.Em write , +a file system member with the same name as an archive member will be +written to the archive if it is newer than the archive member. +During +.Em copy , +the file in the destination hierarchy is replaced by the file in the source +hierarchy or by a link to the file in the source hierarchy if the file in +the source hierarchy is newer. +.It Fl v +During a +.Em list +operation, produce a verbose table of contents using the format of the +.Xr ls 1 +utility with the +.Fl l +option. +For pathnames representing a hard link to a previous member of the archive, +the output has the format: +.Dl == +For pathnames representing a symbolic link, the output has the format: +.Dl => +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 , +and +.Em copy ) , +pathnames are written and flushed to +.Dv standard error +without a trailing +.Dv +as soon as processing begins on that file or +archive member. +The trailing +.Dv , +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: +.Bl -tag -width "sv4cpio" +.It Ar cpio +The extended cpio interchange format specified in the +.St -p1003.2 +standard. +The default blocksize for this format is 5120 bytes. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +and is repaired. +.It Ar bcpio +The old binary cpio format. +The default blocksize for this format is 5120 bytes. +This format is not very portable and should not be used when other formats +are available. +Inode and device information about a file (used for detecting file hard links +by this format) which may be truncated by this format is detected by +.Nm pax +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 +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 +and is repaired. +.It Ar tar +The old BSD tar format as found in BSD4.3. +The default blocksize for this format is 10240 bytes. +Pathnames stored by this format must be 100 characters or less in length. +Only +.Em regular +files, +.Em hard links , soft links , +and +.Em directories +will be archived (other file system types are not supported). +For backwards compatibility with even older tar formats, a +.Fl o +option can be used when writing an archive to omit the storage of directories. +This option takes the form: +.Dl Fl o Cm write_opt=nodir +.It Ar ustar +The extended tar interchange format specified in the +.St -p1003.2 +standard. +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 +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 B Ar bytes +Limit the number of bytes written to a single archive volume to +.Ar bytes . +The +.Ar bytes +limit can end with +.Li m , +.Li k , +or +.Li b +to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively. +A pair of +.Ar bytes +limits can be separated by +.Li x +to indicate a product. +.Pp +.Em Warning : +Only use this option when writing an archive to a device which supports +an end of file read condition based on last (or largest) write offset +(such as a regular file or a tape drive). +The use of this option with a floppy or hard disk is not recommended. +.It Fl D +This option is the same as the +.Fl u +option, except that the file inode change time is checked instead of the +file modification time. +The file inode change time can be used to select files whose inode information +(e.g. uid, gid, etc.) is newer than a copy of the file in the destination +.Ar directory . +.It Fl E Ar limit +Limit the number of consecutive read faults while trying to read a flawed +archives to +.Ar limit . +With a positive +.Ar limit , +.Nm pax +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 +to stop operation after the first read error is detected on an archive volume. +A +.Ar limit +of +.Li NONE +will cause +.Nm pax +to attempt to recover from read errors forever. +The default +.Ar limit +is a small positive number of retries. +.Pp +.Em Warning: +Using this option with +.Li NONE +should be used with extreme caution as +.Nm pax +may get stuck in an infinite loop on a very badly flawed archive. +.It Fl G Ar group +Select a file based on its +.Ar group +name, or when starting with a +.Cm # , +a numeric gid. +A '\\' can be used to escape the +.Cm # . +Multiple +.Fl G +options may be supplied and checking stops with the first match. +.It Fl H +Follow only command line symbolic links while performing a physical file +system traversal. +.It Fl L +Follow all symbolic links to perform a logical file system traversal. +.It Fl P +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 +.Ar from_date +to +.Ar to_date +(the dates are inclusive). +If only a +.Ar from_date +is supplied, all files with a modification or inode change time +equal to or younger are selected. +If only a +.Ar to_date +is supplied, all files with a modification or inode change time +equal to or older will be selected. +When the +.Ar from_date +is equal to the +.Ar to_date , +only files with a modification or inode change time of exactly that +time will be selected. +.Pp +When +.Nm pax +is in the +.Em write +or +.Em copy +mode, the optional trailing field +.Ar [c][m] +can be used to determine which file time (inode change, file modification or +both) are used in the comparison. +If neither is specified, the default is to use file modification time only. +The +.Ar m +specifies the comparison of file modification time (the time when +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 +.Ar c +and +.Ar m +are both specified, then the modification and inode change times are +both compared. +The inode change time comparison is useful in selecting files whose +attributes were recently changed or selecting files which were recently +created and had their modification time reset to an older time (as what +happens when a file is extracted from an archive and the modification time +is preserved). +Time comparisons using both file times is useful when +.Nm pax +is used to create a time based incremental archive (only files that were +changed during a specified time range will be archived). +.Pp +A time range is made up of six different fields and each field must contain two +digits. +The format is: +.Dl [yy[mm[dd[hh]]]]mm[.ss] +Where +.Cm yy +is the last two digits of the year, +the first +.Cm mm +is the month (from 01 to 12), +.Cm dd +is the day of the month (from 01 to 31), +.Cm hh +is the hour of the day (from 00 to 23), +the second +.Cm mm +is the minute (from 00 to 59), +and +.Cm ss +is the seconds (from 00 to 59). +The minute field +.Cm mm +is required, while the other fields are optional and must be added in the +following order: +.Dl Cm hh , dd , mm , yy . +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 +.Fl T +time range can be supplied and checking stops with the first match. +.It Fl U Ar user +Select a file based on its +.Ar user +name, or when starting with a +.Cm # , +a numeric uid. +A '\\' can be used to escape the +.Cm # . +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 +field as described in +.Xr stat 2 +for more information about device ID's. +.It Fl Y +This option is the same as the +.Fl D +option, except that the inode change time is checked using the +pathname created after all the file name modifications have completed. +.It Fl Z +This option is the same as the +.Fl u +option, except that the modification time is checked using the +pathname created after all the file name modifications have completed. +.El +.Pp +The options that operate on the names of files or archive members ( +.Fl c , +.Fl i , +.Fl n , +.Fl s , +.Fl u , +.Fl v , +.Fl D , +.Fl G , +.Fl T , +.Fl U , +.Fl Y , +and +.Fl Z ) +interact as follows. +.Pp +When extracting files during a +.Em read +operation, archive members are +.Sq selected , +based only on the user specified pattern operands as modified by the +.Fl c , +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +.Fl U +options. +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then the +.Fl Y +and +.Fl Z +options will be applied based on the final pathname. +Finally the +.Fl v +option will write the names resulting from these modifications. +.Pp +When archiving files during a +.Em write +operation, or copying files during a +.Em copy +operation, archive members are +.Sq selected , +based only on the user specified pathnames as modified by the +.Fl n , +.Fl u , +.Fl D , +.Fl G , +.Fl T , +and +.Fl U +options (the +.Fl D +option only applies during a copy operation). +Then any +.Fl s +and +.Fl i +options will modify in that order, the names of these selected files. +Then during a +.Em copy +operation the +.Fl Y +and the +.Fl Z +options will be applied based on the final pathname. +Finally the +.Fl v +option will write the names resulting from these modifications. +.Pp +When one or both of the +.Fl u +or +.Fl D +options are specified along with the +.Fl n +option, a file is not considered selected unless it is newer +than the file to which it is compared. +.Sh EXAMPLES +The command: +.Dl pax -w -f /dev/rst0 .\ +copies the contents of the current directory to the device +.Pa /dev/rst0 . +.Pp +The command: +.Dl pax -v -f filename +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 +will copy the entire +.Pa olddir +directory hierarchy to +.Pa newdir . +.Pp +The command: +.Dl pax -r -s ',^//*usr//*,,' -f a.pax +reads the archive +.Pa a.pax , +with all files rooted in ``/usr'' into the archive extracted relative to the +current directory. +.Pp +The command: +.Dl pax -rw -i .\ dest_dir +can be used to interactively select the files to copy from the current +directory to +.Pa dest_dir . +.Pp +The command: +.Dl pax -r -pe -U root -G bin -f a.pax +will extract all files from the archive +.Pa a.pax +which are owned by +.Em root +with group +.Em bin +and will preserve all file permissions. +.Pp +The command: +.Dl pax -r -w -v -Y -Z home /backup +will update (and list) only those files in the destination directory +.Pa /backup +which are older (less recent inode change or file modification times) than +files with the same name found in the source file tree +.Pa home . +.Sh STANDARDS +The +.Nm pax +utility is a superset of the +.St -p1003.2 +standard. +The options +.Fl B , +.Fl D , +.Fl E , +.Fl G , +.Fl H , +.Fl L , +.Fl P , +.Fl T , +.Fl U , +.Fl Y , +.Fl Z , +the archive formats +.Ar bcpio , +.Ar sv4cpio , +.Ar sv4crc , +.Ar tar , +and the flawed archive handling during +.Ar list +and +.Ar read +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: +.Bl -tag -width 2n +.It 0 +All files were processed successfully. +.It 1 +An error occurred. +.El +.Pp +Whenever +.Nm pax +cannot create a file or a link when reading an archive or cannot +find a file when writing an archive, or cannot preserve the user ID, +group ID, or file mode when the +.Fl p +option is specified, a diagnostic message is written to +.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 +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 +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 +may have only partially created the archive which may violate the specific +archive format specification. +.Pp +If while doing a +.Em copy , +.Nm pax +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 +completes it will exit with a non-zero exit status. diff --git a/pax/pax.c b/pax/pax.c new file mode 100644 index 0000000..574ed5b --- /dev/null +++ b/pax/pax.c @@ -0,0 +1,426 @@ +/* $OpenBSD: pax.c,v 1.11 1997/09/01 18:29:58 deraadt Exp $ */ +/* $NetBSD: pax.c,v 1.5 1996/03/26 23:54:20 mrg Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] __attribute__((__unused__)) = +"@(#) Copyright (c) 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)pax.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: pax.c,v 1.11 1997/09/01 18:29:58 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +static int gen_init __P((void)); + +/* + * PAX main routines, general globals and some simple start up routines + */ + +/* + * Variables that can be accessed by any routine within pax + */ +int act = DEFOP; /* read/write/append/copy */ +FSUB *frmt = NULL; /* archive format type */ +int cflag; /* match all EXCEPT pattern/file */ +int cwdfd; /* starting cwd */ +int dflag; /* directory member match only */ +int iflag; /* interactive file/archive rename */ +int kflag; /* do not overwrite existing files */ +int lflag; /* use hard links when possible */ +int nflag; /* select first archive member match */ +int tflag; /* restore access time after read */ +int uflag; /* ignore older modification time files */ +int vflag; /* produce verbose output */ +int zflag; /* use gzip */ +int Dflag; /* same as uflag except inode change time */ +int Hflag; /* follow command line symlinks (write only) */ +int Lflag; /* follow symlinks when writing */ +int Xflag; /* archive files with same device id only */ +int Yflag; /* same as Dflg except after name mode */ +int Zflag; /* same as uflg except after name mode */ +int vfpart; /* is partial verbose output in progress */ +int patime = 1; /* preserve file access time */ +int pmtime = 1; /* preserve file modification times */ +int nodirs; /* do not create directories as needed */ +int pmode; /* preserve file mode bits */ +int pids; /* preserve file uid/gid */ +int rmleadslash = 0; /* remove leading '/' from pathnames */ +int exit_val; /* exit value */ +int docrc; /* check/create file crc */ +char *dirptr; /* destination dir in a copy */ +char *ltmfrmt; /* -v locale time format (if any) */ +char *argv0; /* root of argv[0] */ +sigset_t s_mask; /* signal mask for cleanup critical sect */ + +/* + * PAX - Portable Archive Interchange + * + * A utility to read, write, and write lists of the members of archive + * files and copy directory hierarchies. A variety of archive formats + * are supported (some are described in POSIX 1003.1 10.1): + * + * ustar - 10.1.1 extended tar interchange format + * cpio - 10.1.2 extended cpio interchange format + * tar - old BSD 4.3 tar format + * binary cpio - old cpio with binary header format + * sysVR4 cpio - with and without CRC + * + * This version is a superset of IEEE Std 1003.2b-d3 + * + * Summary of Extensions to the IEEE Standard: + * + * 1 READ ENHANCEMENTS + * 1.1 Operations which read archives will continue to operate even when + * processing archives which may be damaged, truncated, or fail to meet + * format specs in several different ways. Damaged sections of archives + * are detected and avoided if possible. Attempts will be made to resync + * archive read operations even with badly damaged media. + * 1.2 Blocksize requirements are not strictly enforced on archive read. + * Tapes which have variable sized records can be read without errors. + * 1.3 The user can specify via the non-standard option flag -E if error + * resync operation should stop on a media error, try a specified number + * of times to correct, or try to correct forever. + * 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks + * of all zeros will be restored with holes appropriate for the target + * filesystem + * 1.5 The user is notified whenever something is found during archive + * read operations which violates spec (but the read will continue). + * 1.6 Multiple archive volumes can be read and may span over different + * archive devices + * 1.7 Rigidly restores all file attributes exactly as they are stored on the + * archive. + * 1.8 Modification change time ranges can be specified via multiple -T + * options. These allow a user to select files whose modification time + * lies within a specific time range. + * 1.9 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 1.10 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 1.11 File modification time can be checked against exisiting file after + * name modification (-Z) + * + * 2 WRITE ENHANCEMENTS + * 2.1 Write operation will stop instead of allowing a user to create a flawed + * flawed archive (due to any problem). + * 2.2 Archives writtens by pax are forced to strictly conform to both the + * archive and pax the spceific format specifications. + * 2.3 Blocking size and format is rigidly enforced on writes. + * 2.4 Formats which may exhibit header overflow problems (they have fields + * too small for large file systems, such as inode number storage), use + * routines designed to repair this problem. These techniques still + * conform to both pax and format specifications, but no longer truncate + * these fields. This removes any restrictions on using these archive + * formats on large file systems. + * 2.5 Multiple archive volumes can be written and may span over different + * archive devices + * 2.6 A archive volume record limit allows the user to specify the number + * of bytes stored on an archive volume. When reached the user is + * prompted for the next archive volume. This is specified with the + * non-standard -B flag. THe limit is rounded up to the next blocksize. + * 2.7 All archive padding during write use zero filled sections. This makes + * it much easier to pull data out of flawed archive during read + * operations. + * 2.8 Access time reset with the -t applies to all file nodes (including + * directories). + * 2.9 Symbolic links can be followed with -L (optional in the spec). + * 2.10 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 2.11 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 2.12 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 2.13 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * + * 3 COPY ENHANCEMENTS + * 3.1 Sparse files (lseek holes) can be copied without expanding the holes + * into zero filled blocks. The file copy is created with holes which are + * appropriate for the target filesystem + * 3.2 Access time as well as modification time on copied file trees can be + * preserved with the appropriate -p options. + * 3.3 Access time reset with the -t applies to all file nodes (including + * directories). + * 3.4 Symbolic links can be followed with -L (optional in the spec). + * 3.5 Modification or inode change time ranges can be specified via + * multiple -T options. These allow a user to select files whose + * modification or inode change time lies within a specific time range. + * 3.6 Files can be selected based on owner (user name or uid) via one or more + * -U options. + * 3.7 Files can be selected based on group (group name or gid) via one o + * more -G options. + * 3.8 Symlinks which appear on the command line can be followed (without + * following other symlinks; -H flag) + * 3.9 File inode change time can be checked against exisiting file before + * name modification (-D) + * 3.10 File inode change time can be checked against exisiting file after + * name modification (-Y) + * 3.11 File modification time can be checked against exisiting file after + * name modification (-Z) + * + * 4 GENERAL ENHANCEMENTS + * 4.1 Internal structure is designed to isolate format dependent and + * independent functions. Formats are selected via a format driver table. + * This encourages the addition of new archive formats by only having to + * write those routines which id, read and write the archive header. + */ + +/* + * main() + * parse options, set up and operate as specified by the user. + * any operational flaw will set exit_val to non-zero + * Return: 0 if ok, 1 otherwise + */ + +#ifdef __STDC__ +int +main(int argc, char **argv) +#else +int +main(argc, argv) + int argc; + char **argv; +#endif +{ + /* + * Keep a reference to cwd, so we can always come back home. + */ + cwdfd = open(".", O_RDONLY); + if (cwdfd < 0) { + syswarn(0, errno, "Can't open current working directory."); + return(exit_val); + } + + /* + * parse options, determine operational mode, general init + */ + options(argc, argv); + if ((gen_init() < 0) || (tty_init() < 0)) + return(exit_val); + + /* + * select a primary operation mode + */ + switch(act) { + case EXTRACT: + extract(); + break; + case ARCHIVE: + archive(); + break; + case APPND: + append(); + break; + case COPY: + copy(); + break; + default: + case LIST: + list(); + break; + } + return(exit_val); +} + +/* + * sig_cleanup() + * when interrupted we try to do whatever delayed processing we can. + * This is not critical, but we really ought to limit our damage when we + * are aborted by the user. + * Return: + * never.... + */ + +#ifdef __STDC__ +void +sig_cleanup(int which_sig) +#else +void +sig_cleanup(which_sig) + int which_sig; +#endif +{ + /* + * restore modes and times for any dirs we may have created + * or any dirs we may have read. Set vflag and vfpart so the user + * will clearly see the message on a line by itself. + */ + vflag = vfpart = 1; + if (which_sig == SIGXCPU) + paxwarn(0, "Cpu time limit reached, cleaning up."); + else + paxwarn(0, "Signal caught, cleaning up."); + + ar_close(); + proc_dir(); + if (tflag) + atdir_end(); + exit(1); +} + +/* + * gen_init() + * general setup routines. Not all are required, but they really help + * when dealing with a medium to large sized archives. + */ + +#ifdef __STDC__ +static int +gen_init(void) +#else +static int +gen_init() +#endif +{ + struct rlimit reslimit; + struct sigaction n_hand; + struct sigaction o_hand; + + /* + * Really needed to handle large archives. We can run out of memory for + * internal tables really fast when we have a whole lot of files... + */ + if (getrlimit(RLIMIT_DATA , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_DATA , &reslimit); + } + + /* + * should file size limits be waived? if the os limits us, this is + * needed if we want to write a large archive + */ + if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_FSIZE , &reslimit); + } + + /* + * increase the size the stack can grow to + */ + if (getrlimit(RLIMIT_STACK , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_STACK , &reslimit); + } + + /* + * not really needed, but doesn't hurt + */ + if (getrlimit(RLIMIT_RSS , &reslimit) == 0){ + reslimit.rlim_cur = reslimit.rlim_max; + (void)setrlimit(RLIMIT_RSS , &reslimit); + } + + /* + * Handle posix locale + * + * set user defines time printing format for -v option + */ + ltmfrmt = getenv("LC_TIME"); + + /* + * signal handling to reset stored directory times and modes. Since + * we deal with broken pipes via failed writes we ignore it. We also + * deal with any file size limit thorugh failed writes. Cpu time + * limits are caught and a cleanup is forced. + */ + if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) || + (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) || + (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) || + (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) { + paxwarn(1, "Unable to set up signal mask"); + return(-1); + } + n_hand.sa_mask = s_mask; + n_hand.sa_flags = 0; + n_hand.sa_handler = sig_cleanup; + + if ((sigaction(SIGHUP, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGHUP, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGTERM, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGTERM, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGINT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGINT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGQUIT, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGQUIT, &o_hand, &o_hand) < 0)) + goto out; + + if ((sigaction(SIGXCPU, &n_hand, &o_hand) < 0) && + (o_hand.sa_handler == SIG_IGN) && + (sigaction(SIGXCPU, &o_hand, &o_hand) < 0)) + goto out; + + n_hand.sa_handler = SIG_IGN; + if ((sigaction(SIGPIPE, &n_hand, &o_hand) < 0) || + (sigaction(SIGXFSZ, &n_hand, &o_hand) < 0)) + goto out; + return(0); + + out: + syswarn(1, errno, "Unable to set up signal handler"); + return(-1); +} diff --git a/pax/pax.h b/pax/pax.h new file mode 100644 index 0000000..bdf00a4 --- /dev/null +++ b/pax/pax.h @@ -0,0 +1,243 @@ +/* $OpenBSD: pax.h,v 1.9 1997/07/23 19:15:58 kstailey Exp $ */ +/* $NetBSD: pax.h,v 1.3 1995/03/21 09:07:41 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)pax.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * BSD PAX global data structures and constants. + */ + +#define MAXBLK 64512 /* MAX blocksize supported (posix SPEC) */ + /* WARNING: increasing MAXBLK past 32256 */ + /* will violate posix spec. */ +#define MAXBLK_POSIX 32256 /* MAX blocksize supported as per POSIX */ +#define BLKMULT 512 /* blocksize must be even mult of 512 bytes */ + /* Don't even think of changing this */ +#define DEVBLK 8192 /* default read blksize for devices */ +#define FILEBLK 10240 /* default read blksize for files */ +#define PAXPATHLEN 3072 /* maximium path length for pax. MUST be */ + /* longer than the system MAXPATHLEN */ + +/* + * Pax modes of operation + */ +#define LIST 0 /* List the file in an archive */ +#define EXTRACT 1 /* extract the files in an archive */ +#define ARCHIVE 2 /* write a new archive */ +#define APPND 3 /* append to the end of an archive */ +#define COPY 4 /* copy files to destination dir */ +#define DEFOP LIST /* if no flags default is to LIST */ + +/* + * Device type of the current archive volume + */ +#define ISREG 0 /* regular file */ +#define ISCHR 1 /* character device */ +#define ISBLK 2 /* block device */ +#define ISTAPE 3 /* tape drive */ +#define ISPIPE 4 /* pipe/socket */ + +/* + * Format Specific Routine Table + * + * The format specific routine table allows new archive formats to be quickly + * added. Overall pax operation is independent of the actual format used to + * form the archive. Only those routines which deal directly with the archive + * are tailored to the oddities of the specifc format. All other routines are + * independent of the archive format. Data flow in and out of the format + * dependent routines pass pointers to ARCHD structure (described below). + */ +typedef struct { + char *name; /* name of format, this is the name the user */ + /* gives to -x option to select it. */ + int bsz; /* default block size. used when the user */ + /* does not specify a blocksize for writing */ + /* Appends continue to with the blocksize */ + /* the archive is currently using. */ + int hsz; /* Header size in bytes. this is the size of */ + /* the smallest header this format supports. */ + /* Headers are assumed to fit in a BLKMULT. */ + /* If they are bigger, get_head() and */ + /* get_arc() must be adjusted */ + int udev; /* does append require unique dev/ino? some */ + /* formats use the device and inode fields */ + /* to specify hard links. when members in */ + /* the archive have the same inode/dev they */ + /* are assumed to be hard links. During */ + /* append we may have to generate unique ids */ + /* to avoid creating incorrect hard links */ + int hlk; /* does archive store hard links info? if */ + /* not, we do not bother to look for them */ + /* during archive write operations */ + int blkalgn; /* writes must be aligned to blkalgn boundry */ + int inhead; /* is the trailer encoded in a valid header? */ + /* if not, trailers are assumed to be found */ + /* in invalid headers (i.e like tar) */ + int (*id)(); /* checks if a buffer is a valid header */ + /* returns 1 if it is, o.w. returns a 0 */ + int (*st_rd)(); /* initialize routine for read. so format */ + /* can set up tables etc before it starts */ + /* reading an archive */ + int (*rd)(); /* read header routine. passed a pointer to */ + /* ARCHD. It must extract the info from the */ + /* format and store it in the ARCHD struct. */ + /* This routine is expected to fill all the */ + /* fields in the ARCHD (including stat buf) */ + /* 0 is returned when a valid header is */ + /* found. -1 when not valid. This routine */ + /* set the skip and pad fields so the format */ + /* independent routines know the amount of */ + /* padding and the number of bytes of data */ + /* which follow the header. This info is */ + /* used skip to the next file header */ + off_t (*end_rd)(); /* read cleanup. Allows format to clean up */ + /* and MUST RETURN THE LENGTH OF THE TRAILER */ + /* RECORD (so append knows how many bytes */ + /* to move back to rewrite the trailer) */ + int (*st_wr)(); /* initialize routine for write operations */ + int (*wr)(); /* write archive header. Passed an ARCHD */ + /* filled with the specs on the next file to */ + /* archived. Returns a 1 if no file data is */ + /* is to be stored; 0 if file data is to be */ + /* added. A -1 is returned if a write */ + /* operation to the archive failed. this */ + /* function sets the skip and pad fields so */ + /* the proper padding can be added after */ + /* file data. This routine must NEVER write */ + /* a flawed archive header. */ + int (*end_wr)(); /* end write. write the trailer and do any */ + /* other format specific functions needed */ + /* at the ecnd of a archive write */ + int (*trail)(); /* returns 0 if a valid trailer, -1 if not */ + /* For formats which encode the trailer */ + /* outside of a valid header, a return value */ + /* of 1 indicates that the block passed to */ + /* it can never contain a valid header (skip */ + /* this block, no point in looking at it) */ + /* CAUTION: parameters to this function are */ + /* different for trailers inside or outside */ + /* of headers. See get_head() for details */ + int (*rd_data)(); /* read/process file data from the archive */ + int (*wr_data)(); /* write/process file data to the archive */ + int (*options)(); /* process format specific options (-o) */ +} FSUB; + +/* + * Pattern matching structure + * + * Used to store command line patterns + */ +typedef struct pattern { + char *pstr; /* pattern to match, user supplied */ + char *pend; /* end of a prefix match */ + char *chdname; /* the dir to change to if not NULL. */ + int plen; /* length of pstr */ + int flgs; /* processing/state flags */ +#define MTCH 0x1 /* pattern has been matched */ +#define DIR_MTCH 0x2 /* pattern matched a directory */ + struct pattern *fow; /* next pattern */ +} PATTERN; + +/* + * General Archive Structure (used internal to pax) + * + * This structure is used to pass information about archive members between + * the format independent routines and the format specific routines. When + * new archive formats are added, they must accept requests and supply info + * encoded in a structure of this type. The name fields are declared statically + * here, as there is only ONE of these floating around, size is not a major + * consideration. Eventually converting the name fields to a dynamic length + * may be required if and when the supporting operating system removes all + * restrictions on the length of pathnames it will resolve. + */ +typedef struct { + int nlen; /* file name length */ + char name[PAXPATHLEN+1]; /* file name */ + int ln_nlen; /* link name length */ + char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */ + char *org_name; /* orig name in file system */ + PATTERN *pat; /* ptr to pattern match (if any) */ + struct stat sb; /* stat buffer see stat(2) */ + off_t pad; /* bytes of padding after file xfer */ + off_t skip; /* bytes of real data after header */ + /* IMPORTANT. The st_size field does */ + /* not always indicate the amount of */ + /* data following the header. */ + u_long crc; /* file crc */ + int type; /* type of file node */ +#define PAX_DIR 1 /* directory */ +#define PAX_CHR 2 /* character device */ +#define PAX_BLK 3 /* block device */ +#define PAX_REG 4 /* regular file */ +#define PAX_SLK 5 /* symbolic link */ +#define PAX_SCK 6 /* socket */ +#define PAX_FIF 7 /* fifo */ +#define PAX_HLK 8 /* hard link */ +#define PAX_HRG 9 /* hard link to a regular file */ +#define PAX_CTG 10 /* high performance file */ +} ARCHD; + +/* + * Format Specific Options List + * + * Used to pass format options to the format options handler + */ +typedef struct oplist { + char *name; /* option variable name e.g. name= */ + char *value; /* value for option variable */ + struct oplist *fow; /* next option */ +} OPLIST; + +/* + * General Macros + */ +#ifndef MIN +#define MIN(a,b) (((a)<(b))?(a):(b)) +#endif +#define MAJOR(x) major(x) +#define MINOR(x) minor(x) +#define TODEV(x, y) makedev((x), (y)) + +/* + * General Defines + */ +#define HEX 16 +#define OCT 8 +#define _PAX_ 1 diff --git a/pax/sel_subs.c b/pax/sel_subs.c new file mode 100644 index 0000000..a4e1c22 --- /dev/null +++ b/pax/sel_subs.c @@ -0,0 +1,662 @@ +/* $OpenBSD: sel_subs.c,v 1.7 1997/08/17 23:05:09 millert Exp $ */ +/* $NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)sel_subs.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: sel_subs.c,v 1.7 1997/08/17 23:05:09 millert Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "sel_subs.h" +#include "extern.h" + +static int str_sec __P((register char *, time_t *)); +static int usr_match __P((register ARCHD *)); +static int grp_match __P((register ARCHD *)); +static int trng_match __P((register ARCHD *)); + +static TIME_RNG *trhead = NULL; /* time range list head */ +static TIME_RNG *trtail = NULL; /* time range list tail */ +static USRT **usrtb = NULL; /* user selection table */ +static GRPT **grptb = NULL; /* group selection table */ + +/* + * Routines for selection of archive members + */ + +/* + * sel_chk() + * check if this file matches a specfied uid, gid or time range + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#ifdef __STDC__ +int +sel_chk(register ARCHD *arcn) +#else +int +sel_chk(arcn) + register ARCHD *arcn; +#endif +{ + if (((usrtb != NULL) && usr_match(arcn)) || + ((grptb != NULL) && grp_match(arcn)) || + ((trhead != NULL) && trng_match(arcn))) + return(1); + return(0); +} + +/* + * User/group selection routines + * + * Routines to handle user selection of files based on the file uid/gid. To + * add an entry, the user supplies either then name or the uid/gid starting with + * a # on the command line. A \# will eascape the #. + */ + +/* + * usr_add() + * add a user match to the user match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +#ifdef __STDC__ +int +usr_add(register char *str) +#else +int +usr_add(str) + register char *str; +#endif +{ + register u_int indx; + register USRT *pt; + register struct passwd *pw; + register uid_t uid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((usrtb == NULL) && + ((usrtb = (USRT **)calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { + paxwarn(1, "Unable to allocate memory for user selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a user name, \# escapes # as first char in user name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((pw = getpwnam(str)) == NULL) { + paxwarn(1, "Unable to find uid for user: %s", str); + return(-1); + } + uid = (uid_t)pw->pw_uid; + } else +# ifdef NET2_STAT + uid = (uid_t)atoi(str+1); +# else + uid = (uid_t)strtoul(str+1, NULL, 10); +# endif + endpwent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)uid) % USR_TB_SZ; + if ((pt = usrtb[indx]) != NULL) { + while (pt != NULL) { + if (pt->uid == uid) + return(0); + pt = pt->fow; + } + } + + /* + * uid is not yet in the table, add it to the front of the chain + */ + if ((pt = (USRT *)malloc(sizeof(USRT))) != NULL) { + pt->uid = uid; + pt->fow = usrtb[indx]; + usrtb[indx] = pt; + return(0); + } + paxwarn(1, "User selection table out of memory"); + return(-1); +} + +/* + * usr_match() + * check if this files uid matches a selected uid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#ifdef __STDC__ +static int +usr_match(register ARCHD *arcn) +#else +static int +usr_match(arcn) + register ARCHD *arcn; +#endif +{ + register USRT *pt; + + /* + * hash and look for it in the table + */ + pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; + while (pt != NULL) { + if (pt->uid == arcn->sb.st_uid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * grp_add() + * add a group match to the group match hash table + * Return: + * 0 if added ok, -1 otherwise; + */ + +#ifdef __STDC__ +int +grp_add(register char *str) +#else +int +grp_add(str) + register char *str; +#endif +{ + register u_int indx; + register GRPT *pt; + register struct group *gr; + register gid_t gid; + + /* + * create the table if it doesn't exist + */ + if ((str == NULL) || (*str == '\0')) + return(-1); + if ((grptb == NULL) && + ((grptb = (GRPT **)calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { + paxwarn(1, "Unable to allocate memory fo group selection table"); + return(-1); + } + + /* + * figure out user spec + */ + if (str[0] != '#') { + /* + * it is a group name, \# escapes # as first char in group name + */ + if ((str[0] == '\\') && (str[1] == '#')) + ++str; + if ((gr = getgrnam(str)) == NULL) { + paxwarn(1,"Cannot determine gid for group name: %s", str); + return(-1); + } + gid = (gid_t)gr->gr_gid; + } else +# ifdef NET2_STAT + gid = (gid_t)atoi(str+1); +# else + gid = (gid_t)strtoul(str+1, NULL, 10); +# endif + endgrent(); + + /* + * hash it and go down the hash chain (if any) looking for it + */ + indx = ((unsigned)gid) % GRP_TB_SZ; + if ((pt = grptb[indx]) != NULL) { + while (pt != NULL) { + if (pt->gid == gid) + return(0); + pt = pt->fow; + } + } + + /* + * gid not in the table, add it to the front of the chain + */ + if ((pt = (GRPT *)malloc(sizeof(GRPT))) != NULL) { + pt->gid = gid; + pt->fow = grptb[indx]; + grptb[indx] = pt; + return(0); + } + paxwarn(1, "Group selection table out of memory"); + return(-1); +} + +/* + * grp_match() + * check if this files gid matches a selected gid. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#ifdef __STDC__ +static int +grp_match(register ARCHD *arcn) +#else +static int +grp_match(arcn) + register ARCHD *arcn; +#endif +{ + register GRPT *pt; + + /* + * hash and look for it in the table + */ + pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; + while (pt != NULL) { + if (pt->gid == arcn->sb.st_gid) + return(0); + pt = pt->fow; + } + + /* + * not found + */ + return(1); +} + +/* + * Time range selection routines + * + * Routines to handle user selection of files based on the modification and/or + * inode change time falling within a specified time range (the non-standard + * -T flag). The user may specify any number of different file time ranges. + * Time ranges are checked one at a time until a match is found (if at all). + * If the file has a mtime (and/or ctime) which lies within one of the time + * ranges, the file is selected. Time ranges may have a lower and/or a upper + * value. These ranges are inclusive. When no time ranges are supplied to pax + * with the -T option, all members in the archive will be selected by the time + * range routines. When only a lower range is supplied, only files with a + * mtime (and/or ctime) equal to or younger are selected. When only a upper + * range is supplied, only files with a mtime (and/or ctime) equal to or older + * are selected. When the lower time range is equal to the upper time range, + * only files with a mtime (or ctime) of exactly that time are selected. + */ + +/* + * trng_add() + * add a time range match to the time range list. + * This is a non-standard pax option. Lower and upper ranges are in the + * format: [yy[mm[dd[hh]]]]mm[.ss] and are comma separated. + * Time ranges are based on current time, so 1234 would specify a time of + * 12:34 today. + * Return: + * 0 if the time range was added to the list, -1 otherwise + */ + +#ifdef __STDC__ +int +trng_add(register char *str) +#else +int +trng_add(str) + register char *str; +#endif +{ + register TIME_RNG *pt; + register char *up_pt = NULL; + register char *stpt; + register char *flgpt; + register int dot = 0; + + /* + * throw out the badly formed time ranges + */ + if ((str == NULL) || (*str == '\0')) { + paxwarn(1, "Empty time range string"); + return(-1); + } + + /* + * locate optional flags suffix /{cm}. + */ + if ((flgpt = strrchr(str, '/')) != NULL) + *flgpt++ = '\0'; + + for (stpt = str; *stpt != '\0'; ++stpt) { + if ((*stpt >= '0') && (*stpt <= '9')) + continue; + if ((*stpt == ',') && (up_pt == NULL)) { + *stpt = '\0'; + up_pt = stpt + 1; + dot = 0; + continue; + } + + /* + * allow only one dot per range (secs) + */ + if ((*stpt == '.') && (!dot)) { + ++dot; + continue; + } + paxwarn(1, "Improperly specified time range: %s", str); + goto out; + } + + /* + * allocate space for the time range and store the limits + */ + if ((pt = (TIME_RNG *)malloc(sizeof(TIME_RNG))) == NULL) { + paxwarn(1, "Unable to allocate memory for time range"); + return(-1); + } + + /* + * by default we only will check file mtime, but usee can specify + * mtime, ctime (inode change time) or both. + */ + if ((flgpt == NULL) || (*flgpt == '\0')) + pt->flgs = CMPMTME; + else { + pt->flgs = 0; + while (*flgpt != '\0') { + switch(*flgpt) { + case 'M': + case 'm': + pt->flgs |= CMPMTME; + break; + case 'C': + case 'c': + pt->flgs |= CMPCTME; + break; + default: + paxwarn(1, "Bad option %c with time range %s", + *flgpt, str); + goto out; + } + ++flgpt; + } + } + + /* + * start off with the current time + */ + pt->low_time = pt->high_time = time(NULL); + if (*str != '\0') { + /* + * add lower limit + */ + if (str_sec(str, &(pt->low_time)) < 0) { + paxwarn(1, "Illegal lower time range %s", str); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASLOW; + } + + if ((up_pt != NULL) && (*up_pt != '\0')) { + /* + * add upper limit + */ + if (str_sec(up_pt, &(pt->high_time)) < 0) { + paxwarn(1, "Illegal upper time range %s", up_pt); + (void)free((char *)pt); + goto out; + } + pt->flgs |= HASHIGH; + + /* + * check that the upper and lower do not overlap + */ + if (pt->flgs & HASLOW) { + if (pt->low_time > pt->high_time) { + paxwarn(1, "Upper %s and lower %s time overlap", + up_pt, str); + (void)free((char *)pt); + return(-1); + } + } + } + + pt->fow = NULL; + if (trhead == NULL) { + trtail = trhead = pt; + return(0); + } + trtail->fow = pt; + trtail = pt; + return(0); + + out: + paxwarn(1, "Time range format is: [yy[mm[dd[hh]]]]mm[.ss][/[c][m]]"); + return(-1); +} + +/* + * trng_match() + * check if this files mtime/ctime falls within any supplied time range. + * Return: + * 0 if this archive member should be processed, 1 if it should be skipped + */ + +#ifdef __STDC__ +static int +trng_match(register ARCHD *arcn) +#else +static int +trng_match(arcn) + register ARCHD *arcn; +#endif +{ + register TIME_RNG *pt; + + /* + * have to search down the list one at a time looking for a match. + * remember time range limits are inclusive. + */ + pt = trhead; + while (pt != NULL) { + switch(pt->flgs & CMPBOTH) { + case CMPBOTH: + /* + * user wants both mtime and ctime checked for this + * time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPCTME: + /* + * user wants only ctime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_ctime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_ctime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + case CMPMTME: + default: + /* + * user wants only mtime checked for this time range + */ + if (((pt->flgs & HASLOW) && + (arcn->sb.st_mtime < pt->low_time)) || + ((pt->flgs & HASHIGH) && + (arcn->sb.st_mtime > pt->high_time))) { + pt = pt->fow; + continue; + } + break; + } + break; + } + + if (pt == NULL) + return(1); + return(0); +} + +/* + * str_sec() + * Convert a time string in the format of [yy[mm[dd[hh]]]]mm[.ss] to gmt + * seconds. Tval already has current time loaded into it at entry. + * Return: + * 0 if converted ok, -1 otherwise + */ + +#ifdef __STDC__ +static int +str_sec(register char *str, time_t *tval) +#else +static int +str_sec(str, tval) + register char *str; + time_t *tval; +#endif +{ + register struct tm *lt; + register char *dot = NULL; + + lt = localtime(tval); + if ((dot = strchr(str, '.')) != NULL) { + /* + * seconds (.ss) + */ + *dot++ = '\0'; + if (strlen(dot) != 2) + return(-1); + if ((lt->tm_sec = ATOI2(dot)) > 61) + return(-1); + } else + lt->tm_sec = 0; + + switch (strlen(str)) { + case 10: + /* + * year (yy) + * watch out for year 2000 + */ + if ((lt->tm_year = ATOI2(str)) < 69) + lt->tm_year += 100; + str += 2; + /* FALLTHROUGH */ + case 8: + /* + * month (mm) + * watch out months are from 0 - 11 internally + */ + if ((lt->tm_mon = ATOI2(str)) > 12) + return(-1); + --lt->tm_mon; + str += 2; + /* FALLTHROUGH */ + case 6: + /* + * day (dd) + */ + if ((lt->tm_mday = ATOI2(str)) > 31) + return(-1); + str += 2; + /* FALLTHROUGH */ + case 4: + /* + * hour (hh) + */ + if ((lt->tm_hour = ATOI2(str)) > 23) + return(-1); + str += 2; + /* FALLTHROUGH */ + case 2: + /* + * minute (mm) + */ + if ((lt->tm_min = ATOI2(str)) > 59) + return(-1); + break; + default: + return(-1); + } + /* + * convert broken-down time to GMT clock time seconds + */ + if ((*tval = mktime(lt)) == -1) + return(-1); + return(0); +} diff --git a/pax/sel_subs.h b/pax/sel_subs.h new file mode 100644 index 0000000..fc86e9d --- /dev/null +++ b/pax/sel_subs.h @@ -0,0 +1,76 @@ +/* $OpenBSD: sel_subs.h,v 1.2 1996/06/23 14:20:41 deraadt Exp $ */ +/* $NetBSD: sel_subs.h,v 1.3 1995/03/21 09:07:44 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)sel_subs.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structure for storing uid/grp selects (-U, -G non standard options) + */ + +#define USR_TB_SZ 317 /* user selection table size */ +#define GRP_TB_SZ 317 /* user selection table size */ + +typedef struct usrt { + uid_t uid; + struct usrt *fow; /* next uid */ +} USRT; + +typedef struct grpt { + gid_t gid; + struct grpt *fow; /* next gid */ +} GRPT; + +/* + * data structure for storing user supplied time ranges (-T option) + */ + +#define ATOI2(s) ((((s)[0] - '0') * 10) + ((s)[1] - '0')) + +typedef struct time_rng { + time_t low_time; /* lower inclusive time limit */ + time_t high_time; /* higher inclusive time limit */ + int flgs; /* option flags */ +#define HASLOW 0x01 /* has lower time limit */ +#define HASHIGH 0x02 /* has higher time limit */ +#define CMPMTME 0x04 /* compare file modification time */ +#define CMPCTME 0x08 /* compare inode change time */ +#define CMPBOTH (CMPMTME|CMPCTME) /* compare inode and mod time */ + struct time_rng *fow; /* next pattern */ +} TIME_RNG; diff --git a/pax/tables.c b/pax/tables.c new file mode 100644 index 0000000..a0a964e --- /dev/null +++ b/pax/tables.c @@ -0,0 +1,1434 @@ +/* $OpenBSD: tables.c,v 1.9 1997/09/01 18:30:00 deraadt Exp $ */ +/* $NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 5/31/93"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tables.c,v 1.9 1997/09/01 18:30:00 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "tables.h" +#include "extern.h" + +/* + * Routines for controlling the contents of all the different databases pax + * keeps. Tables are dynamically created only when they are needed. The + * goal was speed and the ability to work with HUGE archives. The databases + * were kept simple, but do have complex rules for when the contents change. + * As of this writing, the posix library functions were more complex than + * needed for this application (pax databases have very short lifetimes and + * do not survive after pax is finished). Pax is required to handle very + * large archives. These database routines carefully combine memory usage and + * temporary file storage in ways which will not significantly impact runtime + * performance while allowing the largest possible archives to be handled. + * Trying to force the fit to the posix databases routines was not considered + * time well spent. + */ + +static HRDLNK **ltab = NULL; /* hard link table for detecting hard links */ +static FTM **ftab = NULL; /* file time table for updating arch */ +static NAMT **ntab = NULL; /* interactive rename storage table */ +static DEVT **dtab = NULL; /* device/inode mapping tables */ +static ATDIR **atab = NULL; /* file tree directory time reset table */ +static int dirfd = -1; /* storage for setting created dir time/mode */ +static u_long dircnt; /* entries in dir time/mode storage */ +static int ffd = -1; /* tmp file for file time table name storage */ + +static DEVT *chk_dev __P((dev_t, int)); + +/* + * hard link table routines + * + * The hard link table tries to detect hard links to files using the device and + * inode values. We do this when writing an archive, so we can tell the format + * write routine that this file is a hard link to another file. The format + * write routine then can store this file in whatever way it wants (as a hard + * link if the format supports that like tar, or ignore this info like cpio). + * (Actually a field in the format driver table tells us if the format wants + * hard link info. if not, we do not waste time looking for them). We also use + * the same table when reading an archive. In that situation, this table is + * used by the format read routine to detect hard links from stored dev and + * inode numbers (like cpio). This will allow pax to create a link when one + * can be detected by the archive format. + */ + +/* + * lnk_start + * Creates the hard link table. + * Return: + * 0 if created, -1 if failure + */ + +#ifdef __STDC__ +int +lnk_start(void) +#else +int +lnk_start() +#endif +{ + if (ltab != NULL) + return(0); + if ((ltab = (HRDLNK **)calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) { + paxwarn(1, "Cannot allocate memory for hard link table"); + return(-1); + } + return(0); +} + +/* + * chk_lnk() + * Looks up entry in hard link hash table. If found, it copies the name + * of the file it is linked to (we already saw that file) into ln_name. + * lnkcnt is decremented and if goes to 1 the node is deleted from the + * database. (We have seen all the links to this file). If not found, + * we add the file to the database if it has the potential for having + * hard links to other files we may process (it has a link count > 1) + * Return: + * if found returns 1; if not found returns 0; -1 on error + */ + +#ifdef __STDC__ +int +chk_lnk(register ARCHD *arcn) +#else +int +chk_lnk(arcn) + register ARCHD *arcn; +#endif +{ + register HRDLNK *pt; + register HRDLNK **ppt; + register u_int indx; + + if (ltab == NULL) + return(-1); + /* + * ignore those nodes that cannot have hard links + */ + if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) + return(0); + + /* + * hash inode number and look for this file + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) != NULL) { + /* + * it's hash chain in not empty, walk down looking for it + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found a link. set the node type and copy in the + * name of the file it is to link to. we need to + * handle hardlinks to regular files differently than + * other links. + */ + arcn->ln_nlen = l_strncpy(arcn->ln_name, pt->name, + sizeof(arcn->ln_name) - 1); + arcn->ln_name[arcn->ln_nlen] = '\0'; + if (arcn->type == PAX_REG) + arcn->type = PAX_HRG; + else + arcn->type = PAX_HLK; + + /* + * if we have found all the links to this file, remove + * it from the database + */ + if (--pt->nlink <= 1) { + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); + } + return(1); + } + } + + /* + * we never saw this file before. It has links so we add it to the + * front of this hash chain + */ + if ((pt = (HRDLNK *)malloc(sizeof(HRDLNK))) != NULL) { + if ((pt->name = strdup(arcn->name)) != NULL) { + pt->dev = arcn->sb.st_dev; + pt->ino = arcn->sb.st_ino; + pt->nlink = arcn->sb.st_nlink; + pt->fow = ltab[indx]; + ltab[indx] = pt; + return(0); + } + (void)free((char *)pt); + } + + paxwarn(1, "Hard link table out of memory"); + return(-1); +} + +/* + * purg_lnk + * remove reference for a file that we may have added to the data base as + * a potential source for hard links. We ended up not using the file, so + * we do not want to accidently point another file at it later on. + */ + +#ifdef __STDC__ +void +purg_lnk(register ARCHD *arcn) +#else +void +purg_lnk(arcn) + register ARCHD *arcn; +#endif +{ + register HRDLNK *pt; + register HRDLNK **ppt; + register u_int indx; + + if (ltab == NULL) + return; + /* + * do not bother to look if it could not be in the database + */ + if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) || + (arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) + return; + + /* + * find the hash chain for this inode value, if empty return + */ + indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; + if ((pt = ltab[indx]) == NULL) + return; + + /* + * walk down the list looking for the inode/dev pair, unlink and + * free if found + */ + ppt = &(ltab[indx]); + while (pt != NULL) { + if ((pt->ino == arcn->sb.st_ino) && + (pt->dev == arcn->sb.st_dev)) + break; + ppt = &(pt->fow); + pt = pt->fow; + } + if (pt == NULL) + return; + + /* + * remove and free it + */ + *ppt = pt->fow; + (void)free((char *)pt->name); + (void)free((char *)pt); +} + +/* + * lnk_end() + * pull apart a existing link table so we can reuse it. We do this between + * read and write phases of append with update. (The format may have + * used the link table, and we need to start with a fresh table for the + * write phase + */ + +#ifdef __STDC__ +void +lnk_end(void) +#else +void +lnk_end() +#endif +{ + register int i; + register HRDLNK *pt; + register HRDLNK *ppt; + + if (ltab == NULL) + return; + + for (i = 0; i < L_TAB_SZ; ++i) { + if (ltab[i] == NULL) + continue; + pt = ltab[i]; + ltab[i] = NULL; + + /* + * free up each entry on this chain + */ + while (pt != NULL) { + ppt = pt; + pt = ppt->fow; + (void)free((char *)ppt->name); + (void)free((char *)ppt); + } + } + return; +} + +/* + * modification time table routines + * + * The modification time table keeps track of last modification times for all + * files stored in an archive during a write phase when -u is set. We only + * add a file to the archive if it is newer than a file with the same name + * already stored on the archive (if there is no other file with the same + * name on the archive it is added). This applies to writes and appends. + * An append with an -u must read the archive and store the modification time + * for every file on that archive before starting the write phase. It is clear + * that this is one HUGE database. To save memory space, the actual file names + * are stored in a scatch file and indexed by an in memory hash table. The + * hash table is indexed by hashing the file path. The nodes in the table store + * the length of the filename and the lseek offset within the scratch file + * where the actual name is stored. Since there are never any deletions to this + * table, fragmentation of the scratch file is never a issue. Lookups seem to + * not exhibit any locality at all (files in the database are rarely + * looked up more than once...). So caching is just a waste of memory. The + * only limitation is the amount of scatch file space available to store the + * path names. + */ + +/* + * ftime_start() + * create the file time hash table and open for read/write the scratch + * file. (after created it is unlinked, so when we exit we leave + * no witnesses). + * Return: + * 0 if the table and file was created ok, -1 otherwise + */ + +#ifdef __STDC__ +int +ftime_start(void) +#else +int +ftime_start() +#endif +{ + char *pt; + + if (ftab != NULL) + return(0); + if ((ftab = (FTM **)calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) { + paxwarn(1, "Cannot allocate memory for file time table"); + return(-1); + } + + /* + * get random name and create temporary scratch file, unlink name + * so it will get removed on exit + */ + pt = strdup("/tmp/paxXXXXXX"); + if ((ffd = mkstemp(pt)) < 0) { + syswarn(1, errno, "Unable to create temporary file: %s", pt); + free(pt); + return(-1); + } + (void)unlink(pt); + free(pt); + + return(0); +} + +/* + * chk_ftime() + * looks up entry in file time hash table. If not found, the file is + * added to the hash table and the file named stored in the scratch file. + * If a file with the same name is found, the file times are compared and + * the most recent file time is retained. If the new file was younger (or + * was not in the database) the new file is selected for storage. + * Return: + * 0 if file should be added to the archive, 1 if it should be skipped, + * -1 on error + */ + +#ifdef __STDC__ +int +chk_ftime(register ARCHD *arcn) +#else +int +chk_ftime(arcn) + register ARCHD *arcn; +#endif +{ + register FTM *pt; + register int namelen; + register u_int indx; + char ckname[PAXPATHLEN+1]; + + /* + * no info, go ahead and add to archive + */ + if (ftab == NULL) + return(0); + + /* + * hash the pathname and look up in table + */ + namelen = arcn->nlen; + indx = st_hash(arcn->name, namelen, F_TAB_SZ); + if ((pt = ftab[indx]) != NULL) { + /* + * the hash chain is not empty, walk down looking for match + * only read up the path names if the lengths match, speeds + * up the search a lot + */ + while (pt != NULL) { + if (pt->namelen == namelen) { + /* + * potential match, have to read the name + * from the scratch file. + */ + if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) { + syswarn(1, errno, + "Failed ftime table seek"); + return(-1); + } + if (read(ffd, ckname, namelen) != namelen) { + syswarn(1, errno, + "Failed ftime table read"); + return(-1); + } + + /* + * if the names match, we are done + */ + if (!strncmp(ckname, arcn->name, namelen)) + break; + } + + /* + * try the next entry on the chain + */ + pt = pt->fow; + } + + if (pt != NULL) { + /* + * found the file, compare the times, save the newer + */ + if (arcn->sb.st_mtime > pt->mtime) { + /* + * file is newer + */ + pt->mtime = arcn->sb.st_mtime; + return(0); + } + /* + * file is older + */ + return(1); + } + } + + /* + * not in table, add it + */ + if ((pt = (FTM *)malloc(sizeof(FTM))) != NULL) { + /* + * add the name at the end of the scratch file, saving the + * offset. add the file to the head of the hash chain + */ + if ((pt->seek = lseek(ffd, (off_t)0, SEEK_END)) >= 0) { + if (write(ffd, arcn->name, namelen) == namelen) { + pt->mtime = arcn->sb.st_mtime; + pt->namelen = namelen; + pt->fow = ftab[indx]; + ftab[indx] = pt; + return(0); + } + syswarn(1, errno, "Failed write to file time table"); + } else + syswarn(1, errno, "Failed seek on file time table"); + } else + paxwarn(1, "File time table ran out of memory"); + + if (pt != NULL) + (void)free((char *)pt); + return(-1); +} + +/* + * Interactive rename table routines + * + * The interactive rename table keeps track of the new names that the user + * assignes to files from tty input. Since this map is unique for each file + * we must store it in case there is a reference to the file later in archive + * (a link). Otherwise we will be unable to find the file we know was + * extracted. The remapping of these files is stored in a memory based hash + * table (it is assumed since input must come from /dev/tty, it is unlikely to + * be a very large table). + */ + +/* + * name_start() + * create the interactive rename table + * Return: + * 0 if successful, -1 otherwise + */ + +#ifdef __STDC__ +int +name_start(void) +#else +int +name_start() +#endif +{ + if (ntab != NULL) + return(0); + if ((ntab = (NAMT **)calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) { + paxwarn(1, "Cannot allocate memory for interactive rename table"); + return(-1); + } + return(0); +} + +/* + * add_name() + * add the new name to old name mapping just created by the user. + * If an old name mapping is found (there may be duplicate names on an + * archive) only the most recent is kept. + * Return: + * 0 if added, -1 otherwise + */ + +#ifdef __STDC__ +int +add_name(register char *oname, int onamelen, char *nname) +#else +int +add_name(oname, onamelen, nname) + register char *oname; + int onamelen; + char *nname; +#endif +{ + register NAMT *pt; + register u_int indx; + + if (ntab == NULL) { + /* + * should never happen + */ + paxwarn(0, "No interactive rename table, links may fail\n"); + return(0); + } + + /* + * look to see if we have already mapped this file, if so we + * will update it + */ + indx = st_hash(oname, onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) != NULL) { + /* + * look down the has chain for the file + */ + while ((pt != NULL) && (strcmp(oname, pt->oname) != 0)) + pt = pt->fow; + + if (pt != NULL) { + /* + * found an old mapping, replace it with the new one + * the user just input (if it is different) + */ + if (strcmp(nname, pt->nname) == 0) + return(0); + + (void)free((char *)pt->nname); + if ((pt->nname = strdup(nname)) == NULL) { + paxwarn(1, "Cannot update rename table"); + return(-1); + } + return(0); + } + } + + /* + * this is a new mapping, add it to the table + */ + if ((pt = (NAMT *)malloc(sizeof(NAMT))) != NULL) { + if ((pt->oname = strdup(oname)) != NULL) { + if ((pt->nname = strdup(nname)) != NULL) { + pt->fow = ntab[indx]; + ntab[indx] = pt; + return(0); + } + (void)free((char *)pt->oname); + } + (void)free((char *)pt); + } + paxwarn(1, "Interactive rename table out of memory"); + return(-1); +} + +/* + * sub_name() + * look up a link name to see if it points at a file that has been + * remapped by the user. If found, the link is adjusted to contain the + * new name (oname is the link to name) + */ + +#ifdef __STDC__ +void +sub_name(register char *oname, int *onamelen, size_t onamesize) +#else +void +sub_name(oname, onamelen, onamesize) + register char *oname; + int *onamelen; + size_t onamesize; +#endif +{ + register NAMT *pt; + register u_int indx; + + if (ntab == NULL) + return; + /* + * look the name up in the hash table + */ + indx = st_hash(oname, *onamelen, N_TAB_SZ); + if ((pt = ntab[indx]) == NULL) + return; + + while (pt != NULL) { + /* + * walk down the hash chain looking for a match + */ + if (strcmp(oname, pt->oname) == 0) { + /* + * found it, replace it with the new name + * and return (we know that oname has enough space) + */ + *onamelen = l_strncpy(oname, pt->nname, onamesize - 1); + oname[*onamelen] = '\0'; + return; + } + pt = pt->fow; + } + + /* + * no match, just return + */ + return; +} + +/* + * device/inode mapping table routines + * (used with formats that store device and inodes fields) + * + * device/inode mapping tables remap the device field in a archive header. The + * device/inode fields are used to determine when files are hard links to each + * other. However these values have very little meaning outside of that. This + * database is used to solve one of two different problems. + * + * 1) when files are appended to an archive, while the new files may have hard + * links to each other, you cannot determine if they have hard links to any + * file already stored on the archive from a prior run of pax. We must assume + * that these inode/device pairs are unique only within a SINGLE run of pax + * (which adds a set of files to an archive). So we have to make sure the + * inode/dev pairs we add each time are always unique. We do this by observing + * while the inode field is very dense, the use of the dev field is fairly + * sparse. Within each run of pax, we remap any device number of a new archive + * member that has a device number used in a prior run and already stored in a + * file on the archive. During the read phase of the append, we store the + * device numbers used and mark them to not be used by any file during the + * write phase. If during write we go to use one of those old device numbers, + * we remap it to a new value. + * + * 2) Often the fields in the archive header used to store these values are + * too small to store the entire value. The result is an inode or device value + * which can be truncated. This really can foul up an archive. With truncation + * we end up creating links between files that are really not links (after + * truncation the inodes are the same value). We address that by detecting + * truncation and forcing a remap of the device field to split truncated + * inodes away from each other. Each truncation creates a pattern of bits that + * are removed. We use this pattern of truncated bits to partition the inodes + * on a single device to many different devices (each one represented by the + * truncated bit pattern). All inodes on the same device that have the same + * truncation pattern are mapped to the same new device. Two inodes that + * truncate to the same value clearly will always have different truncation + * bit patterns, so they will be split from away each other. When we spot + * device truncation we remap the device number to a non truncated value. + * (for more info see table.h for the data structures involved). + */ + +/* + * dev_start() + * create the device mapping table + * Return: + * 0 if successful, -1 otherwise + */ + +#ifdef __STDC__ +int +dev_start(void) +#else +int +dev_start() +#endif +{ + if (dtab != NULL) + return(0); + if ((dtab = (DEVT **)calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) { + paxwarn(1, "Cannot allocate memory for device mapping table"); + return(-1); + } + return(0); +} + +/* + * add_dev() + * add a device number to the table. this will force the device to be + * remapped to a new value if it be used during a write phase. This + * function is called during the read phase of an append to prohibit the + * use of any device number already in the archive. + * Return: + * 0 if added ok, -1 otherwise + */ + +#ifdef __STDC__ +int +add_dev(register ARCHD *arcn) +#else +int +add_dev(arcn) + register ARCHD *arcn; +#endif +{ + if (chk_dev(arcn->sb.st_dev, 1) == NULL) + return(-1); + return(0); +} + +/* + * chk_dev() + * check for a device value in the device table. If not found and the add + * flag is set, it is added. This does NOT assign any mapping values, just + * adds the device number as one that need to be remapped. If this device + * is alread mapped, just return with a pointer to that entry. + * Return: + * pointer to the entry for this device in the device map table. Null + * if the add flag is not set and the device is not in the table (it is + * not been seen yet). If add is set and the device cannot be added, null + * is returned (indicates an error). + */ + +#ifdef __STDC__ +static DEVT * +chk_dev(dev_t dev, int add) +#else +static DEVT * +chk_dev(dev, add) + dev_t dev; + int add; +#endif +{ + register DEVT *pt; + register u_int indx; + + if (dtab == NULL) + return(NULL); + /* + * look to see if this device is already in the table + */ + indx = ((unsigned)dev) % D_TAB_SZ; + if ((pt = dtab[indx]) != NULL) { + while ((pt != NULL) && (pt->dev != dev)) + pt = pt->fow; + + /* + * found it, return a pointer to it + */ + if (pt != NULL) + return(pt); + } + + /* + * not in table, we add it only if told to as this may just be a check + * to see if a device number is being used. + */ + if (add == 0) + return(NULL); + + /* + * allocate a node for this device and add it to the front of the hash + * chain. Note we do not assign remaps values here, so the pt->list + * list must be NULL. + */ + if ((pt = (DEVT *)malloc(sizeof(DEVT))) == NULL) { + paxwarn(1, "Device map table out of memory"); + return(NULL); + } + pt->dev = dev; + pt->list = NULL; + pt->fow = dtab[indx]; + dtab[indx] = pt; + return(pt); +} +/* + * map_dev() + * given an inode and device storage mask (the mask has a 1 for each bit + * the archive format is able to store in a header), we check for inode + * and device truncation and remap the device as required. Device mapping + * can also occur when during the read phase of append a device number was + * seen (and was marked as do not use during the write phase). WE ASSUME + * that unsigned longs are the same size or bigger than the fields used + * for ino_t and dev_t. If not the types will have to be changed. + * Return: + * 0 if all ok, -1 otherwise. + */ + +#ifdef __STDC__ +int +map_dev(register ARCHD *arcn, u_long dev_mask, u_long ino_mask) +#else +int +map_dev(arcn, dev_mask, ino_mask) + register ARCHD *arcn; + u_long dev_mask; + u_long ino_mask; +#endif +{ + register DEVT *pt; + register DLIST *dpt; + static dev_t lastdev = 0; /* next device number to try */ + int trc_ino = 0; + int trc_dev = 0; + ino_t trunc_bits = 0; + ino_t nino; + + if (dtab == NULL) + return(0); + /* + * check for device and inode truncation, and extract the truncated + * bit pattern. + */ + if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev) + ++trc_dev; + if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) { + ++trc_ino; + trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask); + } + + /* + * see if this device is already being mapped, look up the device + * then find the truncation bit pattern which applies + */ + if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) { + /* + * this device is already marked to be remapped + */ + for (dpt = pt->list; dpt != NULL; dpt = dpt->fow) + if (dpt->trunc_bits == trunc_bits) + break; + + if (dpt != NULL) { + /* + * we are being remapped for this device and pattern + * change the device number to be stored and return + */ + arcn->sb.st_dev = dpt->dev; + arcn->sb.st_ino = nino; + return(0); + } + } else { + /* + * this device is not being remapped YET. if we do not have any + * form of truncation, we do not need a remap + */ + if (!trc_ino && !trc_dev) + return(0); + + /* + * we have truncation, have to add this as a device to remap + */ + if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL) + goto bad; + + /* + * if we just have a truncated inode, we have to make sure that + * all future inodes that do not truncate (they have the + * truncation pattern of all 0's) continue to map to the same + * device number. We probably have already written inodes with + * this device number to the archive with the truncation + * pattern of all 0's. So we add the mapping for all 0's to the + * same device number. + */ + if (!trc_dev && (trunc_bits != 0)) { + if ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL) + goto bad; + dpt->trunc_bits = 0; + dpt->dev = arcn->sb.st_dev; + dpt->fow = pt->list; + pt->list = dpt; + } + } + + /* + * look for a device number not being used. We must watch for wrap + * around on lastdev (so we do not get stuck looking forever!) + */ + while (++lastdev > 0) { + if (chk_dev(lastdev, 0) != NULL) + continue; + /* + * found an unused value. If we have reached truncation point + * for this format we are hosed, so we give up. Otherwise we + * mark it as being used. + */ + if (((lastdev & ((dev_t)dev_mask)) != lastdev) || + (chk_dev(lastdev, 1) == NULL)) + goto bad; + break; + } + + if ((lastdev <= 0) || ((dpt = (DLIST *)malloc(sizeof(DLIST))) == NULL)) + goto bad; + + /* + * got a new device number, store it under this truncation pattern. + * change the device number this file is being stored with. + */ + dpt->trunc_bits = trunc_bits; + dpt->dev = lastdev; + dpt->fow = pt->list; + pt->list = dpt; + arcn->sb.st_dev = lastdev; + arcn->sb.st_ino = nino; + return(0); + + bad: + paxwarn(1, "Unable to fix truncated inode/device field when storing %s", + arcn->name); + paxwarn(0, "Archive may create improper hard links when extracted"); + return(0); +} + +/* + * directory access/mod time reset table routines (for directories READ by pax) + * + * The pax -t flag requires that access times of archive files to be the same + * before being read by pax. For regular files, access time is restored after + * the file has been copied. This database provides the same functionality for + * directories read during file tree traversal. Restoring directory access time + * is more complex than files since directories may be read several times until + * all the descendants in their subtree are visited by fts. Directory access + * and modification times are stored during the fts pre-order visit (done + * before any descendants in the subtree is visited) and restored after the + * fts post-order visit (after all the descendants have been visited). In the + * case of premature exit from a subtree (like from the effects of -n), any + * directory entries left in this database are reset during final cleanup + * operations of pax. Entries are hashed by inode number for fast lookup. + */ + +/* + * atdir_start() + * create the directory access time database for directories READ by pax. + * Return: + * 0 is created ok, -1 otherwise. + */ + +#ifdef __STDC__ +int +atdir_start(void) +#else +int +atdir_start() +#endif +{ + if (atab != NULL) + return(0); + if ((atab = (ATDIR **)calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) { + paxwarn(1,"Cannot allocate space for directory access time table"); + return(-1); + } + return(0); +} + + +/* + * atdir_end() + * walk through the directory access time table and reset the access time + * of any directory who still has an entry left in the database. These + * entries are for directories READ by pax + */ + +#ifdef __STDC__ +void +atdir_end(void) +#else +void +atdir_end() +#endif +{ + register ATDIR *pt; + register int i; + + if (atab == NULL) + return; + /* + * for each non-empty hash table entry reset all the directories + * chained there. + */ + for (i = 0; i < A_TAB_SZ; ++i) { + if ((pt = atab[i]) == NULL) + continue; + /* + * remember to force the times, set_ftime() looks at pmtime + * and patime, which only applies to things CREATED by pax, + * not read by pax. Read time reset is controlled by -t. + */ + for (; pt != NULL; pt = pt->fow) + set_ftime(pt->name, pt->mtime, pt->atime, 1); + } +} + +/* + * add_atdir() + * add a directory to the directory access time table. Table is hashed + * and chained by inode number. This is for directories READ by pax + */ + +#ifdef __STDC__ +void +add_atdir(char *fname, dev_t dev, ino_t ino, time_t mtime, time_t atime) +#else +void +add_atdir(fname, dev, ino, mtime, atime) + char *fname; + dev_t dev; + ino_t ino; + time_t mtime; + time_t atime; +#endif +{ + register ATDIR *pt; + register u_int indx; + + if (atab == NULL) + return; + + /* + * make sure this directory is not already in the table, if so just + * return (the older entry always has the correct time). The only + * way this will happen is when the same subtree can be traversed by + * different args to pax and the -n option is aborting fts out of a + * subtree before all the post-order visits have been made). + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) != NULL) { + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + pt = pt->fow; + } + + /* + * oops, already there. Leave it alone. + */ + if (pt != NULL) + return; + } + + /* + * add it to the front of the hash chain + */ + if ((pt = (ATDIR *)malloc(sizeof(ATDIR))) != NULL) { + if ((pt->name = strdup(fname)) != NULL) { + pt->dev = dev; + pt->ino = ino; + pt->mtime = mtime; + pt->atime = atime; + pt->fow = atab[indx]; + atab[indx] = pt; + return; + } + (void)free((char *)pt); + } + + paxwarn(1, "Directory access time reset table ran out of memory"); + return; +} + +/* + * get_atdir() + * look up a directory by inode and device number to obtain the access + * and modification time you want to set to. If found, the modification + * and access time parameters are set and the entry is removed from the + * table (as it is no longer needed). These are for directories READ by + * pax + * Return: + * 0 if found, -1 if not found. + */ + +#ifdef __STDC__ +int +get_atdir(dev_t dev, ino_t ino, time_t *mtime, time_t *atime) +#else +int +get_atdir(dev, ino, mtime, atime) + dev_t dev; + ino_t ino; + time_t *mtime; + time_t *atime; +#endif +{ + register ATDIR *pt; + register ATDIR **ppt; + register u_int indx; + + if (atab == NULL) + return(-1); + /* + * hash by inode and search the chain for an inode and device match + */ + indx = ((unsigned)ino) % A_TAB_SZ; + if ((pt = atab[indx]) == NULL) + return(-1); + + ppt = &(atab[indx]); + while (pt != NULL) { + if ((pt->ino == ino) && (pt->dev == dev)) + break; + /* + * no match, go to next one + */ + ppt = &(pt->fow); + pt = pt->fow; + } + + /* + * return if we did not find it. + */ + if (pt == NULL) + return(-1); + + /* + * found it. return the times and remove the entry from the table. + */ + *ppt = pt->fow; + *mtime = pt->mtime; + *atime = pt->atime; + (void)free((char *)pt->name); + (void)free((char *)pt); + return(0); +} + +/* + * directory access mode and time storage routines (for directories CREATED + * by pax). + * + * Pax requires that extracted directories, by default, have their access/mod + * times and permissions set to the values specified in the archive. During the + * actions of extracting (and creating the destination subtree during -rw copy) + * directories extracted may be modified after being created. Even worse is + * that these directories may have been created with file permissions which + * prohibits any descendants of these directories from being extracted. When + * directories are created by pax, access rights may be added to permit the + * creation of files in their subtree. Every time pax creates a directory, the + * times and file permissions specified by the archive are stored. After all + * files have been extracted (or copied), these directories have their times + * and file modes reset to the stored values. The directory info is restored in + * reverse order as entries were added to the data file from root to leaf. To + * restore atime properly, we must go backwards. The data file consists of + * records with two parts, the file name followed by a DIRDATA trailer. The + * fixed sized trailer contains the size of the name plus the off_t location in + * the file. To restore we work backwards through the file reading the trailer + * then the file name. + */ + +/* + * dir_start() + * set up the directory time and file mode storage for directories CREATED + * by pax. + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +dir_start(void) +#else +int +dir_start() +#endif +{ + char *pt; + + if (dirfd != -1) + return(0); + + /* + * unlink the file so it goes away at termination by itself + */ + pt = strdup("/tmp/paxXXXXXX"); + if ((dirfd = mkstemp(pt)) >= 0) { + (void)unlink(pt); + free(pt); + return(0); + } + paxwarn(1, "Unable to create temporary file for directory times: %s", pt); + free(pt); + return(-1); +} + +/* + * add_dir() + * add the mode and times for a newly CREATED directory + * name is name of the directory, psb the stat buffer with the data in it, + * frc_mode is a flag that says whether to force the setting of the mode + * (ignoring the user set values for preserving file mode). Frc_mode is + * for the case where we created a file and found that the resulting + * directory was not writeable and the user asked for file modes to NOT + * be preserved. (we have to preserve what was created by default, so we + * have to force the setting at the end. this is stated explicitly in the + * pax spec) + */ + +#ifdef __STDC__ +void +add_dir(char *name, int nlen, struct stat *psb, int frc_mode) +#else +void +add_dir(name, nlen, psb, frc_mode) + char *name; + int nlen; + struct stat *psb; + int frc_mode; +#endif +{ + DIRDATA dblk; + + if (dirfd < 0) + return; + + /* + * get current position (where file name will start) so we can store it + * in the trailer + */ + if ((dblk.npos = lseek(dirfd, 0L, SEEK_CUR)) < 0) { + paxwarn(1,"Unable to store mode and times for directory: %s",name); + return; + } + + /* + * write the file name followed by the trailer + */ + dblk.nlen = nlen + 1; + dblk.mode = psb->st_mode & 0xffff; + dblk.mtime = psb->st_mtime; + dblk.atime = psb->st_atime; + dblk.frc_mode = frc_mode; + if ((write(dirfd, name, dblk.nlen) == dblk.nlen) && + (write(dirfd, (char *)&dblk, sizeof(dblk)) == sizeof(dblk))) { + ++dircnt; + return; + } + + paxwarn(1,"Unable to store mode and times for created directory: %s",name); + return; +} + +/* + * proc_dir() + * process all file modes and times stored for directories CREATED + * by pax + */ + +#ifdef __STDC__ +void +proc_dir(void) +#else +void +proc_dir() +#endif +{ + char name[PAXPATHLEN+1]; + DIRDATA dblk; + u_long cnt; + + if (dirfd < 0) + return; + /* + * read backwards through the file and process each directory + */ + for (cnt = 0; cnt < dircnt; ++cnt) { + /* + * read the trailer, then the file name, if this fails + * just give up. + */ + if (lseek(dirfd, -((off_t)sizeof(dblk)), SEEK_CUR) < 0) + break; + if (read(dirfd,(char *)&dblk, sizeof(dblk)) != sizeof(dblk)) + break; + if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) + break; + if (read(dirfd, name, dblk.nlen) != dblk.nlen) + break; + if (lseek(dirfd, dblk.npos, SEEK_SET) < 0) + break; + + /* + * frc_mode set, make sure we set the file modes even if + * the user didn't ask for it (see file_subs.c for more info) + */ + if (pmode || dblk.frc_mode) + set_pmode(name, dblk.mode); + if (patime || pmtime) + set_ftime(name, dblk.mtime, dblk.atime, 0); + } + + (void)close(dirfd); + dirfd = -1; + if (cnt != dircnt) + paxwarn(1,"Unable to set mode and times for created directories"); + return; +} + +/* + * database independent routines + */ + +/* + * st_hash() + * hashes filenames to a u_int for hashing into a table. Looks at the tail + * end of file, as this provides far better distribution than any other + * part of the name. For performance reasons we only care about the last + * MAXKEYLEN chars (should be at LEAST large enough to pick off the file + * name). Was tested on 500,000 name file tree traversal from the root + * and gave almost a perfectly uniform distribution of keys when used with + * prime sized tables (MAXKEYLEN was 128 in test). Hashes (sizeof int) + * chars at a time and pads with 0 for last addition. + * Return: + * the hash value of the string MOD (%) the table size. + */ + +#ifdef __STDC__ +u_int +st_hash(char *name, int len, int tabsz) +#else +u_int +st_hash(name, len, tabsz) + char *name; + int len; + int tabsz; +#endif +{ + register char *pt; + register char *dest; + register char *end; + register int i; + register u_int key = 0; + register int steps; + register int res; + u_int val; + + /* + * only look at the tail up to MAXKEYLEN, we do not need to waste + * time here (remember these are pathnames, the tail is what will + * spread out the keys) + */ + if (len > MAXKEYLEN) { + pt = &(name[len - MAXKEYLEN]); + len = MAXKEYLEN; + } else + pt = name; + + /* + * calculate the number of u_int size steps in the string and if + * there is a runt to deal with + */ + steps = len/sizeof(u_int); + res = len % sizeof(u_int); + + /* + * add up the value of the string in unsigned integer sized pieces + * too bad we cannot have unsigned int aligned strings, then we + * could avoid the expensive copy. + */ + for (i = 0; i < steps; ++i) { + end = pt + sizeof(u_int); + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * add in the runt padded with zero to the right + */ + if (res) { + val = 0; + end = pt + res; + dest = (char *)&val; + while (pt < end) + *dest++ = *pt++; + key += val; + } + + /* + * return the result mod the table size + */ + return(key % tabsz); +} diff --git a/pax/tables.h b/pax/tables.h new file mode 100644 index 0000000..1d9c813 --- /dev/null +++ b/pax/tables.h @@ -0,0 +1,175 @@ +/* $OpenBSD: tables.h,v 1.2 1996/06/23 14:20:43 deraadt Exp $ */ +/* $NetBSD: tables.h,v 1.3 1995/03/21 09:07:47 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)tables.h 8.1 (Berkeley) 5/31/93 + */ + +/* + * data structures and constants used by the different databases kept by pax + */ + +/* + * Hash Table Sizes MUST BE PRIME, if set too small performance suffers. + * Probably safe to expect 500000 inodes per tape. Assuming good key + * distribution (inodes) chains of under 50 long (worse case) is ok. + */ +#define L_TAB_SZ 2503 /* hard link hash table size */ +#define F_TAB_SZ 50503 /* file time hash table size */ +#define N_TAB_SZ 541 /* interactive rename hash table */ +#define D_TAB_SZ 317 /* unique device mapping table */ +#define A_TAB_SZ 317 /* ftree dir access time reset table */ +#define MAXKEYLEN 64 /* max number of chars for hash */ + +/* + * file hard link structure (hashed by dev/ino and chained) used to find the + * hard links in a file system or with some archive formats (cpio) + */ +typedef struct hrdlnk { + char *name; /* name of first file seen with this ino/dev */ + dev_t dev; /* files device number */ + ino_t ino; /* files inode number */ + u_long nlink; /* expected link count */ + struct hrdlnk *fow; +} HRDLNK; + +/* + * Archive write update file time table (the -u, -C flag), hashed by filename. + * Filenames are stored in a scratch file at seek offset into the file. The + * file time (mod time) and the file name length (for a quick check) are + * stored in a hash table node. We were forced to use a scratch file because + * with -u, the mtime for every node in the archive must always be available + * to compare against (and this data can get REALLY large with big archives). + * By being careful to read only when we have a good chance of a match, the + * performance loss is not measurable (and the size of the archive we can + * handle is greatly increased). + */ +typedef struct ftm { + int namelen; /* file name length */ + time_t mtime; /* files last modification time */ + off_t seek; /* loacation in scratch file */ + struct ftm *fow; +} FTM; + +/* + * Interactive rename table (-i flag), hashed by orig filename. + * We assume this will not be a large table as this mapping data can only be + * obtained through interactive input by the user. Nobody is going to type in + * changes for 500000 files? We use chaining to resolve collisions. + */ + +typedef struct namt { + char *oname; /* old name */ + char *nname; /* new name typed in by the user */ + struct namt *fow; +} NAMT; + +/* + * Unique device mapping tables. Some protocols (e.g. cpio) require that the + * pair will uniquely identify a file in an archive unless they + * are links to the same file. Appending to archives can break this. For those + * protocols that have this requirement we map c_dev to a unique value not seen + * in the archive when we append. We also try to handle inode truncation with + * this table. (When the inode field in the archive header are too small, we + * remap the dev on writes to remove accidental collisions). + * + * The list is hashed by device number using chain collision resolution. Off of + * each DEVT are linked the various remaps for this device based on those bits + * in the inode which were truncated. For example if we are just remapping to + * avoid a device number during an update append, off the DEVT we would have + * only a single DLIST that has a truncation id of 0 (no inode bits were + * stripped for this device so far). When we spot inode truncation we create + * a new mapping based on the set of bits in the inode which were stripped off. + * so if the top four bits of the inode are stripped and they have a pattern of + * 0110...... (where . are those bits not truncated) we would have a mapping + * assigned for all inodes that has the same 0110.... pattern (with this dev + * number of course). This keeps the mapping sparse and should be able to store + * close to the limit of files which can be represented by the optimal + * combination of dev and inode bits, and without creating a fouled up archive. + * Note we also remap truncated devs in the same way (an exercise for the + * dedicated reader; always wanted to say that...:) + */ + +typedef struct devt { + dev_t dev; /* the orig device number we now have to map */ + struct devt *fow; /* new device map list */ + struct dlist *list; /* map list based on inode truncation bits */ +} DEVT; + +typedef struct dlist { + ino_t trunc_bits; /* truncation pattern for a specific map */ + dev_t dev; /* the new device id we use */ + struct dlist *fow; +} DLIST; + +/* + * ftree directory access time reset table. When we are done with with a + * subtree we reset the access and mod time of the directory when the tflag is + * set. Not really explicitly specified in the pax spec, but easy and fast to + * do (and this may have even been intended in the spec, it is not clear). + * table is hashed by inode with chaining. + */ + +typedef struct atdir { + char *name; /* name of directory to reset */ + dev_t dev; /* dev and inode for fast lookup */ + ino_t ino; + time_t mtime; /* access and mod time to reset to */ + time_t atime; + struct atdir *fow; +} ATDIR; + +/* + * created directory time and mode storage entry. After pax is finished during + * extraction or copy, we must reset directory access modes and times that + * may have been modified after creation (they no longer have the specified + * times and/or modes). We must reset time in the reverse order of creation, + * because entries are added from the top of the file tree to the bottom. + * We MUST reset times from leaf to root (it will not work the other + * direction). Entries are recorded into a spool file to make reverse + * reading faster. + */ + +typedef struct dirdata { + int nlen; /* length of the directory name (includes \0) */ + off_t npos; /* position in file where this dir name starts */ + mode_t mode; /* file mode to restore */ + time_t mtime; /* mtime to set */ + time_t atime; /* atime to set */ + int frc_mode; /* do we force mode settings? */ +} DIRDATA; diff --git a/pax/tar.1 b/pax/tar.1 new file mode 100644 index 0000000..9e31fb2 --- /dev/null +++ b/pax/tar.1 @@ -0,0 +1,239 @@ +.\" +.\" Copyright (c) 1996 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. +.\" 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 THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $OpenBSD: tar.1,v 1.13 1997/05/29 15:47:19 millert Exp $ +.\" +.Dd June 11, 1996 +.Dt TAR 1 +.Os +.Sh NAME +.Nm tar +.Nd tape archiver +.Sh SYNOPSIS +.Nm +.No [-]{crtux}[befhmopvwzHLPXZ014578] +.Op Ar archive +.Op Ar blocksize +.\" XXX how to do this right? +.No [-C +.Ar directory +.No ] +.No [-s +.Ar replstr +.No ] +.Ar file1 +.Op Ar file2... +.Sh DESCRIPTION +The +.Nm +command creates, adds files to, or extracts files from an +archive file in \*Qtar\*U format. A tar archive is often +stored on a magnetic tape, but can be a floppy or a regular +disk file. +.Pp +One of the following flags must be present: +.Bl -tag -width Ar +.It Fl c +Create new archive, or overwrite an existing archive, +adding the specified files to it. +.It Fl r +Append the named new files to existing archive. Note that +this will only work on media on which an end-of-file mark +can be overwritten. +.It Fl t +List contents of archive. If any files are named on the +command line, only those files will be listed. +.It Fl u +Alias for +.Fl r +.It Fl x +Extract files from archive. If any files are named on the +command line, only those files will be extracted from the +archive. If more than one copy of a file exists in the +archive, later copies will overwrite earlier copies during +extration. +.El +.Pp +In addition to the flags mentioned above, any of the following +flags may be used: +.Bl -tag -width Ar +.It Fl b Ar "blocking factor" +Set blocking factor to use for the archive, +.Nm +uses 512 byte blocks. The default is 20, the maximum is 126. +Archives with a blocking factor larger 63 violate the +.Tn POSIX +standard and will not be portable to all systems. +.It Fl e +Stop after first error. +.It Fl f Ar archive +Filename where the archive is stored. Defaults to +.Pa /dev/rst0 +.It Fl h +Follow symbolic links as if they were normal files +or directories. +.It Fl m +Do not preserve modification time. +.It Fl O +Write old-style (non-POSIX) archives. +.It Fl o +Don't write directory information that the older (V7) style +.Nm +is unable to decode. +This implies the +.Fl O +flag. +.It Fl p +Preserve user id, group id, file mode, access and modification +times if possible. The user id and group id will only be set +if the user is the superuser (unless these values correspond +to the user's user and group ids). +.It Fl s Ar replstr +Modify the file or archive member names specified by the +.Ar pattern +or +.Ar file +operands according to the substitution expression +.Ar replstr , +using the syntax of the +.Xr ed 1 +utility regular expressions. +The format of these regular expressions are: +.Dl /old/new/[gp] +As in +.Xr ed 1 , +.Cm old +is a basic regular expression and +.Cm new +can contain an ampersand (&), \\n (where n is a digit) back-references, +or subexpression matching. +The +.Cm old +string may also contain +.Dv +characters. +Any non-null character can be used as a delimiter (/ is shown here). +Multiple +.Fl s +expressions can be specified. +The expressions are applied in the order they are specified on the +command line, terminating with the first successful substitution. +The optional trailing +.Cm g +continues to apply the substitution expression to the pathname substring +which starts with the first character following the end of the last successful +substitution. The first unsuccessful substitution stops the operation of the +.Cm g +option. +The optional trailing +.Cm p +will cause the final result of a successful substitution to be written to +.Dv standard error +in the following format: +.Dl >> +File or archive member names that substitute to the empty string +are not selected and will be skipped. +.It Fl v +Verbose operation mode. +.It Fl w +Interactively rename files. This option causes +.Nm +to prompt the user for the filename to use when storing or +extracting files in an archive. +.It Fl z +Compress archive using gzip. +.It Fl C Ar directory +This is a positional argument which sets the working directory for the +following files. When extracting, files will be extracted into +the specified directory; when creating, the specified files will be matched +from the directory. +.It Fl H +Follow symlinks given on command line only. +.It Fl L +Follow all symlinks. +.It Fl P +Do not strip leading slashes (``/'') from pathnames. +The default is to strip leading slashes. +.It Fl X +Do not cross mount points in the file system. +.It Fl Z +Compress archive using compress. +.El +.Pp +The options +.Op Fl 014578 +can be used to select one of the compiled-in backup devices, +.Pa /dev/rstN . +.Sh FILES +.Bl -tag -width "/dev/rst0" +.It Pa /dev/rst0 +The default archive name +.El +.Sh SEE ALSO +.Xr pax 1 , +.Xr cpio 1 +.Sh AUTHOR +Keith Muller at the University of California, San Diego +.Sh ERRORS +.Nm +will exit with one of the following values: +.Bl -tag -width 2n +.It 0 +All files were processed successfully. +.It 1 +An error occured. +.El +.Pp +Whenever +.Nm +cannot create a file or a link when extracting an archive or cannot +find a file while writing an archive, or cannot preserve the user +ID, group ID, file mode or access and modification times when the +.Fl p +options is specified, a diagnostic message is written to standard +error and a non-zero exit value will be returned, but processing +will continue. In the case where +.Nm +cannot create a link to a file, +.Nm +will not create a second copy of the file. +.Pp +If the extraction of a file from an archive is prematurely terminated +by a signal or error, +.Nm +may have only partially extracted the file the user wanted. +Additionally, the file modes of extracted files and directories may +have incorrect file bits, and the modification and access times may +be wrong. +.Pp +If the creation of an archive is prematurely terminated by a signal +or error, +.Nm +may have only partially created the archive which may violate the +specific archive format specification. diff --git a/pax/tar.c b/pax/tar.c new file mode 100644 index 0000000..9044fe5 --- /dev/null +++ b/pax/tar.c @@ -0,0 +1,1213 @@ +/* $OpenBSD: tar.c,v 1.12 1997/09/01 18:30:03 deraadt Exp $ */ +/* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tar.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tar.c,v 1.12 1997/09/01 18:30:03 deraadt Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#include "tar.h" + +/* + * Routines for reading, writing and header identify of various versions of tar + */ + +static u_long tar_chksm __P((register char *, register int)); +static char *name_split __P((register char *, register int)); +static int ul_oct __P((u_long, register char *, register int, int)); +#ifndef NET2_STAT +static int uqd_oct __P((u_quad_t, register char *, register int, int)); +#endif + +/* + * Routines common to all versions of tar + */ + +static int tar_nodir; /* do not write dirs under old tar */ + +/* + * tar_endwr() + * add the tar trailer of two null blocks + * Return: + * 0 if ok, -1 otherwise (what wr_skip returns) + */ + +#ifdef __STDC__ +int +tar_endwr(void) +#else +int +tar_endwr() +#endif +{ + return(wr_skip((off_t)(NULLCNT*BLKMULT))); +} + +/* + * tar_endrd() + * no cleanup needed here, just return size of trailer (for append) + * Return: + * size of trailer (2 * BLKMULT) + */ + +#ifdef __STDC__ +off_t +tar_endrd(void) +#else +off_t +tar_endrd() +#endif +{ + return((off_t)(NULLCNT*BLKMULT)); +} + +/* + * tar_trail() + * Called to determine if a header block is a valid trailer. We are passed + * the block, the in_sync flag (which tells us we are in resync mode; + * looking for a valid header), and cnt (which starts at zero) which is + * used to count the number of empty blocks we have seen so far. + * Return: + * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block + * could never contain a header. + */ + +#ifdef __STDC__ +int +tar_trail(register char *buf, register int in_resync, register int *cnt) +#else +int +tar_trail(buf, in_resync, cnt) + register char *buf; + register int in_resync; + register int *cnt; +#endif +{ + register int i; + + /* + * look for all zero, trailer is two consecutive blocks of zero + */ + for (i = 0; i < BLKMULT; ++i) { + if (buf[i] != '\0') + break; + } + + /* + * if not all zero it is not a trailer, but MIGHT be a header. + */ + if (i != BLKMULT) + return(-1); + + /* + * When given a zero block, we must be careful! + * If we are not in resync mode, check for the trailer. Have to watch + * out that we do not mis-identify file data as the trailer, so we do + * NOT try to id a trailer during resync mode. During resync mode we + * might as well throw this block out since a valid header can NEVER be + * a block of all 0 (we must have a valid file name). + */ + if (!in_resync && (++*cnt >= NULLCNT)) + return(0); + return(1); +} + +/* + * ul_oct() + * convert an unsigned long to an octal string. many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +#ifdef __STDC__ +static int +ul_oct(u_long val, register char *str, register int len, int term) +#else +static int +ul_oct(val, str, len, term) + u_long val; + register char *str; + register int len; + int term; +#endif +{ + register char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch(term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == (u_long)0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_long)0) + return(-1); + return(0); +} + +#ifndef NET2_STAT +/* + * uqd_oct() + * convert an u_quad_t to an octal string. one of many oddball field + * termination characters are used by the various versions of tar in the + * different fields. term selects which kind to use. str is '0' padded + * at the front to len. we are unable to use only one format as many old + * tar readers are very cranky about this. + * Return: + * 0 if the number fit into the string, -1 otherwise + */ + +#ifdef __STDC__ +static int +uqd_oct(u_quad_t val, register char *str, register int len, int term) +#else +static int +uqd_oct(val, str, len, term) + u_quad_t val; + register char *str; + register int len; + int term; +#endif +{ + register char *pt; + + /* + * term selects the appropriate character(s) for the end of the string + */ + pt = str + len - 1; + switch(term) { + case 3: + *pt-- = '\0'; + break; + case 2: + *pt-- = ' '; + *pt-- = '\0'; + break; + case 1: + *pt-- = ' '; + break; + case 0: + default: + *pt-- = '\0'; + *pt-- = ' '; + break; + } + + /* + * convert and blank pad if there is space + */ + while (pt >= str) { + *pt-- = '0' + (char)(val & 0x7); + if ((val = val >> 3) == 0) + break; + } + + while (pt >= str) + *pt-- = '0'; + if (val != (u_quad_t)0) + return(-1); + return(0); +} +#endif + +/* + * tar_chksm() + * calculate the checksum for a tar block counting the checksum field as + * all blanks (BLNKSUM is that value pre-calculated, the sume of 8 blanks). + * NOTE: we use len to short circuit summing 0's on write since we ALWAYS + * pad headers with 0. + * Return: + * unsigned long checksum + */ + +#ifdef __STDC__ +static u_long +tar_chksm(register char *blk, register int len) +#else +static u_long +tar_chksm(blk, len) + register char *blk; + register int len; +#endif +{ + register char *stop; + register char *pt; + u_long chksm = BLNKSUM; /* inital value is checksum field sum */ + + /* + * add the part of the block before the checksum field + */ + pt = blk; + stop = blk + CHK_OFFSET; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + /* + * move past the checksum field and keep going, spec counts the + * checksum field as the sum of 8 blanks (which is pre-computed as + * BLNKSUM). + * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding + * starts, no point in summing zero's) + */ + pt += CHK_LEN; + stop = blk + len; + while (pt < stop) + chksm += (u_long)(*pt++ & 0xff); + return(chksm); +} + +/* + * Routines for old BSD style tar (also made portable to sysV tar) + */ + +/* + * tar_id() + * determine if a block given to us is a valid tar header (and not a USTAR + * header). We have to be on the lookout for those pesky blocks of all + * zero's. + * Return: + * 0 if a tar header, -1 otherwise + */ + +#ifdef __STDC__ +int +tar_id(register char *blk, int size) +#else +int +tar_id(blk, size) + register char *blk; + int size; +#endif +{ + register HD_TAR *hd; + register HD_USTAR *uhd; + + if (size < BLKMULT) + return(-1); + hd = (HD_TAR *)blk; + uhd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test, then make + * sure this is not a ustar header by looking for the ustar magic + * cookie. We should use TMAGLEN, but some USTAR archive programs are + * wrong and create archives missing the \0. Last we check the + * checksum. If this is ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + return(0); +} + +/* + * tar_opt() + * handle tar format specific -o options + * Return: + * 0 if ok -1 otherwise + */ + +#ifdef __STDC__ +int +tar_opt(void) +#else +int +tar_opt() +#endif +{ + OPLIST *opt; + + while ((opt = opt_next()) != NULL) { + if (strcmp(opt->name, TAR_OPTION) || + strcmp(opt->value, TAR_NODIR)) { + paxwarn(1, "Unknown tar format -o option/value pair %s=%s", + opt->name, opt->value); + paxwarn(1,"%s=%s is the only supported tar format option", + TAR_OPTION, TAR_NODIR); + return(-1); + } + + /* + * we only support one option, and only when writing + */ + if ((act != APPND) && (act != ARCHIVE)) { + paxwarn(1, "%s=%s is only supported when writing.", + opt->name, opt->value); + return(-1); + } + tar_nodir = 1; + } + return(0); +} + + +/* + * tar_rd() + * extract the values out of block already determined to be a tar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +#ifdef __STDC__ +int +tar_rd(register ARCHD *arcn, register char *buf) +#else +int +tar_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_TAR *hd; + register char *pt; + + /* + * we only get proper sized buffers passed to us + */ + if (tar_id(buf, BLKMULT) < 0) + return(-1); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + arcn->pat = NULL; + + /* + * copy out the name and values in the stat buffer + */ + hd = (HD_TAR *)buf; + arcn->nlen = l_strncpy(arcn->name, hd->name, sizeof(arcn->name) - 1); + arcn->name[arcn->nlen] = '\0'; + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & + 0xfff); + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * have to look at the last character, it may be a '/' and that is used + * to encode this as a directory + */ + pt = &(arcn->name[arcn->nlen - 1]); + arcn->pad = 0; + arcn->skip = 0; + switch(hd->linkflag) { + case SYMTYPE: + /* + * symbolic link, need to get the link name and set the type in + * the st_mode so -v printing will look correct. + */ + arcn->type = PAX_SLK; + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(arcn->ln_name) - 1); + arcn->ln_name[arcn->ln_nlen] = '\0'; + arcn->sb.st_mode |= S_IFLNK; + break; + case LNKTYPE: + /* + * hard link, need to get the link name, set the type in the + * st_mode and st_nlink so -v printing will look better. + */ + arcn->type = PAX_HLK; + arcn->sb.st_nlink = 2; + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(arcn->ln_name) - 1); + arcn->ln_name[arcn->ln_nlen] = '\0'; + + /* + * no idea of what type this thing really points at, but + * we set something for printing only. + */ + arcn->sb.st_mode |= S_IFREG; + break; + case DIRTYPE: + /* + * It is a directory, set the mode for -v printing + */ + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + break; + case AREGTYPE: + case REGTYPE: + default: + /* + * If we have a trailing / this is a directory and NOT a file. + */ + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + if (*pt == '/') { + /* + * it is a directory, set the mode for -v printing + */ + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + } else { + /* + * have a file that will be followed by data. Set the + * skip value to the size field and caluculate the size + * of the padding. + */ + arcn->type = PAX_REG; + arcn->sb.st_mode |= S_IFREG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + } + break; + } + + /* + * strip off any trailing slash. + */ + if (*pt == '/') { + *pt = '\0'; + --arcn->nlen; + } + return(0); +} + +/* + * tar_wr() + * write a tar header for the file specified in the ARCHD to the archive. + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, each field + * of tar has it own spec for the termination character(s). + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#ifdef __STDC__ +int +tar_wr(register ARCHD *arcn) +#else +int +tar_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_TAR *hd; + int len; + char hdblk[sizeof(HD_TAR)]; + + /* + * check for those file system types which tar cannot store + */ + switch(arcn->type) { + case PAX_DIR: + /* + * user asked that dirs not be written to the archive + */ + if (tar_nodir) + return(1); + break; + case PAX_CHR: + paxwarn(1, "Tar cannot archive a character device %s", + arcn->org_name); + return(1); + case PAX_BLK: + paxwarn(1, "Tar cannot archive a block device %s", arcn->org_name); + return(1); + case PAX_SCK: + paxwarn(1, "Tar cannot archive a socket %s", arcn->org_name); + return(1); + case PAX_FIF: + paxwarn(1, "Tar cannot archive a fifo %s", arcn->org_name); + return(1); + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->ln_nlen > sizeof(hd->linkname)) { + paxwarn(1,"Link name too long for tar %s", arcn->ln_name); + return(1); + } + break; + case PAX_REG: + case PAX_CTG: + default: + break; + } + + /* + * check file name len, remember extra char for dirs (the / at the end) + */ + len = arcn->nlen; + if (arcn->type == PAX_DIR) + ++len; + if (len >= sizeof(hd->name)) { + paxwarn(1, "File name too long for tar %s", arcn->name); + return(1); + } + + /* + * copy the data out of the ARCHD into the tar header based on the type + * of the file. Remember many tar readers want the unused fields to be + * padded with zero. We set the linkflag field (type), the linkname + * (or zero if not used),the size, and set the padding (if any) to be + * added after the file data (0 for all other types, as they only have + * a header) + */ + hd = (HD_TAR *)hdblk; + l_strncpy(hd->name, arcn->name, sizeof(hd->name) - 1); + hd->name[sizeof(hd->name) - 1] = '\0'; + arcn->pad = 0; + + if (arcn->type == PAX_DIR) { + /* + * directories are the same as files, except have a filename + * that ends with a /, we add the slash here. No data follows, + * dirs, so no pad. + */ + hd->linkflag = AREGTYPE; + memset(hd->linkname, 0, sizeof(hd->linkname)); + hd->name[len-1] = '/'; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if (arcn->type == PAX_SLK) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = SYMTYPE; + l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1); + hd->linkname[sizeof(hd->linkname) - 1] = '\0'; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else if ((arcn->type == PAX_HLK) || (arcn->type == PAX_HRG)) { + /* + * no data follows this file, so no pad + */ + hd->linkflag = LNKTYPE; + l_strncpy(hd->linkname,arcn->ln_name, sizeof(hd->linkname) - 1); + hd->linkname[sizeof(hd->linkname) - 1] = '\0'; + if (ul_oct((u_long)0L, hd->size, sizeof(hd->size), 1)) + goto out; + } else { + /* + * data follows this file, so set the pad + */ + hd->linkflag = AREGTYPE; + memset(hd->linkname, 0, sizeof(hd->linkname)); +# ifdef NET2_STAT + if (ul_oct((u_long)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), 1)) { +# endif + paxwarn(1,"File is too large for tar %s", arcn->org_name); + return(1); + } + arcn->pad = TAR_PAD(arcn->sb.st_size); + } + + /* + * copy those fields that are independent of the type + */ + if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) || + ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) || + ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0) || + ul_oct((u_long)arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1)) + goto out; + + /* + * calculate and add the checksum, then write the header. A return of + * 0 tells the caller to now write the file data, 1 says no data needs + * to be written + */ + if (ul_oct(tar_chksm(hdblk, sizeof(HD_TAR)), hd->chksum, + sizeof(hd->chksum), 3)) + goto out; + if (wr_rdbuf(hdblk, sizeof(HD_TAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_TAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Tar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * Routines for POSIX ustar + */ + +/* + * ustar_strd() + * initialization for ustar read + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +ustar_strd(void) +#else +int +ustar_strd() +#endif +{ + if ((usrtb_start() < 0) || (grptb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_stwr() + * initialization for ustar write + * Return: + * 0 if ok, -1 otherwise + */ + +#ifdef __STDC__ +int +ustar_stwr(void) +#else +int +ustar_stwr() +#endif +{ + if ((uidtb_start() < 0) || (gidtb_start() < 0)) + return(-1); + return(0); +} + +/* + * ustar_id() + * determine if a block given to us is a valid ustar header. We have to + * be on the lookout for those pesky blocks of all zero's + * Return: + * 0 if a ustar header, -1 otherwise + */ + +#ifdef __STDC__ +int +ustar_id(char *blk, int size) +#else +int +ustar_id(blk, size) + char *blk; + int size; +#endif +{ + register HD_USTAR *hd; + + if (size < BLKMULT) + return(-1); + hd = (HD_USTAR *)blk; + + /* + * check for block of zero's first, a simple and fast test then check + * ustar magic cookie. We should use TMAGLEN, but some USTAR archive + * programs are fouled up and create archives missing the \0. Last we + * check the checksum. If ok we have to assume it is a valid header. + */ + if (hd->name[0] == '\0') + return(-1); + if (strncmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) + return(-1); + if (asc_ul(hd->chksum,sizeof(hd->chksum),OCT) != tar_chksm(blk,BLKMULT)) + return(-1); + return(0); +} + +/* + * ustar_rd() + * extract the values out of block already determined to be a ustar header. + * store the values in the ARCHD parameter. + * Return: + * 0 + */ + +#ifdef __STDC__ +int +ustar_rd(register ARCHD *arcn, register char *buf) +#else +int +ustar_rd(arcn, buf) + register ARCHD *arcn; + register char *buf; +#endif +{ + register HD_USTAR *hd; + register char *dest; + register int cnt = 0; + dev_t devmajor; + dev_t devminor; + + /* + * we only get proper sized buffers + */ + if (ustar_id(buf, BLKMULT) < 0) + return(-1); + arcn->org_name = arcn->name; + arcn->sb.st_nlink = 1; + arcn->pat = NULL; + arcn->nlen = 0; + hd = (HD_USTAR *)buf; + + /* + * see if the filename is split into two parts. if, so joint the parts. + * we copy the prefix first and add a / between the prefix and name. + */ + dest = arcn->name; + if (*(hd->prefix) != '\0') { + cnt = l_strncpy(dest, hd->prefix, sizeof(arcn->name) - 2); + dest += cnt; + *dest++ = '/'; + cnt++; + } + arcn->nlen = cnt + l_strncpy(dest, hd->name, sizeof(arcn->name) - cnt); + arcn->name[arcn->nlen] = '\0'; + + /* + * follow the spec to the letter. we should only have mode bits, strip + * off all other crud we may be passed. + */ + arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & + 0xfff); + arcn->sb.st_size = (size_t)asc_ul(hd->size, sizeof(hd->size), OCT); + arcn->sb.st_mtime = (time_t)asc_ul(hd->mtime, sizeof(hd->mtime), OCT); + arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; + + /* + * If we can find the ascii names for gname and uname in the password + * and group files we will use the uid's and gid they bind. Otherwise + * we use the uid and gid values stored in the header. (This is what + * the posix spec wants). + */ + hd->gname[sizeof(hd->gname) - 1] = '\0'; + if (gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) + arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); + hd->uname[sizeof(hd->uname) - 1] = '\0'; + if (uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) + arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); + + /* + * set the defaults, these may be changed depending on the file type + */ + arcn->ln_name[0] = '\0'; + arcn->ln_nlen = 0; + arcn->pad = 0; + arcn->skip = 0; + arcn->sb.st_rdev = (dev_t)0; + + /* + * set the mode and PAX type according to the typeflag in the header + */ + switch(hd->typeflag) { + case FIFOTYPE: + arcn->type = PAX_FIF; + arcn->sb.st_mode |= S_IFIFO; + break; + case DIRTYPE: + arcn->type = PAX_DIR; + arcn->sb.st_mode |= S_IFDIR; + arcn->sb.st_nlink = 2; + + /* + * Some programs that create ustar archives append a '/' + * to the pathname for directories. This clearly violates + * ustar specs, but we will silently strip it off anyway. + */ + if (arcn->name[arcn->nlen - 1] == '/') + arcn->name[--arcn->nlen] = '\0'; + break; + case BLKTYPE: + case CHRTYPE: + /* + * this type requires the rdev field to be set. + */ + if (hd->typeflag == BLKTYPE) { + arcn->type = PAX_BLK; + arcn->sb.st_mode |= S_IFBLK; + } else { + arcn->type = PAX_CHR; + arcn->sb.st_mode |= S_IFCHR; + } + devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); + devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); + arcn->sb.st_rdev = TODEV(devmajor, devminor); + break; + case SYMTYPE: + case LNKTYPE: + if (hd->typeflag == SYMTYPE) { + arcn->type = PAX_SLK; + arcn->sb.st_mode |= S_IFLNK; + } else { + arcn->type = PAX_HLK; + /* + * so printing looks better + */ + arcn->sb.st_mode |= S_IFREG; + arcn->sb.st_nlink = 2; + } + /* + * copy the link name + */ + arcn->ln_nlen = l_strncpy(arcn->ln_name, hd->linkname, + sizeof(arcn->ln_name) - 1); + arcn->ln_name[arcn->ln_nlen] = '\0'; + break; + case CONTTYPE: + case AREGTYPE: + case REGTYPE: + default: + /* + * these types have file data that follows. Set the skip and + * pad fields. + */ + arcn->type = PAX_REG; + arcn->pad = TAR_PAD(arcn->sb.st_size); + arcn->skip = arcn->sb.st_size; + arcn->sb.st_mode |= S_IFREG; + break; + } + return(0); +} + +/* + * ustar_wr() + * write a ustar header for the file specified in the ARCHD to the archive + * Have to check for file types that cannot be stored and file names that + * are too long. Be careful of the term (last arg) to ul_oct, we only use + * '\0' for the termination character (this is different than picky tar) + * ASSUMED: space after header in header block is zero filled + * Return: + * 0 if file has data to be written after the header, 1 if file has NO + * data to write after the header, -1 if archive write failed + */ + +#ifdef __STDC__ +int +ustar_wr(register ARCHD *arcn) +#else +int +ustar_wr(arcn) + register ARCHD *arcn; +#endif +{ + register HD_USTAR *hd; + register char *pt; + char hdblk[sizeof(HD_USTAR)]; + + /* + * check for those file system types ustar cannot store + */ + if (arcn->type == PAX_SCK) { + paxwarn(1, "Ustar cannot archive a socket %s", arcn->org_name); + return(1); + } + + /* + * check the length of the linkname + */ + if (((arcn->type == PAX_SLK) || (arcn->type == PAX_HLK) || + (arcn->type == PAX_HRG)) && (arcn->ln_nlen >= sizeof(hd->linkname))){ + paxwarn(1, "Link name too long for ustar %s", arcn->ln_name); + return(1); + } + + /* + * split the path name into prefix and name fields (if needed). if + * pt != arcn->name, the name has to be split + */ + if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { + paxwarn(1, "File name too long for ustar %s", arcn->name); + return(1); + } + hd = (HD_USTAR *)hdblk; + arcn->pad = 0L; + + /* + * split the name, or zero out the prefix + */ + if (pt != arcn->name) { + /* + * name was split, pt points at the / where the split is to + * occur, we remove the / and copy the first part to the prefix + */ + *pt = '\0'; + l_strncpy(hd->prefix, arcn->name, sizeof(hd->prefix) - 1); + *pt++ = '/'; + } else + memset(hd->prefix, 0, sizeof(hd->prefix)); + + /* + * copy the name part. this may be the whole path or the part after + * the prefix + */ + l_strncpy(hd->name, pt, sizeof(hd->name) - 1); + hd->name[sizeof(hd->name) - 1] = '\0'; + + /* + * set the fields in the header that are type dependent + */ + switch(arcn->type) { + 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; + case PAX_CHR: + case PAX_BLK: + if (arcn->type == PAX_CHR) + hd->typeflag = CHRTYPE; + 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)) + 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; + case PAX_SLK: + case PAX_HLK: + case PAX_HRG: + if (arcn->type == PAX_SLK) + hd->typeflag = SYMTYPE; + else + 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; + case PAX_REG: + case PAX_CTG: + default: + /* + * file data with this type, set the padding + */ + if (arcn->type == PAX_CTG) + hd->typeflag = CONTTYPE; + else + hd->typeflag = REGTYPE; + 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, + sizeof(hd->size), 3)) { +# else + if (uqd_oct((u_quad_t)arcn->sb.st_size, hd->size, + sizeof(hd->size), 3)) { +# endif + paxwarn(1,"File is too long for ustar %s",arcn->org_name); + return(1); + } + break; + } + + l_strncpy(hd->magic, TMAGIC, TMAGLEN); + l_strncpy(hd->version, TVERSION, TVERSLEN); + + /* + * set the remaining fields. Some versions want all 16 bits of mode + * we better humor them (they really do not meet spec though).... + */ + if (ul_oct((u_long)arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3) || + ul_oct((u_long)arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 3) || + ul_oct((u_long)arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 3) || + ul_oct((u_long)arcn->sb.st_mtime,hd->mtime,sizeof(hd->mtime),3)) + goto out; + l_strncpy(hd->uname,name_uid(arcn->sb.st_uid, 0),sizeof(hd->uname)); + l_strncpy(hd->gname,name_gid(arcn->sb.st_gid, 0),sizeof(hd->gname)); + + /* + * calculate and store the checksum write the header to the archive + * return 0 tells the caller to now write the file data, 1 says no data + * needs to be written + */ + if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, + sizeof(hd->chksum), 3)) + goto out; + if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) + return(-1); + if (wr_skip((off_t)(BLKMULT - sizeof(HD_USTAR))) < 0) + return(-1); + if ((arcn->type == PAX_CTG) || (arcn->type == PAX_REG)) + return(0); + return(1); + + out: + /* + * header field is out of range + */ + paxwarn(1, "Ustar header field is too small for %s", arcn->org_name); + return(1); +} + +/* + * name_split() + * see if the name has to be split for storage in a ustar header. We try + * to fit the entire name in the name field without splitting if we can. + * The split point is always at a / + * Return + * character pointer to split point (always the / that is to be removed + * if the split is not needed, the points is set to the start of the file + * name (it would violate the spec to split there). A NULL is returned if + * the file name is too long + */ + +#ifdef __STDC__ +static char * +name_split(register char *name, register int len) +#else +static char * +name_split(name, len) + register char *name; + register int len; +#endif +{ + register char *start; + + /* + * check to see if the file name is small enough to fit in the name + * field. if so just return a pointer to the name. + */ + if (len < TNMSZ) + return(name); + if (len > (TPFSZ + TNMSZ)) + return(NULL); + + /* + * we start looking at the biggest sized piece that fits in the name + * field. We walk foward looking for a slash to split at. The idea is + * to find the biggest piece to fit in the name field (or the smallest + * prefix we can find) + */ + start = name + len - TNMSZ; + while ((*start != '\0') && (*start != '/')) + ++start; + + /* + * if we hit the end of the string, this name cannot be split, so we + * cannot store this file. + */ + if (*start == '\0') + return(NULL); + len = start - name; + + /* + * NOTE: /str where the length of str == TNMSZ can not be stored under + * the p1003.1-1990 spec for ustar. We could force a prefix of / and + * the file would then expand on extract to //str. The len == 0 below + * makes this special case follow the spec to the letter. + */ + if ((len >= TPFSZ) || (len == 0)) + return(NULL); + + /* + * ok have a split point, return it to the caller + */ + return(start); +} diff --git a/pax/tar.h b/pax/tar.h new file mode 100644 index 0000000..79a8ce4 --- /dev/null +++ b/pax/tar.h @@ -0,0 +1,151 @@ +/* $OpenBSD: tar.h,v 1.5 1997/04/16 03:50:25 millert Exp $ */ +/* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. 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. + * + * @(#)tar.h 8.2 (Berkeley) 4/18/94 + */ + +/* + * defines and data structures common to all tar formats + */ +#define CHK_LEN 8 /* length of checksum field */ +#define TNMSZ 100 /* size of name field */ +#ifdef _PAX_ +#define NULLCNT 2 /* number of null blocks in trailer */ +#define CHK_OFFSET 148 /* start of chksum field */ +#define BLNKSUM 256L /* sum of checksum field using ' ' */ +#endif /* _PAX_ */ + +/* + * Values used in typeflag field in all tar formats + * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers) + */ +#define REGTYPE '0' /* Regular File */ +#define AREGTYPE '\0' /* Regular File */ +#define LNKTYPE '1' /* Link */ +#define SYMTYPE '2' /* Symlink */ +#define CHRTYPE '3' /* Character Special File */ +#define BLKTYPE '4' /* Block Special File */ +#define DIRTYPE '5' /* Directory */ +#define FIFOTYPE '6' /* FIFO */ +#define CONTTYPE '7' /* high perf file */ + +/* + * Mode field encoding of the different file types - values in octal + */ +#define TSUID 04000 /* Set UID on execution */ +#define TSGID 02000 /* Set GID on execution */ +#define TSVTX 01000 /* Reserved */ +#define TUREAD 00400 /* Read by owner */ +#define TUWRITE 00200 /* Write by owner */ +#define TUEXEC 00100 /* Execute/Search by owner */ +#define TGREAD 00040 /* Read by group */ +#define TGWRITE 00020 /* Write by group */ +#define TGEXEC 00010 /* Execute/Search by group */ +#define TOREAD 00004 /* Read by other */ +#define TOWRITE 00002 /* Write by other */ +#define TOEXEC 00001 /* Execute/Search by other */ + +#ifdef _PAX_ +/* + * Pad with a bit mask, much faster than doing a mod but only works on powers + * of 2. Macro below is for block of 512 bytes. + */ +#define TAR_PAD(x) ((512 - ((x) & 511)) & 511) +#endif /* _PAX_ */ + +/* + * structure of an old tar header as it appeared in BSD releases + */ +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char linkflag; /* norm, hard, or sym. */ + char linkname[TNMSZ]; /* linked to name */ +} HD_TAR; + +#ifdef _PAX_ +/* + * -o options for BSD tar to not write directories to the archive + */ +#define TAR_NODIR "nodir" +#define TAR_OPTION "write_opt" + +/* + * default device names + */ +#define DEV_0 "/dev/rst0" +#define DEV_1 "/dev/rst1" +#define DEV_4 "/dev/rst4" +#define DEV_5 "/dev/rst5" +#define DEV_7 "/dev/rst7" +#define DEV_8 "/dev/rst8" +#endif /* _PAX_ */ + +/* + * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990 + */ +#define TPFSZ 155 +#define TMAGIC "ustar" /* ustar and a null */ +#define TMAGLEN 6 +#define TVERSION "00" /* 00 and no null */ +#define TVERSLEN 2 + +typedef struct { + char name[TNMSZ]; /* name of entry */ + char mode[8]; /* mode */ + char uid[8]; /* uid */ + char gid[8]; /* gid */ + char size[12]; /* size */ + char mtime[12]; /* modification time */ + char chksum[CHK_LEN]; /* checksum */ + char typeflag; /* type of file. */ + char linkname[TNMSZ]; /* linked to name */ + char magic[TMAGLEN]; /* magic cookie */ + char version[TVERSLEN]; /* version */ + char uname[32]; /* ascii owner name */ + char gname[32]; /* ascii group name */ + char devmajor[8]; /* major device number */ + char devminor[8]; /* minor device number */ + char prefix[TPFSZ]; /* linked to name */ +} HD_USTAR; diff --git a/pax/tty_subs.c b/pax/tty_subs.c new file mode 100644 index 0000000..61f4e78 --- /dev/null +++ b/pax/tty_subs.c @@ -0,0 +1,251 @@ +/* $OpenBSD: tty_subs.c,v 1.5 1997/07/25 18:58:39 mickey Exp $ */ +/* $NetBSD: tty_subs.c,v 1.5 1995/03/21 09:07:52 cgd Exp $ */ + +/*- + * Copyright (c) 1992 Keith Muller. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Keith Muller of the University of California, San Diego. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tty_subs.c 8.2 (Berkeley) 4/18/94"; +#else +static char rcsid[] __attribute__((__unused__)) = "$OpenBSD: tty_subs.c,v 1.5 1997/07/25 18:58:39 mickey Exp $"; +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pax.h" +#include "extern.h" +#ifdef __STDC__ +#include +#else +#include +#endif + +/* + * routines that deal with I/O to and from the user + */ + +#define DEVTTY "/dev/tty" /* device for interactive i/o */ +static FILE *ttyoutf = NULL; /* output pointing at control tty */ +static FILE *ttyinf = NULL; /* input pointing at control tty */ + +/* + * tty_init() + * try to open the controlling termina (if any) for this process. if the + * open fails, future ops that require user input will get an EOF + */ + +#ifdef __STDC__ +int +tty_init(void) +#else +int +tty_init() +#endif +{ + int ttyfd; + + if ((ttyfd = open(DEVTTY, O_RDWR)) >= 0) { + if ((ttyoutf = fdopen(ttyfd, "w")) != NULL) { + if ((ttyinf = fdopen(ttyfd, "r")) != NULL) + return(0); + (void)fclose(ttyoutf); + } + (void)close(ttyfd); + } + + if (iflag) { + paxwarn(1, "Fatal error, cannot open %s", DEVTTY); + return(-1); + } + return(0); +} + +/* + * tty_prnt() + * print a message using the specified format to the controlling tty + * if there is no controlling terminal, just return. + */ + +#ifdef __STDC__ +void +tty_prnt(char *fmt, ...) +#else +void +tty_prnt(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +# ifdef __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (ttyoutf == NULL) + return; + (void)vfprintf(ttyoutf, fmt, ap); + va_end(ap); + (void)fflush(ttyoutf); +} + +/* + * tty_read() + * read a string from the controlling terminal if it is open into the + * supplied buffer + * Return: + * 0 if data was read, -1 otherwise. + */ + +#ifdef __STDC__ +int +tty_read(char *str, int len) +#else +int +tty_read(str, len) + char *str; + int len; +#endif +{ + register char *pt; + + if ((--len <= 0) || (ttyinf == NULL) || (fgets(str,len,ttyinf) == NULL)) + return(-1); + *(str + len) = '\0'; + + /* + * strip off that trailing newline + */ + if ((pt = strchr(str, '\n')) != NULL) + *pt = '\0'; + return(0); +} + +/* + * paxwarn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#ifdef __STDC__ +void +paxwarn(int set, char *fmt, ...) +#else +void +paxwarn(set, fmt, va_alist) + int set; + char *fmt; + va_dcl +#endif +{ + va_list ap; +# ifdef __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (set) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fputc('\n', stderr); +} + +/* + * syswarn() + * write a warning message to stderr. if "set" the exit value of pax + * will be non-zero. + */ + +#ifdef __STDC__ +void +syswarn(int set, int errnum, char *fmt, ...) +#else +void +syswarn(set, errnum, fmt, va_alist) + int set; + int errnum; + char *fmt; + va_dcl +#endif +{ + va_list ap; +# ifdef __STDC__ + va_start(ap, fmt); +# else + va_start(ap); +# endif + if (set) + exit_val = 1; + /* + * when vflag we better ship out an extra \n to get this message on a + * line by itself + */ + if (vflag && vfpart) { + (void)fputc('\n', stderr); + vfpart = 0; + } + (void)fprintf(stderr, "%s: ", argv0); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + + /* + * format and print the errno + */ + if (errnum > 0) + (void)fprintf(stderr, " <%s>", strerror(errnum)); + (void)fputc('\n', stderr); +} diff --git a/rm/Makefile b/rm/Makefile new file mode 100644 index 0000000..f557347 --- /dev/null +++ b/rm/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = rm + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = rm.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rm.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/rm/Makefile.postamble b/rm/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/rm/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/rm/Makefile.preamble b/rm/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/rm/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/rm/PB.project b/rm/PB.project new file mode 100644 index 0000000..9eb1a48 --- /dev/null +++ b/rm/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (rm.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rm.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = rm; + 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/rm/rm.1 b/rm/rm.1 new file mode 100644 index 0000000..98bf94f --- /dev/null +++ b/rm/rm.1 @@ -0,0 +1,157 @@ +.\" $NetBSD: rm.1,v 1.9 1997/10/20 08:53:14 enami Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rm.1 8.5 (Berkeley) 12/5/94 +.\" +.Dd December 5, 1994 +.Dt RM 1 +.Os +.Sh NAME +.Nm rm +.Nd remove directory entries +.Sh SYNOPSIS +.Nm +.Op Fl f | Fl i +.Op Fl dPRrW +.Ar file ... +.Sh DESCRIPTION +The +.Nm +utility attempts to remove the non-directory type files specified on the +command line. +If the permissions of the file do not permit writing, and the standard +input device is a terminal, the user is prompted (on the standard error +output) for confirmation. +.Pp +The options are as follows: +.Bl -tag -width flag +.It Fl d +Attempt to remove directories as well as other types of files. +.It Fl f +Attempt to remove the files without prompting for confirmation, +regardless of the file's permissions. +If the file does not exist, do not display a diagnostic message or modify +the exit status to reflect an error. +The +.Fl f +option overrides any previous +.Fl i +options. +.It Fl i +Request confirmation before attempting to remove each file, regardless of +the file's permissions, or whether or not the standard input device is a +terminal. +The +.Fl i +option overrides any previous +.Fl f +options. +.It Fl P +Overwrite regular files before deleting them. +Files are overwritten three times, first with the byte pattern 0xff, +then 0x00, and then 0xff again, before they are deleted. +.It Fl R +Attempt to remove the file hierarchy rooted in each file argument. +The +.Fl R +option implies the +.Fl d +option. +If the +.Fl i +option is specified, the user is prompted for confirmation before +each directory's contents are processed (as well as before the attempt +is made to remove the directory). +If the user does not respond affirmatively, the file hierarchy rooted in +that directory is skipped. +.Pp +.It Fl r +Equivalent to +.Fl R . +.It Fl W +Attempts to undelete the named files. +Currently, this option can only be used to recover +files covered by whiteouts. +.El +.Pp +The +.Nm +utility removes symbolic links, not the files referenced by the links. +.Pp +It is an error to attempt to remove the files ``.'' and ``..''. +.Pp +The +.Nm +utility exits 0 if all of the named files or file hierarchies were removed, +or if the +.Fl f +option was specified and all of the existing files or file hierarchies were +removed. +If an error occurs, +.Nm +exits with a value >0. +.Sh SEE ALSO +.Xr rmdir 1 , +.Xr undelete 2 , +.Xr unlink 2 , +.Xr fts 3 , +.Xr symlink 7 +.Sh BUGS +The +.Fl P +option assumes that the underlying file system is a fixed-block file +system. +UFS is a fixed-block file system, LFS is not. +In addition, only regular files are overwritten, other types of files +are not. +.Sh COMPATIBILITY +The +.Nm +utility differs from historical implementations in that the +.Fl f +option only masks attempts to remove non-existent files instead of +masking a large variety of errors. +.Pp +Also, historical +.Bx +implementations prompted on the standard output, +not the standard error output. +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. diff --git a/rm/rm.c b/rm/rm.c new file mode 100644 index 0000000..38e01b8 --- /dev/null +++ b/rm/rm.c @@ -0,0 +1,448 @@ +/* $NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rm.c 8.8 (Berkeley) 4/27/95"; +#else +__RCSID("$NetBSD: rm.c,v 1.24 1998/07/28 11:41:51 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int dflag, eval, fflag, iflag, Pflag, Wflag, stdin_ok; + +int check __P((char *, char *, struct stat *)); +void checkdot __P((char **)); +void rm_file __P((char **)); +void rm_overwrite __P((char *, struct stat *)); +void rm_tree __P((char **)); +void usage __P((void)); +int main __P((int, char *[])); + +#ifdef __APPLE__ /* We're missing this prototype */ +int undelete __P((char *)); +#endif + +/* + * For the sake of the `-f' flag, check whether an error number indicates the + * failure of an operation due to an non-existent file, either per se (ENOENT) + * or because its filename argument was illegal (ENAMETOOLONG, ENOTDIR). + */ +#define NONEXISTENT(x) \ + ((x) == ENOENT || (x) == ENAMETOOLONG || (x) == ENOTDIR) + +/* + * rm -- + * This rm is different from historic rm's, but is expected to match + * POSIX 1003.2 behavior. The most visible difference is that -f + * has two specific effects now, ignore non-existent files and force + * file removal. + */ +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, rflag; + + (void)setlocale(LC_ALL, ""); + + Pflag = rflag = 0; + while ((ch = getopt(argc, argv, "dfiPRrW")) != -1) + switch(ch) { + case 'd': + dflag = 1; + break; + case 'f': + fflag = 1; + iflag = 0; + break; + case 'i': + fflag = 0; + iflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'R': + case 'r': /* Compatibility. */ + rflag = 1; + break; + case 'W': + Wflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + checkdot(argv); + + if (*argv) { + stdin_ok = isatty(STDIN_FILENO); + + if (rflag) + rm_tree(argv); + else + rm_file(argv); + } + + exit(eval); + /* NOTREACHED */ +} + +void +rm_tree(argv) + char **argv; +{ + FTS *fts; + FTSENT *p; + int needstat; + int flags; + + /* + * Remove a file hierarchy. If forcing removal (-f), or interactive + * (-i) or can't ask anyway (stdin_ok), don't stat the file. + */ + needstat = !fflag && !iflag && stdin_ok; + + /* + * If the -i option is specified, the user can skip on the pre-order + * visit. The fts_number field flags skipped directories. + */ +#define SKIPPED 1 + + flags = FTS_PHYSICAL; + if (!needstat) + flags |= FTS_NOSTAT; + if (Wflag) + flags |= FTS_WHITEOUT; + if (!(fts = fts_open(argv, flags, + (int (*) __P((const FTSENT **, const FTSENT **)))NULL))) + err(1, "%s", ""); + while ((p = fts_read(fts)) != NULL) { + switch (p->fts_info) { + case FTS_DNR: + if (!fflag || p->fts_errno != ENOENT) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_ERR: + errx(1, "%s: %s", p->fts_path, strerror(p->fts_errno)); + /* NOTREACHED */ + case FTS_NS: + /* + * FTS_NS: assume that if can't stat the file, it + * can't be unlinked. + */ + if (!needstat) + break; + if (!fflag || !NONEXISTENT(p->fts_errno)) { + warnx("%s: %s", + p->fts_path, strerror(p->fts_errno)); + eval = 1; + } + continue; + case FTS_D: + /* Pre-order: give user chance to skip. */ + if (!fflag && !check(p->fts_path, p->fts_accpath, + p->fts_statp)) { + (void)fts_set(fts, p, FTS_SKIP); + p->fts_number = SKIPPED; + } + continue; + case FTS_DP: + /* Post-order: see if user skipped. */ + if (p->fts_number == SKIPPED) + continue; + break; + default: + if (!fflag && + !check(p->fts_path, p->fts_accpath, p->fts_statp)) + continue; + } + + /* + * If we can't read or search the directory, may still be + * able to remove it. Don't print out the un{read,search}able + * message unless the remove fails. + */ + switch (p->fts_info) { + case FTS_DP: + case FTS_DNR: + if (!rmdir(p->fts_accpath) || + (fflag && errno == ENOENT)) + continue; + break; + + case FTS_W: + if (!undelete(p->fts_accpath) || + (fflag && errno == ENOENT)) + continue; + break; + + default: + if (Pflag) + rm_overwrite(p->fts_accpath, NULL); + if (!unlink(p->fts_accpath) || + (fflag && NONEXISTENT(errno))) + continue; + } + warn("%s", p->fts_path); + eval = 1; + } + if (errno) + err(1, "fts_read"); +} + +void +rm_file(argv) + char **argv; +{ + struct stat sb; + int rval; + char *f; + + /* + * Remove a file. POSIX 1003.2 states that, by default, attempting + * to remove a directory is an error, so must always stat the file. + */ + while ((f = *argv++) != NULL) { + /* Assume if can't stat the file, can't unlink it. */ + if (lstat(f, &sb)) { + if (Wflag) { + sb.st_mode = S_IFWHT|S_IWUSR|S_IRUSR; + } else { + if (!fflag || !NONEXISTENT(errno)) { + warn("%s", f); + eval = 1; + } + continue; + } + } else if (Wflag) { + warnx("%s: %s", f, strerror(EEXIST)); + eval = 1; + continue; + } + + if (S_ISDIR(sb.st_mode) && !dflag) { + warnx("%s: is a directory", f); + eval = 1; + continue; + } + if (!fflag && !S_ISWHT(sb.st_mode) && !check(f, f, &sb)) + continue; + if (S_ISWHT(sb.st_mode)) + rval = undelete(f); + else if (S_ISDIR(sb.st_mode)) + rval = rmdir(f); + else { + if (Pflag) + rm_overwrite(f, &sb); + rval = unlink(f); + } + if (rval && (!fflag || !NONEXISTENT(errno))) { + warn("%s", f); + eval = 1; + } + } +} + +/* + * rm_overwrite -- + * Overwrite the file 3 times with varying bit patterns. + * + * XXX + * This is a cheap way to *really* delete files. Note that only regular + * files are deleted, directories (and therefore names) will remain. + * Also, this assumes a fixed-block file system (like FFS, or a V7 or a + * System V file system). In a logging file system, you'll have to have + * kernel support. + */ +void +rm_overwrite(file, sbp) + char *file; + struct stat *sbp; +{ + struct stat sb; + off_t len; + int fd, wlen; + char buf[8 * 1024]; + + fd = -1; + if (sbp == NULL) { + if (lstat(file, &sb)) + goto err; + sbp = &sb; + } + if (!S_ISREG(sbp->st_mode)) + return; + if ((fd = open(file, O_WRONLY, 0)) == -1) + goto err; + +#define PASS(byte) { \ + memset(buf, byte, sizeof(buf)); \ + for (len = sbp->st_size; len > 0; len -= wlen) { \ + wlen = len < sizeof(buf) ? len : sizeof(buf); \ + if (write(fd, buf, wlen) != wlen) \ + goto err; \ + } \ +} + PASS(0xff); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0x00); + if (fsync(fd) || lseek(fd, (off_t)0, SEEK_SET)) + goto err; + PASS(0xff); + if (!fsync(fd) && !close(fd)) + return; + +err: eval = 1; + warn("%s", file); +} + + +int +check(path, name, sp) + char *path, *name; + struct stat *sp; +{ + int ch, first; + char modep[15]; + + /* Check -i first. */ + if (iflag) + (void)fprintf(stderr, "remove %s? ", path); + else { + /* + * If it's not a symbolic link and it's unwritable and we're + * talking to a terminal, ask. Symbolic links are excluded + * because their permissions are meaningless. Check stdin_ok + * first because we may not have stat'ed the file. + */ + if (!stdin_ok || S_ISLNK(sp->st_mode) || !access(name, W_OK)) + return (1); + strmode(sp->st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sp->st_uid, 0), + group_from_gid(sp->st_gid, 0), path); + } + (void)fflush(stderr); + + first = ch = getchar(); + while (ch != '\n' && ch != EOF) + ch = getchar(); + return (first == 'y' || first == 'Y'); +} + +/* + * POSIX.2 requires that if "." or ".." are specified as the basename + * portion of an operand, a diagnostic message be written to standard + * error and nothing more be done with such operands. + * + * Since POSIX.2 defines basename as the final portion of a path after + * trailing slashes have been removed, we'll remove them here. + */ +#define ISDOT(a) ((a)[0] == '.' && (!(a)[1] || ((a)[1] == '.' && !(a)[2]))) +void +checkdot(argv) + char **argv; +{ + char *p, **save, **t; + int complained; + + complained = 0; + for (t = argv; *t;) { + /* strip trailing slashes */ + p = strrchr (*t, '\0'); + while (--p > *t && *p == '/') + *p = '\0'; + + /* extract basename */ + if ((p = strrchr(*t, '/')) != NULL) + ++p; + else + p = *t; + + if (ISDOT(p)) { + if (!complained++) + warnx("\".\" and \"..\" may not be removed"); + eval = 1; + for (save = t; (t[0] = t[1]) != NULL; ++t) + continue; + t = save; + } else + ++t; + } +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rm [-dfiPRrW] file ...\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/rmdir/Makefile b/rmdir/Makefile new file mode 100644 index 0000000..9eb48aa --- /dev/null +++ b/rmdir/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = rmdir + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = rmdir.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rmdir.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/rmdir/Makefile.postamble b/rmdir/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/rmdir/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/rmdir/Makefile.preamble b/rmdir/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/rmdir/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/rmdir/PB.project b/rmdir/PB.project new file mode 100644 index 0000000..8475318 --- /dev/null +++ b/rmdir/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (rmdir.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rmdir.1); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /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 = rmdir; + 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/rmdir/rmdir.1 b/rmdir/rmdir.1 new file mode 100644 index 0000000..2d67c7a --- /dev/null +++ b/rmdir/rmdir.1 @@ -0,0 +1,94 @@ +.\" $NetBSD: rmdir.1,v 1.11 1997/10/20 08:53:22 enami Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rmdir.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt RMDIR 1 +.Os +.Sh NAME +.Nm rmdir +.Nd remove directories +.Sh SYNOPSIS +.Nm +.Op Fl p +.Ar directory ... +.Sh DESCRIPTION +The rmdir utility removes the directory entry specified by +each +.Ar directory +argument, provided it is empty. +.Pp +Arguments are processed in the order given. +In order to remove both a parent directory and a subdirectory +of that parent, the subdirectory +must be specified first so the parent directory +is empty when +.Nm +tries to remove it. +.Pp +The following option is available: +.Bl -tag -width Ds +.It Fl p +Each +.Ar directory +argument is treated as a pathname of which all +components will be removed, if they are empty, +starting with the last most component. +(See +.Xr rm 1 +for fully non-discriminant recursive removal.) +.El +.Pp +The +.Nm +utility exits with one of the following values: +.Bl -tag -width Ds +.It Li \&0 +Each directory entry specified by a dir operand +referred to an empty directory and was removed +successfully. +.It Li \&>\&0 +An error occurred. +.El +.Sh SEE ALSO +.Xr rm 1 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. diff --git a/rmdir/rmdir.c b/rmdir/rmdir.c new file mode 100644 index 0000000..8f61e93 --- /dev/null +++ b/rmdir/rmdir.c @@ -0,0 +1,136 @@ +/* $NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmdir.c 8.3 (Berkeley) 4/2/94"; +#else +__RCSID("$NetBSD: rmdir.c,v 1.16 1998/07/28 05:31:27 mycroft Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include + +int rm_path __P((char *)); +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, errors; + int pflag; + + (void)setlocale(LC_ALL, ""); + + pflag = 0; + while ((ch = getopt(argc, argv, "p")) != -1) + switch(ch) { + case 'p': + pflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + for (errors = 0; *argv; argv++) { + char *p; + + /* Delete trailing slashes, per POSIX. */ + p = *argv + strlen(*argv); + while (--p > *argv && *p == '/') + ; + *++p = '\0'; + + if (rmdir(*argv) < 0) { + warn("%s", *argv); + errors = 1; + } else if (pflag) + errors |= rm_path(*argv); + } + + exit(errors); + /* NOTREACHED */ +} + +int +rm_path(path) + char *path; +{ + char *p; + + while ((p = strrchr(path, '/')) != NULL) { + /* Delete trailing slashes. */ + while (--p > path && *p == '/') + ; + *++p = '\0'; + + if (rmdir(path) < 0) { + warn("%s", path); + return (1); + } + } + + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: rmdir [-p] directory ...\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/rmt/Makefile b/rmt/Makefile new file mode 100644 index 0000000..805eda7 --- /dev/null +++ b/rmt/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = rmt + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = rmt.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble rmt.8 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/sbin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/$(NAME)/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/rmt/Makefile.postamble b/rmt/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/rmt/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/rmt/Makefile.preamble b/rmt/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/rmt/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/rmt/PB.project b/rmt/PB.project new file mode 100644 index 0000000..08efaea --- /dev/null +++ b/rmt/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (rmt.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, rmt.8); + }; + LANGUAGE = English; + LOCALIZABLE_FILES = {}; + MAKEFILEDIR = "$(MAKEFILEPATH)/pb_makefiles"; + NEXTSTEP_BUILDDIR = "/tmp/$(NAME)/Build"; + NEXTSTEP_BUILDTOOL = /bin/gnumake; + NEXTSTEP_INSTALLDIR = /usr/sbin; + 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 = rmt; + 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/rmt/rmt.8 b/rmt/rmt.8 new file mode 100644 index 0000000..77a9074 --- /dev/null +++ b/rmt/rmt.8 @@ -0,0 +1,220 @@ +.\" $NetBSD: rmt.8,v 1.6 1997/10/17 13:03:15 lukem Exp $ +.\" +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94 +.\" +.Dd June 1, 1994 +.Dt RMT 8 +.Os BSD 4.2 +.Sh NAME +.Nm rmt +.Nd remote magtape protocol module +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +is a program used by the remote dump and restore programs +in manipulating a magnetic tape drive through an interprocess +communication connection. +.Nm +is normally started up with an +.Xr rexec 3 +or +.Xr rcmd 3 +call. +.Pp +The +.Nm +program accepts requests specific to the manipulation of +magnetic tapes, performs the commands, then responds with +a status indication. All responses are in +.Tn ASCII +and in +one of two forms. +Successful commands have responses of: +.Bd -filled -offset indent +.Sm off +.Sy A Ar number No \en +.Sm on +.Ed +.Pp +.Ar Number +is an +.Tn ASCII +representation of a decimal number. +Unsuccessful commands are responded to with: +.Bd -filled -offset indent +.Sm off +.Xo Sy E Ar error-number +.No \en Ar error-message +.No \en +.Xc +.Sm on +.Ed +.Pp +.Ar Error-number +is one of the possible error +numbers described in +.Xr intro 2 +and +.Ar error-message +is the corresponding error string as printed +from a call to +.Xr perror 3 . +The protocol is comprised of the +following commands, which are sent as indicated - no spaces are supplied +between the command and its arguments, or between its arguments, and +.Ql \en +indicates that a newline should be supplied: +.Bl -tag -width Ds +.Sm off +.It Xo Sy \&O Ar device +.No \en Ar mode No \en +.Xc +Open the specified +.Ar device +using the indicated +.Ar mode . +.Ar Device +is a full pathname and +.Ar mode +is an +.Tn ASCII +representation of a decimal +number suitable for passing to +.Xr open 2 . +If a device had already been opened, it is +closed before a new open is performed. +.It Xo Sy C Ar device No \en +.Xc +Close the currently open device. The +.Ar device +specified is ignored. +.It Xo Sy L +.Ar offset No \en +.Ar whence No \en +.Xc +.Sm on +Perform an +.Xr lseek 2 +operation using the specified parameters. +The response value is that returned from the +.Xr lseek +call. +.Sm off +.It Sy W Ar count No \en +.Sm on +Write data onto the open device. +.Nm +reads +.Ar count +bytes from the connection, aborting if +a premature end-of-file is encountered. +The response value is that returned from +the +.Xr write 2 +call. +.Sm off +.It Sy R Ar count No \en +.Sm on +Read +.Ar count +bytes of data from the open device. +If +.Ar count +exceeds the size of the data buffer (10 kilobytes), it is +truncated to the data buffer size. +.Nm +then performs the requested +.Xr read 2 +and responds with +.Sm off +.Sy A Ar count-read No \en +.Sm on +if the read was +successful; otherwise an error in the +standard format is returned. If the read +was successful, the data read is then sent. +.Sm off +.It Xo Sy I Ar operation +.No \en Ar count No \en +.Xc +.Sm on +Perform a +.Dv MTIOCOP +.Xr ioctl 2 +command using the specified parameters. +The parameters are interpreted as the +.Tn ASCII +representations of the decimal values +to place in the +.Ar mt_op +and +.Ar mt_count +fields of the structure used in the +.Xr ioctl +call. The return value is the +.Ar count +parameter when the operation is successful. +.ne 1i +.It Sy S +Return the status of the open device, as +obtained with a +.Dv MTIOCGET +.Xr ioctl +call. If the operation was successful, +an ``ack'' is sent with the size of the +status buffer, then the status buffer is +sent (in binary). +.El +.Sm on +.Pp +Any other command causes +.Nm +to exit. +.Sh DIAGNOSTICS +All responses are of the form described above. +.Sh SEE ALSO +.Xr rcmd 3 , +.Xr rexec 3 , +.Xr mtio 4 , +.Xr rdump 8 , +.Xr rrestore 8 +.Sh BUGS +People should be discouraged from using this for a remote +file access protocol. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/rmt/rmt.c b/rmt/rmt.c new file mode 100644 index 0000000..bfb4d37 --- /dev/null +++ b/rmt/rmt.c @@ -0,0 +1,259 @@ +/* $NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $ */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1983, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: rmt.c,v 1.9 1997/10/17 13:03:25 lukem Exp $"); +#endif +#endif /* not lint */ + +/* + * rmt + */ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +int tape = -1; + +char *record; +int maxrecsize = -1; + +#define SSIZE 64 +char device[SSIZE]; +char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE]; + +char resp[BUFSIZ]; + +FILE *debug; +#define DEBUG(f) if (debug) fprintf(debug, f) +#define DEBUG1(f,a) if (debug) fprintf(debug, f, a) +#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2) + +char *checkbuf __P((char *, int)); +void error __P((int)); +int main __P((int, char **)); +void getstring __P((char *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int rval; + char c; + int n, i, cc; + + argc--, argv++; + if (argc > 0) { + debug = fopen(*argv, "w"); + if (debug == 0) + exit(1); + (void)setbuf(debug, (char *)0); + } +top: + errno = 0; + rval = 0; + if (read(STDIN_FILENO, &c, 1) != 1) + exit(0); + switch (c) { + + case 'O': + if (tape >= 0) + (void) close(tape); + getstring(device); + getstring(mode); + DEBUG2("rmtd: O %s %s\n", device, mode); + tape = open(device, atoi(mode), + S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH); + if (tape < 0) + goto ioerror; + goto respond; + + case 'C': + DEBUG("rmtd: C\n"); + getstring(device); /* discard */ + if (close(tape) < 0) + goto ioerror; + tape = -1; + goto respond; + + case 'L': + getstring(count); + getstring(pos); + DEBUG2("rmtd: L %s %s\n", count, pos); + rval = lseek(tape, (off_t)strtoq(count, NULL, 10), atoi(pos)); + if (rval < 0) + goto ioerror; + goto respond; + + case 'W': + getstring(count); + n = atoi(count); + DEBUG1("rmtd: W %s\n", count); + record = checkbuf(record, n); + for (i = 0; i < n; i += cc) { + cc = read(STDIN_FILENO, &record[i], n - i); + if (cc <= 0) { + DEBUG("rmtd: premature eof\n"); + exit(2); + } + } + rval = write(tape, record, n); + if (rval < 0) + goto ioerror; + goto respond; + + case 'R': + getstring(count); + DEBUG1("rmtd: R %s\n", count); + n = atoi(count); + record = checkbuf(record, n); + rval = read(tape, record, n); + if (rval < 0) + goto ioerror; + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + (void)write(STDOUT_FILENO, record, rval); + goto top; + + case 'I': + getstring(op); + getstring(count); + DEBUG2("rmtd: I %s %s\n", op, count); + { + struct mtop mtop; + + mtop.mt_op = atoi(op); + mtop.mt_count = atoi(count); + if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0) + goto ioerror; + rval = mtop.mt_count; + } + goto respond; + + case 'S': /* status */ + DEBUG("rmtd: S\n"); + { + struct mtget mtget; + + if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0) + goto ioerror; + rval = sizeof (mtget); + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + (void)write(STDOUT_FILENO, (char *)&mtget, + sizeof (mtget)); + goto top; + } + + default: + DEBUG1("rmtd: garbage command %c\n", c); + exit(3); + } +respond: + DEBUG1("rmtd: A %d\n", rval); + (void)sprintf(resp, "A%d\n", rval); + (void)write(STDOUT_FILENO, resp, strlen(resp)); + goto top; +ioerror: + error(errno); + goto top; +} + +void +getstring(bp) + char *bp; +{ + int i; + char *cp = bp; + + for (i = 0; i < SSIZE - 1; i++) { + if (read(STDIN_FILENO, cp+i, 1) != 1) + exit(0); + if (cp[i] == '\n') + break; + } + cp[i] = '\0'; +} + +char * +checkbuf(record, size) + char *record; + int size; +{ + + if (size <= maxrecsize) + return (record); + if (record != 0) + free(record); + record = malloc(size); + if (record == 0) { + DEBUG("rmtd: cannot allocate buffer space\n"); + exit(4); + } + maxrecsize = size; + while (size > 1024 && + setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0) + size -= 1024; + return (record); +} + +void +error(num) + int num; +{ + + DEBUG2("rmtd: E %d (%s)\n", num, strerror(num)); + (void)sprintf(resp, "E%d\n%s\n", num, strerror(num)); + (void)write(STDOUT_FILENO, resp, strlen(resp)); +} diff --git a/shar/Makefile b/shar/Makefile new file mode 100644 index 0000000..b7a027d --- /dev/null +++ b/shar/Makefile @@ -0,0 +1,49 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = shar + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +OTHERLINKED = shar.sh + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble shar.1 + +OTHERLINKEDOFILES = shar.o + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/shar/Makefile.postamble b/shar/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/shar/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/shar/Makefile.preamble b/shar/Makefile.preamble new file mode 100644 index 0000000..eaa3b46 --- /dev/null +++ b/shar/Makefile.preamble @@ -0,0 +1,3 @@ +SHELLTOOL = shar.sh + +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/shar/PB.project b/shar/PB.project new file mode 100644 index 0000000..ed6df54 --- /dev/null +++ b/shar/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (shar.sh); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, shar.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 = shar; + 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/shar/shar.1 b/shar/shar.1 new file mode 100644 index 0000000..e6b0f15 --- /dev/null +++ b/shar/shar.1 @@ -0,0 +1,105 @@ +.\" $NetBSD: shar.1,v 1.6 1998/06/08 12:41:44 lukem Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)shar.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt SHAR 1 +.Os BSD 4.4 +.Sh NAME +.Nm shar +.Nd create a shell archive of files +.Sh SYNOPSIS +.Nm +.Ar +.Sh DESCRIPTION +.Nm +writes an +.Xr sh 1 +shell script to the standard output which will recreate the file +hierarchy specified by the command line operands. +Directories will be recreated and must be specified before the +files they contain (the +.Xr find 1 +utility does this correctly). +.Pp +.Nm +is normally used for distributing files by +.Xr ftp 1 +or +.Xr mail 1 . +.Sh SEE ALSO +.Xr compress 1 , +.Xr mail 1 , +.Xr uuencode 1 , +.Xr tar 1 +.Sh BUGS +.Nm +makes no provisions for special types of files or files containing +magic characters. +.Sh EXAMPLES +To create a shell archive of the program +.Xr ls 1 +and mail it to Rick: +.Bd -literal -offset indent +cd ls +shar `find . -print` \&| mail -s "ls source" rick +.Ed +.Pp +To recreate the program directory: +.Bd -literal -offset indent +mkdir ls +cd ls +\&... + +\&... +sh archive +.Ed +.Sh HISTORY +The +.Nm +command appears in +.Bx 4.4 . +.Sh SECURITY CONSIDERATIONS +It is easy to insert trojan horses into +.Nm +files. +It is strongly recommended that all shell archive files be examined +before running them through +.Xr sh 1 . +Archives produced using this implementation of +.Nm +may be easily examined with the command: +.Bd -literal -offset indent +egrep -v '^[X#]' shar.file +.Ed diff --git a/shar/shar.sh b/shar/shar.sh new file mode 100644 index 0000000..7048b44 --- /dev/null +++ b/shar/shar.sh @@ -0,0 +1,76 @@ +#!/bin/sh - +# +# $NetBSD: shar.sh,v 1.2 1994/12/21 08:42:04 jtc Exp $ +# +# Copyright (c) 1990, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)shar.sh 8.1 (Berkeley) 6/6/93 +# + +if [ $# -eq 0 ]; then + echo 'usage: shar file ...' + exit 1 +fi + +cat << EOF +# This is a shell archive. Save it in a file, remove anything before +# this line, and then unpack it by entering "sh file". Note, it may +# create directories; files and directories will be owned by you and +# have default permissions. +# +# This archive contains: +# +EOF + +for i +do + echo "# $i" +done + +echo "#" + +for i +do + if [ -d $i ]; then + echo "echo c - $i" + echo "mkdir -p $i > /dev/null 2>&1" + else + echo "echo x - $i" + echo "sed 's/^X//' >$i << 'END-of-$i'" + sed 's/^/X/' $i + echo "END-of-$i" + fi +done +echo exit +echo "" + +exit 0 diff --git a/tcopy/Makefile b/tcopy/Makefile new file mode 100644 index 0000000..0d2d6b1 --- /dev/null +++ b/tcopy/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = tcopy + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = tcopy.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble tcopy.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/tcopy/Makefile.postamble b/tcopy/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/tcopy/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/tcopy/Makefile.preamble b/tcopy/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/tcopy/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/tcopy/PB.project b/tcopy/PB.project new file mode 100644 index 0000000..d628535 --- /dev/null +++ b/tcopy/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (tcopy.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, tcopy.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 = tcopy; + 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/tcopy/tcopy.1 b/tcopy/tcopy.1 new file mode 100644 index 0000000..ed27627 --- /dev/null +++ b/tcopy/tcopy.1 @@ -0,0 +1,91 @@ +.\" $NetBSD: tcopy.1,v 1.5 1997/10/20 00:35:14 lukem Exp $ +.\" +.\" Copyright (c) 1985, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tcopy.1 8.2 (Berkeley) 4/17/94 +.\" +.Dd April 17, 1994 +.Dt TCOPY 1 +.Os BSD 4.3 +.Sh NAME +.Nm tcopy +.Nd copy and/or verify mag tapes +.Sh SYNOPSIS +.Nm +.Op Fl cvx +.Op Fl s Ar maxblk +.Oo Ar src Op Ar dest +.Oc +.Sh DESCRIPTION +.Nm +is designed to copy magnetic tapes. The only assumption made +about the tape is that there are two tape marks at the end. +.Nm +with only a source tape +.Pf ( Ar /dev/rst0 +by default) specified will print +information about the sizes of records and tape files. If a destination +is specified a copy will be made of the source tape. The blocking on the +destination tape will be identical to that used on the source tape. Copying +a tape will yield the same output as if just printing the sizes. +.Pp +Options: +.Bl -tag -width s_maxblk +.It Fl c +Copy +.Ar src +to +.Ar dest +and then verify that the two tapes are identical. +.It Fl s Ar maxblk +Specify a maximum block size, +.Ar maxblk . +.It Fl v +Given the two tapes, +.ar src +and +.Ar dest +verify that they are identical. +.It Fl x +Output all informational messages to the standard error. +This option is useful when +.Ar dest +is +.Pa /dev/stdout . +.El +.Sh SEE ALSO +.Xr mtio 4 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 . diff --git a/tcopy/tcopy.c b/tcopy/tcopy.c new file mode 100644 index 0000000..297ad01 --- /dev/null +++ b/tcopy/tcopy.c @@ -0,0 +1,339 @@ +/* $NetBSD: tcopy.c,v 1.8 1998/08/25 20:59:41 ross Exp $ */ + +/* + * Copyright (c) 1985, 1987, 1993, 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1985, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tcopy.c 8.3 (Berkeley) 1/23/95"; +#endif +__RCSID("$NetBSD: tcopy.c,v 1.8 1998/08/25 20:59:41 ross Exp $"); +#endif /* not lint */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAXREC (64 * 1024) +#define NOCOUNT (-2) + +int filen, guesslen, maxblk = MAXREC; +long lastrec, record; +off_t size, tsize; +FILE *msg = stdout; + +void *getspace __P((int)); +void intr __P((int)); +int main __P((int, char **)); +void usage __P((void)); +void verify __P((int, int, char *)); +void writeop __P((int, int)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch, needeof, nw, inp, outp; + ssize_t lastnread, nread; + enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; + sig_t oldsig; + char *buff, *inf; + + outp = 0; + inf = NULL; + guesslen = 1; + while ((ch = getopt(argc, argv, "cs:vx")) != -1) + switch((char)ch) { + case 'c': + op = COPYVERIFY; + break; + case 's': + maxblk = atoi(optarg); + if (maxblk <= 0) { + warnx("illegal block size"); + usage(); + } + guesslen = 0; + break; + case 'v': + op = VERIFY; + break; + case 'x': + msg = stderr; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (op != READ) + usage(); + inf = _PATH_DEFTAPE; + break; + case 1: + if (op != READ) + usage(); + inf = argv[0]; + break; + case 2: + if (op == READ) + op = COPY; + inf = argv[0]; + if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : + op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) { + err(3, argv[1]); + } + break; + default: + usage(); + } + + if ((inp = open(inf, O_RDONLY, 0)) < 0) + err(1, inf); + + buff = getspace(maxblk); + + if (op == VERIFY) { + verify(inp, outp, buff); + exit(0); + } + + if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) + (void) signal(SIGINT, intr); + + needeof = 0; + for (lastnread = NOCOUNT;;) { + if ((nread = read(inp, buff, maxblk)) == -1) { + while (errno == EINVAL && (maxblk -= 1024)) { + nread = read(inp, buff, maxblk); + if (nread >= 0) + goto r1; + } + err(1, "read error, file %d, record %ld", + filen, record); + } else if (nread != lastnread) { + if (lastnread != 0 && lastnread != NOCOUNT) { + if (lastrec == 0 && nread == 0) + fprintf(msg, "%ld records\n", record); + else if (record - lastrec > 1) + fprintf(msg, "records %ld to %ld\n", + lastrec, record); + else + fprintf(msg, "record %ld\n", lastrec); + } + if (nread != 0) + fprintf(msg, "file %d: block size %ld: ", + filen, (long)nread); + (void) fflush(stdout); + lastrec = record; + } +r1: guesslen = 0; + if (nread > 0) { + if (op == COPY || op == COPYVERIFY) { + if (needeof) { + writeop(outp, MTWEOF); + needeof = 0; + } + nw = write(outp, buff, nread); + if (nw != nread) { + int error = errno; + fprintf(stderr, + "write error, file %d, record %ld: ", + filen, record); + if (nw == -1) + fprintf(stderr, + ": %s", strerror(error)); + else + fprintf(stderr, + "write (%d) != read (%ld)\n", + nw, (long)nread); + fprintf(stderr, "copy aborted\n"); + exit(5); + } + } + size += nread; + record++; + } else { + if (lastnread <= 0 && lastnread != NOCOUNT) { + fprintf(msg, "eot\n"); + break; + } + fprintf(msg, + "file %d: eof after %ld records: %qd bytes\n", + filen, record, (long long)size); + needeof = 1; + filen++; + tsize += size; + size = record = lastrec = 0; + lastnread = 0; + } + lastnread = nread; + } + fprintf(msg, "total length: %qd bytes\n", (long long)tsize); + (void)signal(SIGINT, oldsig); + if (op == COPY || op == COPYVERIFY) { + writeop(outp, MTWEOF); + writeop(outp, MTWEOF); + if (op == COPYVERIFY) { + writeop(outp, MTREW); + writeop(inp, MTREW); + verify(inp, outp, buff); + } + } + exit(0); +} + +void +verify(inp, outp, outb) + int inp, outp; + char *outb; +{ + int eot, inmaxblk, inn, outmaxblk, outn; + char *inb; + + inb = getspace(maxblk); + inmaxblk = outmaxblk = maxblk; + for (eot = 0;; guesslen = 0) { + if ((inn = read(inp, inb, inmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (inmaxblk -= 1024)) { + inn = read(inp, inb, inmaxblk); + if (inn >= 0) + goto r1; + } + warn("read error"); + break; + } +r1: if ((outn = read(outp, outb, outmaxblk)) == -1) { + if (guesslen) + while (errno == EINVAL && (outmaxblk -= 1024)) { + outn = read(outp, outb, outmaxblk); + if (outn >= 0) + goto r2; + } + warn("read error"); + break; + } +r2: if (inn != outn) { + fprintf(msg, + "%s: tapes have different block sizes; %d != %d.\n", + "tcopy", inn, outn); + break; + } + if (!inn) { + if (eot++) { + fprintf(msg, "%s: tapes are identical.\n", + "tcopy"); + return; + } + } else { + if (memcmp(inb, outb, inn)) { + fprintf(msg, + "%s: tapes have different data.\n", + "tcopy"); + break; + } + eot = 0; + } + } + exit(1); +} + +void +intr(signo) + int signo; +{ + if (record) { + if (record - lastrec > 1) + fprintf(msg, "records %ld to %ld\n", lastrec, record); + else + fprintf(msg, "record %ld\n", lastrec); + } + fprintf(msg, "interrupt at file %d: record %ld\n", filen, record); + fprintf(msg, "total length: %qd bytes\n", (long long)(tsize + size)); + exit(1); +} + +void * +getspace(blk) + int blk; +{ + void *bp; + + if ((bp = malloc((size_t)blk)) == NULL) + errx(11, "no memory"); + + return (bp); +} + +void +writeop(fd, type) + int fd, type; +{ + struct mtop op; + + op.mt_op = type; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op"); +} + +void +usage() +{ + + fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] src [dest]\n"); + exit(1); +} diff --git a/touch/Makefile b/touch/Makefile new file mode 100644 index 0000000..774770c --- /dev/null +++ b/touch/Makefile @@ -0,0 +1,48 @@ +# +# Generated by the NeXT Project Builder. +# +# NOTE: Do NOT change this file -- Project Builder maintains it. +# +# Put all of your customizations in files called Makefile.preamble +# and Makefile.postamble (both optional), and Makefile will include them. +# + +NAME = touch + +PROJECTVERSION = 2.8 +PROJECT_TYPE = Tool + +CFILES = touch.c + +OTHERSRCS = Makefile Makefile.preamble Makefile.postamble touch.1 + + +MAKEFILEDIR = $(MAKEFILEPATH)/pb_makefiles +CODE_GEN_STYLE = DYNAMIC +MAKEFILE = tool.make +NEXTSTEP_INSTALLDIR = /usr/bin +LIBS = +DEBUG_LIBS = $(LIBS) +PROF_LIBS = $(LIBS) + + + + +NEXTSTEP_BUILD_OUTPUT_DIR = /tmp/developer_cmds/Build + +NEXTSTEP_OBJCPLUS_COMPILER = /usr/bin/cc +WINDOWS_OBJCPLUS_COMPILER = $(DEVDIR)/gcc +PDO_UNIX_OBJCPLUS_COMPILER = $(NEXTDEV_BIN)/gcc +NEXTSTEP_JAVA_COMPILER = /usr/bin/javac +WINDOWS_JAVA_COMPILER = $(JDKBINDIR)/javac.exe +PDO_UNIX_JAVA_COMPILER = $(NEXTDEV_BIN)/javac + +include $(MAKEFILEDIR)/platform.make + +-include Makefile.preamble + +include $(MAKEFILEDIR)/$(MAKEFILE) + +-include Makefile.postamble + +-include Makefile.dependencies diff --git a/touch/Makefile.postamble b/touch/Makefile.postamble new file mode 100644 index 0000000..013b558 --- /dev/null +++ b/touch/Makefile.postamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Postamble.Common diff --git a/touch/Makefile.preamble b/touch/Makefile.preamble new file mode 100644 index 0000000..9e10e90 --- /dev/null +++ b/touch/Makefile.preamble @@ -0,0 +1 @@ +include $(CoreOSMakefiles)/ProjectBuilder/Makefile.Preamble.Common diff --git a/touch/PB.project b/touch/PB.project new file mode 100644 index 0000000..0f39cfd --- /dev/null +++ b/touch/PB.project @@ -0,0 +1,25 @@ +{ + DYNAMIC_CODE_GEN = YES; + FILESTABLE = { + FRAMEWORKS = (); + OTHER_LINKED = (touch.c); + OTHER_SOURCES = (Makefile, Makefile.preamble, Makefile.postamble, touch.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 = touch; + 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/touch/touch.1 b/touch/touch.1 new file mode 100644 index 0000000..d2bb977 --- /dev/null +++ b/touch/touch.1 @@ -0,0 +1,177 @@ +.\" $NetBSD: touch.1,v 1.13 1998/01/20 21:18:25 mycroft Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)touch.1 8.3 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt TOUCH 1 +.Os +.Sh NAME +.Nm touch +.Nd change file access and modification times +.Sh SYNOPSIS +.Nm +.Op Fl acfhm +.Op Fl r Ar file +.Op Fl t Ar [[CC]YY]MMDDhhmm[.SS] +.Ar file ... +.Sh DESCRIPTION +The +.Nm +utility sets the modification and access times of files to the +current time of day. +If the file doesn't exist, it is created with default permissions. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Change the access time of the file. +The modification time of the file is not changed unless the +.Fl m +flag is also specified. +.It Fl c +Do not create the file if it does not exist. +The +.Nm +utility does not treat this as an error. +No error messages are displayed and the exit value is not affected. +.It Fl f +Attempt to force the update, even if the file permissions do not +currently permit it. +.It Fl h +If +.Ar file +is a symbolic link, access and/or modification time of the link is changed. +This option implies +.Fl c . +.It Fl m +Change the modification time of the file. +The access time of the file is not changed unless the +.Fl a +flag is also specified. +.It Fl r +Use the access and modifications times from the specified file +instead of the current time of day. +.It Fl t +Change the access and modification times to the specified time. +The argument should be in the form +.Dq [[CC]YY]MMDDhhmm[.SS] +where each pair of letters represents the following: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar CC +The first two digits of the year (the century). +.It Ar YY +The second two digits of the year. +If +.Dq YY +is specified, but +.Dq CC +is not, a value for +.Dq YY +between 69 and 99 results in a +.Dq CC +value of 19. +Otherwise, a +.Dq CC +value of 20 is used. +.It Ar MM +The month of the year, from 1 to 12. +.It Ar DD +The day of the month, from 1 to 31. +.It Ar hh +The hour of the day, from 0 to 23. +.It Ar mm +The minute of the hour, from 0 to 59. +.It Ar SS +The second of the minute, from 0 to 61. +.El +.Pp +If the +.Dq CC +and +.Dq YY +letter pairs are not specified, the values default to the current +year. +If the +.Dq SS +letter pair is not specified, the value defaults to 0. +.El +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr utimes 2 +.Sh COMPATIBILITY +The obsolescent form of +.Nm "" , +where a time format is specified as the first argument, is supported. +When no +.Fl r +or +.Fl t +option is specified, there are at least two arguments, and the first +argument is a string of digits either eight or ten characters in length, +the first argument is interpreted as a time specification of the form +.Dq MMDDhhmm[YY] . +.Pp +The +.Dq MM , +.Dq DD , +.Dq hh +and +.Dq mm +letter pairs are treated as their counterparts specified to the +.Fl t +option. +If the +.Dq YY +letter pair is in the range 69 to 99, the year is set to 1969 to 1999, +otherwise, the year is set in the 21st century. +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +specification. +.Sh HISTORY +A +.Nm +utility appeared in +.At v7 . +.Sh BUGS +A symbolic link can't be a reference file of access and/or modification time. diff --git a/touch/touch.c b/touch/touch.c new file mode 100644 index 0000000..71499fa --- /dev/null +++ b/touch/touch.c @@ -0,0 +1,389 @@ +/* $NetBSD: touch.c,v 1.22 1998/08/25 20:59:42 ross Exp $ */ + +/* + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)touch.c 8.2 (Berkeley) 4/28/95"; +#endif +__RCSID("$NetBSD: touch.c,v 1.22 1998/08/25 20:59:42 ross Exp $"); +#endif /* not lint */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main __P((int, char **)); +int rw __P((char *, struct stat *, int)); +void stime_arg1 __P((char *, struct timeval *)); +void stime_arg2 __P((char *, int, struct timeval *)); +void stime_file __P((char *, struct timeval *)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + struct timeval tv[2]; + int aflag, cflag, fflag, hflag, mflag, ch, fd, len, rval, timeset; + char *p; + int (*change_file_times) __P((const char *, const struct timeval *)); + int (*get_file_status) __P((const char *, struct stat *)); + + setlocale(LC_ALL, ""); + + aflag = cflag = fflag = hflag = mflag = timeset = 0; + if (gettimeofday(&tv[0], NULL)) + err(1, "gettimeofday"); + + while ((ch = getopt(argc, argv, "acfhmr:t:")) != -1) + switch(ch) { + case 'a': + aflag = 1; + break; + case 'c': + cflag = 1; + break; + case 'f': + fflag = 1; + break; +#ifndef __APPLE__ + case 'h': + hflag = 1; + break; +#endif + case 'm': + mflag = 1; + break; + case 'r': + timeset = 1; + stime_file(optarg, tv); + break; + case 't': + timeset = 1; + stime_arg1(optarg, tv); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* Default is both -a and -m. */ + if (aflag == 0 && mflag == 0) + aflag = mflag = 1; + +#ifndef __APPLE__ + if (hflag) { + cflag = 1; /* Don't create new file */ + change_file_times = lutimes; + get_file_status = lstat; + } else { +#endif + change_file_times = utimes; + get_file_status = stat; +#ifndef __APPLE__ + } +#endif + + /* + * If no -r or -t flag, at least two operands, the first of which + * is an 8 or 10 digit number, use the obsolete time specification. + */ + if (!timeset && argc > 1) { + (void)strtol(argv[0], &p, 10); + len = p - argv[0]; + if (*p == '\0' && (len == 8 || len == 10)) { + timeset = 1; + stime_arg2(*argv++, len == 10, tv); + } + } + + /* Otherwise use the current time of day. */ + if (!timeset) + tv[1] = tv[0]; + + if (*argv == NULL) + usage(); + + for (rval = 0; *argv; ++argv) { + /* See if the file exists. */ + if ((*get_file_status)(*argv, &sb)) { + if (!cflag) { + /* Create the file. */ + fd = open(*argv, + O_WRONLY | O_CREAT, DEFFILEMODE); + if (fd == -1 || fstat(fd, &sb) || close(fd)) { + rval = 1; + warn("%s", *argv); + continue; + } + + /* If using the current time, we're done. */ + if (!timeset) + continue; + } else + continue; + } + if (!aflag) + TIMESPEC_TO_TIMEVAL(&tv[0], &sb.st_atimespec); + if (!mflag) + TIMESPEC_TO_TIMEVAL(&tv[1], &sb.st_mtimespec); + + /* Try utimes(2). */ + if (!(*change_file_times)(*argv, tv)) + continue; + + /* If the user specified a time, nothing else we can do. */ + if (timeset) { + rval = 1; + warn("%s", *argv); + } + + /* + * System V and POSIX 1003.1 require that a NULL argument + * set the access/modification times to the current time. + * The permission checks are different, too, in that the + * ability to write the file is sufficient. Take a shot. + */ + if (!(*change_file_times)(*argv, NULL)) + continue; + + /* Try reading/writing. */ + if (!S_ISLNK(sb.st_mode) && rw(*argv, &sb, fflag)) + rval = 1; + } + exit(rval); +} + +#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) + +void +stime_arg1(arg, tvp) + char *arg; + struct timeval *tvp; +{ + struct tm *t; + time_t tmptime; + int yearset; + char *p; + /* Start with the current time. */ + tmptime = tvp[0].tv_sec; + if ((t = localtime(&tmptime)) == NULL) + err(1, "localtime"); + /* [[CC]YY]MMDDhhmm[.SS] */ + if ((p = strchr(arg, '.')) == NULL) + t->tm_sec = 0; /* Seconds defaults to 0. */ + else { + if (strlen(p + 1) != 2) + goto terr; + *p++ = '\0'; + t->tm_sec = ATOI2(p); + } + + yearset = 0; + switch (strlen(arg)) { + case 12: /* CCYYMMDDhhmm */ + t->tm_year = ATOI2(arg) * 100 - TM_YEAR_BASE; + yearset = 1; + /* FALLTHOUGH */ + case 10: /* YYMMDDhhmm */ + if (yearset) { + t->tm_year += ATOI2(arg); + } else { + yearset = ATOI2(arg); + if (yearset < 69) + t->tm_year = yearset + 2000 - TM_YEAR_BASE; + else + t->tm_year = yearset + 1900 - TM_YEAR_BASE; + } + /* FALLTHROUGH */ + case 8: /* MMDDhhmm */ + t->tm_mon = ATOI2(arg); + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + /* FALLTHROUGH */ + case 6: + t->tm_mday = ATOI2(arg); + /* FALLTHROUGH */ + case 4: + t->tm_hour = ATOI2(arg); + /* FALLTHROUGH */ + case 2: + t->tm_min = ATOI2(arg); + break; + default: + goto terr; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) +terr: errx(1, + "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_arg2(arg, year, tvp) + char *arg; + int year; + struct timeval *tvp; +{ + struct tm *t; + time_t tmptime; + /* Start with the current time. */ + tmptime = tvp[0].tv_sec; + if ((t = localtime(&tmptime)) == NULL) + err(1, "localtime"); + + t->tm_mon = ATOI2(arg); /* MMDDhhmm[yy] */ + --t->tm_mon; /* Convert from 01-12 to 00-11 */ + t->tm_mday = ATOI2(arg); + t->tm_hour = ATOI2(arg); + t->tm_min = ATOI2(arg); + if (year) { + year = ATOI2(arg); + if (year < 69) + t->tm_year = year + 2000 - TM_YEAR_BASE; + else + t->tm_year = year + 1900 - TM_YEAR_BASE; + } + + t->tm_isdst = -1; /* Figure out DST. */ + tvp[0].tv_sec = tvp[1].tv_sec = mktime(t); + if (tvp[0].tv_sec == -1) + errx(1, + "out of range or illegal time specification: MMDDhhmm[yy]"); + + tvp[0].tv_usec = tvp[1].tv_usec = 0; +} + +void +stime_file(fname, tvp) + char *fname; + struct timeval *tvp; +{ + struct stat sb; + + if (stat(fname, &sb)) + err(1, "%s", fname); + TIMESPEC_TO_TIMEVAL(&tvp[0], &sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(&tvp[1], &sb.st_mtimespec); +} + +int +rw(fname, sbp, force) + char *fname; + struct stat *sbp; + int force; +{ + int fd, needed_chmod, rval; + u_char byte; + + /* Try regular files and directories. */ + if (!S_ISREG(sbp->st_mode) && !S_ISDIR(sbp->st_mode)) { + warnx("%s: %s", fname, strerror(EFTYPE)); + return (1); + } + + needed_chmod = rval = 0; + if ((fd = open(fname, O_RDWR, 0)) == -1) { + if (!force || chmod(fname, DEFFILEMODE)) + goto err; + if ((fd = open(fname, O_RDWR, 0)) == -1) + goto err; + needed_chmod = 1; + } + + if (sbp->st_size != 0) { + if (read(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + if (lseek(fd, (off_t)0, SEEK_SET) == -1) + goto err; + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) + goto err; + } else { + if (write(fd, &byte, sizeof(byte)) != sizeof(byte)) { +err: rval = 1; + warn("%s", fname); + } else if (ftruncate(fd, (off_t)0)) { + rval = 1; + warn("%s: file modified", fname); + } + } + + if (close(fd) && rval != 1) { + rval = 1; + warn("%s", fname); + } + if (needed_chmod && chmod(fname, sbp->st_mode) && rval != 1) { + rval = 1; + warn("%s: permissions modified", fname); + } + return (rval); +} + +__dead void +usage() +{ +#ifndef __APPLE__ + (void)fprintf(stderr, + "usage: touch [-acfhm] [-r file] [-t time] file ...\n"); +#else + (void)fprintf(stderr, + "usage: touch [-acfm] [-r file] [-t time] file ...\n"); +#endif + exit(1); +} -- 2.45.2