diff options
author | Kim Kibum <kb0929.kim@samsung.com> | 2012-05-21 17:40:28 +0900 |
---|---|---|
committer | Kim Kibum <kb0929.kim@samsung.com> | 2012-05-21 17:40:28 +0900 |
commit | 5fab833656f8722f000d40da4733c164690a18df (patch) | |
tree | 06c6b7063a663592937cbcd47dd400ac931e0490 /src | |
parent | 43e15e32d26b017f436da3dd40334b4df292e5a5 (diff) | |
download | cpio-5fab833656f8722f000d40da4733c164690a18df.tar.gz cpio-5fab833656f8722f000d40da4733c164690a18df.tar.bz2 cpio-5fab833656f8722f000d40da4733c164690a18df.zip |
Upload Tizen:Base source
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 53 | ||||
-rw-r--r-- | src/Makefile.in | 1175 | ||||
-rw-r--r-- | src/copyin.c | 1502 | ||||
-rw-r--r-- | src/copyout.c | 898 | ||||
-rw-r--r-- | src/copypass.c | 398 | ||||
-rw-r--r-- | src/cpio.h | 72 | ||||
-rw-r--r-- | src/cpiohdr.h | 132 | ||||
-rw-r--r-- | src/defer.c | 44 | ||||
-rw-r--r-- | src/defer.h | 27 | ||||
-rw-r--r-- | src/dstring.c | 104 | ||||
-rw-r--r-- | src/dstring.h | 51 | ||||
-rw-r--r-- | src/extern.h | 219 | ||||
-rw-r--r-- | src/fatal.c | 27 | ||||
-rw-r--r-- | src/filemode.c | 244 | ||||
-rw-r--r-- | src/filetypes.h | 85 | ||||
-rw-r--r-- | src/global.c | 195 | ||||
-rw-r--r-- | src/idcache.c | 198 | ||||
-rw-r--r-- | src/main.c | 743 | ||||
-rw-r--r-- | src/makepath.c | 188 | ||||
-rw-r--r-- | src/mt.c | 361 | ||||
-rw-r--r-- | src/safe-stat.h | 1 | ||||
-rw-r--r-- | src/tar.c | 480 | ||||
-rw-r--r-- | src/tar.h | 112 | ||||
-rw-r--r-- | src/tarhdr.h | 63 | ||||
-rw-r--r-- | src/userspec.c | 233 | ||||
-rw-r--r-- | src/util.c | 1620 |
26 files changed, 9225 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..ec71397 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,53 @@ +# This file is part of GNU cpio +# Copyright (C) 2003, 2004, 2007, 2009, 2010 Free Software Foundation, +# Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. + +INCLUDES=-I. -I.. -I$(top_srcdir)/gnu -I$(top_builddir)/gnu -I$(top_srcdir)/lib -I$(top_builddir)/lib + +bin_PROGRAMS=cpio @CPIO_MT_PROG@ +EXTRA_PROGRAMS=mt + +cpio_SOURCES = \ + copyin.c\ + copyout.c\ + copypass.c\ + defer.c\ + dstring.c\ + global.c\ + fatal.c\ + main.c\ + tar.c\ + util.c\ + filemode.c\ + idcache.c\ + makepath.c\ + userspec.c + +noinst_HEADERS =\ + cpio.h\ + cpiohdr.h\ + tar.h\ + tarhdr.h\ + defer.h\ + dstring.h\ + extern.h\ + filetypes.h\ + safe-stat.h + +LDADD=../lib/libpax.a ../gnu/libgnu.a @INTLLIBS@ + diff --git a/src/Makefile.in b/src/Makefile.in new file mode 100644 index 0000000..c69433d --- /dev/null +++ b/src/Makefile.in @@ -0,0 +1,1175 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# This file is part of GNU cpio +# Copyright (C) 2003, 2004, 2007, 2009, 2010 Free Software Foundation, +# Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA. + + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +bin_PROGRAMS = cpio$(EXEEXT) @CPIO_MT_PROG@ +EXTRA_PROGRAMS = mt$(EXEEXT) +subdir = src +DIST_COMMON = $(noinst_HEADERS) $(srcdir)/Makefile.am \ + $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/am/flushleft.m4 \ + $(top_srcdir)/am/pack.m4 $(top_srcdir)/am/sysdep.m4 \ + $(top_srcdir)/m4/00gnulib.m4 $(top_srcdir)/m4/alloca.m4 \ + $(top_srcdir)/m4/argmatch.m4 $(top_srcdir)/m4/argp.m4 \ + $(top_srcdir)/m4/bison.m4 $(top_srcdir)/m4/chdir-long.m4 \ + $(top_srcdir)/m4/chown.m4 $(top_srcdir)/m4/clock_time.m4 \ + $(top_srcdir)/m4/close-stream.m4 $(top_srcdir)/m4/close.m4 \ + $(top_srcdir)/m4/closeout.m4 $(top_srcdir)/m4/codeset.m4 \ + $(top_srcdir)/m4/d-ino.m4 $(top_srcdir)/m4/dirent-safer.m4 \ + $(top_srcdir)/m4/dirent_h.m4 $(top_srcdir)/m4/dirfd.m4 \ + $(top_srcdir)/m4/dirname.m4 $(top_srcdir)/m4/dos.m4 \ + $(top_srcdir)/m4/double-slash-root.m4 $(top_srcdir)/m4/dup2.m4 \ + $(top_srcdir)/m4/eealloc.m4 $(top_srcdir)/m4/environ.m4 \ + $(top_srcdir)/m4/errno_h.m4 $(top_srcdir)/m4/error.m4 \ + $(top_srcdir)/m4/extensions.m4 $(top_srcdir)/m4/fchdir.m4 \ + $(top_srcdir)/m4/fclose.m4 $(top_srcdir)/m4/fcntl-o.m4 \ + $(top_srcdir)/m4/fcntl.m4 $(top_srcdir)/m4/fcntl_h.m4 \ + $(top_srcdir)/m4/fdopendir.m4 $(top_srcdir)/m4/fileblocks.m4 \ + $(top_srcdir)/m4/float_h.m4 $(top_srcdir)/m4/fnmatch.m4 \ + $(top_srcdir)/m4/fpending.m4 $(top_srcdir)/m4/fseeko.m4 \ + $(top_srcdir)/m4/getcwd-abort-bug.m4 \ + $(top_srcdir)/m4/getcwd-path-max.m4 $(top_srcdir)/m4/getcwd.m4 \ + $(top_srcdir)/m4/getdate.m4 $(top_srcdir)/m4/getdtablesize.m4 \ + $(top_srcdir)/m4/getopt.m4 $(top_srcdir)/m4/gettext.m4 \ + $(top_srcdir)/m4/gettime.m4 $(top_srcdir)/m4/gettimeofday.m4 \ + $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/gnulib-common.m4 \ + $(top_srcdir)/m4/gnulib-comp.m4 $(top_srcdir)/m4/hash.m4 \ + $(top_srcdir)/m4/iconv.m4 $(top_srcdir)/m4/include_next.m4 \ + $(top_srcdir)/m4/inline.m4 $(top_srcdir)/m4/intlmacosx.m4 \ + $(top_srcdir)/m4/intmax_t.m4 $(top_srcdir)/m4/inttostr.m4 \ + $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \ + $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/lchown.m4 \ + $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ + $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/localcharset.m4 $(top_srcdir)/m4/locale-fr.m4 \ + $(top_srcdir)/m4/locale-ja.m4 $(top_srcdir)/m4/locale-zh.m4 \ + $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/lseek.m4 \ + $(top_srcdir)/m4/lstat.m4 $(top_srcdir)/m4/malloc.m4 \ + $(top_srcdir)/m4/malloca.m4 $(top_srcdir)/m4/mbrtowc.m4 \ + $(top_srcdir)/m4/mbsinit.m4 $(top_srcdir)/m4/mbsrtowcs.m4 \ + $(top_srcdir)/m4/mbstate_t.m4 $(top_srcdir)/m4/memchr.m4 \ + $(top_srcdir)/m4/mempcpy.m4 $(top_srcdir)/m4/memrchr.m4 \ + $(top_srcdir)/m4/mkdir.m4 $(top_srcdir)/m4/mktime.m4 \ + $(top_srcdir)/m4/mmap-anon.m4 $(top_srcdir)/m4/mode_t.m4 \ + $(top_srcdir)/m4/multiarch.m4 $(top_srcdir)/m4/nls.m4 \ + $(top_srcdir)/m4/open.m4 $(top_srcdir)/m4/openat.m4 \ + $(top_srcdir)/m4/paxutils.m4 $(top_srcdir)/m4/po.m4 \ + $(top_srcdir)/m4/printf.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/quote.m4 $(top_srcdir)/m4/quotearg.m4 \ + $(top_srcdir)/m4/rawmemchr.m4 $(top_srcdir)/m4/realloc.m4 \ + $(top_srcdir)/m4/rmdir.m4 $(top_srcdir)/m4/rmt.m4 \ + $(top_srcdir)/m4/rtapelib.m4 $(top_srcdir)/m4/safe-read.m4 \ + $(top_srcdir)/m4/safe-write.m4 $(top_srcdir)/m4/save-cwd.m4 \ + $(top_srcdir)/m4/savedir.m4 $(top_srcdir)/m4/setenv.m4 \ + $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/sleep.m4 \ + $(top_srcdir)/m4/ssize_t.m4 $(top_srcdir)/m4/stat-time.m4 \ + $(top_srcdir)/m4/stat.m4 $(top_srcdir)/m4/stdarg.m4 \ + $(top_srcdir)/m4/stdbool.m4 $(top_srcdir)/m4/stddef_h.m4 \ + $(top_srcdir)/m4/stdint.m4 $(top_srcdir)/m4/stdint_h.m4 \ + $(top_srcdir)/m4/stdio_h.m4 $(top_srcdir)/m4/stdlib_h.m4 \ + $(top_srcdir)/m4/stpcpy.m4 $(top_srcdir)/m4/strcase.m4 \ + $(top_srcdir)/m4/strchrnul.m4 $(top_srcdir)/m4/strdup.m4 \ + $(top_srcdir)/m4/strerror.m4 $(top_srcdir)/m4/string_h.m4 \ + $(top_srcdir)/m4/strings_h.m4 $(top_srcdir)/m4/strndup.m4 \ + $(top_srcdir)/m4/strnlen.m4 $(top_srcdir)/m4/strtol.m4 \ + $(top_srcdir)/m4/sys_stat_h.m4 $(top_srcdir)/m4/sys_time_h.m4 \ + $(top_srcdir)/m4/sysexits.m4 $(top_srcdir)/m4/system.m4 \ + $(top_srcdir)/m4/time_h.m4 $(top_srcdir)/m4/time_r.m4 \ + $(top_srcdir)/m4/timespec.m4 $(top_srcdir)/m4/tm_gmtoff.m4 \ + $(top_srcdir)/m4/unistd-safer.m4 $(top_srcdir)/m4/unistd_h.m4 \ + $(top_srcdir)/m4/unlink.m4 $(top_srcdir)/m4/unlocked-io.m4 \ + $(top_srcdir)/m4/utimbuf.m4 $(top_srcdir)/m4/utimens.m4 \ + $(top_srcdir)/m4/utimes.m4 $(top_srcdir)/m4/vasnprintf.m4 \ + $(top_srcdir)/m4/version-etc.m4 $(top_srcdir)/m4/vsnprintf.m4 \ + $(top_srcdir)/m4/warn-on-use.m4 $(top_srcdir)/m4/wchar_h.m4 \ + $(top_srcdir)/m4/wchar_t.m4 $(top_srcdir)/m4/wctype_h.m4 \ + $(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/write.m4 \ + $(top_srcdir)/m4/xalloc.m4 $(top_srcdir)/m4/xgetcwd.m4 \ + $(top_srcdir)/m4/xsize.m4 $(top_srcdir)/m4/xstrndup.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" +PROGRAMS = $(bin_PROGRAMS) +am_cpio_OBJECTS = copyin.$(OBJEXT) copyout.$(OBJEXT) \ + copypass.$(OBJEXT) defer.$(OBJEXT) dstring.$(OBJEXT) \ + global.$(OBJEXT) fatal.$(OBJEXT) main.$(OBJEXT) tar.$(OBJEXT) \ + util.$(OBJEXT) filemode.$(OBJEXT) idcache.$(OBJEXT) \ + makepath.$(OBJEXT) userspec.$(OBJEXT) +cpio_OBJECTS = $(am_cpio_OBJECTS) +cpio_LDADD = $(LDADD) +cpio_DEPENDENCIES = ../lib/libpax.a ../gnu/libgnu.a +mt_SOURCES = mt.c +mt_OBJECTS = mt.$(OBJEXT) +mt_LDADD = $(LDADD) +mt_DEPENDENCIES = ../lib/libpax.a ../gnu/libgnu.a +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/build-aux/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_$(V)) +am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY)) +am__v_CC_0 = @echo " CC " $@; +AM_V_at = $(am__v_at_$(V)) +am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY)) +am__v_at_0 = @ +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_$(V)) +am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY)) +am__v_CCLD_0 = @echo " CCLD " $@; +AM_V_GEN = $(am__v_GEN_$(V)) +am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY)) +am__v_GEN_0 = @echo " GEN " $@; +SOURCES = $(cpio_SOURCES) mt.c +DIST_SOURCES = $(cpio_SOURCES) mt.c +HEADERS = $(noinst_HEADERS) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +ALLOCA_H = @ALLOCA_H@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPLE_UNIVERSAL_BUILD = @APPLE_UNIVERSAL_BUILD@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOM4TE = @AUTOM4TE@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BITSIZEOF_PTRDIFF_T = @BITSIZEOF_PTRDIFF_T@ +BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@ +BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@ +BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@ +BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPIO_MT_PROG = @CPIO_MT_PROG@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFAULT_RMT_COMMAND = @DEFAULT_RMT_COMMAND@ +DEFAULT_RMT_DIR = @DEFAULT_RMT_DIR@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EMULTIHOP_HIDDEN = @EMULTIHOP_HIDDEN@ +EMULTIHOP_VALUE = @EMULTIHOP_VALUE@ +ENOLINK_HIDDEN = @ENOLINK_HIDDEN@ +ENOLINK_VALUE = @ENOLINK_VALUE@ +EOVERFLOW_HIDDEN = @EOVERFLOW_HIDDEN@ +EOVERFLOW_VALUE = @EOVERFLOW_VALUE@ +ERRNO_H = @ERRNO_H@ +EXEEXT = @EXEEXT@ +FLOAT_H = @FLOAT_H@ +FNMATCH_H = @FNMATCH_H@ +GETOPT_H = @GETOPT_H@ +GETTEXT_MACRO_VERSION = @GETTEXT_MACRO_VERSION@ +GLIBC21 = @GLIBC21@ +GMSGFMT = @GMSGFMT@ +GMSGFMT_015 = @GMSGFMT_015@ +GNULIB_ALPHASORT = @GNULIB_ALPHASORT@ +GNULIB_ATOLL = @GNULIB_ATOLL@ +GNULIB_BTOWC = @GNULIB_BTOWC@ +GNULIB_CALLOC_POSIX = @GNULIB_CALLOC_POSIX@ +GNULIB_CANONICALIZE_FILE_NAME = @GNULIB_CANONICALIZE_FILE_NAME@ +GNULIB_CHOWN = @GNULIB_CHOWN@ +GNULIB_CLOSE = @GNULIB_CLOSE@ +GNULIB_DIRFD = @GNULIB_DIRFD@ +GNULIB_DPRINTF = @GNULIB_DPRINTF@ +GNULIB_DUP2 = @GNULIB_DUP2@ +GNULIB_DUP3 = @GNULIB_DUP3@ +GNULIB_ENVIRON = @GNULIB_ENVIRON@ +GNULIB_EUIDACCESS = @GNULIB_EUIDACCESS@ +GNULIB_FACCESSAT = @GNULIB_FACCESSAT@ +GNULIB_FCHDIR = @GNULIB_FCHDIR@ +GNULIB_FCHMODAT = @GNULIB_FCHMODAT@ +GNULIB_FCHOWNAT = @GNULIB_FCHOWNAT@ +GNULIB_FCLOSE = @GNULIB_FCLOSE@ +GNULIB_FCNTL = @GNULIB_FCNTL@ +GNULIB_FDOPENDIR = @GNULIB_FDOPENDIR@ +GNULIB_FFLUSH = @GNULIB_FFLUSH@ +GNULIB_FOPEN = @GNULIB_FOPEN@ +GNULIB_FPRINTF = @GNULIB_FPRINTF@ +GNULIB_FPRINTF_POSIX = @GNULIB_FPRINTF_POSIX@ +GNULIB_FPURGE = @GNULIB_FPURGE@ +GNULIB_FPUTC = @GNULIB_FPUTC@ +GNULIB_FPUTS = @GNULIB_FPUTS@ +GNULIB_FREOPEN = @GNULIB_FREOPEN@ +GNULIB_FSEEK = @GNULIB_FSEEK@ +GNULIB_FSEEKO = @GNULIB_FSEEKO@ +GNULIB_FSTATAT = @GNULIB_FSTATAT@ +GNULIB_FSYNC = @GNULIB_FSYNC@ +GNULIB_FTELL = @GNULIB_FTELL@ +GNULIB_FTELLO = @GNULIB_FTELLO@ +GNULIB_FTRUNCATE = @GNULIB_FTRUNCATE@ +GNULIB_FUTIMENS = @GNULIB_FUTIMENS@ +GNULIB_FWRITE = @GNULIB_FWRITE@ +GNULIB_GETCWD = @GNULIB_GETCWD@ +GNULIB_GETDELIM = @GNULIB_GETDELIM@ +GNULIB_GETDOMAINNAME = @GNULIB_GETDOMAINNAME@ +GNULIB_GETDTABLESIZE = @GNULIB_GETDTABLESIZE@ +GNULIB_GETGROUPS = @GNULIB_GETGROUPS@ +GNULIB_GETHOSTNAME = @GNULIB_GETHOSTNAME@ +GNULIB_GETLINE = @GNULIB_GETLINE@ +GNULIB_GETLOADAVG = @GNULIB_GETLOADAVG@ +GNULIB_GETLOGIN = @GNULIB_GETLOGIN@ +GNULIB_GETLOGIN_R = @GNULIB_GETLOGIN_R@ +GNULIB_GETPAGESIZE = @GNULIB_GETPAGESIZE@ +GNULIB_GETSUBOPT = @GNULIB_GETSUBOPT@ +GNULIB_GETTIMEOFDAY = @GNULIB_GETTIMEOFDAY@ +GNULIB_GETUSERSHELL = @GNULIB_GETUSERSHELL@ +GNULIB_IMAXABS = @GNULIB_IMAXABS@ +GNULIB_IMAXDIV = @GNULIB_IMAXDIV@ +GNULIB_LCHMOD = @GNULIB_LCHMOD@ +GNULIB_LCHOWN = @GNULIB_LCHOWN@ +GNULIB_LINK = @GNULIB_LINK@ +GNULIB_LINKAT = @GNULIB_LINKAT@ +GNULIB_LSEEK = @GNULIB_LSEEK@ +GNULIB_LSTAT = @GNULIB_LSTAT@ +GNULIB_MALLOC_POSIX = @GNULIB_MALLOC_POSIX@ +GNULIB_MBRLEN = @GNULIB_MBRLEN@ +GNULIB_MBRTOWC = @GNULIB_MBRTOWC@ +GNULIB_MBSCASECMP = @GNULIB_MBSCASECMP@ +GNULIB_MBSCASESTR = @GNULIB_MBSCASESTR@ +GNULIB_MBSCHR = @GNULIB_MBSCHR@ +GNULIB_MBSCSPN = @GNULIB_MBSCSPN@ +GNULIB_MBSINIT = @GNULIB_MBSINIT@ +GNULIB_MBSLEN = @GNULIB_MBSLEN@ +GNULIB_MBSNCASECMP = @GNULIB_MBSNCASECMP@ +GNULIB_MBSNLEN = @GNULIB_MBSNLEN@ +GNULIB_MBSNRTOWCS = @GNULIB_MBSNRTOWCS@ +GNULIB_MBSPBRK = @GNULIB_MBSPBRK@ +GNULIB_MBSPCASECMP = @GNULIB_MBSPCASECMP@ +GNULIB_MBSRCHR = @GNULIB_MBSRCHR@ +GNULIB_MBSRTOWCS = @GNULIB_MBSRTOWCS@ +GNULIB_MBSSEP = @GNULIB_MBSSEP@ +GNULIB_MBSSPN = @GNULIB_MBSSPN@ +GNULIB_MBSSTR = @GNULIB_MBSSTR@ +GNULIB_MBSTOK_R = @GNULIB_MBSTOK_R@ +GNULIB_MEMCHR = @GNULIB_MEMCHR@ +GNULIB_MEMMEM = @GNULIB_MEMMEM@ +GNULIB_MEMPCPY = @GNULIB_MEMPCPY@ +GNULIB_MEMRCHR = @GNULIB_MEMRCHR@ +GNULIB_MKDIRAT = @GNULIB_MKDIRAT@ +GNULIB_MKDTEMP = @GNULIB_MKDTEMP@ +GNULIB_MKFIFO = @GNULIB_MKFIFO@ +GNULIB_MKFIFOAT = @GNULIB_MKFIFOAT@ +GNULIB_MKNOD = @GNULIB_MKNOD@ +GNULIB_MKNODAT = @GNULIB_MKNODAT@ +GNULIB_MKOSTEMP = @GNULIB_MKOSTEMP@ +GNULIB_MKOSTEMPS = @GNULIB_MKOSTEMPS@ +GNULIB_MKSTEMP = @GNULIB_MKSTEMP@ +GNULIB_MKSTEMPS = @GNULIB_MKSTEMPS@ +GNULIB_MKTIME = @GNULIB_MKTIME@ +GNULIB_NANOSLEEP = @GNULIB_NANOSLEEP@ +GNULIB_OBSTACK_PRINTF = @GNULIB_OBSTACK_PRINTF@ +GNULIB_OBSTACK_PRINTF_POSIX = @GNULIB_OBSTACK_PRINTF_POSIX@ +GNULIB_OPEN = @GNULIB_OPEN@ +GNULIB_OPENAT = @GNULIB_OPENAT@ +GNULIB_PERROR = @GNULIB_PERROR@ +GNULIB_PIPE2 = @GNULIB_PIPE2@ +GNULIB_POPEN = @GNULIB_POPEN@ +GNULIB_PREAD = @GNULIB_PREAD@ +GNULIB_PRINTF = @GNULIB_PRINTF@ +GNULIB_PRINTF_POSIX = @GNULIB_PRINTF_POSIX@ +GNULIB_PUTC = @GNULIB_PUTC@ +GNULIB_PUTCHAR = @GNULIB_PUTCHAR@ +GNULIB_PUTENV = @GNULIB_PUTENV@ +GNULIB_PUTS = @GNULIB_PUTS@ +GNULIB_RANDOM_R = @GNULIB_RANDOM_R@ +GNULIB_RAWMEMCHR = @GNULIB_RAWMEMCHR@ +GNULIB_READLINK = @GNULIB_READLINK@ +GNULIB_READLINKAT = @GNULIB_READLINKAT@ +GNULIB_REALLOC_POSIX = @GNULIB_REALLOC_POSIX@ +GNULIB_REALPATH = @GNULIB_REALPATH@ +GNULIB_REMOVE = @GNULIB_REMOVE@ +GNULIB_RENAME = @GNULIB_RENAME@ +GNULIB_RENAMEAT = @GNULIB_RENAMEAT@ +GNULIB_RMDIR = @GNULIB_RMDIR@ +GNULIB_RPMATCH = @GNULIB_RPMATCH@ +GNULIB_SCANDIR = @GNULIB_SCANDIR@ +GNULIB_SETENV = @GNULIB_SETENV@ +GNULIB_SLEEP = @GNULIB_SLEEP@ +GNULIB_SNPRINTF = @GNULIB_SNPRINTF@ +GNULIB_SPRINTF_POSIX = @GNULIB_SPRINTF_POSIX@ +GNULIB_STAT = @GNULIB_STAT@ +GNULIB_STDIO_H_SIGPIPE = @GNULIB_STDIO_H_SIGPIPE@ +GNULIB_STPCPY = @GNULIB_STPCPY@ +GNULIB_STPNCPY = @GNULIB_STPNCPY@ +GNULIB_STRCASESTR = @GNULIB_STRCASESTR@ +GNULIB_STRCHRNUL = @GNULIB_STRCHRNUL@ +GNULIB_STRDUP = @GNULIB_STRDUP@ +GNULIB_STRERROR = @GNULIB_STRERROR@ +GNULIB_STRNDUP = @GNULIB_STRNDUP@ +GNULIB_STRNLEN = @GNULIB_STRNLEN@ +GNULIB_STRPBRK = @GNULIB_STRPBRK@ +GNULIB_STRPTIME = @GNULIB_STRPTIME@ +GNULIB_STRSEP = @GNULIB_STRSEP@ +GNULIB_STRSIGNAL = @GNULIB_STRSIGNAL@ +GNULIB_STRSTR = @GNULIB_STRSTR@ +GNULIB_STRTOD = @GNULIB_STRTOD@ +GNULIB_STRTOIMAX = @GNULIB_STRTOIMAX@ +GNULIB_STRTOK_R = @GNULIB_STRTOK_R@ +GNULIB_STRTOLL = @GNULIB_STRTOLL@ +GNULIB_STRTOULL = @GNULIB_STRTOULL@ +GNULIB_STRTOUMAX = @GNULIB_STRTOUMAX@ +GNULIB_STRVERSCMP = @GNULIB_STRVERSCMP@ +GNULIB_SYMLINK = @GNULIB_SYMLINK@ +GNULIB_SYMLINKAT = @GNULIB_SYMLINKAT@ +GNULIB_TIMEGM = @GNULIB_TIMEGM@ +GNULIB_UNISTD_H_GETOPT = @GNULIB_UNISTD_H_GETOPT@ +GNULIB_UNISTD_H_SIGPIPE = @GNULIB_UNISTD_H_SIGPIPE@ +GNULIB_UNLINK = @GNULIB_UNLINK@ +GNULIB_UNLINKAT = @GNULIB_UNLINKAT@ +GNULIB_UNSETENV = @GNULIB_UNSETENV@ +GNULIB_USLEEP = @GNULIB_USLEEP@ +GNULIB_UTIMENSAT = @GNULIB_UTIMENSAT@ +GNULIB_VASPRINTF = @GNULIB_VASPRINTF@ +GNULIB_VDPRINTF = @GNULIB_VDPRINTF@ +GNULIB_VFPRINTF = @GNULIB_VFPRINTF@ +GNULIB_VFPRINTF_POSIX = @GNULIB_VFPRINTF_POSIX@ +GNULIB_VPRINTF = @GNULIB_VPRINTF@ +GNULIB_VPRINTF_POSIX = @GNULIB_VPRINTF_POSIX@ +GNULIB_VSNPRINTF = @GNULIB_VSNPRINTF@ +GNULIB_VSPRINTF_POSIX = @GNULIB_VSPRINTF_POSIX@ +GNULIB_WCRTOMB = @GNULIB_WCRTOMB@ +GNULIB_WCSNRTOMBS = @GNULIB_WCSNRTOMBS@ +GNULIB_WCSRTOMBS = @GNULIB_WCSRTOMBS@ +GNULIB_WCTOB = @GNULIB_WCTOB@ +GNULIB_WCWIDTH = @GNULIB_WCWIDTH@ +GNULIB_WRITE = @GNULIB_WRITE@ +GREP = @GREP@ +HAVE_ALPHASORT = @HAVE_ALPHASORT@ +HAVE_ATOLL = @HAVE_ATOLL@ +HAVE_BTOWC = @HAVE_BTOWC@ +HAVE_CALLOC_POSIX = @HAVE_CALLOC_POSIX@ +HAVE_CANONICALIZE_FILE_NAME = @HAVE_CANONICALIZE_FILE_NAME@ +HAVE_CHOWN = @HAVE_CHOWN@ +HAVE_DECL_DIRFD = @HAVE_DECL_DIRFD@ +HAVE_DECL_ENVIRON = @HAVE_DECL_ENVIRON@ +HAVE_DECL_FPURGE = @HAVE_DECL_FPURGE@ +HAVE_DECL_GETDELIM = @HAVE_DECL_GETDELIM@ +HAVE_DECL_GETLINE = @HAVE_DECL_GETLINE@ +HAVE_DECL_GETLOADAVG = @HAVE_DECL_GETLOADAVG@ +HAVE_DECL_GETLOGIN_R = @HAVE_DECL_GETLOGIN_R@ +HAVE_DECL_IMAXABS = @HAVE_DECL_IMAXABS@ +HAVE_DECL_IMAXDIV = @HAVE_DECL_IMAXDIV@ +HAVE_DECL_MEMMEM = @HAVE_DECL_MEMMEM@ +HAVE_DECL_MEMRCHR = @HAVE_DECL_MEMRCHR@ +HAVE_DECL_OBSTACK_PRINTF = @HAVE_DECL_OBSTACK_PRINTF@ +HAVE_DECL_SNPRINTF = @HAVE_DECL_SNPRINTF@ +HAVE_DECL_STRDUP = @HAVE_DECL_STRDUP@ +HAVE_DECL_STRERROR = @HAVE_DECL_STRERROR@ +HAVE_DECL_STRNCASECMP = @HAVE_DECL_STRNCASECMP@ +HAVE_DECL_STRNDUP = @HAVE_DECL_STRNDUP@ +HAVE_DECL_STRNLEN = @HAVE_DECL_STRNLEN@ +HAVE_DECL_STRSIGNAL = @HAVE_DECL_STRSIGNAL@ +HAVE_DECL_STRTOIMAX = @HAVE_DECL_STRTOIMAX@ +HAVE_DECL_STRTOK_R = @HAVE_DECL_STRTOK_R@ +HAVE_DECL_STRTOUMAX = @HAVE_DECL_STRTOUMAX@ +HAVE_DECL_VSNPRINTF = @HAVE_DECL_VSNPRINTF@ +HAVE_DECL_WCTOB = @HAVE_DECL_WCTOB@ +HAVE_DECL_WCWIDTH = @HAVE_DECL_WCWIDTH@ +HAVE_DPRINTF = @HAVE_DPRINTF@ +HAVE_DUP2 = @HAVE_DUP2@ +HAVE_DUP3 = @HAVE_DUP3@ +HAVE_EUIDACCESS = @HAVE_EUIDACCESS@ +HAVE_FACCESSAT = @HAVE_FACCESSAT@ +HAVE_FCHMODAT = @HAVE_FCHMODAT@ +HAVE_FCHOWNAT = @HAVE_FCHOWNAT@ +HAVE_FCNTL = @HAVE_FCNTL@ +HAVE_FDOPENDIR = @HAVE_FDOPENDIR@ +HAVE_FSTATAT = @HAVE_FSTATAT@ +HAVE_FSYNC = @HAVE_FSYNC@ +HAVE_FTRUNCATE = @HAVE_FTRUNCATE@ +HAVE_FUTIMENS = @HAVE_FUTIMENS@ +HAVE_GETDOMAINNAME = @HAVE_GETDOMAINNAME@ +HAVE_GETDTABLESIZE = @HAVE_GETDTABLESIZE@ +HAVE_GETGROUPS = @HAVE_GETGROUPS@ +HAVE_GETHOSTNAME = @HAVE_GETHOSTNAME@ +HAVE_GETLOGIN = @HAVE_GETLOGIN@ +HAVE_GETOPT_H = @HAVE_GETOPT_H@ +HAVE_GETPAGESIZE = @HAVE_GETPAGESIZE@ +HAVE_GETSUBOPT = @HAVE_GETSUBOPT@ +HAVE_GETTIMEOFDAY = @HAVE_GETTIMEOFDAY@ +HAVE_GETUSERSHELL = @HAVE_GETUSERSHELL@ +HAVE_INTTYPES_H = @HAVE_INTTYPES_H@ +HAVE_ISWCNTRL = @HAVE_ISWCNTRL@ +HAVE_LCHMOD = @HAVE_LCHMOD@ +HAVE_LCHOWN = @HAVE_LCHOWN@ +HAVE_LINK = @HAVE_LINK@ +HAVE_LINKAT = @HAVE_LINKAT@ +HAVE_LONG_LONG_INT = @HAVE_LONG_LONG_INT@ +HAVE_LSTAT = @HAVE_LSTAT@ +HAVE_MALLOC_POSIX = @HAVE_MALLOC_POSIX@ +HAVE_MBRLEN = @HAVE_MBRLEN@ +HAVE_MBRTOWC = @HAVE_MBRTOWC@ +HAVE_MBSINIT = @HAVE_MBSINIT@ +HAVE_MBSLEN = @HAVE_MBSLEN@ +HAVE_MBSNRTOWCS = @HAVE_MBSNRTOWCS@ +HAVE_MBSRTOWCS = @HAVE_MBSRTOWCS@ +HAVE_MEMPCPY = @HAVE_MEMPCPY@ +HAVE_MKDIRAT = @HAVE_MKDIRAT@ +HAVE_MKDTEMP = @HAVE_MKDTEMP@ +HAVE_MKFIFO = @HAVE_MKFIFO@ +HAVE_MKFIFOAT = @HAVE_MKFIFOAT@ +HAVE_MKNOD = @HAVE_MKNOD@ +HAVE_MKNODAT = @HAVE_MKNODAT@ +HAVE_MKOSTEMP = @HAVE_MKOSTEMP@ +HAVE_MKOSTEMPS = @HAVE_MKOSTEMPS@ +HAVE_MKSTEMPS = @HAVE_MKSTEMPS@ +HAVE_OPENAT = @HAVE_OPENAT@ +HAVE_OS_H = @HAVE_OS_H@ +HAVE_PIPE2 = @HAVE_PIPE2@ +HAVE_PREAD = @HAVE_PREAD@ +HAVE_RANDOM_H = @HAVE_RANDOM_H@ +HAVE_RANDOM_R = @HAVE_RANDOM_R@ +HAVE_RAWMEMCHR = @HAVE_RAWMEMCHR@ +HAVE_READLINK = @HAVE_READLINK@ +HAVE_READLINKAT = @HAVE_READLINKAT@ +HAVE_REALLOC_POSIX = @HAVE_REALLOC_POSIX@ +HAVE_REALPATH = @HAVE_REALPATH@ +HAVE_RENAMEAT = @HAVE_RENAMEAT@ +HAVE_RPMATCH = @HAVE_RPMATCH@ +HAVE_SCANDIR = @HAVE_SCANDIR@ +HAVE_SETENV = @HAVE_SETENV@ +HAVE_SIGNED_SIG_ATOMIC_T = @HAVE_SIGNED_SIG_ATOMIC_T@ +HAVE_SIGNED_WCHAR_T = @HAVE_SIGNED_WCHAR_T@ +HAVE_SIGNED_WINT_T = @HAVE_SIGNED_WINT_T@ +HAVE_SLEEP = @HAVE_SLEEP@ +HAVE_STDINT_H = @HAVE_STDINT_H@ +HAVE_STPCPY = @HAVE_STPCPY@ +HAVE_STPNCPY = @HAVE_STPNCPY@ +HAVE_STRCASECMP = @HAVE_STRCASECMP@ +HAVE_STRCASESTR = @HAVE_STRCASESTR@ +HAVE_STRCHRNUL = @HAVE_STRCHRNUL@ +HAVE_STRPBRK = @HAVE_STRPBRK@ +HAVE_STRSEP = @HAVE_STRSEP@ +HAVE_STRTOD = @HAVE_STRTOD@ +HAVE_STRTOLL = @HAVE_STRTOLL@ +HAVE_STRTOULL = @HAVE_STRTOULL@ +HAVE_STRUCT_RANDOM_DATA = @HAVE_STRUCT_RANDOM_DATA@ +HAVE_STRUCT_TIMEVAL = @HAVE_STRUCT_TIMEVAL@ +HAVE_STRVERSCMP = @HAVE_STRVERSCMP@ +HAVE_SYMLINK = @HAVE_SYMLINK@ +HAVE_SYMLINKAT = @HAVE_SYMLINKAT@ +HAVE_SYSEXITS_H = @HAVE_SYSEXITS_H@ +HAVE_SYS_BITYPES_H = @HAVE_SYS_BITYPES_H@ +HAVE_SYS_INTTYPES_H = @HAVE_SYS_INTTYPES_H@ +HAVE_SYS_LOADAVG_H = @HAVE_SYS_LOADAVG_H@ +HAVE_SYS_PARAM_H = @HAVE_SYS_PARAM_H@ +HAVE_SYS_TIME_H = @HAVE_SYS_TIME_H@ +HAVE_SYS_TYPES_H = @HAVE_SYS_TYPES_H@ +HAVE_UNISTD_H = @HAVE_UNISTD_H@ +HAVE_UNLINKAT = @HAVE_UNLINKAT@ +HAVE_UNSETENV = @HAVE_UNSETENV@ +HAVE_UNSIGNED_LONG_LONG_INT = @HAVE_UNSIGNED_LONG_LONG_INT@ +HAVE_USLEEP = @HAVE_USLEEP@ +HAVE_UTIMENSAT = @HAVE_UTIMENSAT@ +HAVE_VASPRINTF = @HAVE_VASPRINTF@ +HAVE_VDPRINTF = @HAVE_VDPRINTF@ +HAVE_WCHAR_H = @HAVE_WCHAR_H@ +HAVE_WCHAR_T = @HAVE_WCHAR_T@ +HAVE_WCRTOMB = @HAVE_WCRTOMB@ +HAVE_WCSNRTOMBS = @HAVE_WCSNRTOMBS@ +HAVE_WCSRTOMBS = @HAVE_WCSRTOMBS@ +HAVE_WCTYPE_H = @HAVE_WCTYPE_H@ +HAVE_WINT_T = @HAVE_WINT_T@ +HAVE__BOOL = @HAVE__BOOL@ +INCLUDE_NEXT = @INCLUDE_NEXT@ +INCLUDE_NEXT_AS_FIRST_DIRECTIVE = @INCLUDE_NEXT_AS_FIRST_DIRECTIVE@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +INT32_MAX_LT_INTMAX_MAX = @INT32_MAX_LT_INTMAX_MAX@ +INT64_MAX_EQ_LONG_MAX = @INT64_MAX_EQ_LONG_MAX@ +INTLLIBS = @INTLLIBS@ +INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@ +LDFLAGS = @LDFLAGS@ +LIBGNU_LIBDEPS = @LIBGNU_LIBDEPS@ +LIBGNU_LTLIBDEPS = @LIBGNU_LTLIBDEPS@ +LIBICONV = @LIBICONV@ +LIBINTL = @LIBINTL@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIB_CLOCK_GETTIME = @LIB_CLOCK_GETTIME@ +LIB_SETSOCKOPT = @LIB_SETSOCKOPT@ +LOCALCHARSET_TESTS_ENVIRONMENT = @LOCALCHARSET_TESTS_ENVIRONMENT@ +LOCALE_FR_UTF8 = @LOCALE_FR_UTF8@ +LOCALE_JA = @LOCALE_JA@ +LOCALE_ZH_CN = @LOCALE_ZH_CN@ +LTLIBICONV = @LTLIBICONV@ +LTLIBINTL = @LTLIBINTL@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MSGFMT = @MSGFMT@ +MSGFMT_015 = @MSGFMT_015@ +MSGMERGE = @MSGMERGE@ +NEXT_AS_FIRST_DIRECTIVE_DIRENT_H = @NEXT_AS_FIRST_DIRECTIVE_DIRENT_H@ +NEXT_AS_FIRST_DIRECTIVE_ERRNO_H = @NEXT_AS_FIRST_DIRECTIVE_ERRNO_H@ +NEXT_AS_FIRST_DIRECTIVE_FCNTL_H = @NEXT_AS_FIRST_DIRECTIVE_FCNTL_H@ +NEXT_AS_FIRST_DIRECTIVE_FLOAT_H = @NEXT_AS_FIRST_DIRECTIVE_FLOAT_H@ +NEXT_AS_FIRST_DIRECTIVE_GETOPT_H = @NEXT_AS_FIRST_DIRECTIVE_GETOPT_H@ +NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H = @NEXT_AS_FIRST_DIRECTIVE_INTTYPES_H@ +NEXT_AS_FIRST_DIRECTIVE_STDARG_H = @NEXT_AS_FIRST_DIRECTIVE_STDARG_H@ +NEXT_AS_FIRST_DIRECTIVE_STDDEF_H = @NEXT_AS_FIRST_DIRECTIVE_STDDEF_H@ +NEXT_AS_FIRST_DIRECTIVE_STDINT_H = @NEXT_AS_FIRST_DIRECTIVE_STDINT_H@ +NEXT_AS_FIRST_DIRECTIVE_STDIO_H = @NEXT_AS_FIRST_DIRECTIVE_STDIO_H@ +NEXT_AS_FIRST_DIRECTIVE_STDLIB_H = @NEXT_AS_FIRST_DIRECTIVE_STDLIB_H@ +NEXT_AS_FIRST_DIRECTIVE_STRINGS_H = @NEXT_AS_FIRST_DIRECTIVE_STRINGS_H@ +NEXT_AS_FIRST_DIRECTIVE_STRING_H = @NEXT_AS_FIRST_DIRECTIVE_STRING_H@ +NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H = @NEXT_AS_FIRST_DIRECTIVE_SYSEXITS_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_STAT_H@ +NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_SYS_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_TIME_H = @NEXT_AS_FIRST_DIRECTIVE_TIME_H@ +NEXT_AS_FIRST_DIRECTIVE_UNISTD_H = @NEXT_AS_FIRST_DIRECTIVE_UNISTD_H@ +NEXT_AS_FIRST_DIRECTIVE_WCHAR_H = @NEXT_AS_FIRST_DIRECTIVE_WCHAR_H@ +NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H = @NEXT_AS_FIRST_DIRECTIVE_WCTYPE_H@ +NEXT_DIRENT_H = @NEXT_DIRENT_H@ +NEXT_ERRNO_H = @NEXT_ERRNO_H@ +NEXT_FCNTL_H = @NEXT_FCNTL_H@ +NEXT_FLOAT_H = @NEXT_FLOAT_H@ +NEXT_GETOPT_H = @NEXT_GETOPT_H@ +NEXT_INTTYPES_H = @NEXT_INTTYPES_H@ +NEXT_STDARG_H = @NEXT_STDARG_H@ +NEXT_STDDEF_H = @NEXT_STDDEF_H@ +NEXT_STDINT_H = @NEXT_STDINT_H@ +NEXT_STDIO_H = @NEXT_STDIO_H@ +NEXT_STDLIB_H = @NEXT_STDLIB_H@ +NEXT_STRINGS_H = @NEXT_STRINGS_H@ +NEXT_STRING_H = @NEXT_STRING_H@ +NEXT_SYSEXITS_H = @NEXT_SYSEXITS_H@ +NEXT_SYS_STAT_H = @NEXT_SYS_STAT_H@ +NEXT_SYS_TIME_H = @NEXT_SYS_TIME_H@ +NEXT_TIME_H = @NEXT_TIME_H@ +NEXT_UNISTD_H = @NEXT_UNISTD_H@ +NEXT_WCHAR_H = @NEXT_WCHAR_H@ +NEXT_WCTYPE_H = @NEXT_WCTYPE_H@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +POSUB = @POSUB@ +PRAGMA_SYSTEM_HEADER = @PRAGMA_SYSTEM_HEADER@ +PRIPTR_PREFIX = @PRIPTR_PREFIX@ +PRI_MACROS_BROKEN = @PRI_MACROS_BROKEN@ +PTRDIFF_T_SUFFIX = @PTRDIFF_T_SUFFIX@ +PU_RMT_PROG = @PU_RMT_PROG@ +RANLIB = @RANLIB@ +REPLACE_BTOWC = @REPLACE_BTOWC@ +REPLACE_CANONICALIZE_FILE_NAME = @REPLACE_CANONICALIZE_FILE_NAME@ +REPLACE_CHOWN = @REPLACE_CHOWN@ +REPLACE_CLOSE = @REPLACE_CLOSE@ +REPLACE_CLOSEDIR = @REPLACE_CLOSEDIR@ +REPLACE_DPRINTF = @REPLACE_DPRINTF@ +REPLACE_DUP = @REPLACE_DUP@ +REPLACE_DUP2 = @REPLACE_DUP2@ +REPLACE_FCHDIR = @REPLACE_FCHDIR@ +REPLACE_FCHOWNAT = @REPLACE_FCHOWNAT@ +REPLACE_FCLOSE = @REPLACE_FCLOSE@ +REPLACE_FCNTL = @REPLACE_FCNTL@ +REPLACE_FDOPENDIR = @REPLACE_FDOPENDIR@ +REPLACE_FFLUSH = @REPLACE_FFLUSH@ +REPLACE_FOPEN = @REPLACE_FOPEN@ +REPLACE_FPRINTF = @REPLACE_FPRINTF@ +REPLACE_FPURGE = @REPLACE_FPURGE@ +REPLACE_FREOPEN = @REPLACE_FREOPEN@ +REPLACE_FSEEK = @REPLACE_FSEEK@ +REPLACE_FSEEKO = @REPLACE_FSEEKO@ +REPLACE_FSTAT = @REPLACE_FSTAT@ +REPLACE_FSTATAT = @REPLACE_FSTATAT@ +REPLACE_FTELL = @REPLACE_FTELL@ +REPLACE_FTELLO = @REPLACE_FTELLO@ +REPLACE_FUTIMENS = @REPLACE_FUTIMENS@ +REPLACE_GETCWD = @REPLACE_GETCWD@ +REPLACE_GETDELIM = @REPLACE_GETDELIM@ +REPLACE_GETGROUPS = @REPLACE_GETGROUPS@ +REPLACE_GETLINE = @REPLACE_GETLINE@ +REPLACE_GETPAGESIZE = @REPLACE_GETPAGESIZE@ +REPLACE_GETTIMEOFDAY = @REPLACE_GETTIMEOFDAY@ +REPLACE_ISWCNTRL = @REPLACE_ISWCNTRL@ +REPLACE_LCHOWN = @REPLACE_LCHOWN@ +REPLACE_LINK = @REPLACE_LINK@ +REPLACE_LINKAT = @REPLACE_LINKAT@ +REPLACE_LOCALTIME_R = @REPLACE_LOCALTIME_R@ +REPLACE_LSEEK = @REPLACE_LSEEK@ +REPLACE_LSTAT = @REPLACE_LSTAT@ +REPLACE_MBRLEN = @REPLACE_MBRLEN@ +REPLACE_MBRTOWC = @REPLACE_MBRTOWC@ +REPLACE_MBSINIT = @REPLACE_MBSINIT@ +REPLACE_MBSNRTOWCS = @REPLACE_MBSNRTOWCS@ +REPLACE_MBSRTOWCS = @REPLACE_MBSRTOWCS@ +REPLACE_MBSTATE_T = @REPLACE_MBSTATE_T@ +REPLACE_MEMCHR = @REPLACE_MEMCHR@ +REPLACE_MEMMEM = @REPLACE_MEMMEM@ +REPLACE_MKDIR = @REPLACE_MKDIR@ +REPLACE_MKFIFO = @REPLACE_MKFIFO@ +REPLACE_MKNOD = @REPLACE_MKNOD@ +REPLACE_MKSTEMP = @REPLACE_MKSTEMP@ +REPLACE_MKTIME = @REPLACE_MKTIME@ +REPLACE_NANOSLEEP = @REPLACE_NANOSLEEP@ +REPLACE_NULL = @REPLACE_NULL@ +REPLACE_OBSTACK_PRINTF = @REPLACE_OBSTACK_PRINTF@ +REPLACE_OPEN = @REPLACE_OPEN@ +REPLACE_OPENAT = @REPLACE_OPENAT@ +REPLACE_OPENDIR = @REPLACE_OPENDIR@ +REPLACE_PERROR = @REPLACE_PERROR@ +REPLACE_POPEN = @REPLACE_POPEN@ +REPLACE_PREAD = @REPLACE_PREAD@ +REPLACE_PRINTF = @REPLACE_PRINTF@ +REPLACE_PUTENV = @REPLACE_PUTENV@ +REPLACE_READLINK = @REPLACE_READLINK@ +REPLACE_REALPATH = @REPLACE_REALPATH@ +REPLACE_REMOVE = @REPLACE_REMOVE@ +REPLACE_RENAME = @REPLACE_RENAME@ +REPLACE_RENAMEAT = @REPLACE_RENAMEAT@ +REPLACE_RMDIR = @REPLACE_RMDIR@ +REPLACE_SETENV = @REPLACE_SETENV@ +REPLACE_SLEEP = @REPLACE_SLEEP@ +REPLACE_SNPRINTF = @REPLACE_SNPRINTF@ +REPLACE_SPRINTF = @REPLACE_SPRINTF@ +REPLACE_STAT = @REPLACE_STAT@ +REPLACE_STDIO_WRITE_FUNCS = @REPLACE_STDIO_WRITE_FUNCS@ +REPLACE_STRCASESTR = @REPLACE_STRCASESTR@ +REPLACE_STRDUP = @REPLACE_STRDUP@ +REPLACE_STRERROR = @REPLACE_STRERROR@ +REPLACE_STRNDUP = @REPLACE_STRNDUP@ +REPLACE_STRPTIME = @REPLACE_STRPTIME@ +REPLACE_STRSIGNAL = @REPLACE_STRSIGNAL@ +REPLACE_STRSTR = @REPLACE_STRSTR@ +REPLACE_STRTOD = @REPLACE_STRTOD@ +REPLACE_STRTOK_R = @REPLACE_STRTOK_R@ +REPLACE_SYMLINK = @REPLACE_SYMLINK@ +REPLACE_TIMEGM = @REPLACE_TIMEGM@ +REPLACE_UNLINK = @REPLACE_UNLINK@ +REPLACE_UNLINKAT = @REPLACE_UNLINKAT@ +REPLACE_UNSETENV = @REPLACE_UNSETENV@ +REPLACE_USLEEP = @REPLACE_USLEEP@ +REPLACE_UTIMENSAT = @REPLACE_UTIMENSAT@ +REPLACE_VASPRINTF = @REPLACE_VASPRINTF@ +REPLACE_VDPRINTF = @REPLACE_VDPRINTF@ +REPLACE_VFPRINTF = @REPLACE_VFPRINTF@ +REPLACE_VPRINTF = @REPLACE_VPRINTF@ +REPLACE_VSNPRINTF = @REPLACE_VSNPRINTF@ +REPLACE_VSPRINTF = @REPLACE_VSPRINTF@ +REPLACE_WCRTOMB = @REPLACE_WCRTOMB@ +REPLACE_WCSNRTOMBS = @REPLACE_WCSNRTOMBS@ +REPLACE_WCSRTOMBS = @REPLACE_WCSRTOMBS@ +REPLACE_WCTOB = @REPLACE_WCTOB@ +REPLACE_WCWIDTH = @REPLACE_WCWIDTH@ +REPLACE_WRITE = @REPLACE_WRITE@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SIG_ATOMIC_T_SUFFIX = @SIG_ATOMIC_T_SUFFIX@ +SIZE_T_SUFFIX = @SIZE_T_SUFFIX@ +STDARG_H = @STDARG_H@ +STDBOOL_H = @STDBOOL_H@ +STDDEF_H = @STDDEF_H@ +STDINT_H = @STDINT_H@ +STRIP = @STRIP@ +SYSEXITS_H = @SYSEXITS_H@ +SYS_TIME_H_DEFINES_STRUCT_TIMESPEC = @SYS_TIME_H_DEFINES_STRUCT_TIMESPEC@ +TIME_H_DEFINES_STRUCT_TIMESPEC = @TIME_H_DEFINES_STRUCT_TIMESPEC@ +UINT32_MAX_LT_UINTMAX_MAX = @UINT32_MAX_LT_UINTMAX_MAX@ +UINT64_MAX_EQ_ULONG_MAX = @UINT64_MAX_EQ_ULONG_MAX@ +UNDEFINE_STRTOK_R = @UNDEFINE_STRTOK_R@ +UNISTD_H_HAVE_WINSOCK2_H = @UNISTD_H_HAVE_WINSOCK2_H@ +UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS = @UNISTD_H_HAVE_WINSOCK2_H_AND_USE_SOCKETS@ +USE_NLS = @USE_NLS@ +VERSION = @VERSION@ +WCHAR_T_SUFFIX = @WCHAR_T_SUFFIX@ +WINT_T_SUFFIX = @WINT_T_SUFFIX@ +XGETTEXT = @XGETTEXT@ +XGETTEXT_015 = @XGETTEXT_015@ +XGETTEXT_EXTRA_OPTIONS = @XGETTEXT_EXTRA_OPTIONS@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gl_LIBOBJS = @gl_LIBOBJS@ +gl_LTLIBOBJS = @gl_LTLIBOBJS@ +gltests_LIBOBJS = @gltests_LIBOBJS@ +gltests_LTLIBOBJS = @gltests_LTLIBOBJS@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +INCLUDES = -I. -I.. -I$(top_srcdir)/gnu -I$(top_builddir)/gnu -I$(top_srcdir)/lib -I$(top_builddir)/lib +cpio_SOURCES = \ + copyin.c\ + copyout.c\ + copypass.c\ + defer.c\ + dstring.c\ + global.c\ + fatal.c\ + main.c\ + tar.c\ + util.c\ + filemode.c\ + idcache.c\ + makepath.c\ + userspec.c + +noinst_HEADERS = \ + cpio.h\ + cpiohdr.h\ + tar.h\ + tarhdr.h\ + defer.h\ + dstring.h\ + extern.h\ + filetypes.h\ + safe-stat.h + +LDADD = ../lib/libpax.a ../gnu/libgnu.a @INTLLIBS@ +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnits src/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnits src/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p; \ + then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +installcheck-binPROGRAMS: $(bin_PROGRAMS) + bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \ + case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \ + *" $$p "* | *" $(srcdir)/$$p "*) continue;; \ + esac; \ + f=`echo "$$p" | \ + sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \ + for opt in --help --version; do \ + if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \ + 2>c$${pid}_.err </dev/null \ + && test -n "`cat c$${pid}_.out`" \ + && test -z "`cat c$${pid}_.err`"; then :; \ + else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \ + done; \ + done; rm -f c$${pid}_.???; exit $$bad +cpio$(EXEEXT): $(cpio_OBJECTS) $(cpio_DEPENDENCIES) + @rm -f cpio$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(cpio_OBJECTS) $(cpio_LDADD) $(LIBS) +mt$(EXEEXT): $(mt_OBJECTS) $(mt_DEPENDENCIES) + @rm -f mt$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(mt_OBJECTS) $(mt_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copyin.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copyout.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/copypass.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/defer.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstring.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fatal.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/filemode.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/global.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idcache.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/makepath.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mt.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tar.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/userspec.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/util.Po@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(PROGRAMS) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-generic mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: installcheck-binPROGRAMS + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ + clean-generic ctags distclean distclean-compile \ + distclean-generic distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-binPROGRAMS \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installcheck-binPROGRAMS \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ + ps ps-am tags uninstall uninstall-am uninstall-binPROGRAMS + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/copyin.c b/src/copyin.c new file mode 100644 index 0000000..ac921e1 --- /dev/null +++ b/src/copyin.c @@ -0,0 +1,1502 @@ +/* copyin.c - extract or list a cpio archive + Copyright (C) 1990, 1991, 1992, 2001, 2002, 2003, 2004, 2005, 2006, + 2007, 2009, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "filetypes.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "defer.h" +#include <rmt.h> +#ifndef FNM_PATHNAME +# include <fnmatch.h> +#endif + +#ifndef HAVE_LCHOWN +# define lchown(f,u,g) 0 +#endif + +static void copyin_regular_file(struct cpio_file_stat* file_hdr, + int in_file_des); + +void +warn_junk_bytes (long bytes_skipped) +{ + error (0, 0, ngettext ("warning: skipped %ld byte of junk", + "warning: skipped %ld bytes of junk", bytes_skipped), + bytes_skipped); +} + + +static int +query_rename(struct cpio_file_stat* file_hdr, FILE *tty_in, FILE *tty_out, + FILE *rename_in) +{ + char *str_res; /* Result for string function. */ + static dynamic_string new_name; /* New file name for rename option. */ + static int initialized_new_name = false; + if (!initialized_new_name) + { + ds_init (&new_name, 128); + initialized_new_name = true; + } + + if (rename_flag) + { + fprintf (tty_out, _("rename %s -> "), file_hdr->c_name); + fflush (tty_out); + str_res = ds_fgets (tty_in, &new_name); + } + else + { + str_res = ds_fgetstr (rename_in, &new_name, '\n'); + } + if (str_res == NULL || str_res[0] == 0) + { + return -1; + } + else + /* Debian hack: file_hrd.c_name is sometimes set to + point to static memory by code in tar.c. This + causes a segfault. This has been fixed and an + additional check to ensure that the file name + is not too long has been added. (Reported by + Horst Knobloch.) This bug has been reported to + "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */ + { + if (archive_format != arf_tar && archive_format != arf_ustar) + { + free (file_hdr->c_name); + file_hdr->c_name = xstrdup (new_name.ds_string); + } + else + { + if (is_tar_filename_too_long (new_name.ds_string)) + error (0, 0, _("%s: file name too long"), + new_name.ds_string); + else + strcpy (file_hdr->c_name, new_name.ds_string); + } + } + return 0; +} + +/* Skip the padding on IN_FILE_DES after a header or file, + up to the next header. + The number of bytes skipped is based on OFFSET -- the current offset + from the last start of a header (or file) -- and the current + header type. */ + +static void +tape_skip_padding (int in_file_des, off_t offset) +{ + off_t pad; + + if (archive_format == arf_crcascii || archive_format == arf_newascii) + pad = (4 - (offset % 4)) % 4; + else if (archive_format == arf_binary || archive_format == arf_hpbinary) + pad = (2 - (offset % 2)) % 2; + else if (archive_format == arf_tar || archive_format == arf_ustar) + pad = (512 - (offset % 512)) % 512; + else + pad = 0; + + if (pad != 0) + tape_toss_input (in_file_des, pad); +} + + +static void +list_file(struct cpio_file_stat* file_hdr, int in_file_des) +{ + if (verbose_flag) + { +#ifdef CP_IFLNK + if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK) + { + if (archive_format != arf_tar && archive_format != arf_ustar) + { + char *link_name = NULL; /* Name of hard and symbolic links. */ + + link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1); + link_name[file_hdr->c_filesize] = '\0'; + tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize); + long_format (file_hdr, link_name); + free (link_name); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + else + { + long_format (file_hdr, file_hdr->c_tar_linkname); + return; + } + } + else +#endif + long_format (file_hdr, (char *) 0); + } + else + { + /* Debian hack: Modified to print a list of filenames + terminiated by a null character when the -t and -0 + flags are used. This has been submitted as a + suggestion to "bug-gnu-utils@prep.ai.mit.edu". -BEM */ + printf ("%s%c", file_hdr->c_name, name_end); + } + + crc = 0; + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + if (only_verify_crc_flag) + { +#ifdef CP_IFLNK + if ((file_hdr->c_mode & CP_IFMT) == CP_IFLNK) + { + return; /* links don't have a checksum */ + } +#endif + if (crc != file_hdr->c_chksum) + { + error (0, 0, _("%s: checksum error (0x%lx, should be 0x%lx)"), + file_hdr->c_name, crc, file_hdr->c_chksum); + } + } +} + +static int +try_existing_file (struct cpio_file_stat* file_hdr, int in_file_des, + int *existing_dir) +{ + struct stat file_stat; + + *existing_dir = false; + if (lstat (file_hdr->c_name, &file_stat) == 0) + { + if (S_ISDIR (file_stat.st_mode) + && ((file_hdr->c_mode & CP_IFMT) == CP_IFDIR)) + { + /* If there is already a directory there that + we are trying to create, don't complain about + it. */ + *existing_dir = true; + return 0; + } + else if (!unconditional_flag + && file_hdr->c_mtime <= file_stat.st_mtime) + { + error (0, 0, _("%s not created: newer or same age version exists"), + file_hdr->c_name); + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return -1; /* Go to the next file. */ + } + else if (S_ISDIR (file_stat.st_mode) + ? rmdir (file_hdr->c_name) + : unlink (file_hdr->c_name)) + { + error (0, errno, _("cannot remove current %s"), + file_hdr->c_name); + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return -1; /* Go to the next file. */ + } + } + return 0; +} + +/* The newc and crc formats store multiply linked copies of the same file + in the archive only once. The actual data is attached to the last link + in the archive, and the other links all have a filesize of 0. When a + file in the archive has multiple links and a filesize of 0, its data is + probably "attatched" to another file in the archive, so we can't create + it right away. We have to "defer" creating it until we have created + the file that has the data "attatched" to it. We keep a list of the + "defered" links on deferments. */ + +struct deferment *deferments = NULL; + +/* Add a file header to the deferments list. For now they all just + go on one list, although we could optimize this if necessary. */ + +static void +defer_copyin (struct cpio_file_stat *file_hdr) +{ + struct deferment *d; + d = create_deferment (file_hdr); + d->next = deferments; + deferments = d; + return; +} + +/* We just created a file that (probably) has some other links to it + which have been defered. Go through all of the links on the deferments + list and create any which are links to this file. */ + +static void +create_defered_links (struct cpio_file_stat *file_hdr) +{ + struct deferment *d; + struct deferment *d_prev; + ino_t ino; + int maj; + int min; + int link_res; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d = deferments; + d_prev = NULL; + while (d != NULL) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + { + struct deferment *d_free; + link_res = link_to_name (d->header.c_name, file_hdr->c_name); + if (link_res < 0) + { + error (0, errno, _("cannot link %s to %s"), + d->header.c_name, file_hdr->c_name); + } + if (d_prev != NULL) + d_prev->next = d->next; + else + deferments = d->next; + d_free = d; + d = d->next; + free_deferment (d_free); + } + else + { + d_prev = d; + d = d->next; + } + } +} + +/* We are skipping a file but there might be other links to it that we + did not skip, so we have to copy its data for the other links. Find + the first link that we didn't skip and try to create that. That will + then create the other deferred links. */ + +static int +create_defered_links_to_skipped (struct cpio_file_stat *file_hdr, + int in_file_des) +{ + struct deferment *d; + struct deferment *d_prev; + ino_t ino; + int maj; + int min; + if (file_hdr->c_filesize == 0) + { + /* The file doesn't have any data attached to it so we don't have + to bother. */ + return -1; + } + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d = deferments; + d_prev = NULL; + while (d != NULL) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + { + if (d_prev != NULL) + d_prev->next = d->next; + else + deferments = d->next; + free (file_hdr->c_name); + file_hdr->c_name = xstrdup(d->header.c_name); + free_deferment (d); + copyin_regular_file(file_hdr, in_file_des); + return 0; + } + else + { + d_prev = d; + d = d->next; + } + } + return -1; +} + +/* If we had a multiply linked file that really was empty then we would + have defered all of its links, since we never found any with data + "attached", and they will still be on the deferment list even when + we are done reading the whole archive. Write out all of these + empty links that are still on the deferments list. */ + +static void +create_final_defers () +{ + struct deferment *d; + int link_res; + int out_file_des; + + for (d = deferments; d != NULL; d = d->next) + { + /* Debian hack: A line, which could cause an endless loop, was + removed (97/1/2). It was reported by Ronald F. Guilmette to + the upstream maintainers. -BEM */ + /* Debian hack: This was reported by Horst Knobloch. This bug has + been reported to "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM + */ + link_res = link_to_maj_min_ino (d->header.c_name, + d->header.c_dev_maj, d->header.c_dev_min, + d->header.c_ino); + if (link_res == 0) + { + continue; + } + out_file_des = open (d->header.c_name, + O_CREAT | O_WRONLY | O_BINARY, 0600); + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (d->header.c_name); + out_file_des = open (d->header.c_name, + O_CREAT | O_WRONLY | O_BINARY, + 0600); + } + if (out_file_des < 0) + { + open_error (d->header.c_name); + continue; + } + + set_perms (out_file_des, &d->header); + + if (close (out_file_des) < 0) + close_error (d->header.c_name); + + } +} + +static void +copyin_regular_file (struct cpio_file_stat* file_hdr, int in_file_des) +{ + int out_file_des; /* Output file descriptor. */ + + if (to_stdout_option) + out_file_des = STDOUT_FILENO; + else + { + /* Can the current file be linked to a previously copied file? */ + if (file_hdr->c_nlink > 1 + && (archive_format == arf_newascii + || archive_format == arf_crcascii) ) + { + int link_res; + if (file_hdr->c_filesize == 0) + { + /* The newc and crc formats store multiply linked copies + of the same file in the archive only once. The + actual data is attached to the last link in the + archive, and the other links all have a filesize + of 0. Since this file has multiple links and a + filesize of 0, its data is probably attatched to + another file in the archive. Save the link, and + process it later when we get the actual data. We + can't just create it with length 0 and add the + data later, in case the file is readonly. We still + lose if its parent directory is readonly (and we aren't + running as root), but there's nothing we can do about + that. */ + defer_copyin (file_hdr); + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + /* If the file has data (filesize != 0), then presumably + any other links have already been defer_copyin'ed(), + but GNU cpio version 2.0-2.2 didn't do that, so we + still have to check for links here (and also in case + the archive was created and later appeneded to). */ + /* Debian hack: (97/1/2) This was reported by Ronald + F. Guilmette to the upstream maintainers. -BEM */ + link_res = link_to_maj_min_ino (file_hdr->c_name, + file_hdr->c_dev_maj, file_hdr->c_dev_min, + file_hdr->c_ino); + if (link_res == 0) + { + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + } + else if (file_hdr->c_nlink > 1 + && archive_format != arf_tar + && archive_format != arf_ustar) + { + int link_res; + /* Debian hack: (97/1/2) This was reported by Ronald + F. Guilmette to the upstream maintainers. -BEM */ + link_res = link_to_maj_min_ino (file_hdr->c_name, + file_hdr->c_dev_maj, + file_hdr->c_dev_min, + file_hdr->c_ino); + if (link_res == 0) + { + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + } + else if ((archive_format == arf_tar || archive_format == arf_ustar) + && file_hdr->c_tar_linkname + && file_hdr->c_tar_linkname[0] != '\0') + { + int link_res; + link_res = link_to_name (file_hdr->c_name, file_hdr->c_tar_linkname); + if (link_res < 0) + { + error (0, errno, _("cannot link %s to %s"), + file_hdr->c_tar_linkname, file_hdr->c_name); + } + return; + } + + /* If not linked, copy the contents of the file. */ + out_file_des = open (file_hdr->c_name, + O_CREAT | O_WRONLY | O_BINARY, 0600); + + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (file_hdr->c_name); + out_file_des = open (file_hdr->c_name, + O_CREAT | O_WRONLY | O_BINARY, + 0600); + } + + if (out_file_des < 0) + { + open_error (file_hdr->c_name); + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + } + + crc = 0; + if (swap_halfwords_flag) + { + if ((file_hdr->c_filesize % 4) == 0) + swapping_halfwords = true; + else + error (0, 0, _("cannot swap halfwords of %s: odd number of halfwords"), + file_hdr->c_name); + } + if (swap_bytes_flag) + { + if ((file_hdr->c_filesize % 2) == 0) + swapping_bytes = true; + else + error (0, 0, _("cannot swap bytes of %s: odd number of bytes"), + file_hdr->c_name); + } + copy_files_tape_to_disk (in_file_des, out_file_des, file_hdr->c_filesize); + disk_empty_output_buffer (out_file_des); + + if (to_stdout_option) + { + if (archive_format == arf_crcascii) + { + if (crc != file_hdr->c_chksum) + error (0, 0, _("%s: checksum error (0x%lx, should be 0x%lx)"), + file_hdr->c_name, crc, file_hdr->c_chksum); + } + tape_skip_padding (in_file_des, file_hdr->c_filesize); + return; + } + + /* Debian hack to fix a bug in the --sparse option. + This bug has been reported to + "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */ + if (delayed_seek_count > 0) + { + lseek (out_file_des, delayed_seek_count-1, SEEK_CUR); + write (out_file_des, "", 1); + delayed_seek_count = 0; + } + + set_perms (out_file_des, file_hdr); + + if (close (out_file_des) < 0) + close_error (file_hdr->c_name); + + if (archive_format == arf_crcascii) + { + if (crc != file_hdr->c_chksum) + error (0, 0, _("%s: checksum error (0x%lx, should be 0x%lx)"), + file_hdr->c_name, crc, file_hdr->c_chksum); + } + + tape_skip_padding (in_file_des, file_hdr->c_filesize); + if (file_hdr->c_nlink > 1 + && (archive_format == arf_newascii || archive_format == arf_crcascii) ) + { + /* (see comment above for how the newc and crc formats + store multiple links). Now that we have the data + for this file, create any other links to it which + we defered. */ + create_defered_links (file_hdr); + } +} + +static void +copyin_device (struct cpio_file_stat* file_hdr) +{ + int res; /* Result of various function calls. */ + + if (to_stdout_option) + return; + + if (file_hdr->c_nlink > 1 && archive_format != arf_tar + && archive_format != arf_ustar) + { + int link_res; + /* Debian hack: This was reported by Horst + Knobloch. This bug has been reported to + "bug-gnu-utils@prep.ai.mit.edu". (99/1/6) -BEM */ + link_res = link_to_maj_min_ino (file_hdr->c_name, + file_hdr->c_dev_maj, file_hdr->c_dev_min, + file_hdr->c_ino); + if (link_res == 0) + { + return; + } + } + else if (archive_format == arf_ustar && + file_hdr->c_tar_linkname && + file_hdr->c_tar_linkname [0] != '\0') + { + int link_res; + link_res = link_to_name (file_hdr->c_name, + file_hdr->c_tar_linkname); + if (link_res < 0) + { + error (0, errno, _("cannot link %s to %s"), + file_hdr->c_tar_linkname, file_hdr->c_name); + /* Something must be wrong, because we couldn't + find the file to link to. But can we assume + that the device maj/min numbers are correct + and fall through to the mknod? It's probably + safer to just return, rather than possibly + creating a bogus device file. */ + } + return; + } + + res = mknod (file_hdr->c_name, file_hdr->c_mode, + makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr->c_name); + res = mknod (file_hdr->c_name, file_hdr->c_mode, + makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min)); + } + if (res < 0) + { + mknod_error (file_hdr->c_name); + return; + } + if (!no_chown_flag) + { + uid_t uid = set_owner_flag ? set_owner : file_hdr->c_uid; + gid_t gid = set_group_flag ? set_group : file_hdr->c_gid; + if ((chown (file_hdr->c_name, uid, gid) < 0) + && errno != EPERM) + chown_error_details (file_hdr->c_name, uid, gid); + } + /* chown may have turned off some permissions we wanted. */ + if (chmod (file_hdr->c_name, file_hdr->c_mode) < 0) + chmod_error_details (file_hdr->c_name, file_hdr->c_mode); + if (retain_time_flag) + set_file_times (-1, file_hdr->c_name, file_hdr->c_mtime, + file_hdr->c_mtime); +} + +static void +copyin_link(struct cpio_file_stat *file_hdr, int in_file_des) +{ + char *link_name = NULL; /* Name of hard and symbolic links. */ + int res; /* Result of various function calls. */ + + if (to_stdout_option) + return; + + if (archive_format != arf_tar && archive_format != arf_ustar) + { + link_name = (char *) xmalloc ((unsigned int) file_hdr->c_filesize + 1); + link_name[file_hdr->c_filesize] = '\0'; + tape_buffered_read (link_name, in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + } + else + { + link_name = xstrdup (file_hdr->c_tar_linkname); + } + + res = UMASKED_SYMLINK (link_name, file_hdr->c_name, + file_hdr->c_mode); + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr->c_name); + res = UMASKED_SYMLINK (link_name, file_hdr->c_name, + file_hdr->c_mode); + } + if (res < 0) + { + error (0, errno, _("%s: Cannot symlink to %s"), + quotearg_colon (link_name), quote_n (1, file_hdr->c_name)); + free (link_name); + return; + } + if (!no_chown_flag) + { + uid_t uid = set_owner_flag ? set_owner : file_hdr->c_uid; + gid_t gid = set_group_flag ? set_group : file_hdr->c_gid; + if ((lchown (file_hdr->c_name, uid, gid) < 0) + && errno != EPERM) + chown_error_details (file_hdr->c_name, uid, gid); + } + free (link_name); +} + +static void +copyin_file (struct cpio_file_stat *file_hdr, int in_file_des) +{ + int existing_dir; + + if (!to_stdout_option + && try_existing_file (file_hdr, in_file_des, &existing_dir) < 0) + return; + + /* Do the real copy or link. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFREG: + copyin_regular_file (file_hdr, in_file_des); + break; + + case CP_IFDIR: + cpio_create_dir (file_hdr, existing_dir); + break; + + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + copyin_device (file_hdr); + break; + +#ifdef CP_IFLNK + case CP_IFLNK: + copyin_link (file_hdr, in_file_des); + break; +#endif + + default: + error (0, 0, _("%s: unknown file type"), file_hdr->c_name); + tape_toss_input (in_file_des, file_hdr->c_filesize); + tape_skip_padding (in_file_des, file_hdr->c_filesize); + } +} + + +/* Current time for verbose table. */ +static time_t current_time; + + +/* Print the file described by FILE_HDR in long format. + If LINK_NAME is nonzero, it is the name of the file that + this file is a symbolic link to. */ + +void +long_format (struct cpio_file_stat *file_hdr, char *link_name) +{ + char mbuf[11]; + char tbuf[40]; + time_t when; + + mode_string (file_hdr->c_mode, mbuf); + mbuf[10] = '\0'; + + /* Get time values ready to print. */ + when = file_hdr->c_mtime; + strcpy (tbuf, ctime (&when)); + if (current_time - when > 6L * 30L * 24L * 60L * 60L + || current_time - when < 0L) + { + /* The file is older than 6 months, or in the future. + Show the year instead of the time of day. */ + strcpy (tbuf + 11, tbuf + 19); + } + tbuf[16] = '\0'; + + printf ("%s %3lu ", mbuf, (unsigned long) file_hdr->c_nlink); + + if (numeric_uid) + printf ("%-8u %-8u ", (unsigned int) file_hdr->c_uid, + (unsigned int) file_hdr->c_gid); + else + printf ("%-8.8s %-8.8s ", getuser (file_hdr->c_uid), + getgroup (file_hdr->c_gid)); + + if ((file_hdr->c_mode & CP_IFMT) == CP_IFCHR + || (file_hdr->c_mode & CP_IFMT) == CP_IFBLK) + printf ("%3lu, %3lu ", file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + else + printf ("%8"PRIuMAX" ", (uintmax_t) file_hdr->c_filesize); + + printf ("%s ", tbuf + 4); + + print_name_with_quoting (file_hdr->c_name); + if (link_name) + { + printf (" -> "); + print_name_with_quoting (link_name); + } + putc ('\n', stdout); +} + +void +print_name_with_quoting (register char *p) +{ + register unsigned char c; + + while ( (c = *p++) ) + { + switch (c) + { + case '\\': + printf ("\\\\"); + break; + + case '\n': + printf ("\\n"); + break; + + case '\b': + printf ("\\b"); + break; + + case '\r': + printf ("\\r"); + break; + + case '\t': + printf ("\\t"); + break; + + case '\f': + printf ("\\f"); + break; + + case ' ': + printf ("\\ "); + break; + + case '"': + printf ("\\\""); + break; + + default: + if (c > 040 && c < 0177) + putchar (c); + else + printf ("\\%03o", (unsigned int) c); + } + } +} + +/* Read a pattern file (for the -E option). Put a list of + `num_patterns' elements in `save_patterns'. Any patterns that were + already in `save_patterns' (from the command line) are preserved. */ + +static void +read_pattern_file () +{ + int max_new_patterns; + char **new_save_patterns; + int new_num_patterns; + int i; + dynamic_string pattern_name; + FILE *pattern_fp; + + if (num_patterns < 0) + num_patterns = 0; + max_new_patterns = 1 + num_patterns; + new_save_patterns = (char **) xmalloc (max_new_patterns * sizeof (char *)); + new_num_patterns = num_patterns; + ds_init (&pattern_name, 128); + + pattern_fp = fopen (pattern_file_name, "r"); + if (pattern_fp == NULL) + open_error (pattern_file_name); + while (ds_fgetstr (pattern_fp, &pattern_name, '\n') != NULL) + { + if (new_num_patterns >= max_new_patterns) + { + max_new_patterns += 1; + new_save_patterns = (char **) + xrealloc ((char *) new_save_patterns, + max_new_patterns * sizeof (char *)); + } + new_save_patterns[new_num_patterns] = xstrdup (pattern_name.ds_string); + ++new_num_patterns; + } + if (ferror (pattern_fp) || fclose (pattern_fp) == EOF) + close_error (pattern_file_name); + + for (i = 0; i < num_patterns; ++i) + new_save_patterns[i] = save_patterns[i]; + + save_patterns = new_save_patterns; + num_patterns = new_num_patterns; +} + + +uintmax_t +from_ascii (char const *where, size_t digs, unsigned logbase) +{ + uintmax_t value = 0; + char const *buf = where; + char const *end = buf + digs; + int overflow = 0; + static char codetab[] = "0123456789ABCDEF"; + + for (; *buf == ' '; buf++) + { + if (buf == end) + return 0; + } + + if (buf == end || *buf == 0) + return 0; + while (1) + { + unsigned d; + + char *p = strchr (codetab, toupper (*buf)); + if (!p) + { + error (0, 0, _("Malformed number %.*s"), digs, where); + break; + } + + d = p - codetab; + if ((d >> logbase) > 1) + { + error (0, 0, _("Malformed number %.*s"), digs, where); + break; + } + value += d; + if (++buf == end || *buf == 0) + break; + overflow |= value ^ (value << logbase >> logbase); + value <<= logbase; + } + if (overflow) + error (0, 0, _("Archive value %.*s is out of range"), + digs, where); + return value; +} + + + +/* Return 16-bit integer I with the bytes swapped. */ +#define swab_short(i) ((((i) << 8) & 0xff00) | (((i) >> 8) & 0x00ff)) + +/* Read the header, including the name of the file, from file + descriptor IN_DES into FILE_HDR. */ + +void +read_in_header (struct cpio_file_stat *file_hdr, int in_des) +{ + union { + char str[6]; + unsigned short num; + struct old_cpio_header old_header; + } magic; + long bytes_skipped = 0; /* Bytes of junk found before magic number. */ + + /* Search for a valid magic number. */ + + if (archive_format == arf_unknown) + { + char tmpbuf[512]; + int check_tar; + int peeked_bytes; + + while (archive_format == arf_unknown) + { + peeked_bytes = tape_buffered_peek (tmpbuf, in_des, 512); + if (peeked_bytes < 6) + error (1, 0, _("premature end of archive")); + + if (!strncmp (tmpbuf, "070701", 6)) + archive_format = arf_newascii; + else if (!strncmp (tmpbuf, "070707", 6)) + archive_format = arf_oldascii; + else if (!strncmp (tmpbuf, "070702", 6)) + { + archive_format = arf_crcascii; + crc_i_flag = true; + } + else if ((*((unsigned short *) tmpbuf) == 070707) || + (*((unsigned short *) tmpbuf) == swab_short ((unsigned short) 070707))) + archive_format = arf_binary; + else if (peeked_bytes >= 512 + && (check_tar = is_tar_header (tmpbuf))) + { + if (check_tar == 2) + archive_format = arf_ustar; + else + archive_format = arf_tar; + } + else + { + tape_buffered_read ((char *) tmpbuf, in_des, 1L); + ++bytes_skipped; + } + } + } + + if (archive_format == arf_tar || archive_format == arf_ustar) + { + if (append_flag) + last_header_start = input_bytes - io_block_size + + (in_buff - input_buffer); + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); + + read_in_tar_header (file_hdr, in_des); + return; + } + + file_hdr->c_tar_linkname = NULL; + + tape_buffered_read (magic.str, in_des, 6L); + while (1) + { + if (append_flag) + last_header_start = input_bytes - io_block_size + + (in_buff - input_buffer) - 6; + if (archive_format == arf_newascii + && !strncmp (magic.str, "070701", 6)) + { + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); + file_hdr->c_magic = 070701; + read_in_new_ascii (file_hdr, in_des); + break; + } + if (archive_format == arf_crcascii + && !strncmp (magic.str, "070702", 6)) + { + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); + file_hdr->c_magic = 070702; + read_in_new_ascii (file_hdr, in_des); + break; + } + if ( (archive_format == arf_oldascii || archive_format == arf_hpoldascii) + && !strncmp (magic.str, "070707", 6)) + { + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); + file_hdr->c_magic = 070707; + read_in_old_ascii (file_hdr, in_des); + break; + } + if ( (archive_format == arf_binary || archive_format == arf_hpbinary) + && (magic.num == 070707 + || magic.num == swab_short ((unsigned short) 070707))) + { + /* Having to skip 1 byte because of word alignment is normal. */ + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); + file_hdr->c_magic = 070707; + read_in_binary (file_hdr, &magic.old_header, in_des); + break; + } + bytes_skipped++; + memmove (magic.str, magic.str + 1, 5); + tape_buffered_read (magic.str, in_des, 1L); + } +} + +/* Fill in FILE_HDR by reading an old-format ASCII format cpio header from + file descriptor IN_DES, except for the magic number, which is + already filled in. */ + +void +read_in_old_ascii (struct cpio_file_stat *file_hdr, int in_des) +{ + struct old_ascii_header ascii_header; + unsigned long dev; + + tape_buffered_read (ascii_header.c_dev, in_des, + sizeof ascii_header - sizeof ascii_header.c_magic); + dev = FROM_OCTAL (ascii_header.c_dev); + file_hdr->c_dev_maj = major (dev); + file_hdr->c_dev_min = minor (dev); + + file_hdr->c_ino = FROM_OCTAL (ascii_header.c_ino); + file_hdr->c_mode = FROM_OCTAL (ascii_header.c_mode); + file_hdr->c_uid = FROM_OCTAL (ascii_header.c_uid); + file_hdr->c_gid = FROM_OCTAL (ascii_header.c_gid); + file_hdr->c_nlink = FROM_OCTAL (ascii_header.c_nlink); + dev = FROM_OCTAL (ascii_header.c_rdev); + file_hdr->c_rdev_maj = major (dev); + file_hdr->c_rdev_min = minor (dev); + + file_hdr->c_mtime = FROM_OCTAL (ascii_header.c_mtime); + file_hdr->c_namesize = FROM_OCTAL (ascii_header.c_namesize); + file_hdr->c_filesize = FROM_OCTAL (ascii_header.c_filesize); + + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize + 1); + tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); + + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. See if this + is an HP/UX cpio archive, and if so fix it. We have to do this + here because process_copy_in() assumes filesize is always 0 + for devices. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (file_hdr->c_filesize != 0 + && file_hdr->c_rdev_maj == 0 + && file_hdr->c_rdev_min == 1) + { + file_hdr->c_rdev_maj = major (file_hdr->c_filesize); + file_hdr->c_rdev_min = minor (file_hdr->c_filesize); + file_hdr->c_filesize = 0; + } + break; + default: + break; + } +} + +/* Fill in FILE_HDR by reading a new-format ASCII format cpio header from + file descriptor IN_DES, except for the magic number, which is + already filled in. */ + +void +read_in_new_ascii (struct cpio_file_stat *file_hdr, int in_des) +{ + struct new_ascii_header ascii_header; + + tape_buffered_read (ascii_header.c_ino, in_des, + sizeof ascii_header - sizeof ascii_header.c_magic); + + file_hdr->c_ino = FROM_HEX (ascii_header.c_ino); + file_hdr->c_mode = FROM_HEX (ascii_header.c_mode); + file_hdr->c_uid = FROM_HEX (ascii_header.c_uid); + file_hdr->c_gid = FROM_HEX (ascii_header.c_gid); + file_hdr->c_nlink = FROM_HEX (ascii_header.c_nlink); + file_hdr->c_mtime = FROM_HEX (ascii_header.c_mtime); + file_hdr->c_filesize = FROM_HEX (ascii_header.c_filesize); + file_hdr->c_dev_maj = FROM_HEX (ascii_header.c_dev_maj); + file_hdr->c_dev_min = FROM_HEX (ascii_header.c_dev_min); + file_hdr->c_rdev_maj = FROM_HEX (ascii_header.c_rdev_maj); + file_hdr->c_rdev_min = FROM_HEX (ascii_header.c_rdev_min); + file_hdr->c_namesize = FROM_HEX (ascii_header.c_namesize); + file_hdr->c_chksum = FROM_HEX (ascii_header.c_chksum); + + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize); + tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); + + /* In SVR4 ASCII format, the amount of space allocated for the header + is rounded up to the next long-word, so we might need to drop + 1-3 bytes. */ + tape_skip_padding (in_des, file_hdr->c_namesize + 110); +} + +/* Fill in FILE_HDR by reading a binary format cpio header from + file descriptor IN_DES, except for the first 6 bytes (the magic + number, device, and inode number), which are already filled in. */ + +void +read_in_binary (struct cpio_file_stat *file_hdr, + struct old_cpio_header *short_hdr, + int in_des) +{ + file_hdr->c_magic = short_hdr->c_magic; + + tape_buffered_read (((char *) short_hdr) + 6, in_des, + sizeof *short_hdr - 6 /* = 20 */); + + /* If the magic number is byte swapped, fix the header. */ + if (file_hdr->c_magic == swab_short ((unsigned short) 070707)) + { + static int warned = 0; + + /* Alert the user that they might have to do byte swapping on + the file contents. */ + if (warned == 0) + { + error (0, 0, _("warning: archive header has reverse byte-order")); + warned = 1; + } + swab_array ((char *) short_hdr, 13); + } + + file_hdr->c_dev_maj = major (short_hdr->c_dev); + file_hdr->c_dev_min = minor (short_hdr->c_dev); + file_hdr->c_ino = short_hdr->c_ino; + file_hdr->c_mode = short_hdr->c_mode; + file_hdr->c_uid = short_hdr->c_uid; + file_hdr->c_gid = short_hdr->c_gid; + file_hdr->c_nlink = short_hdr->c_nlink; + file_hdr->c_rdev_maj = major (short_hdr->c_rdev); + file_hdr->c_rdev_min = minor (short_hdr->c_rdev); + file_hdr->c_mtime = (unsigned long) short_hdr->c_mtimes[0] << 16 + | short_hdr->c_mtimes[1]; + + file_hdr->c_namesize = short_hdr->c_namesize; + file_hdr->c_filesize = (unsigned long) short_hdr->c_filesizes[0] << 16 + | short_hdr->c_filesizes[1]; + + /* Read file name from input. */ + if (file_hdr->c_name != NULL) + free (file_hdr->c_name); + file_hdr->c_name = (char *) xmalloc (file_hdr->c_namesize); + tape_buffered_read (file_hdr->c_name, in_des, (long) file_hdr->c_namesize); + + /* In binary mode, the amount of space allocated in the header for + the filename is `c_namesize' rounded up to the next short-word, + so we might need to drop a byte. */ + if (file_hdr->c_namesize % 2) + tape_toss_input (in_des, 1L); + + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. See if this + is an HP/UX cpio archive, and if so fix it. We have to do this + here because process_copy_in() assumes filesize is always 0 + for devices. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (file_hdr->c_filesize != 0 + && file_hdr->c_rdev_maj == 0 + && file_hdr->c_rdev_min == 1) + { + file_hdr->c_rdev_maj = major (file_hdr->c_filesize); + file_hdr->c_rdev_min = minor (file_hdr->c_filesize); + file_hdr->c_filesize = 0; + } + break; + default: + break; + } +} + +/* Exchange the bytes of each element of the array of COUNT shorts + starting at PTR. */ + +void +swab_array (char *ptr, int count) +{ + char tmp; + + while (count-- > 0) + { + tmp = *ptr; + *ptr = *(ptr + 1); + ++ptr; + *ptr = tmp; + ++ptr; + } +} + +/* Read the collection from standard input and create files + in the file system. */ + +void +process_copy_in () +{ + char done = false; /* True if trailer reached. */ + FILE *tty_in = NULL; /* Interactive file for rename option. */ + FILE *tty_out = NULL; /* Interactive file for rename option. */ + FILE *rename_in = NULL; /* Batch file for rename option. */ + struct stat file_stat; /* Output file stat record. */ + struct cpio_file_stat file_hdr; /* Output header information. */ + int in_file_des; /* Input file descriptor. */ + char skip_file; /* Flag for use with patterns. */ + int i; /* Loop index variable. */ + + newdir_umask = umask (0); /* Reset umask to preserve modes of + created files */ + + /* Initialize the copy in. */ + if (pattern_file_name) + { + read_pattern_file (); + } + file_hdr.c_name = NULL; + + if (rename_batch_file) + { + rename_in = fopen (rename_batch_file, "r"); + if (rename_in == NULL) + { + error (2, errno, TTY_NAME); + } + } + else if (rename_flag) + { + /* Open interactive file pair for rename operation. */ + tty_in = fopen (TTY_NAME, "r"); + if (tty_in == NULL) + { + error (2, errno, TTY_NAME); + } + tty_out = fopen (TTY_NAME, "w"); + if (tty_out == NULL) + { + error (2, errno, TTY_NAME); + } + } + + /* Get date and time if needed for processing the table option. */ + if (table_flag && verbose_flag) + { + time (¤t_time); + } + + /* Check whether the input file might be a tape. */ + in_file_des = archive_des; + if (_isrmt (in_file_des)) + { + input_is_special = 1; + input_is_seekable = 0; + } + else + { + if (fstat (in_file_des, &file_stat)) + error (1, errno, _("standard input is closed")); + input_is_special = +#ifdef S_ISBLK + S_ISBLK (file_stat.st_mode) || +#endif + S_ISCHR (file_stat.st_mode); + input_is_seekable = S_ISREG (file_stat.st_mode); + } + output_is_seekable = true; + + /* While there is more input in the collection, process the input. */ + while (!done) + { + swapping_halfwords = swapping_bytes = false; + + /* Start processing the next file by reading the header. */ + read_in_header (&file_hdr, in_file_des); + +#ifdef DEBUG_CPIO + if (debug_flag) + { + struct cpio_file_stat *h; + h = &file_hdr; + fprintf (stderr, + "magic = 0%o, ino = %ld, mode = 0%o, uid = %d, gid = %d\n", + h->c_magic, (long)h->c_ino, h->c_mode, h->c_uid, h->c_gid); + fprintf (stderr, + "nlink = %d, mtime = %d, filesize = %d, dev_maj = 0x%x\n", + h->c_nlink, h->c_mtime, h->c_filesize, h->c_dev_maj); + fprintf (stderr, + "dev_min = 0x%x, rdev_maj = 0x%x, rdev_min = 0x%x, namesize = %d\n", + h->c_dev_min, h->c_rdev_maj, h->c_rdev_min, h->c_namesize); + fprintf (stderr, + "chksum = %d, name = \"%s\", tar_linkname = \"%s\"\n", + h->c_chksum, h->c_name, + h->c_tar_linkname ? h->c_tar_linkname : "(null)" ); + + } +#endif + /* Is this the header for the TRAILER file? */ + if (strcmp (CPIO_TRAILER_NAME, file_hdr.c_name) == 0) + { + done = true; + break; + } + + cpio_safer_name_suffix (file_hdr.c_name, false, !no_abs_paths_flag, + false); + + /* Does the file name match one of the given patterns? */ + if (num_patterns <= 0) + skip_file = false; + else + { + skip_file = copy_matching_files; + for (i = 0; i < num_patterns + && skip_file == copy_matching_files; i++) + { + if (fnmatch (save_patterns[i], file_hdr.c_name, 0) == 0) + skip_file = !copy_matching_files; + } + } + + if (skip_file) + { + /* If we're skipping a file with links, there might be other + links that we didn't skip, and this file might have the + data for the links. If it does, we'll copy in the data + to the links, but not to this file. */ + if (file_hdr.c_nlink > 1 && (archive_format == arf_newascii + || archive_format == arf_crcascii) ) + { + if (create_defered_links_to_skipped(&file_hdr, in_file_des) < 0) + { + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + } + } + else + { + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + } + } + else if (table_flag) + { + list_file(&file_hdr, in_file_des); + } + else if (append_flag) + { + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + } + else if (only_verify_crc_flag) + { +#ifdef CP_IFLNK + if ((file_hdr.c_mode & CP_IFMT) == CP_IFLNK) + { + if (archive_format != arf_tar && archive_format != arf_ustar) + { + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + continue; + } + } +#endif + crc = 0; + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + if (crc != file_hdr.c_chksum) + { + error (0, 0, _("%s: checksum error (0x%lx, should be 0x%lx)"), + file_hdr.c_name, crc, file_hdr.c_chksum); + } + /* Debian hack: -v and -V now work with --only-verify-crc. + (99/11/10) -BEM */ + if (verbose_flag) + { + fprintf (stderr, "%s\n", file_hdr.c_name); + } + if (dot_flag) + { + fputc ('.', stderr); + } + } + else + { + /* Copy the input file into the directory structure. */ + + /* Do we need to rename the file? */ + if (rename_flag || rename_batch_file) + { + if (query_rename(&file_hdr, tty_in, tty_out, rename_in) < 0) + { + tape_toss_input (in_file_des, file_hdr.c_filesize); + tape_skip_padding (in_file_des, file_hdr.c_filesize); + continue; + } + } + + copyin_file(&file_hdr, in_file_des); + + if (verbose_flag) + fprintf (stderr, "%s\n", file_hdr.c_name); + if (dot_flag) + fputc ('.', stderr); + } + } + + if (dot_flag) + fputc ('\n', stderr); + + apply_delayed_set_stat (); + + if (append_flag) + return; + + if (archive_format == arf_newascii || archive_format == arf_crcascii) + { + create_final_defers (); + } + if (!quiet_flag) + { + size_t blocks; + blocks = (input_bytes + io_block_size - 1) / io_block_size; + fprintf (stderr, + ngettext ("%lu block\n", "%lu blocks\n", + (unsigned long) blocks), + (unsigned long) blocks); + } +} + diff --git a/src/copyout.c b/src/copyout.c new file mode 100644 index 0000000..7e6b624 --- /dev/null +++ b/src/copyout.c @@ -0,0 +1,898 @@ +/* copyout.c - create a cpio archive + Copyright (C) 1990, 1991, 1992, 2001, 2003, 2004, 2006, 2007, 2009, + 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "filetypes.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "defer.h" +#include <rmt.h> +#include <paxlib.h> + +/* Read FILE_SIZE bytes of FILE_NAME from IN_FILE_DES and + compute and return a checksum for them. */ + +static unsigned long +read_for_checksum (int in_file_des, int file_size, char *file_name) +{ + unsigned long crc; + char buf[BUFSIZ]; + int bytes_left; + int bytes_read; + int i; + + crc = 0; + + for (bytes_left = file_size; bytes_left > 0; bytes_left -= bytes_read) + { + bytes_read = read (in_file_des, buf, BUFSIZ); + if (bytes_read < 0) + error (1, errno, _("cannot read checksum for %s"), file_name); + if (bytes_read == 0) + break; + if (bytes_left < bytes_read) + bytes_read = bytes_left; + for (i = 0; i < bytes_read; ++i) + crc += buf[i] & 0xff; + } + if (lseek (in_file_des, 0L, SEEK_SET)) + error (1, errno, _("cannot read checksum for %s"), file_name); + + return crc; +} + +/* Write out NULs to fill out the rest of the current block on + OUT_FILE_DES. */ + +static void +tape_clear_rest_of_block (int out_file_des) +{ + write_nuls_to_file (io_block_size - output_size, out_file_des, + tape_buffered_write); +} + +/* Write NULs on OUT_FILE_DES to move from OFFSET (the current location) + to the end of the header. */ + +static void +tape_pad_output (int out_file_des, int offset) +{ + size_t pad; + + if (archive_format == arf_newascii || archive_format == arf_crcascii) + pad = (4 - (offset % 4)) % 4; + else if (archive_format == arf_tar || archive_format == arf_ustar) + pad = (512 - (offset % 512)) % 512; + else if (archive_format != arf_oldascii && archive_format != arf_hpoldascii) + pad = (2 - (offset % 2)) % 2; + else + pad = 0; + + if (pad != 0) + write_nuls_to_file (pad, out_file_des, tape_buffered_write); +} + + +/* When creating newc and crc archives if a file has multiple (hard) + links, we don't put any of them into the archive until we have seen + all of them (or until we get to the end of the list of files that + are going into the archive and know that we have seen all of the links + to the file that we will see). We keep these "defered" files on + this list. */ + +struct deferment *deferouts = NULL; + +/* Count the number of other (hard) links to this file that have + already been defered. */ + +static int +count_defered_links_to_dev_ino (struct cpio_file_stat *file_hdr) +{ + struct deferment *d; + ino_t ino; + int maj; + int min; + int count; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + count = 0; + for (d = deferouts; d != NULL; d = d->next) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + ++count; + } + return count; +} + +/* Is this file_hdr the last (hard) link to a file? I.e., have + we already seen and defered all of the other links? */ + +static int +last_link (struct cpio_file_stat *file_hdr) +{ + int other_files_sofar; + + other_files_sofar = count_defered_links_to_dev_ino (file_hdr); + if (file_hdr->c_nlink == (other_files_sofar + 1) ) + { + return 1; + } + return 0; +} + + +/* Add the file header for a link that is being defered to the deferouts + list. */ + +static void +add_link_defer (struct cpio_file_stat *file_hdr) +{ + struct deferment *d; + d = create_deferment (file_hdr); + d->next = deferouts; + deferouts = d; +} + +/* We are about to put a file into a newc or crc archive that is + multiply linked. We have already seen and deferred all of the + other links to the file but haven't written them into the archive. + Write the other links into the archive, and remove them from the + deferouts list. */ + +static void +writeout_other_defers (struct cpio_file_stat *file_hdr, int out_des) +{ + struct deferment *d; + struct deferment *d_prev; + ino_t ino; + int maj; + int min; + ino = file_hdr->c_ino; + maj = file_hdr->c_dev_maj; + min = file_hdr->c_dev_min; + d_prev = NULL; + d = deferouts; + while (d != NULL) + { + if ( (d->header.c_ino == ino) && (d->header.c_dev_maj == maj) + && (d->header.c_dev_min == min) ) + { + struct deferment *d_free; + d->header.c_filesize = 0; + write_out_header (&d->header, out_des); + if (d_prev != NULL) + d_prev->next = d->next; + else + deferouts = d->next; + d_free = d; + d = d->next; + free_deferment (d_free); + } + else + { + d_prev = d; + d = d->next; + } + } + return; +} + +/* Write a file into the archive. This code is the same as + the code in process_copy_out(), but we need it here too + for writeout_final_defers() to call. */ + +static void +writeout_defered_file (struct cpio_file_stat *header, int out_file_des) +{ + int in_file_des; + struct cpio_file_stat file_hdr; + + file_hdr = *header; + + + in_file_des = open (header->c_name, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + open_error (header->c_name); + return; + } + + if (archive_format == arf_crcascii) + file_hdr.c_chksum = read_for_checksum (in_file_des, + file_hdr.c_filesize, + header->c_name); + + if (write_out_header (&file_hdr, out_file_des)) + return; + copy_files_disk_to_tape (in_file_des, out_file_des, file_hdr.c_filesize, + header->c_name); + warn_if_file_changed(header->c_name, file_hdr.c_filesize, file_hdr.c_mtime); + + if (archive_format == arf_tar || archive_format == arf_ustar) + add_inode (file_hdr.c_ino, file_hdr.c_name, file_hdr.c_dev_maj, + file_hdr.c_dev_min); + + tape_pad_output (out_file_des, file_hdr.c_filesize); + + if (reset_time_flag) + set_file_times (in_file_des, file_hdr.c_name, file_hdr.c_mtime, + file_hdr.c_mtime); + if (close (in_file_des) < 0) + close_error (header->c_name); +} + +/* When writing newc and crc format archives we defer multiply linked + files until we have seen all of the links to the file. If a file + has links to it that aren't going into the archive, then we will + never see the "last" link to the file, so at the end we just write + all of the leftover defered files into the archive. */ + +static void +writeout_final_defers (int out_des) +{ + struct deferment *d; + int other_count; + while (deferouts != NULL) + { + d = deferouts; + other_count = count_defered_links_to_dev_ino (&d->header); + if (other_count == 1) + { + writeout_defered_file (&d->header, out_des); + } + else + { + struct cpio_file_stat file_hdr; + file_hdr = d->header; + file_hdr.c_filesize = 0; + write_out_header (&file_hdr, out_des); + } + deferouts = deferouts->next; + } +} + +/* FIXME: to_ascii could be used instead of to_oct() and to_octal() from tar, + so it should be moved to paxutils too. + Allowed values for logbase are: 1 (binary), 2, 3 (octal), 4 (hex) */ +int +to_ascii (char *where, uintmax_t v, size_t digits, unsigned logbase) +{ + static char codetab[] = "0123456789ABCDEF"; + int i = digits; + + do + { + where[--i] = codetab[(v & ((1 << logbase) - 1))]; + v >>= logbase; + } + while (i); + + return v != 0; +} + +static void +field_width_error (const char *filename, const char *fieldname) +{ + error (0, 0, _("%s: field width not sufficient for storing %s"), + filename, fieldname); +} + +static void +field_width_warning (const char *filename, const char *fieldname) +{ + if (warn_option & CPIO_WARN_TRUNCATE) + error (0, 0, _("%s: truncating %s"), filename, fieldname); +} + +void +to_ascii_or_warn (char *where, uintmax_t n, size_t digits, + unsigned logbase, + const char *filename, const char *fieldname) +{ + if (to_ascii (where, n, digits, logbase)) + field_width_warning (filename, fieldname); +} + +int +to_ascii_or_error (char *where, uintmax_t n, size_t digits, + unsigned logbase, + const char *filename, const char *fieldname) +{ + if (to_ascii (where, n, digits, logbase)) + { + field_width_error (filename, fieldname); + return 1; + } + return 0; +} + + +int +write_out_new_ascii_header (const char *magic_string, + struct cpio_file_stat *file_hdr, int out_des) +{ + char ascii_header[110]; + char *p; + + p = stpcpy (ascii_header, magic_string); + to_ascii_or_warn (p, file_hdr->c_ino, 8, LG_16, + file_hdr->c_name, _("inode number")); + p += 8; + to_ascii_or_warn (p, file_hdr->c_mode, 8, LG_16, file_hdr->c_name, + _("file mode")); + p += 8; + to_ascii_or_warn (p, file_hdr->c_uid, 8, LG_16, file_hdr->c_name, + _("uid")); + p += 8; + to_ascii_or_warn (p, file_hdr->c_gid, 8, LG_16, file_hdr->c_name, + _("gid")); + p += 8; + to_ascii_or_warn (p, file_hdr->c_nlink, 8, LG_16, file_hdr->c_name, + _("number of links")); + p += 8; + to_ascii_or_warn (p, file_hdr->c_mtime, 8, LG_16, file_hdr->c_name, + _("modification time")); + p += 8; + if (to_ascii_or_error (p, file_hdr->c_filesize, 8, LG_16, file_hdr->c_name, + _("file size"))) + return 1; + p += 8; + if (to_ascii_or_error (p, file_hdr->c_dev_maj, 8, LG_16, file_hdr->c_name, + _("device major number"))) + return 1; + p += 8; + if (to_ascii_or_error (p, file_hdr->c_dev_min, 8, LG_16, file_hdr->c_name, + _("device minor number"))) + return 1; + p += 8; + if (to_ascii_or_error (p, file_hdr->c_rdev_maj, 8, LG_16, file_hdr->c_name, + _("rdev major"))) + return 1; + p += 8; + if (to_ascii_or_error (p, file_hdr->c_rdev_min, 8, LG_16, file_hdr->c_name, + _("rdev minor"))) + return 1; + p += 8; + if (to_ascii_or_error (p, file_hdr->c_namesize, 8, LG_16, file_hdr->c_name, + _("name size"))) + return 1; + p += 8; + to_ascii (p, file_hdr->c_chksum & 0xffffffff, 8, LG_16); + + tape_buffered_write (ascii_header, out_des, sizeof ascii_header); + + /* Write file name to output. */ + tape_buffered_write (file_hdr->c_name, out_des, (long) file_hdr->c_namesize); + tape_pad_output (out_des, file_hdr->c_namesize + sizeof ascii_header); + return 0; +} + +int +write_out_old_ascii_header (dev_t dev, dev_t rdev, + struct cpio_file_stat *file_hdr, int out_des) +{ + char ascii_header[76]; + char *p = ascii_header; + + to_ascii (p, file_hdr->c_magic, 6, LG_8); + p += 6; + to_ascii_or_warn (p, dev, 6, LG_8, file_hdr->c_name, _("device number")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_ino, 6, LG_8, file_hdr->c_name, + _("inode number")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_mode, 6, LG_8, file_hdr->c_name, + _("file mode")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_uid, 6, LG_8, file_hdr->c_name, _("uid")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_gid, 6, LG_8, file_hdr->c_name, _("gid")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_nlink, 6, LG_8, file_hdr->c_name, + _("number of links")); + p += 6; + to_ascii_or_warn (p, rdev, 6, LG_8, file_hdr->c_name, _("rdev")); + p += 6; + to_ascii_or_warn (p, file_hdr->c_mtime, 11, LG_8, file_hdr->c_name, + _("modification time")); + p += 11; + if (to_ascii_or_error (p, file_hdr->c_namesize, 6, LG_8, file_hdr->c_name, + _("name size"))) + return 1; + p += 6; + if (to_ascii_or_error (p, file_hdr->c_filesize, 11, LG_8, file_hdr->c_name, + _("file size"))) + return 1; + + tape_buffered_write (ascii_header, out_des, sizeof ascii_header); + + /* Write file name to output. */ + tape_buffered_write (file_hdr->c_name, out_des, file_hdr->c_namesize); + return 0; +} + +void +hp_compute_dev (struct cpio_file_stat *file_hdr, dev_t *pdev, dev_t *prdev) +{ + /* HP/UX cpio creates archives that look just like ordinary archives, + but for devices it sets major = 0, minor = 1, and puts the + actual major/minor number in the filesize field. */ + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + file_hdr->c_filesize = makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min); + *pdev = *prdev = makedev (0, 1); + break; + + default: + *pdev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + *prdev = makedev (file_hdr->c_rdev_maj, file_hdr->c_rdev_min); + break; + } +} + +int +write_out_binary_header (dev_t rdev, + struct cpio_file_stat *file_hdr, int out_des) +{ + struct old_cpio_header short_hdr; + + short_hdr.c_magic = 070707; + short_hdr.c_dev = makedev (file_hdr->c_dev_maj, file_hdr->c_dev_min); + + if ((warn_option & CPIO_WARN_TRUNCATE) && (file_hdr->c_ino >> 16) != 0) + error (0, 0, _("%s: truncating inode number"), file_hdr->c_name); + + short_hdr.c_ino = file_hdr->c_ino & 0xFFFF; + if (short_hdr.c_ino != file_hdr->c_ino) + field_width_warning (file_hdr->c_name, _("inode number")); + + short_hdr.c_mode = file_hdr->c_mode & 0xFFFF; + if (short_hdr.c_mode != file_hdr->c_mode) + field_width_warning (file_hdr->c_name, _("file mode")); + + short_hdr.c_uid = file_hdr->c_uid & 0xFFFF; + if (short_hdr.c_uid != file_hdr->c_uid) + field_width_warning (file_hdr->c_name, _("uid")); + + short_hdr.c_gid = file_hdr->c_gid & 0xFFFF; + if (short_hdr.c_gid != file_hdr->c_gid) + field_width_warning (file_hdr->c_name, _("gid")); + + short_hdr.c_nlink = file_hdr->c_nlink & 0xFFFF; + if (short_hdr.c_nlink != file_hdr->c_nlink) + field_width_warning (file_hdr->c_name, _("number of links")); + + short_hdr.c_rdev = rdev; + short_hdr.c_mtimes[0] = file_hdr->c_mtime >> 16; + short_hdr.c_mtimes[1] = file_hdr->c_mtime & 0xFFFF; + + short_hdr.c_namesize = file_hdr->c_namesize & 0xFFFF; + if (short_hdr.c_namesize != file_hdr->c_namesize) + { + field_width_error (file_hdr->c_name, _("name size")); + return 1; + } + + short_hdr.c_filesizes[0] = file_hdr->c_filesize >> 16; + short_hdr.c_filesizes[1] = file_hdr->c_filesize & 0xFFFF; + + if (((off_t)short_hdr.c_filesizes[0] << 16) + short_hdr.c_filesizes[1] + != file_hdr->c_filesize) + { + field_width_error (file_hdr->c_name, _("file size")); + return 1; + } + + /* Output the file header. */ + tape_buffered_write ((char *) &short_hdr, out_des, 26); + + /* Write file name to output. */ + tape_buffered_write (file_hdr->c_name, out_des, file_hdr->c_namesize); + + tape_pad_output (out_des, file_hdr->c_namesize + 26); + return 0; +} + + +/* Write out header FILE_HDR, including the file name, to file + descriptor OUT_DES. */ + +int +write_out_header (struct cpio_file_stat *file_hdr, int out_des) +{ + dev_t dev; + dev_t rdev; + + switch (archive_format) + { + case arf_newascii: + return write_out_new_ascii_header ("070701", file_hdr, out_des); + + case arf_crcascii: + return write_out_new_ascii_header ("070702", file_hdr, out_des); + + case arf_oldascii: + return write_out_old_ascii_header (makedev (file_hdr->c_dev_maj, + file_hdr->c_dev_min), + makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min), + file_hdr, out_des); + + case arf_hpoldascii: + hp_compute_dev (file_hdr, &dev, &rdev); + return write_out_old_ascii_header (dev, rdev, file_hdr, out_des); + + case arf_tar: + case arf_ustar: + if (is_tar_filename_too_long (file_hdr->c_name)) + { + error (0, 0, _("%s: file name too long"), file_hdr->c_name); + return 1; + } + write_out_tar_header (file_hdr, out_des); /* FIXME: No error checking */ + return 0; + + case arf_binary: + return write_out_binary_header (makedev (file_hdr->c_rdev_maj, + file_hdr->c_rdev_min), + file_hdr, out_des); + + case arf_hpbinary: + hp_compute_dev (file_hdr, &dev, &rdev); + /* FIXME: dev ignored. Should it be? */ + return write_out_binary_header (rdev, file_hdr, out_des); + + default: + abort (); + } +} + +static void +assign_string (char **pvar, char *value) +{ + char *p = xrealloc (*pvar, strlen (value) + 1); + strcpy (p, value); + *pvar = p; +} + +/* Read a list of file names from the standard input + and write a cpio collection on the standard output. + The format of the header depends on the compatibility (-c) flag. */ + +void +process_copy_out () +{ + dynamic_string input_name; /* Name of file read from stdin. */ + struct stat file_stat; /* Stat record for file. */ + struct cpio_file_stat file_hdr; /* Output header information. */ + int in_file_des; /* Source file descriptor. */ + int out_file_des; /* Output file descriptor. */ + char *orig_file_name = NULL; + + /* Initialize the copy out. */ + ds_init (&input_name, 128); + file_hdr.c_magic = 070707; + + /* Check whether the output file might be a tape. */ + out_file_des = archive_des; + if (_isrmt (out_file_des)) + { + output_is_special = 1; + output_is_seekable = 0; + } + else + { + if (fstat (out_file_des, &file_stat)) + error (1, errno, _("standard output is closed")); + output_is_special = +#ifdef S_ISBLK + S_ISBLK (file_stat.st_mode) || +#endif + S_ISCHR (file_stat.st_mode); + output_is_seekable = S_ISREG (file_stat.st_mode); + } + + if (append_flag) + { + process_copy_in (); + prepare_append (out_file_des); + } + + /* Copy files with names read from stdin. */ + while (ds_fgetstr (stdin, &input_name, name_end) != NULL) + { + /* Check for blank line. */ + if (input_name.ds_string[0] == 0) + { + error (0, 0, _("blank line ignored")); + continue; + } + + /* Process next file. */ + if ((*xstat) (input_name.ds_string, &file_stat) < 0) + stat_error (input_name.ds_string); + else + { + /* Set values in output header. */ + stat_to_cpio (&file_hdr, &file_stat); + + if (archive_format == arf_tar || archive_format == arf_ustar) + { + if (file_hdr.c_mode & CP_IFDIR) + { + int len = strlen (input_name.ds_string); + /* Make sure the name ends with a slash */ + if (input_name.ds_string[len-1] != '/') + { + ds_resize (&input_name, len + 2); + input_name.ds_string[len] = '/'; + input_name.ds_string[len+1] = 0; + } + } + } + + assign_string (&orig_file_name, input_name.ds_string); + cpio_safer_name_suffix (input_name.ds_string, false, + !no_abs_paths_flag, true); +#ifndef HPUX_CDF + file_hdr.c_name = input_name.ds_string; + file_hdr.c_namesize = strlen (input_name.ds_string) + 1; +#else + if ( (archive_format != arf_tar) && (archive_format != arf_ustar) ) + { + /* We mark CDF's in cpio files by adding a 2nd `/' after the + "hidden" directory name. We need to do this so we can + properly recreate the directory as hidden (in case the + files of a directory go into the archive before the + directory itself (e.g from "find ... -depth ... | cpio")). */ + file_hdr.c_name = add_cdf_double_slashes (input_name.ds_string); + file_hdr.c_namesize = strlen (file_hdr.c_name) + 1; + } + else + { + /* We don't mark CDF's in tar files. We assume the "hidden" + directory will always go into the archive before any of + its files. */ + file_hdr.c_name = input_name.ds_string; + file_hdr.c_namesize = strlen (input_name.ds_string) + 1; + } +#endif + + /* Copy the named file to the output. */ + switch (file_hdr.c_mode & CP_IFMT) + { + case CP_IFREG: + if (archive_format == arf_tar || archive_format == arf_ustar) + { + char *otherfile; + if ((otherfile = find_inode_file (file_hdr.c_ino, + file_hdr.c_dev_maj, + file_hdr.c_dev_min))) + { + file_hdr.c_tar_linkname = otherfile; + if (write_out_header (&file_hdr, out_file_des)) + continue; + break; + } + } + if ( (archive_format == arf_newascii || archive_format == arf_crcascii) + && (file_hdr.c_nlink > 1) ) + { + if (last_link (&file_hdr) ) + { + writeout_other_defers (&file_hdr, out_file_des); + } + else + { + add_link_defer (&file_hdr); + break; + } + } + in_file_des = open (orig_file_name, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + open_error (orig_file_name); + continue; + } + + if (archive_format == arf_crcascii) + file_hdr.c_chksum = read_for_checksum (in_file_des, + file_hdr.c_filesize, + orig_file_name); + + if (write_out_header (&file_hdr, out_file_des)) + continue; + copy_files_disk_to_tape (in_file_des, + out_file_des, file_hdr.c_filesize, + orig_file_name); + warn_if_file_changed(orig_file_name, file_hdr.c_filesize, + file_hdr.c_mtime); + + if (archive_format == arf_tar || archive_format == arf_ustar) + add_inode (file_hdr.c_ino, orig_file_name, file_hdr.c_dev_maj, + file_hdr.c_dev_min); + + tape_pad_output (out_file_des, file_hdr.c_filesize); + + if (reset_time_flag) + set_file_times (in_file_des, + orig_file_name, + file_stat.st_atime, file_stat.st_mtime); + if (close (in_file_des) < 0) + close_error (orig_file_name); + break; + + case CP_IFDIR: + file_hdr.c_filesize = 0; + if (write_out_header (&file_hdr, out_file_des)) + continue; + break; + + case CP_IFCHR: + case CP_IFBLK: +#ifdef CP_IFSOCK + case CP_IFSOCK: +#endif +#ifdef CP_IFIFO + case CP_IFIFO: +#endif + if (archive_format == arf_tar) + { + error (0, 0, _("%s not dumped: not a regular file"), + orig_file_name); + continue; + } + else if (archive_format == arf_ustar) + { + char *otherfile; + if ((otherfile = find_inode_file (file_hdr.c_ino, + file_hdr.c_dev_maj, + file_hdr.c_dev_min))) + { + /* This file is linked to another file already in the + archive, so write it out as a hard link. */ + file_hdr.c_mode = (file_stat.st_mode & 07777); + file_hdr.c_mode |= CP_IFREG; + file_hdr.c_tar_linkname = otherfile; + if (write_out_header (&file_hdr, out_file_des)) + continue; + break; + } + add_inode (file_hdr.c_ino, orig_file_name, + file_hdr.c_dev_maj, file_hdr.c_dev_min); + } + file_hdr.c_filesize = 0; + if (write_out_header (&file_hdr, out_file_des)) + continue; + break; + +#ifdef CP_IFLNK + case CP_IFLNK: + { + char *link_name = (char *) xmalloc (file_stat.st_size + 1); + int link_size; + + link_size = readlink (orig_file_name, link_name, + file_stat.st_size); + if (link_size < 0) + { + readlink_warn (orig_file_name); + free (link_name); + continue; + } + link_name[link_size] = 0; + cpio_safer_name_suffix (link_name, false, + !no_abs_paths_flag, true); + link_size = strlen (link_name); + file_hdr.c_filesize = link_size; + if (archive_format == arf_tar || archive_format == arf_ustar) + { + if (link_size + 1 > 100) + { + error (0, 0, _("%s: symbolic link too long"), + file_hdr.c_name); + } + else + { + link_name[link_size] = '\0'; + file_hdr.c_tar_linkname = link_name; + if (write_out_header (&file_hdr, out_file_des)) + continue; + } + } + else + { + if (write_out_header (&file_hdr, out_file_des)) + continue; + tape_buffered_write (link_name, out_file_des, link_size); + tape_pad_output (out_file_des, link_size); + } + free (link_name); + } + break; +#endif + + default: + error (0, 0, _("%s: unknown file type"), orig_file_name); + } + + if (verbose_flag) + fprintf (stderr, "%s\n", orig_file_name); + if (dot_flag) + fputc ('.', stderr); + } + } + + free (orig_file_name); + + writeout_final_defers(out_file_des); + /* The collection is complete; append the trailer. */ + file_hdr.c_ino = 0; + file_hdr.c_mode = 0; + file_hdr.c_uid = 0; + file_hdr.c_gid = 0; + file_hdr.c_nlink = 1; /* Must be 1 for crc format. */ + file_hdr.c_dev_maj = 0; + file_hdr.c_dev_min = 0; + file_hdr.c_rdev_maj = 0; + file_hdr.c_rdev_min = 0; + file_hdr.c_mtime = 0; + file_hdr.c_chksum = 0; + + file_hdr.c_filesize = 0; + file_hdr.c_namesize = 11; + file_hdr.c_name = CPIO_TRAILER_NAME; + if (archive_format != arf_tar && archive_format != arf_ustar) + write_out_header (&file_hdr, out_file_des); + else + write_nuls_to_file (1024, out_file_des, tape_buffered_write); + + /* Fill up the output block. */ + tape_clear_rest_of_block (out_file_des); + tape_empty_output_buffer (out_file_des); + if (dot_flag) + fputc ('\n', stderr); + if (!quiet_flag) + { + size_t blocks = (output_bytes + io_block_size - 1) / io_block_size; + fprintf (stderr, + ngettext ("%lu block\n", "%lu blocks\n", + (unsigned long) blocks), (unsigned long) blocks); + } +} + + diff --git a/src/copypass.c b/src/copypass.c new file mode 100644 index 0000000..d249a31 --- /dev/null +++ b/src/copypass.c @@ -0,0 +1,398 @@ +/* copypass.c - cpio copy pass sub-function. + Copyright (C) 1990, 1991, 1992, 2001, 2003, 2004, 2006, 2007, 2010 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "filetypes.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include "paxlib.h" + +#ifndef HAVE_LCHOWN +# define lchown chown +#endif + + +/* A wrapper around set_perms using another set of arguments */ +static void +set_copypass_perms (int fd, const char *name, struct stat *st) +{ + struct cpio_file_stat header; + header.c_name = (char*)name; + stat_to_cpio (&header, st); + set_perms (fd, &header); +} + +/* Copy files listed on the standard input into directory `directory_name'. + If `link_flag', link instead of copying. */ + +void +process_copy_pass () +{ + dynamic_string input_name; /* Name of file from stdin. */ + dynamic_string output_name; /* Name of new file. */ + int dirname_len; /* Length of `directory_name'. */ + int res; /* Result of functions. */ + char *slash; /* For moving past slashes in input name. */ + struct stat in_file_stat; /* Stat record for input file. */ + struct stat out_file_stat; /* Stat record for output file. */ + int in_file_des; /* Input file descriptor. */ + int out_file_des; /* Output file descriptor. */ + int existing_dir; /* True if file is a dir & already exists. */ +#ifdef HPUX_CDF + int cdf_flag; + int cdf_char; +#endif + + newdir_umask = umask (0); /* Reset umask to preserve modes of + created files */ + + /* Initialize the copy pass. */ + dirname_len = strlen (directory_name); + ds_init (&input_name, 128); + ds_init (&output_name, dirname_len + 2); + strcpy (output_name.ds_string, directory_name); + output_name.ds_string[dirname_len] = '/'; + output_is_seekable = true; + + /* Copy files with names read from stdin. */ + while (ds_fgetstr (stdin, &input_name, name_end) != NULL) + { + int link_res = -1; + + /* Check for blank line and ignore it if found. */ + if (input_name.ds_string[0] == '\0') + { + error (0, 0, _("blank line ignored")); + continue; + } + + /* Check for current directory and ignore it if found. */ + if (input_name.ds_string[0] == '.' + && (input_name.ds_string[1] == '\0' + || (input_name.ds_string[1] == '/' + && input_name.ds_string[2] == '\0'))) + continue; + + if ((*xstat) (input_name.ds_string, &in_file_stat) < 0) + { + stat_error (input_name.ds_string); + continue; + } + + /* Make the name of the new file. */ + for (slash = input_name.ds_string; *slash == '/'; ++slash) + ; +#ifdef HPUX_CDF + /* For CDF's we add a 2nd `/' after all "hidden" directories. + This kind of a kludge, but it's what we do when creating + archives, and it's easier to do this than to separately + keep track of which directories in a path are "hidden". */ + slash = add_cdf_double_slashes (slash); +#endif + ds_resize (&output_name, dirname_len + strlen (slash) + 2); + strcpy (output_name.ds_string + dirname_len + 1, slash); + + existing_dir = false; + if (lstat (output_name.ds_string, &out_file_stat) == 0) + { + if (S_ISDIR (out_file_stat.st_mode) + && S_ISDIR (in_file_stat.st_mode)) + { + /* If there is already a directory there that + we are trying to create, don't complain about it. */ + existing_dir = true; + } + else if (!unconditional_flag + && in_file_stat.st_mtime <= out_file_stat.st_mtime) + { + error (0, 0, _("%s not created: newer or same age version exists"), + output_name.ds_string); + continue; /* Go to the next file. */ + } + else if (S_ISDIR (out_file_stat.st_mode) + ? rmdir (output_name.ds_string) + : unlink (output_name.ds_string)) + { + error (0, errno, _("cannot remove current %s"), + output_name.ds_string); + continue; /* Go to the next file. */ + } + } + + /* Do the real copy or link. */ + if (S_ISREG (in_file_stat.st_mode)) + { + /* Can the current file be linked to a another file? + Set link_name to the original file name. */ + if (link_flag) + /* User said to link it if possible. Try and link to + the original copy. If that fails we'll still try + and link to a copy we've already made. */ + link_res = link_to_name (output_name.ds_string, + input_name.ds_string); + if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) + link_res = link_to_maj_min_ino (output_name.ds_string, + major (in_file_stat.st_dev), + minor (in_file_stat.st_dev), + in_file_stat.st_ino); + + /* If the file was not linked, copy contents of file. */ + if (link_res < 0) + { + in_file_des = open (input_name.ds_string, + O_RDONLY | O_BINARY, 0); + if (in_file_des < 0) + { + open_error (input_name.ds_string); + continue; + } + out_file_des = open (output_name.ds_string, + O_CREAT | O_WRONLY | O_BINARY, 0600); + if (out_file_des < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + out_file_des = open (output_name.ds_string, + O_CREAT | O_WRONLY | O_BINARY, 0600); + } + if (out_file_des < 0) + { + open_error (output_name.ds_string); + close (in_file_des); + continue; + } + + copy_files_disk_to_disk (in_file_des, out_file_des, in_file_stat.st_size, input_name.ds_string); + disk_empty_output_buffer (out_file_des); + /* Debian hack to fix a bug in the --sparse option. + This bug has been reported to + "bug-gnu-utils@prep.ai.mit.edu". (96/7/10) -BEM */ + if (delayed_seek_count > 0) + { + lseek (out_file_des, delayed_seek_count-1, SEEK_CUR); + write (out_file_des, "", 1); + delayed_seek_count = 0; + } + + set_copypass_perms (out_file_des, + output_name.ds_string, &in_file_stat); + + if (reset_time_flag) + { + set_file_times (in_file_des, + input_name.ds_string, + in_file_stat.st_atime, + in_file_stat.st_mtime); + set_file_times (out_file_des, + output_name.ds_string, + in_file_stat.st_atime, + in_file_stat.st_mtime); + } + + if (close (in_file_des) < 0) + close_error (input_name.ds_string); + + if (close (out_file_des) < 0) + close_error (output_name.ds_string); + + warn_if_file_changed(input_name.ds_string, in_file_stat.st_size, + in_file_stat.st_mtime); + } + } + else if (S_ISDIR (in_file_stat.st_mode)) + { + struct cpio_file_stat file_stat; + + stat_to_cpio (&file_stat, &in_file_stat); + file_stat.c_name = output_name.ds_string; + cpio_create_dir (&file_stat, existing_dir); + } + else if (S_ISCHR (in_file_stat.st_mode) || + S_ISBLK (in_file_stat.st_mode) || +#ifdef S_ISFIFO + S_ISFIFO (in_file_stat.st_mode) || +#endif +#ifdef S_ISSOCK + S_ISSOCK (in_file_stat.st_mode) || +#endif + 0) + { + /* Can the current file be linked to a another file? + Set link_name to the original file name. */ + if (link_flag) + /* User said to link it if possible. */ + link_res = link_to_name (output_name.ds_string, + input_name.ds_string); + if ( (link_res < 0) && (in_file_stat.st_nlink > 1) ) + link_res = link_to_maj_min_ino (output_name.ds_string, + major (in_file_stat.st_dev), + minor (in_file_stat.st_dev), + in_file_stat.st_ino); + + if (link_res < 0) + { + res = mknod (output_name.ds_string, in_file_stat.st_mode, + in_file_stat.st_rdev); + if (res < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + res = mknod (output_name.ds_string, in_file_stat.st_mode, + in_file_stat.st_rdev); + } + if (res < 0) + { + mknod_error (output_name.ds_string); + continue; + } + set_copypass_perms (-1, output_name.ds_string, &in_file_stat); + } + } + +#ifdef S_ISLNK + else if (S_ISLNK (in_file_stat.st_mode)) + { + char *link_name; + int link_size; + link_name = (char *) xmalloc ((unsigned int) in_file_stat.st_size + 1); + + link_size = readlink (input_name.ds_string, link_name, + in_file_stat.st_size); + if (link_size < 0) + { + readlink_error (input_name.ds_string); + free (link_name); + continue; + } + link_name[link_size] = '\0'; + + res = UMASKED_SYMLINK (link_name, output_name.ds_string, + in_file_stat.st_mode); + if (res < 0 && create_dir_flag) + { + create_all_directories (output_name.ds_string); + res = UMASKED_SYMLINK (link_name, output_name.ds_string, + in_file_stat.st_mode); + } + if (res < 0) + { + symlink_error (output_name.ds_string, link_name); + free (link_name); + continue; + } + + /* Set the attributes of the new link. */ + if (!no_chown_flag) + { + uid_t uid = set_owner_flag ? set_owner : in_file_stat.st_uid; + gid_t gid = set_group_flag ? set_group : in_file_stat.st_gid; + if ((lchown (output_name.ds_string, uid, gid) < 0) + && errno != EPERM) + chown_error_details (output_name.ds_string, uid, gid); + } + free (link_name); + } +#endif + else + { + error (0, 0, _("%s: unknown file type"), input_name.ds_string); + } + + if (verbose_flag) + fprintf (stderr, "%s\n", output_name.ds_string); + if (dot_flag) + fputc ('.', stderr); + } + + if (dot_flag) + fputc ('\n', stderr); + + apply_delayed_set_stat (); + + if (!quiet_flag) + { + size_t blocks = (output_bytes + io_block_size - 1) / io_block_size; + fprintf (stderr, + ngettext ("%lu block\n", "%lu blocks\n", + (unsigned long) blocks), + (unsigned long) blocks); + } +} + +/* Try and create a hard link from FILE_NAME to another file + with the given major/minor device number and inode. If no other + file with the same major/minor/inode numbers is known, add this file + to the list of known files and associated major/minor/inode numbers + and return -1. If another file with the same major/minor/inode + numbers is found, try and create another link to it using + link_to_name, and return 0 for success and -1 for failure. */ + +int +link_to_maj_min_ino (char *file_name, int st_dev_maj, int st_dev_min, + ino_t st_ino) +{ + int link_res; + char *link_name; + link_res = -1; + /* Is the file a link to a previously copied file? */ + link_name = find_inode_file (st_ino, + st_dev_maj, + st_dev_min); + if (link_name == NULL) + add_inode (st_ino, file_name, + st_dev_maj, + st_dev_min); + else + link_res = link_to_name (file_name, link_name); + return link_res; +} + +/* Try and create a hard link from LINK_NAME to LINK_TARGET. If + `create_dir_flag' is set, any non-existent (parent) directories + needed by LINK_NAME will be created. If the link is successfully + created and `verbose_flag' is set, print "LINK_TARGET linked to LINK_NAME\n". + If the link can not be created and `link_flag' is set, print + "cannot link LINK_TARGET to LINK_NAME\n". Return 0 if the link + is created, -1 otherwise. */ + +int +link_to_name (char *link_name, char *link_target) +{ + int res = link (link_target, link_name); + if (res < 0 && create_dir_flag) + { + create_all_directories (link_name); + res = link (link_target, link_name); + } + if (res == 0) + { + if (verbose_flag) + error (0, 0, _("%s linked to %s"), + link_target, link_name); + } + else if (link_flag) + { + error (0, errno, _("cannot link %s to %s"), + link_target, link_name); + } + return res; +} diff --git a/src/cpio.h b/src/cpio.h new file mode 100644 index 0000000..b2aec61 --- /dev/null +++ b/src/cpio.h @@ -0,0 +1,72 @@ +/* Extended cpio format from POSIX.1. + Copyright (C) 1992, 2005, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifndef _CPIO_H + +#define _CPIO_H 1 + +/* A cpio archive consists of a sequence of files. + Each file has a 76 byte header, + a variable length, NUL terminated filename, + and variable length file data. + A header for a filename "TRAILER!!!" indicates the end of the archive. */ + +#define CPIO_TRAILER_NAME "TRAILER!!!" + +/* All the fields in the header are ISO 646 (approximately ASCII) strings + of octal numbers, left padded, not NUL terminated. + + Field Name Length in Bytes Notes + c_magic 6 must be "070707" + c_dev 6 + c_ino 6 + c_mode 6 see below for value + c_uid 6 + c_gid 6 + c_nlink 6 + c_rdev 6 only valid for chr and blk special files + c_mtime 11 + c_namesize 6 count includes terminating NUL in pathname + c_filesize 11 must be 0 for FIFOs and directories */ + +/* Values for c_mode, OR'd together: */ + +#define C_IRUSR 000400 +#define C_IWUSR 000200 +#define C_IXUSR 000100 +#define C_IRGRP 000040 +#define C_IWGRP 000020 +#define C_IXGRP 000010 +#define C_IROTH 000004 +#define C_IWOTH 000002 +#define C_IXOTH 000001 + +#define C_ISUID 004000 +#define C_ISGID 002000 +#define C_ISVTX 001000 + +#define C_ISBLK 060000 +#define C_ISCHR 020000 +#define C_ISDIR 040000 +#define C_ISFIFO 010000 +#define C_ISSOCK 0140000 +#define C_ISLNK 0120000 +#define C_ISCTG 0110000 +#define C_ISREG 0100000 + +#endif /* cpio.h */ diff --git a/src/cpiohdr.h b/src/cpiohdr.h new file mode 100644 index 0000000..bb1ad6b --- /dev/null +++ b/src/cpiohdr.h @@ -0,0 +1,132 @@ +/* Extended cpio header from POSIX.1. + Copyright (C) 1992, 2006, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifndef _CPIOHDR_H + +#define _CPIOHDR_H 1 + +#include <cpio.h> + +#ifdef HAVE_ATTRIB_PACKED +#define ATTRIB_PACKED __attribute__((packed)) +#endif + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif + +#ifdef HAVE_PRAGMA_PACK_HPPA +#pragma pack 1 +#endif + +struct old_cpio_header +{ + unsigned short c_magic; + unsigned short c_dev; + unsigned short c_ino; + unsigned short c_mode; + unsigned short c_uid; + unsigned short c_gid; + unsigned short c_nlink; + unsigned short c_rdev; + unsigned short c_mtimes[2]; + unsigned short c_namesize; + unsigned short c_filesizes[2]; +} ATTRIB_PACKED; + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif + +#ifdef HAVE_PRAGMA_PACK_HPPA +#pragma pack 1 +#endif +struct old_ascii_header +{ + char c_magic[6]; + char c_dev[6]; + char c_ino[6]; + char c_mode[6]; + char c_uid[6]; + char c_gid[6]; + char c_nlink[6]; + char c_rdev[6]; + char c_mtime[11]; + char c_namesize[6]; + char c_filesize[11]; +} ATTRIB_PACKED; + +/* "New" portable format and CRC format: + + Each file has a 110 byte header, + a variable length, NUL terminated filename, + and variable length file data. + A header for a filename "TRAILER!!!" indicates the end of the archive. */ + +/* All the fields in the header are ISO 646 (approximately ASCII) strings + of hexadecimal numbers, left padded, not NUL terminated: */ + +#ifdef HAVE_PRAGMA_PACK +#pragma pack(1) +#endif + +#ifdef HAVE_PRAGMA_PACK_HPPA +#pragma pack 1 +#endif +struct new_ascii_header +{ + char c_magic[6]; /* "070701" for "new" portable format + "070702" for CRC format */ + char c_ino[8]; + char c_mode[8]; + char c_uid[8]; + char c_gid[8]; + char c_nlink[8]; + char c_mtime[8]; + char c_filesize[8]; /* must be 0 for FIFOs and directories */ + char c_dev_maj[8]; + char c_dev_min[8]; + char c_rdev_maj[8]; /* only valid for chr and blk special files */ + char c_rdev_min[8]; /* only valid for chr and blk special files */ + char c_namesize[8]; /* count includes terminating NUL in pathname */ + char c_chksum[8]; /* 0 for "new" portable format; for CRC format + the sum of all the bytes in the file */ +} ATTRIB_PACKED; + +struct cpio_file_stat /* Internal representation of a CPIO header */ +{ + unsigned short c_magic; + ino_t c_ino; + mode_t c_mode; + uid_t c_uid; + gid_t c_gid; + size_t c_nlink; + time_t c_mtime; + off_t c_filesize; + long c_dev_maj; + long c_dev_min; + long c_rdev_maj; + long c_rdev_min; + size_t c_namesize; + unsigned long c_chksum; + char *c_name; + char *c_tar_linkname; +}; + + +#endif /* cpiohdr.h */ diff --git a/src/defer.c b/src/defer.c new file mode 100644 index 0000000..a213f81 --- /dev/null +++ b/src/defer.c @@ -0,0 +1,44 @@ +/* defer.c - handle "defered" links in newc and crc archives + Copyright (C) 1993, 2003, 2004, 2006, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include "cpiohdr.h" +#include "extern.h" +#include "defer.h" + +struct deferment * +create_deferment (struct cpio_file_stat *file_hdr) +{ + struct deferment *d; + d = (struct deferment *) xmalloc (sizeof (struct deferment) ); + d->header = *file_hdr; + d->header.c_name = (char *) xmalloc (strlen (file_hdr->c_name) + 1); + strcpy (d->header.c_name, file_hdr->c_name); + return d; +} + +void +free_deferment (struct deferment *d) +{ + free (d->header.c_name); + free (d); +} diff --git a/src/defer.h b/src/defer.h new file mode 100644 index 0000000..3262649 --- /dev/null +++ b/src/defer.h @@ -0,0 +1,27 @@ +/* defer.h + Copyright (C) 1993, 2001, 2004, 2006, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +struct deferment + { + struct deferment *next; + struct cpio_file_stat header; + }; + +struct deferment *create_deferment (struct cpio_file_stat *file_hdr); +void free_deferment (struct deferment *d); diff --git a/src/dstring.c b/src/dstring.c new file mode 100644 index 0000000..6ff75bf --- /dev/null +++ b/src/dstring.c @@ -0,0 +1,104 @@ +/* dstring.c - The dynamic string handling routines used by cpio. + Copyright (C) 1990, 1991, 1992, 2004, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#if defined(HAVE_CONFIG_H) +# include <config.h> +#endif + +#include <stdio.h> +#if defined(HAVE_STRING_H) || defined(STDC_HEADERS) +#include <string.h> +#else +#include <strings.h> +#endif +#include "dstring.h" + +char *xmalloc (unsigned n); +char *xrealloc (char *p, unsigned n); + +/* Initialiaze dynamic string STRING with space for SIZE characters. */ + +void +ds_init (dynamic_string *string, int size) +{ + string->ds_length = size; + string->ds_string = (char *) xmalloc (size); +} + +/* Expand dynamic string STRING, if necessary, to hold SIZE characters. */ + +void +ds_resize (dynamic_string *string, int size) +{ + if (size > string->ds_length) + { + string->ds_length = size; + string->ds_string = (char *) xrealloc ((char *) string->ds_string, size); + } +} + +/* Dynamic string S gets a string terminated by the EOS character + (which is removed) from file F. S will increase + in size during the function if the string from F is longer than + the current size of S. + Return NULL if end of file is detected. Otherwise, + Return a pointer to the null-terminated string in S. */ + +char * +ds_fgetstr (FILE *f, dynamic_string *s, char eos) +{ + int insize; /* Amount needed for line. */ + int strsize; /* Amount allocated for S. */ + int next_ch; + + /* Initialize. */ + insize = 0; + strsize = s->ds_length; + + /* Read the input string. */ + next_ch = getc (f); + while (next_ch != eos && next_ch != EOF) + { + if (insize >= strsize - 1) + { + ds_resize (s, strsize * 2 + 2); + strsize = s->ds_length; + } + s->ds_string[insize++] = next_ch; + next_ch = getc (f); + } + s->ds_string[insize++] = '\0'; + + if (insize == 1 && next_ch == EOF) + return NULL; + else + return s->ds_string; +} + +char * +ds_fgets (FILE *f, dynamic_string *s) +{ + return ds_fgetstr (f, s, '\n'); +} + +char * +ds_fgetname (FILE *f, dynamic_string *s) +{ + return ds_fgetstr (f, s, '\0'); +} diff --git a/src/dstring.h b/src/dstring.h new file mode 100644 index 0000000..39d63f0 --- /dev/null +++ b/src/dstring.h @@ -0,0 +1,51 @@ +/* dstring.h - Dynamic string handling include file. Requires strings.h. + Copyright (C) 1990, 1991, 1992, 2004, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifndef NULL +#define NULL 0 +#endif + +/* A dynamic string consists of record that records the size of an + allocated string and the pointer to that string. The actual string + is a normal zero byte terminated string that can be used with the + usual string functions. The major difference is that the + dynamic_string routines know how to get more space if it is needed + by allocating new space and copying the current string. */ + +typedef struct +{ + int ds_length; /* Actual amount of storage allocated. */ + char *ds_string; /* String. */ +} dynamic_string; + + +/* Macros that look similar to the original string functions. + WARNING: These macros work only on pointers to dynamic string records. + If used with a real record, an "&" must be used to get the pointer. */ +#define ds_strlen(s) strlen ((s)->ds_string) +#define ds_strcmp(s1, s2) strcmp ((s1)->ds_string, (s2)->ds_string) +#define ds_strncmp(s1, s2, n) strncmp ((s1)->ds_string, (s2)->ds_string, n) +#define ds_index(s, c) index ((s)->ds_string, c) +#define ds_rindex(s, c) rindex ((s)->ds_string, c) + +void ds_init (dynamic_string *string, int size); +void ds_resize (dynamic_string *string, int size); +char *ds_fgetname (FILE *f, dynamic_string *s); +char *ds_fgets (FILE *f, dynamic_string *s); +char *ds_fgetstr (FILE *f, dynamic_string *s, char eos); diff --git a/src/extern.h b/src/extern.h new file mode 100644 index 0000000..4f94d40 --- /dev/null +++ b/src/extern.h @@ -0,0 +1,219 @@ +/* extern.h - External declarations for cpio. Requires system.h. + Copyright (C) 1990, 1991, 1992, 2001, 2006, 2007, 2009, 2010 Free + Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include "paxlib.h" +#include "quotearg.h" +#include "quote.h" + +enum archive_format +{ + arf_unknown, arf_binary, arf_oldascii, arf_newascii, arf_crcascii, + arf_tar, arf_ustar, arf_hpoldascii, arf_hpbinary +}; + +extern enum archive_format archive_format; +extern int reset_time_flag; +extern int io_block_size; +extern int create_dir_flag; +extern int rename_flag; +extern char *rename_batch_file; +extern int table_flag; +extern int unconditional_flag; +extern int verbose_flag; +extern int dot_flag; +extern int link_flag; +extern int retain_time_flag; +extern int crc_i_flag; +extern int append_flag; +extern int swap_bytes_flag; +extern int swap_halfwords_flag; +extern int swapping_bytes; +extern int swapping_halfwords; +extern int set_owner_flag; +extern uid_t set_owner; +extern int set_group_flag; +extern gid_t set_group; +extern int no_chown_flag; +extern int sparse_flag; +extern int quiet_flag; +extern int only_verify_crc_flag; +extern int no_abs_paths_flag; +extern unsigned int warn_option; +extern mode_t newdir_umask; + +/* Values for warn_option */ +#define CPIO_WARN_NONE 0 +#define CPIO_WARN_TRUNCATE 0x01 +#define CPIO_WARN_INTERDIR 0x02 +#define CPIO_WARN_ALL (unsigned int)-1 + +extern bool to_stdout_option; + +extern int last_header_start; +extern int copy_matching_files; +extern int numeric_uid; +extern char *pattern_file_name; +extern char *new_media_message; +extern char *new_media_message_with_number; +extern char *new_media_message_after_number; +extern int archive_des; +extern char *archive_name; +extern char *rsh_command_option; +extern unsigned long crc; +extern int delayed_seek_count; +#ifdef DEBUG_CPIO +extern int debug_flag; +#endif + +extern char *input_buffer, *output_buffer; +extern char *in_buff, *out_buff; +extern size_t input_buffer_size; +extern size_t input_size, output_size; +extern off_t input_bytes, output_bytes; + +extern char *directory_name; +extern char **save_patterns; +extern int num_patterns; +extern char name_end; +extern char input_is_special; +extern char output_is_special; +extern char input_is_seekable; +extern char output_is_seekable; +extern int (*xstat) (); +extern void (*copy_function) (); + + +/* copyin.c */ +void warn_junk_bytes (long bytes_skipped); +/* FIXME: make read_* static in copyin.c */ +void read_in_header (struct cpio_file_stat *file_hdr, int in_des); +void read_in_old_ascii (struct cpio_file_stat *file_hdr, int in_des); +void read_in_new_ascii (struct cpio_file_stat *file_hdr, int in_des); +void read_in_binary (struct cpio_file_stat *file_hdr, + struct old_cpio_header *short_hdr, int in_des); +void swab_array (char *arg, int count); +void process_copy_in (void); +void long_format (struct cpio_file_stat *file_hdr, char *link_name); +void print_name_with_quoting (char *p); + +/* copyout.c */ +int write_out_header (struct cpio_file_stat *file_hdr, int out_des); +void process_copy_out (void); + +/* copypass.c */ +void process_copy_pass (void); +int link_to_maj_min_ino (char *file_name, int st_dev_maj, + int st_dev_min, ino_t st_ino); +int link_to_name (char *link_name, char *link_target); + +/* dirname.c */ +char *dirname (char *path); + +/* filemode.c */ +void mode_string (unsigned int mode, char *str); + +/* idcache.c */ +char *getgroup (gid_t gid); +char *getuser (uid_t uid); +uid_t *getuidbyname (char *user); +gid_t *getgidbyname (char *group); + +/* main.c */ +void process_args (int argc, char *argv[]); +void initialize_buffers (void); + +/* makepath.c */ +int make_path (char *argpath, uid_t owner, gid_t group, + const char *verbose_fmt_string); + +/* tar.c */ +void write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des); +int null_block (long *block, int size); +void read_in_tar_header (struct cpio_file_stat *file_hdr, int in_des); +int otoa (char *s, unsigned long *n); +int is_tar_header (char *buf); +int is_tar_filename_too_long (char *name); + +/* userspec.c */ +char *parse_user_spec (char *name, uid_t *uid, gid_t *gid, + char **username, char **groupname); + +/* util.c */ +void tape_empty_output_buffer (int out_des); +void disk_empty_output_buffer (int out_des); +void swahw_array (char *ptr, int count); +void tape_buffered_write (char *in_buf, int out_des, off_t num_bytes); +void tape_buffered_read (char *in_buf, int in_des, off_t num_bytes); +int tape_buffered_peek (char *peek_buf, int in_des, int num_bytes); +void tape_toss_input (int in_des, off_t num_bytes); +void copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes); +void copy_files_disk_to_tape (int in_des, int out_des, off_t num_bytes, char *filename); +void copy_files_disk_to_disk (int in_des, int out_des, off_t num_bytes, char *filename); +void warn_if_file_changed (char *file_name, off_t old_file_size, + time_t old_file_mtime); +void create_all_directories (char *name); +void prepare_append (int out_file_des); +char *find_inode_file (ino_t node_num, + unsigned long major_num, unsigned long minor_num); +void add_inode (ino_t node_num, char *file_name, + unsigned long major_num, unsigned long minor_num); +int open_archive (char *file); +void tape_offline (int tape_des); +void get_next_reel (int tape_des); +void set_new_media_message (char *message); +#ifdef HPUX_CDF +char *add_cdf_double_slashes (char *filename); +#endif +void write_nuls_to_file (off_t num_bytes, int out_des, + void (*writer) (char *in_buf, + int out_des, off_t num_bytes)); +#define DISK_IO_BLOCK_SIZE 512 + +/* FIXME: Move to system.h? */ +#ifndef SYMLINK_USES_UMASK +# define UMASKED_SYMLINK(name1,name2,mode) symlink(name1,name2) +#else +# define UMASKED_SYMLINK(name1,name2,mode) umasked_symlink(name1,name2,mode) +#endif /* SYMLINK_USES_UMASK */ + +void set_perms (int fd, struct cpio_file_stat *header); +void set_file_times (int fd, const char *name, unsigned long atime, + unsigned long mtime); +void stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st); +void cpio_to_stat (struct stat *st, struct cpio_file_stat *hdr); +void cpio_safer_name_suffix (char *name, bool link_target, + bool absolute_names, bool strip_leading_dots); +int cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir); + +/* FIXME: These two defines should be defined in paxutils */ +#define LG_8 3 +#define LG_16 4 + +uintmax_t from_ascii (char const *where, size_t digs, unsigned logbase); + +#define FROM_OCTAL(f) from_ascii (f, sizeof f, LG_8) +#define FROM_HEX(f) from_ascii (f, sizeof f, LG_16) + +void delay_cpio_set_stat (struct cpio_file_stat *file_stat, + mode_t invert_permissions); +void delay_set_stat (char const *file_name, struct stat *st, + mode_t invert_permissions); +int repair_delayed_set_stat (struct cpio_file_stat *file_hdr); +void apply_delayed_set_stat (void); + diff --git a/src/fatal.c b/src/fatal.c new file mode 100644 index 0000000..16413cd --- /dev/null +++ b/src/fatal.c @@ -0,0 +1,27 @@ +/* This file is part of GNU cpio. + Copyright (C) 2005, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> +#include <paxlib.h> + +void +fatal_exit () +{ + exit (PAXEXIT_FAILURE); +} + diff --git a/src/filemode.c b/src/filemode.c new file mode 100644 index 0000000..9c816ea --- /dev/null +++ b/src/filemode.c @@ -0,0 +1,244 @@ +/* filemode.c -- make a string describing file modes + Copyright (C) 1985, 1990, 1993, 2004, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#if !S_IRUSR +# if S_IREAD +# define S_IRUSR S_IREAD +# else +# define S_IRUSR 00400 +# endif +#endif + +#if !S_IWUSR +# if S_IWRITE +# define S_IWUSR S_IWRITE +# else +# define S_IWUSR 00200 +# endif +#endif + +#if !S_IXUSR +# if S_IEXEC +# define S_IXUSR S_IEXEC +# else +# define S_IXUSR 00100 +# endif +#endif + +#ifdef STAT_MACROS_BROKEN +#undef S_ISBLK +#undef S_ISCHR +#undef S_ISDIR +#undef S_ISFIFO +#undef S_ISLNK +#undef S_ISMPB +#undef S_ISMPC +#undef S_ISNWK +#undef S_ISREG +#undef S_ISSOCK +#endif /* STAT_MACROS_BROKEN. */ + +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISMPB) && defined(S_IFMPB) /* V7 */ +#define S_ISMPB(m) (((m) & S_IFMT) == S_IFMPB) +#define S_ISMPC(m) (((m) & S_IFMT) == S_IFMPC) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +/* Return a character indicating the type of file described by + file mode BITS: + 'd' for directories + 'b' for block special files + 'c' for character special files + 'm' for multiplexor files + 'l' for symbolic links + 's' for sockets + 'p' for fifos + '-' for regular files + '?' for any other file type. */ + +static char +ftypelet (long bits) +{ +#ifdef S_ISBLK + if (S_ISBLK (bits)) + return 'b'; +#endif + if (S_ISCHR (bits)) + return 'c'; + if (S_ISDIR (bits)) + return 'd'; + if (S_ISREG (bits)) + return '-'; +#ifdef S_ISFIFO + if (S_ISFIFO (bits)) + return 'p'; +#endif +#ifdef S_ISLNK + if (S_ISLNK (bits)) + return 'l'; +#endif +#ifdef S_ISSOCK + if (S_ISSOCK (bits)) + return 's'; +#endif +#ifdef S_ISMPC + if (S_ISMPC (bits)) + return 'm'; +#endif +#ifdef S_ISNWK + if (S_ISNWK (bits)) + return 'n'; +#endif + return '?'; +} + +/* Look at read, write, and execute bits in BITS and set + flags in CHARS accordingly. */ + +static void +rwx (unsigned short bits, char *chars) +{ + chars[0] = (bits & S_IRUSR) ? 'r' : '-'; + chars[1] = (bits & S_IWUSR) ? 'w' : '-'; + chars[2] = (bits & S_IXUSR) ? 'x' : '-'; +} + +/* Set the 's' and 't' flags in file attributes string CHARS, + according to the file mode BITS. */ + +static void +setst (unsigned short bits, char *chars) +{ +#ifdef S_ISUID + if (bits & S_ISUID) + { + if (chars[3] != 'x') + /* Set-uid, but not executable by owner. */ + chars[3] = 'S'; + else + chars[3] = 's'; + } +#endif +#ifdef S_ISGID + if (bits & S_ISGID) + { + if (chars[6] != 'x') + /* Set-gid, but not executable by group. */ + chars[6] = 'S'; + else + chars[6] = 's'; + } +#endif +#ifdef S_ISVTX + if (bits & S_ISVTX) + { + if (chars[9] != 'x') + /* Sticky, but not executable by others. */ + chars[9] = 'T'; + else + chars[9] = 't'; + } +#endif +} + +/* Like filemodestring (see below), but only the relevant part of the + `struct stat' is given as an argument. */ + +void +mode_string (unsigned short mode, char *str) +{ + str[0] = ftypelet ((long) mode); + rwx ((mode & 0700) << 0, &str[1]); + rwx ((mode & 0070) << 3, &str[4]); + rwx ((mode & 0007) << 6, &str[7]); + setst (mode, str); +} + +/* filemodestring - fill in string STR with an ls-style ASCII + representation of the st_mode field of file stats block STATP. + 10 characters are stored in STR; no terminating null is added. + The characters stored in STR are: + + 0 File type. 'd' for directory, 'c' for character + special, 'b' for block special, 'm' for multiplex, + 'l' for symbolic link, 's' for socket, 'p' for fifo, + '-' for regular, '?' for any other file type + + 1 'r' if the owner may read, '-' otherwise. + + 2 'w' if the owner may write, '-' otherwise. + + 3 'x' if the owner may execute, 's' if the file is + set-user-id, '-' otherwise. + 'S' if the file is set-user-id, but the execute + bit isn't set. + + 4 'r' if group members may read, '-' otherwise. + + 5 'w' if group members may write, '-' otherwise. + + 6 'x' if group members may execute, 's' if the file is + set-group-id, '-' otherwise. + 'S' if it is set-group-id but not executable. + + 7 'r' if any user may read, '-' otherwise. + + 8 'w' if any user may write, '-' otherwise. + + 9 'x' if any user may execute, 't' if the file is "sticky" + (will be retained in swap space after execution), '-' + otherwise. + 'T' if the file is sticky but not executable. */ + +void +filemodestring (struct stat *statp, char *str) +{ + mode_string (statp->st_mode, str); +} + diff --git a/src/filetypes.h b/src/filetypes.h new file mode 100644 index 0000000..f80faab --- /dev/null +++ b/src/filetypes.h @@ -0,0 +1,85 @@ +/* filetypes.h - deal with POSIX annoyances + Copyright (C) 1991, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +/* Include sys/types.h and sys/stat.h before this file. */ + +#ifndef S_ISREG /* Doesn't have POSIX.1 stat stuff. */ +#define mode_t unsigned short +#endif + +/* Define the POSIX macros for systems that lack them. */ +#if !defined(S_ISBLK) && defined(S_IFBLK) +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#if !defined(S_ISCHR) && defined(S_IFCHR) +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#if !defined(S_ISFIFO) && defined(S_IFIFO) +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#if !defined(S_ISLNK) && defined(S_IFLNK) +#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) +#endif +#if !defined(S_ISSOCK) && defined(S_IFSOCK) +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif +#if !defined(S_ISNWK) && defined(S_IFNWK) /* HP/UX network special */ +#define S_ISNWK(m) (((m) & S_IFMT) == S_IFNWK) +#endif + +/* Define the file type bits used in cpio archives. + They have the same values as the S_IF bits in traditional Unix. */ + +#define CP_IFMT 0170000 /* Mask for all file type bits. */ + +#if defined(S_ISBLK) +#define CP_IFBLK 0060000 +#endif +#if defined(S_ISCHR) +#define CP_IFCHR 0020000 +#endif +#if defined(S_ISDIR) +#define CP_IFDIR 0040000 +#endif +#if defined(S_ISREG) +#define CP_IFREG 0100000 +#endif +#if defined(S_ISFIFO) +#define CP_IFIFO 0010000 +#endif +#if defined(S_ISLNK) +#define CP_IFLNK 0120000 +#endif +#if defined(S_ISSOCK) +#define CP_IFSOCK 0140000 +#endif +#if defined(S_ISNWK) +#define CP_IFNWK 0110000 +#endif + +#ifndef S_ISLNK +#define lstat stat +#endif +int lstat (); +int stat (); diff --git a/src/global.c b/src/global.c new file mode 100644 index 0000000..cff9720 --- /dev/null +++ b/src/global.c @@ -0,0 +1,195 @@ +/* global.c - global variables and initial values for cpio. + Copyright (C) 1990, 1991, 1992, 2001, 2006, 2007, 2009, 2010 Free + Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <sys/types.h> +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" + +/* If true, reset access times after reading files (-a). */ +int reset_time_flag = false; + +/* Block size value, initially 512. -B sets to 5120. */ +int io_block_size = 512; + +/* The header format to recognize and produce. */ +enum archive_format archive_format = arf_unknown; + +/* If true, create directories as needed. (-d with -i or -p) */ +int create_dir_flag = false; + +/* If true, interactively rename files. (-r) */ +int rename_flag = false; + +/* If non-NULL, the name of a file that will be read to + rename all of the files in the archive. --rename-batch-file. */ +char *rename_batch_file = NULL; + +/* If true, print a table of contents of input. (-t) */ +int table_flag = false; + +/* If true, copy unconditionally (older replaces newer). (-u) */ +int unconditional_flag = false; + +/* If true, list the files processed, or ls -l style output with -t. (-v) */ +int verbose_flag = false; + +/* If true, print a . for each file processed. (-V) */ +int dot_flag = false; + +/* If true, link files whenever possible. Used with -p option. (-l) */ +int link_flag = false; + +/* If true, retain previous file modification time. (-m) */ +int retain_time_flag = false; + +/* Set true if crc_flag is true and we are doing a cpio -i. Used + by copy_files so it knows whether to compute the crc. */ +int crc_i_flag = false; + +/* If true, append to end of archive. (-A) */ +int append_flag = false; + +/* If true, swap bytes of each file during cpio -i. */ +int swap_bytes_flag = false; + +/* If true, swap halfwords of each file during cpio -i. */ +int swap_halfwords_flag = false; + +/* If true, we are swapping halfwords on the current file. */ +int swapping_halfwords = false; + +/* If true, we are swapping bytes on the current file. */ +int swapping_bytes = false; + +/* Umask for creating new directories */ +mode_t newdir_umask; + +/* If true, set ownership of all files to UID `set_owner'. */ +int set_owner_flag = false; +uid_t set_owner; + +/* If true, set group ownership of all files to GID `set_group'. */ +int set_group_flag = false; +gid_t set_group; + +/* If true, do not chown the files. */ +int no_chown_flag = false; + +/* If true, try to write sparse ("holey") files. */ +int sparse_flag = false; + +/* If true, don't report number of blocks copied. */ +int quiet_flag = false; + +/* If true, only read the archive and verify the files' CRC's, don't + actually extract the files. */ +int only_verify_crc_flag = false; + +/* If true, don't use any absolute paths, prefix them by `./'. */ +int no_abs_paths_flag = false; + +#ifdef DEBUG_CPIO +/* If true, print debugging information. */ +int debug_flag = false; +#endif + +/* File position of last header read. Only used during -A to determine + where the old TRAILER!!! record started. */ +int last_header_start = 0; + +/* With -i; if true, copy only files that match any of the given patterns; + if false, copy only files that do not match any of the patterns. (-f) */ +int copy_matching_files = true; + +/* With -itv; if true, list numeric uid and gid instead of translating them + into names. */ +int numeric_uid = false; + +/* Name of file containing additional patterns (-E). */ +char *pattern_file_name = NULL; + +/* Message to print when end of medium is reached (-M). */ +char *new_media_message = NULL; + +/* With -M with %d, message to print when end of medium is reached. */ +char *new_media_message_with_number = NULL; +char *new_media_message_after_number = NULL; + +/* File descriptor containing the archive. */ +int archive_des; + +/* Name of file containing the archive, if known; NULL if stdin/out. */ +char *archive_name = NULL; + +/* Name of the remote shell command, if known; NULL otherwise. */ +char *rsh_command_option = NULL; + +/* CRC checksum. */ +unsigned long crc; + +/* Input and output buffers. */ +char *input_buffer, *output_buffer; + +/* The size of the input buffer. */ +size_t input_buffer_size; + +/* Current locations in `input_buffer' and `output_buffer'. */ +char *in_buff, *out_buff; + +/* Current number of bytes stored at `input_buff' and `output_buff'. */ +size_t input_size, output_size; + +off_t input_bytes, output_bytes; + +/* Saving of argument values for later reference. */ +char *directory_name = NULL; +char **save_patterns; +int num_patterns; + +/* Character that terminates file names read from stdin. */ +char name_end = '\n'; + +/* true if input (cpio -i) or output (cpio -o) is a device node. */ +char input_is_special = false; +char output_is_special = false; + +/* true if lseek works on the input. */ +char input_is_seekable = false; + +/* true if lseek works on the output. */ +char output_is_seekable = false; + +/* Print extra warning messages */ +unsigned int warn_option = 0; + +/* Extract to standard output? */ +bool to_stdout_option = false; + +/* The name this program was run with. */ +char *program_name; + +/* A pointer to either lstat or stat, depending on whether + dereferencing of symlinks is done for input files. */ +int (*xstat) (); + +/* Which copy operation to perform. (-i, -o, -p) */ +void (*copy_function) () = 0; diff --git a/src/idcache.c b/src/idcache.c new file mode 100644 index 0000000..4bcf79d --- /dev/null +++ b/src/idcache.c @@ -0,0 +1,198 @@ +/* idcache.c -- map user and group IDs, cached for speed + Copyright (C) 1985, 1988, 1989, 1990, 2004, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <sys/types.h> +#include <xalloc.h> + +#include <system.h> + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#include <string.h> +#else +#include <strings.h> +#endif + +#include <unistd.h> + +struct userid +{ + union + { + uid_t u; + gid_t g; + } id; + char *name; + struct userid *next; +}; + +static struct userid *user_alist; + +/* The members of this list have names not in the local passwd file. */ +static struct userid *nouser_alist; + +/* Translate UID to a login name or a stringified number, + with cache. */ + +char * +getuser (uid_t uid) +{ + register struct userid *tail; + struct passwd *pwent; + char usernum_string[20]; + + for (tail = user_alist; tail; tail = tail->next) + if (tail->id.u == uid) + return tail->name; + + pwent = getpwuid (uid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.u = uid; + if (pwent == 0) + { + sprintf (usernum_string, "%u", (unsigned) uid); + tail->name = xstrdup (usernum_string); + } + else + tail->name = xstrdup (pwent->pw_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = user_alist; + user_alist = tail; + return tail->name; +} + +/* Translate USER to a UID, with cache. + Return NULL if there is no such user. + (We also cache which user names have no passwd entry, + so we don't keep looking them up.) */ + +uid_t * +getuidbyname (char *user) +{ + register struct userid *tail; + struct passwd *pwent; + + for (tail = user_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return &tail->id.u; + + for (tail = nouser_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *user && !strcmp (tail->name, user)) + return 0; + + pwent = getpwnam (user); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (user); + + /* Add to the head of the list, so most recently used is first. */ + if (pwent) + { + tail->id.u = pwent->pw_uid; + tail->next = user_alist; + user_alist = tail; + return &tail->id.u; + } + + tail->next = nouser_alist; + nouser_alist = tail; + return 0; +} + +/* Use the same struct as for userids. */ +static struct userid *group_alist; +static struct userid *nogroup_alist; + +/* Translate GID to a group name or a stringified number, + with cache. */ + +char * +getgroup (gid_t gid) +{ + register struct userid *tail; + struct group *grent; + char groupnum_string[20]; + + for (tail = group_alist; tail; tail = tail->next) + if (tail->id.g == gid) + return tail->name; + + grent = getgrgid (gid); + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->id.g = gid; + if (grent == 0) + { + sprintf (groupnum_string, "%u", (unsigned int) gid); + tail->name = xstrdup (groupnum_string); + } + else + tail->name = xstrdup (grent->gr_name); + + /* Add to the head of the list, so most recently used is first. */ + tail->next = group_alist; + group_alist = tail; + return tail->name; +} + +/* Translate GROUP to a UID, with cache. + Return NULL if there is no such group. + (We also cache which group names have no group entry, + so we don't keep looking them up.) */ + +gid_t * +getgidbyname (char *group) +{ + register struct userid *tail; + struct group *grent; + + for (tail = group_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return &tail->id.g; + + for (tail = nogroup_alist; tail; tail = tail->next) + /* Avoid a function call for the most common case. */ + if (*tail->name == *group && !strcmp (tail->name, group)) + return 0; + + grent = getgrnam (group); + + tail = (struct userid *) xmalloc (sizeof (struct userid)); + tail->name = xstrdup (group); + + /* Add to the head of the list, so most recently used is first. */ + if (grent) + { + tail->id.g = grent->gr_gid; + tail->next = group_alist; + group_alist = tail; + return &tail->id.g; + } + + tail->next = nogroup_alist; + nogroup_alist = tail; + return 0; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..ba1b969 --- /dev/null +++ b/src/main.c @@ -0,0 +1,743 @@ +/* main.c - main program and argument processing for cpio. + Copyright (C) 1990, 1991, 1992, 2001, 2003, 2004, 2005, 2006, 2007, + 2009, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +/* Written by Phil Nelson <phil@cs.wwu.edu>, + David MacKenzie <djm@gnu.ai.mit.edu>, + John Oleynick <juo@klinzhai.rutgers.edu>, + and Sergey Poznyakoff <gray@gnu.org> */ + +#include <system.h> +#include <paxlib.h> + +#include <stdio.h> +#include <argp.h> +#include <argp-version-etc.h> +#include <sys/types.h> +#include <sys/stat.h> + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif + +#include <progname.h> + +#include "filetypes.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include <rmt.h> +#include <rmt-command.h> +#include "configmake.h" + +enum cpio_options { + NO_ABSOLUTE_FILENAMES_OPTION=256, + ABSOLUTE_FILENAMES_OPTION, + NO_PRESERVE_OWNER_OPTION, + ONLY_VERIFY_CRC_OPTION, + RENAME_BATCH_FILE_OPTION, + RSH_COMMAND_OPTION, + QUIET_OPTION, + SPARSE_OPTION, + FORCE_LOCAL_OPTION, + DEBUG_OPTION, + BLOCK_SIZE_OPTION, + TO_STDOUT_OPTION +}; + +const char *program_authors[] = + { + "Phil Nelson", + "David MacKenzie", + "John Oleynick", + "Sergey Poznyakoff", + NULL + }; + +const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; +static char doc[] = N_("GNU `cpio' copies files to and from archives\n\ +\n\ +Examples:\n\ + # Copy files named in name-list to the archive\n\ + cpio -o < name-list [> archive]\n\ + # Extract files from the archive\n\ + cpio -i [< archive]\n\ + # Copy files named in name-list to destination-directory\n\ + cpio -p destination-directory < name-list\n"); + +/* Print usage error message and exit with error. */ + +#define CHECK_USAGE(cond, opt, mode_opt) \ + if (cond) \ + ERROR((PAXEXIT_FAILURE, 0, _("%s is meaningless with %s"), opt, mode_opt)); + +static struct argp_option options[] = { + /* ********** */ +#define GRID 10 + {NULL, 0, NULL, 0, + N_("Main operation mode:"), GRID }, + {"create", 'o', 0, 0, + N_("Create the archive (run in copy-out mode)"), GRID }, + {"extract", 'i', 0, 0, + N_("Extract files from an archive (run in copy-in mode)"), GRID }, + {"pass-through", 'p', 0, 0, + N_("Run in copy-pass mode"), GRID }, + {"list", 't', 0, 0, + N_("Print a table of contents of the input"), GRID }, +#undef GRID + + /* ********** */ +#define GRID 100 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid in any mode:"), GRID }, + + {"file", 'F', N_("[[USER@]HOST:]FILE-NAME"), 0, + N_("Use this FILE-NAME instead of standard input or output. Optional USER and HOST specify the user and host names in case of a remote archive"), GRID+1 }, + {"force-local", FORCE_LOCAL_OPTION, 0, 0, + N_("Archive file is local, even if its name contains colons"), GRID+1 }, + {"format", 'H', N_("FORMAT"), 0, + N_("Use given archive FORMAT"), GRID+1 }, + {NULL, 'B', NULL, 0, + N_("Set the I/O block size to 5120 bytes"), GRID+1 }, + {"block-size", BLOCK_SIZE_OPTION, N_("BLOCK-SIZE"), 0, + N_("Set the I/O block size to BLOCK-SIZE * 512 bytes"), GRID+1 }, + {NULL, 'c', NULL, 0, + N_("Use the old portable (ASCII) archive format"), GRID+1 }, + {"dot", 'V', NULL, 0, + N_("Print a \".\" for each file processed"), GRID+1 }, + {"io-size", 'C', N_("NUMBER"), 0, + N_("Set the I/O block size to the given NUMBER of bytes"), GRID+1 }, + {"message", 'M', N_("STRING"), 0, + N_("Print STRING when the end of a volume of the backup media is reached"), + GRID+1 }, + {"nonmatching", 'f', 0, 0, + N_("Only copy files that do not match any of the given patterns"), GRID+1 }, + {"numeric-uid-gid", 'n', 0, 0, + N_("In the verbose table of contents listing, show numeric UID and GID"), + GRID+1 }, + {"rsh-command", RSH_COMMAND_OPTION, N_("COMMAND"), 0, + N_("Use remote COMMAND instead of rsh"), GRID+1 }, + {"quiet", QUIET_OPTION, NULL, 0, + N_("Do not print the number of blocks copied"), GRID+1 }, + {"verbose", 'v', NULL, 0, + N_("Verbosely list the files processed"), GRID+1 }, +#ifdef DEBUG_CPIO + {"debug", DEBUG_OPTION, NULL, 0, + N_("Enable debugging info"), GRID+1 }, +#endif + {"warning", 'W', N_("FLAG"), 0, + N_("Control warning display. Currently FLAG is one of 'none', 'truncate', 'all'. Multiple options accumulate."), GRID+1 }, +#undef GRID + + /* ********** */ +#define GRID 200 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid only in copy-in mode:"), GRID }, + {"pattern-file", 'E', N_("FILE"), 0, + N_("Read additional patterns specifying filenames to extract or list from FILE"), 210}, + {"only-verify-crc", ONLY_VERIFY_CRC_OPTION, 0, 0, + N_("When reading a CRC format archive, only verify the CRC's of each file in the archive, don't actually extract the files"), 210}, + {"rename", 'r', 0, 0, + N_("Interactively rename files"), GRID+1 }, + {"rename-batch-file", RENAME_BATCH_FILE_OPTION, N_("FILE"), OPTION_HIDDEN, + "", GRID+1 }, + {"swap", 'b', NULL, 0, + N_("Swap both halfwords of words and bytes of halfwords in the data. Equivalent to -sS"), GRID+1 }, + {"swap-bytes", 's', NULL, 0, + N_("Swap the bytes of each halfword in the files"), GRID+1 }, + {"swap-halfwords", 'S', NULL, 0, + N_("Swap the halfwords of each word (4 bytes) in the files"), + GRID+1 }, + {"to-stdout", TO_STDOUT_OPTION, NULL, 0, + N_("Extract files to standard output"), GRID+1 }, +#undef GRID + + /* ********** */ +#define GRID 300 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid only in copy-out mode:"), GRID }, + {"append", 'A', 0, 0, + N_("Append to an existing archive."), GRID+1 }, + {NULL, 'O', N_("[[USER@]HOST:]FILE-NAME"), 0, + N_("Archive filename to use instead of standard output. Optional USER and HOST specify the user and host names in case of a remote archive"), GRID+1 }, +#undef GRID + + /* ********** */ +#define GRID 400 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid only in copy-pass mode:"), GRID}, + {"link", 'l', 0, 0, + N_("Link files instead of copying them, when possible"), GRID+1 }, + +#undef GRID + + /* ********** */ +#define GRID 500 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid in copy-in and copy-out modes:"), GRID }, + {"absolute-filenames", ABSOLUTE_FILENAMES_OPTION, 0, 0, + N_("Do not strip file system prefix components from the file names"), + GRID+1 }, + {"no-absolute-filenames", NO_ABSOLUTE_FILENAMES_OPTION, 0, 0, + N_("Create all files relative to the current directory"), GRID+1 }, +#undef GRID + /* ********** */ +#define GRID 600 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid in copy-out and copy-pass modes:"), GRID }, + {"null", '0', 0, 0, + N_("A list of filenames is terminated by a null character instead of a newline"), GRID+1 }, + {NULL, 'I', N_("[[USER@]HOST:]FILE-NAME"), 0, + N_("Archive filename to use instead of standard input. Optional USER and HOST specify the user and host names in case of a remote archive"), GRID+1 }, + {"dereference", 'L', 0, 0, + N_("Dereference symbolic links (copy the files that they point to instead of copying the links)."), GRID+1 }, + {"owner", 'R', N_("[USER][:.][GROUP]"), 0, + N_("Set the ownership of all files created to the specified USER and/or GROUP"), GRID+1 }, + {"reset-access-time", 'a', NULL, 0, + N_("Reset the access times of files after reading them"), GRID+1 }, + +#undef GRID + /* ********** */ +#define GRID 700 + {NULL, 0, NULL, 0, + N_("Operation modifiers valid in copy-in and copy-pass modes:"), GRID }, + {"preserve-modification-time", 'm', 0, 0, + N_("Retain previous file modification times when creating files"), GRID+1 }, + {"make-directories", 'd', 0, 0, + N_("Create leading directories where needed"), GRID+1 }, + {"no-preserve-owner", NO_PRESERVE_OWNER_OPTION, 0, 0, + N_("Do not change the ownership of the files"), GRID+1 }, + {"unconditional", 'u', NULL, 0, + N_("Replace all files unconditionally"), GRID+1 }, + {"sparse", SPARSE_OPTION, NULL, 0, + N_("Write files with large blocks of zeros as sparse files"), GRID+1 }, +#undef GRID + + {0, 0, 0, 0} +}; + +static char *input_archive_name = 0; +static char *output_archive_name = 0; + +static int +warn_control (char *arg) +{ + static struct warn_tab { + char *name; + int flag; + } warn_tab[] = { + { "none", CPIO_WARN_ALL }, + { "truncate", CPIO_WARN_TRUNCATE }, + { "all", CPIO_WARN_ALL }, + { "interdir", CPIO_WARN_INTERDIR }, + { NULL } + }; + struct warn_tab *wt; + int offset = 0; + + if (strcmp (arg, "none") == 0) + { + warn_option = 0; + return 0; + } + + if (strlen (arg) > 2 && memcmp (arg, "no-", 3) == 0) + offset = 3; + + for (wt = warn_tab; wt->name; wt++) + if (strcmp (arg + offset, wt->name) == 0) + { + if (offset) + warn_option &= ~wt->flag; + else + warn_option |= wt->flag; + return 0; + } + + return 1; +} + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case '0': /* Read null-terminated filenames. */ + name_end = '\0'; + break; + + case 'a': /* Reset access times. */ + reset_time_flag = true; + break; + + case 'A': /* Append to the archive. */ + append_flag = true; + break; + + case 'b': /* Swap bytes and halfwords. */ + swap_bytes_flag = true; + swap_halfwords_flag = true; + break; + + case 'B': /* Set block size to 5120. */ + io_block_size = 5120; + break; + + case BLOCK_SIZE_OPTION: /* --block-size */ + io_block_size = atoi (arg); + if (io_block_size < 1) + error (2, 0, _("invalid block size")); + io_block_size *= 512; + break; + + case 'c': /* Use the old portable ASCII format. */ + if (archive_format != arf_unknown) + error (0, EXIT_FAILURE, _("Archive format multiply defined")); +#ifdef SVR4_COMPAT + archive_format = arf_newascii; /* -H newc. */ +#else + archive_format = arf_oldascii; /* -H odc. */ +#endif + break; + + case 'C': /* Block size. */ + io_block_size = atoi (arg); + if (io_block_size < 1) + error (2, 0, _("invalid block size")); + break; + + case 'd': /* Create directories where needed. */ + create_dir_flag = true; + break; + + case 'f': /* Only copy files not matching patterns. */ + copy_matching_files = false; + break; + + case 'E': /* Pattern file name. */ + pattern_file_name = arg; + break; + + case 'F': /* Archive file name. */ + archive_name = arg; + break; + + case 'H': /* Header format name. */ + if (archive_format != arf_unknown) + error (PAXEXIT_FAILURE, 0, _("Archive format multiply defined")); + if (!strcasecmp (arg, "crc")) + archive_format = arf_crcascii; + else if (!strcasecmp (arg, "newc")) + archive_format = arf_newascii; + else if (!strcasecmp (arg, "odc")) + archive_format = arf_oldascii; + else if (!strcasecmp (arg, "bin")) + archive_format = arf_binary; + else if (!strcasecmp (arg, "ustar")) + archive_format = arf_ustar; + else if (!strcasecmp (arg, "tar")) + archive_format = arf_tar; + else if (!strcasecmp (arg, "hpodc")) + archive_format = arf_hpoldascii; + else if (!strcasecmp (arg, "hpbin")) + archive_format = arf_hpbinary; + else + error (2, 0, _("\ +invalid archive format `%s'; valid formats are:\n\ +crc newc odc bin ustar tar (all-caps also recognized)"), arg); + break; + + case 'i': /* Copy-in mode. */ + if (copy_function != 0) + error (PAXEXIT_FAILURE, 0, _("Mode already defined")); + copy_function = process_copy_in; + break; + + case 'I': /* Input archive file name. */ + input_archive_name = arg; + break; + + case 'k': /* Handle corrupted archives. We always handle + corrupted archives, but recognize this + option for compatability. */ + break; + + case 'l': /* Link files when possible. */ + link_flag = true; + break; + + case 'L': /* Dereference symbolic links. */ + xstat = stat; + break; + + case 'm': /* Retain previous file modify times. */ + retain_time_flag = true; + break; + + case 'M': /* New media message. */ + set_new_media_message (arg); + break; + + case 'n': /* Long list owner and group as numbers. */ + numeric_uid = true; + break; + + case NO_ABSOLUTE_FILENAMES_OPTION: /* --no-absolute-filenames */ + no_abs_paths_flag = true; + break; + + case ABSOLUTE_FILENAMES_OPTION: /* --absolute-filenames */ + no_abs_paths_flag = false; + break; + + case NO_PRESERVE_OWNER_OPTION: /* --no-preserve-owner */ + if (set_owner_flag || set_group_flag) + error (PAXEXIT_FAILURE, 0, + _("--no-preserve-owner cannot be used with --owner")); + no_chown_flag = true; + break; + + case 'o': /* Copy-out mode. */ + if (copy_function != 0) + error (PAXEXIT_FAILURE, 0, _("Mode already defined")); + copy_function = process_copy_out; + break; + + case 'O': /* Output archive file name. */ + output_archive_name = arg; + break; + + case ONLY_VERIFY_CRC_OPTION: + only_verify_crc_flag = true; + break; + + case 'p': /* Copy-pass mode. */ + if (copy_function != 0) + error (PAXEXIT_FAILURE, 0, _("Mode already defined")); + copy_function = process_copy_pass; + break; + + case RSH_COMMAND_OPTION: + rsh_command_option = arg; + break; + + case 'r': /* Interactively rename. */ + rename_flag = true; + break; + + case RENAME_BATCH_FILE_OPTION: + rename_batch_file = arg; + break; + + case QUIET_OPTION: + quiet_flag = true; + break; + + case 'R': /* Set the owner. */ + if (no_chown_flag) + error (PAXEXIT_FAILURE, 0, + _("--owner cannot be used with --no-preserve-owner")); + else + { + char *e, *u, *g; + + e = parse_user_spec (arg, &set_owner, &set_group, &u, &g); + if (e) + error (PAXEXIT_FAILURE, 0, "%s: %s", arg, e); + if (u) + { + free (u); + set_owner_flag = true; + } + if (g) + { + free (g); + set_group_flag = true; + } + } + break; + + case 's': /* Swap bytes. */ + swap_bytes_flag = true; + break; + + case 'S': /* Swap halfwords. */ + swap_halfwords_flag = true; + break; + + case 't': /* Only print a list. */ + table_flag = true; + break; + + case 'u': /* Replace all! Unconditionally! */ + unconditional_flag = true; + break; + + case 'v': /* Verbose! */ + verbose_flag = true; + break; + + case 'V': /* Print `.' for each file. */ + dot_flag = true; + break; + + case 'W': + if (warn_control (arg)) + argp_error (state, _("Invalid value for --warning option: %s"), arg); + break; + + case SPARSE_OPTION: + sparse_flag = true; + break; + + case FORCE_LOCAL_OPTION: + force_local_option = 1; + break; + +#ifdef DEBUG_CPIO + case DEBUG_OPTION: + debug_flag = true; + break; +#endif + + case TO_STDOUT_OPTION: + to_stdout_option = true; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, + parse_opt, + N_("[destination-directory]"), + doc, + NULL, + NULL, + NULL +}; + +/* Process the arguments. Set all options and set up the copy pass + directory or the copy in patterns. */ + +void +process_args (int argc, char *argv[]) +{ + void (*copy_in) (); /* Work around for pcc bug. */ + void (*copy_out) (); + int index; + + xstat = lstat; + + if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, &index, NULL)) + exit (PAXEXIT_FAILURE); + + /* Do error checking and look at other args. */ + + if (copy_function == 0) + { + if (table_flag) + copy_function = process_copy_in; + else + error (PAXEXIT_FAILURE, 0, + _("You must specify one of -oipt options.\nTry `%s --help' or `%s --usage' for more information.\n"), + program_name, program_name); + } + + /* Work around for pcc bug. */ + copy_in = process_copy_in; + copy_out = process_copy_out; + + if (copy_function == copy_in) + { + archive_des = 0; + CHECK_USAGE(link_flag, "--link", "--extract"); + CHECK_USAGE(reset_time_flag, "--reset", "--extract"); + CHECK_USAGE(xstat != lstat, "--dereference", "--extract"); + CHECK_USAGE(append_flag, "--append", "--extract"); + CHECK_USAGE(output_archive_name, "-O", "--extract"); + if (to_stdout_option) + { + CHECK_USAGE(create_dir_flag, "--make-directories", "--to-stdout"); + CHECK_USAGE(rename_flag, "--rename", "--to-stdout"); + CHECK_USAGE(no_chown_flag, "--no-preserve-owner", "--to-stdout"); + CHECK_USAGE(set_owner_flag||set_group_flag, "--owner", "--to-stdout"); + CHECK_USAGE(retain_time_flag, "--preserve-modification-time", + "--to-stdout"); + } + + if (archive_name && input_archive_name) + error (PAXEXIT_FAILURE, 0, + _("Both -I and -F are used in copy-in mode")); + + if (archive_format == arf_crcascii) + crc_i_flag = true; + num_patterns = argc - index; + save_patterns = &argv[index]; + if (input_archive_name) + archive_name = input_archive_name; + } + else if (copy_function == copy_out) + { + if (index != argc) + error (PAXEXIT_FAILURE, 0, _("Too many arguments")); + + archive_des = 1; + CHECK_USAGE(create_dir_flag, "--make-directories", "--create"); + CHECK_USAGE(rename_flag, "--rename", "--create"); + CHECK_USAGE(table_flag, "--list", "--create"); + CHECK_USAGE(unconditional_flag, "--unconditional", "--create"); + CHECK_USAGE(link_flag, "--link", "--create"); + CHECK_USAGE(sparse_flag, "--sparse", "--create"); + CHECK_USAGE(retain_time_flag, "--preserve-modification-time", + "--create"); + CHECK_USAGE(no_chown_flag, "--no-preserve-owner", "--create"); + CHECK_USAGE(swap_bytes_flag, "--swap-bytes (--swap)", "--create"); + CHECK_USAGE(swap_halfwords_flag, "--swap-halfwords (--swap)", + "--create"); + CHECK_USAGE(to_stdout_option, "--to-stdout", "--create"); + + if (append_flag && !(archive_name || output_archive_name)) + error (PAXEXIT_FAILURE, 0, + _("--append is used but no archive file name is given (use -F or -O options)")); + + CHECK_USAGE(rename_batch_file, "--rename-batch-file", "--create"); + CHECK_USAGE(input_archive_name, "-I", "--create"); + if (archive_name && output_archive_name) + error (PAXEXIT_FAILURE, 0, + _("Both -O and -F are used in copy-out mode")); + + if (archive_format == arf_unknown) + archive_format = arf_binary; + if (output_archive_name) + archive_name = output_archive_name; + } + else + { + /* Copy pass. */ + if (index < argc - 1) + error (PAXEXIT_FAILURE, 0, _("Too many arguments")); + else if (index > argc - 1) + error (PAXEXIT_FAILURE, 0, _("Not enough arguments")); + + if (archive_format != arf_unknown) + error (PAXEXIT_FAILURE, 0, + _("Archive format is not specified in copy-pass mode (use --format option)")); + + CHECK_USAGE(swap_bytes_flag, "--swap-bytes (--swap)", "--pass-through"); + CHECK_USAGE(swap_halfwords_flag, "--swap-halfwords (--swap)", + "--pass-through"); + CHECK_USAGE(table_flag, "--list", "--pass-through"); + CHECK_USAGE(rename_flag, "--rename", "--pass-through"); + CHECK_USAGE(append_flag, "--append", "--pass-through"); + CHECK_USAGE(rename_batch_file, "--rename-batch-file", "--pass-through"); + CHECK_USAGE(no_abs_paths_flag, "--no-absolute-pathnames", + "--pass-through"); + CHECK_USAGE(no_abs_paths_flag, "--absolute-pathnames", + "--pass-through"); + CHECK_USAGE(to_stdout_option, "--to-stdout", "--pass-through"); + + directory_name = argv[index]; + } + + if (archive_name) + { + if (copy_function != copy_in && copy_function != copy_out) + error (PAXEXIT_FAILURE, 0, + _("-F can be used only with --create or --extract")); + archive_des = open_archive (archive_name); + if (archive_des < 0) + error (PAXEXIT_FAILURE, errno, _("Cannot open %s"), + quotearg_colon (archive_name)); + } + + /* Prevent SysV non-root users from giving away files inadvertantly. + This happens automatically on BSD, where only root can give + away files. */ + if (set_owner_flag == false && set_group_flag == false && geteuid ()) + no_chown_flag = true; +} + +/* Initialize the input and output buffers to their proper size and + initialize all variables associated with the input and output + buffers. */ + +void +initialize_buffers () +{ + int in_buf_size, out_buf_size; + + if (copy_function == process_copy_in) + { + /* Make sure the input buffer can always hold 2 blocks and that it + is big enough to hold 1 tar record (512 bytes) even if it + is not aligned on a block boundary. The extra buffer space + is needed by process_copyin and peek_in_buf to automatically + figure out what kind of archive it is reading. */ + if (io_block_size >= 512) + in_buf_size = 2 * io_block_size; + else + in_buf_size = 1024; + out_buf_size = DISK_IO_BLOCK_SIZE; + } + else if (copy_function == process_copy_out) + { + in_buf_size = DISK_IO_BLOCK_SIZE; + out_buf_size = io_block_size; + } + else + { + in_buf_size = DISK_IO_BLOCK_SIZE; + out_buf_size = DISK_IO_BLOCK_SIZE; + } + + input_buffer = (char *) xmalloc (in_buf_size); + in_buff = input_buffer; + input_buffer_size = in_buf_size; + input_size = 0; + input_bytes = 0; + + output_buffer = (char *) xmalloc (out_buf_size); + out_buff = output_buffer; + output_size = 0; + output_bytes = 0; +} + +int +main (int argc, char *argv[]) +{ + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + set_program_name (argv[0]); + argp_version_setup ("cpio", program_authors); + process_args (argc, argv); + + initialize_buffers (); + + (*copy_function) (); + + if (archive_des >= 0 && rmtclose (archive_des) == -1) + error (PAXEXIT_FAILURE, errno, _("error closing archive")); + + pax_exit (); +} diff --git a/src/makepath.c b/src/makepath.c new file mode 100644 index 0000000..7631772 --- /dev/null +++ b/src/makepath.c @@ -0,0 +1,188 @@ +/* makepath.c -- Ensure that a directory path exists. + Copyright (C) 1990, 2006, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +/* Written by David MacKenzie <djm@gnu.ai.mit.edu> and + Jim Meyering <meyering@cs.utexas.edu>. */ + +/* This copy of makepath is almost like the fileutils one, but has + changes for HPUX CDF's. Maybe the 2 versions of makepath can + come together again in the future. */ + +#include <system.h> +#include <paxlib.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" + +/* Ensure that the directory ARGPATH exists. + Remove any trailing slashes from ARGPATH before calling this function. + + Make all directory components that don't already exist with + permissions 700. + If OWNER and GROUP are non-negative, make them the UID and GID of + created directories. + If VERBOSE_FMT_STRING is nonzero, use it as a printf format + string for printing a message after successfully making a directory, + with the name of the directory that was just made as an argument. + + Return 0 if ARGPATH exists as a directory with the proper + ownership and permissions when done, otherwise 1. */ + +int +make_path (char *argpath, + uid_t owner, + gid_t group, + const char *verbose_fmt_string) +{ + char *dirpath; /* A copy we can scribble NULs on. */ + struct stat stats; + int retval = 0; + mode_t tmpmode; + mode_t invert_permissions; + int we_are_root = getuid () == 0; + dirpath = alloca (strlen (argpath) + 1); + + strcpy (dirpath, argpath); + + if (stat (dirpath, &stats)) + { + tmpmode = MODE_RWX & ~ newdir_umask; + invert_permissions = we_are_root ? 0 : MODE_WXUSR & ~ tmpmode; + + char *slash = dirpath; + while (*slash == '/') + slash++; + while ((slash = strchr (slash, '/'))) + { +#ifdef HPUX_CDF + int iscdf; + iscdf = 0; +#endif + *slash = '\0'; + if (stat (dirpath, &stats)) + { +#ifdef HPUX_CDF + /* If this component of the pathname ends in `+' and is + followed by 2 `/'s, then this is a CDF. We remove the + `+' from the name and create the directory. Later + we will "hide" the directory. */ + if ( (*(slash +1) == '/') && (*(slash -1) == '+') ) + { + iscdf = 1; + *(slash -1) = '\0'; + } +#endif + if (mkdir (dirpath, tmpmode ^ invert_permissions)) + { + error (0, errno, _("cannot make directory `%s'"), dirpath); + return 1; + } + else + { + if (verbose_fmt_string != NULL) + error (0, 0, verbose_fmt_string, dirpath); + + if (stat (dirpath, &stats)) + stat_error (dirpath); + else + { + if (owner != -1) + stats.st_uid = owner; + if (group != -1) + stats.st_gid = group; + + delay_set_stat (dirpath, &stats, invert_permissions); + } + +#ifdef HPUX_CDF + if (iscdf) + { + /* If this is a CDF, "hide" the directory by setting + its hidden/setuid bit. Also add the `+' back to + its name (since once it's "hidden" we must refer + to as `name+' instead of `name'). */ + chmod (dirpath, 04700); + *(slash - 1) = '+'; + } +#endif + } + } + else if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, _("`%s' exists but is not a directory"), dirpath); + return 1; + } + + *slash++ = '/'; + + /* Avoid unnecessary calls to `stat' when given + pathnames containing multiple adjacent slashes. */ + while (*slash == '/') + slash++; + } + + /* We're done making leading directories. + Make the final component of the path. */ + + if (mkdir (dirpath, tmpmode ^ invert_permissions)) + { + /* In some cases, if the final component in dirpath was `.' then we + just got an EEXIST error from that last mkdir(). If that's + the case, ignore it. */ + if ( (errno != EEXIST) || + (stat (dirpath, &stats) != 0) || + (!S_ISDIR (stats.st_mode) ) ) + { + error (0, errno, _("cannot make directory `%s'"), dirpath); + return 1; + } + } + else if (stat (dirpath, &stats)) + stat_error (dirpath); + else + { + if (owner != -1) + stats.st_uid = owner; + if (group != -1) + stats.st_gid = group; + + delay_set_stat (dirpath, &stats, invert_permissions); + } + + if (verbose_fmt_string != NULL) + error (0, 0, verbose_fmt_string, dirpath); + + } + else + { + /* We get here if the entire path already exists. */ + + if (!S_ISDIR (stats.st_mode)) + { + error (0, 0, _("`%s' exists but is not a directory"), dirpath); + return 1; + } + + } + + return retval; +} diff --git a/src/mt.c b/src/mt.c new file mode 100644 index 0000000..a25e1bb --- /dev/null +++ b/src/mt.c @@ -0,0 +1,361 @@ +/* mt -- control magnetic tape drive operation + Copyright (C) 1991, 1992, 1995, 2001, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301 USA +*/ + + +/* If -f is not given, the environment variable TAPE is used; + if that is not set, a default device defined in sys/mtio.h is used. + The device must be either a character special file or a remote + tape drive with the form "[user@]system:path". + The default count is 1. Some operations ignore it. + + Exit status: + 0 success + 1 invalid operation or device name + 2 operation failed + + Operations (unique abbreviations are accepted): + eof, weof Write COUNT EOF marks at current position on tape. + fsf Forward space COUNT files. + Tape is positioned on the first block of the file. + bsf Backward space COUNT files. + Tape is positioned on the first block of the file. + fsr Forward space COUNT records. + bsr Backward space COUNT records. + bsfm Backward space COUNT file marks. + Tape is positioned on the beginning-of-the-tape side of + the file mark. + asf Absolute space to file number COUNT. + Equivalent to rewind followed by fsf COUNT. + eom Space to the end of the recorded media on the tape + (for appending files onto tapes). + rewind Rewind the tape. + offline, rewoffl + Rewind the tape and, if applicable, unload the tape. + status Print status information about the tape unit. + retension Rewind the tape, then wind it to the end of the reel, + then rewind it again. + erase Erase the tape. + + David MacKenzie <djm@gnu.ai.mit.edu> */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#ifdef HAVE_SYS_MTIO_H +# ifdef HAVE_SYS_IO_TRIOCTL_H +# include <sys/io/trioctl.h> +# endif +# include <sys/mtio.h> +#endif +#include <sys/file.h> +#include <fcntl.h> +#include <errno.h> +#include <stdbool.h> +#include <argp.h> +#include <argp-version-etc.h> +#include <progname.h> + +#ifdef HAVE_LOCALE_H +# include <locale.h> +#endif +#include <rmt-command.h> + +#include <rmt.h> + +#include <argmatch.h> +#include <paxlib.h> +#include "configmake.h" + +#define MT_EXIT_SUCCESS 0 +#define MT_EXIT_INVOP 1 +#define MT_EXIT_FAILURE 2 + +char const * const opnames[] = +{ + "eof", + "weof", + "fsf", + "bsf", + "fsr", + "bsr", + "rewind", + "offline", + "rewoffl", + "eject", + "status", +#ifdef MTBSFM + "bsfm", +#endif +#ifdef MTEOM + "eom", +#endif +#ifdef MTRETEN + "retension", +#endif +#ifdef MTERASE + "erase", +#endif + "asf", +#ifdef MTFSFM + "fsfm", +#endif +#ifdef MTSEEK + "seek", +#endif + NULL +}; + +#define MTASF 600 /* Random unused number. */ +short operations[] = +{ + MTWEOF, + MTWEOF, + MTFSF, + MTBSF, + MTFSR, + MTBSR, + MTREW, + MTOFFL, + MTOFFL, + MTOFFL, + MTNOP, +#ifdef MTBSFM + MTBSFM, +#endif +#ifdef MTEOM + MTEOM, +#endif +#ifdef MTRETEN + MTRETEN, +#endif +#ifdef MTERASE + MTERASE, +#endif + MTASF, +#ifdef MTFSFM + MTFSFM, +#endif +#ifdef MTSEEK + MTSEEK, +#endif +}; + +ARGMATCH_VERIFY (opnames, operations); + +const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; +static char doc[] = N_("control magnetic tape drive operation"); +const char *program_authors[] = + { + "David MacKenzie", + "Sergey Poznyakoff", + NULL + }; + +enum + { + RSH_COMMAND_OPTION = 256 + }; + +static struct argp_option options[] = { + { "file", 'f', N_("DEVICE"), 0, + N_("use device as the file name of the tape drive to operate on") }, + { "rsh-command", RSH_COMMAND_OPTION, N_("COMMAND"), 0, + N_("use remote COMMAND instead of rsh") }, + { NULL } +}; + +char *tapedev; /* tape device */ +char *rsh_command_option = NULL; /* rsh command */ +short operation; /* operation code */ +int count = 1; /* count */ + +int argcnt = 0; /* number of command line arguments + processed so far */ + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case ARGP_KEY_ARG: + switch (argcnt++) + { + case 0: + operation = XARGMATCH (N_("operation"), arg, opnames, operations); + break; + + case 1: + { + char *p; + long val = strtol (arg, &p, 0); + if (*p || (count = val) != count) + error (MT_EXIT_INVOP, 0, _("invalid count value")); + } + break; + + default: + argp_usage (state); + } + break; + + case ARGP_KEY_FINI: + if (argcnt == 0) + argp_usage (state); + if (tapedev == NULL) + { + tapedev = getenv ("TAPE"); + if (tapedev == NULL) +#ifdef DEFTAPE /* From sys/mtio.h. */ + tapedev = DEFTAPE; +#else + error (MT_EXIT_INVOP, 0, _("no tape device specified")); +#endif + } + break; + + case 'f': + case 't': + tapedev = arg; + break; + + case RSH_COMMAND_OPTION: + rsh_command_option = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, + parse_opt, + N_("operation [count]"), + doc, + NULL, + NULL, + NULL +}; + +void +check_type (char *dev, int desc) +{ + struct stat stats; + + if (_isrmt (desc)) + return; + if (fstat (desc, &stats) == -1) + stat_error (dev); + if ((stats.st_mode & S_IFMT) != S_IFCHR) + error (MT_EXIT_INVOP, 0, _("%s is not a character special file"), dev); +} + +void +perform_operation (char *dev, int desc, short op, int count) +{ + struct mtop control; + + control.mt_op = op; + control.mt_count = count; + if (rmtioctl (desc, MTIOCTOP, (char*)&control) == -1) + error (MT_EXIT_FAILURE, errno, _("%s: rmtioctl failed"), dev); +} + +void +print_status (char *dev, int desc) +{ + struct mtget status; + + if (rmtioctl (desc, MTIOCGET, (char*)&status) == -1) + error (MT_EXIT_FAILURE, errno, _("%s: rmtioctl failed"), dev); + + printf ("drive type = %d\n", (int) status.mt_type); +#if defined(hpux) || defined(__hpux) + printf ("drive status (high) = %d\n", (int) status.mt_dsreg1); + printf ("drive status (low) = %d\n", (int) status.mt_dsreg2); +#else + printf ("drive status = %d\n", (int) status.mt_dsreg); +#endif + printf ("sense key error = %d\n", (int) status.mt_erreg); + printf ("residue count = %d\n", (int) status.mt_resid); +#if !defined(ultrix) && !defined(__ultrix__) && !defined(hpux) && !defined(__hpux) && !defined(__osf__) + printf ("file number = %d\n", (int) status.mt_fileno); + printf ("block number = %d\n", (int) status.mt_blkno); +#endif +} + +void +fatal_exit () +{ + exit (MT_EXIT_INVOP); +} + +int +main (int argc, char **argv) +{ + int tapedesc; + + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + + set_program_name (argv[0]); + argp_version_setup ("mt", program_authors); + argmatch_die = fatal_exit; + argp_err_exit_status = MT_EXIT_INVOP; + if (argp_parse (&argp, argc, argv, ARGP_IN_ORDER, NULL, NULL)) + exit (MT_EXIT_INVOP); + + switch (operation) + { + case MTWEOF: +#ifdef MTERASE + case MTERASE: +#endif + tapedesc = rmtopen (tapedev, O_WRONLY, 0, rsh_command_option); + break; + + default: + tapedesc = rmtopen (tapedev, O_RDONLY, 0, rsh_command_option); + } + + if (tapedesc == -1) + error (MT_EXIT_INVOP, errno, _("%s: rmtopen failed"), tapedev); + check_type (tapedev, tapedesc); + + if (operation == MTASF) + { + perform_operation (tapedev, tapedesc, MTREW, 1); + operation = MTFSF; + } + perform_operation (tapedev, tapedesc, operation, count); + if (operation == MTNOP) + print_status (tapedev, tapedesc); + + if (rmtclose (tapedesc) == -1) + error (MT_EXIT_FAILURE, errno, _("%s: rmtclose failed"), tapedev); + + exit (MT_EXIT_SUCCESS); +} + diff --git a/src/safe-stat.h b/src/safe-stat.h new file mode 100644 index 0000000..3a37970 --- /dev/null +++ b/src/safe-stat.h @@ -0,0 +1 @@ +#define SAFE_STAT(path,pbuf) stat(path,pbuf) diff --git a/src/tar.c b/src/tar.c new file mode 100644 index 0000000..04d1e32 --- /dev/null +++ b/src/tar.c @@ -0,0 +1,480 @@ +/* tar.c - read in write tar headers for cpio + Copyright (C) 1992, 2001, 2004, 2006, 2007, 2010 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "filetypes.h" +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include <rmt.h> +#include "tarhdr.h" + +/* Stash the tar linkname in static storage. */ + +static char * +stash_tar_linkname (char *linkname) +{ + static char hold_tar_linkname[TARLINKNAMESIZE + 1]; + + strncpy (hold_tar_linkname, linkname, TARLINKNAMESIZE); + hold_tar_linkname[TARLINKNAMESIZE] = '\0'; + return hold_tar_linkname; +} + +/* Try to split a long file name into prefix and suffix parts separated + by a slash. Return the length of the prefix (not counting the slash). */ + +static size_t +split_long_name (const char *name, size_t length) +{ + size_t i; + + if (length > TARPREFIXSIZE) + length = TARPREFIXSIZE+2; + for (i = length - 1; i > 0; i--) + if (name[i] == '/') + break; + return i; +} + +/* Stash the tar filename and optional prefix in static storage. */ + +static char * +stash_tar_filename (char *prefix, char *filename) +{ + static char hold_tar_filename[TARNAMESIZE + TARPREFIXSIZE + 2]; + if (prefix == NULL || *prefix == '\0') + { + strncpy (hold_tar_filename, filename, TARNAMESIZE); + hold_tar_filename[TARNAMESIZE] = '\0'; + } + else + { + strncpy (hold_tar_filename, prefix, TARPREFIXSIZE); + hold_tar_filename[TARPREFIXSIZE] = '\0'; + strcat (hold_tar_filename, "/"); + strncat (hold_tar_filename, filename, TARNAMESIZE); + hold_tar_filename[TARPREFIXSIZE + TARNAMESIZE] = '\0'; + } + return hold_tar_filename; +} + +/* Convert a number into a string of octal digits. + Convert long VALUE into a DIGITS-digit field at WHERE, + including a trailing space and room for a NUL. DIGITS==3 means + 1 digit, a space, and room for a NUL. + + We assume the trailing NUL is already there and don't fill it in. + This fact is used by start_header and finish_header, so don't change it! + + This is be equivalent to: + sprintf (where, "%*lo ", digits - 2, value); + except that sprintf fills in the trailing NUL and we don't. */ + +static void +to_oct (register long value, register int digits, register char *where) +{ + --digits; /* Leave the trailing NUL slot alone. */ + + /* Produce the digits -- at least one. */ + do + { + where[--digits] = '0' + (char) (value & 7); /* One octal digit. */ + value >>= 3; + } + while (digits > 0 && value != 0); + + /* Add leading zeroes, if necessary. */ + while (digits > 0) + where[--digits] = '0'; +} + + + +/* Compute and return a checksum for TAR_HDR, + counting the checksum bytes as if they were spaces. */ + +unsigned int +tar_checksum (struct tar_header *tar_hdr) +{ + unsigned int sum = 0; + char *p = (char *) tar_hdr; + char *q = p + TARRECORDSIZE; + int i; + + while (p < tar_hdr->chksum) + sum += *p++ & 0xff; + for (i = 0; i < 8; ++i) + { + sum += ' '; + ++p; + } + while (p < q) + sum += *p++ & 0xff; + return sum; +} + +/* Write out header FILE_HDR, including the file name, to file + descriptor OUT_DES. */ + +void +write_out_tar_header (struct cpio_file_stat *file_hdr, int out_des) +{ + int name_len; + union tar_record tar_rec; + struct tar_header *tar_hdr = (struct tar_header *) &tar_rec; + + memset (&tar_rec, 0, sizeof tar_rec); + + /* process_copy_out must ensure that file_hdr->c_name is short enough, + or we will lose here. */ + + name_len = strlen (file_hdr->c_name); + if (name_len <= TARNAMESIZE) + { + strncpy (tar_hdr->name, file_hdr->c_name, name_len); + } + else + { + /* Fit as much as we can into `name', the rest into `prefix'. */ + int prefix_len = split_long_name (file_hdr->c_name, name_len); + + strncpy (tar_hdr->prefix, file_hdr->c_name, prefix_len); + strncpy (tar_hdr->name, file_hdr->c_name + prefix_len + 1, + name_len - prefix_len - 1); + } + + /* Ustar standard (POSIX.1-1988) requires the mode to contain only 3 octal + digits */ + to_oct (file_hdr->c_mode & MODE_ALL, 8, tar_hdr->mode); + to_oct (file_hdr->c_uid, 8, tar_hdr->uid); + to_oct (file_hdr->c_gid, 8, tar_hdr->gid); + to_oct (file_hdr->c_filesize, 12, tar_hdr->size); + to_oct (file_hdr->c_mtime, 12, tar_hdr->mtime); + + switch (file_hdr->c_mode & CP_IFMT) + { + case CP_IFREG: + if (file_hdr->c_tar_linkname) + { + /* process_copy_out makes sure that c_tar_linkname is shorter + than TARLINKNAMESIZE. */ + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); + tar_hdr->typeflag = LNKTYPE; + to_oct (0, 12, tar_hdr->size); + } + else + tar_hdr->typeflag = REGTYPE; + break; + case CP_IFDIR: + tar_hdr->typeflag = DIRTYPE; + break; + case CP_IFCHR: + tar_hdr->typeflag = CHRTYPE; + break; + case CP_IFBLK: + tar_hdr->typeflag = BLKTYPE; + break; +#ifdef CP_IFIFO + case CP_IFIFO: + tar_hdr->typeflag = FIFOTYPE; + break; +#endif /* CP_IFIFO */ +#ifdef CP_IFLNK + case CP_IFLNK: + tar_hdr->typeflag = SYMTYPE; + /* process_copy_out makes sure that c_tar_linkname is shorter + than TARLINKNAMESIZE. */ + strncpy (tar_hdr->linkname, file_hdr->c_tar_linkname, + TARLINKNAMESIZE); + to_oct (0, 12, tar_hdr->size); + break; +#endif /* CP_IFLNK */ + } + + if (archive_format == arf_ustar) + { + char *name; + + strncpy (tar_hdr->magic, TMAGIC, TMAGLEN); + strncpy (tar_hdr->version, TVERSION, TVERSLEN); + + name = getuser (file_hdr->c_uid); + if (name) + strcpy (tar_hdr->uname, name); + name = getgroup (file_hdr->c_gid); + if (name) + strcpy (tar_hdr->gname, name); + + to_oct (file_hdr->c_rdev_maj, 8, tar_hdr->devmajor); + to_oct (file_hdr->c_rdev_min, 8, tar_hdr->devminor); + } + + to_oct (tar_checksum (tar_hdr), 8, tar_hdr->chksum); + + tape_buffered_write ((char *) &tar_rec, out_des, TARRECORDSIZE); +} + +/* Return nonzero iff all the bytes in BLOCK are NUL. + SIZE is the number of bytes to check in BLOCK; it must be a + multiple of sizeof (long). */ + +int +null_block (long *block, int size) +{ + register long *p = block; + register int i = size / sizeof (long); + + while (i--) + if (*p++) + return 0; + return 1; +} + +/* Read a tar header, including the file name, from file descriptor IN_DES + into FILE_HDR. */ + +void +read_in_tar_header (struct cpio_file_stat *file_hdr, int in_des) +{ + long bytes_skipped = 0; + int warned = false; + union tar_record tar_rec; + struct tar_header *tar_hdr = (struct tar_header *) &tar_rec; + uid_t *uidp; + gid_t *gidp; + + tape_buffered_read ((char *) &tar_rec, in_des, TARRECORDSIZE); + + /* Check for a block of 0's. */ + if (null_block ((long *) &tar_rec, TARRECORDSIZE)) + { +#if 0 + /* Found one block of 512 0's. If the next block is also all 0's + then this is the end of the archive. If not, assume the + previous block was all corruption and continue reading + the archive. */ + /* Commented out because GNU tar sometimes creates archives with + only one block of 0's at the end. This happened for the + cpio 2.0 distribution! */ + tape_buffered_read ((char *) &tar_rec, in_des, TARRECORDSIZE); + if (null_block ((long *) &tar_rec, TARRECORDSIZE)) +#endif + { + file_hdr->c_name = CPIO_TRAILER_NAME; + return; + } +#if 0 + bytes_skipped = TARRECORDSIZE; +#endif + } + + while (1) + { + file_hdr->c_chksum = FROM_OCTAL (tar_hdr->chksum); + + if (file_hdr->c_chksum != tar_checksum (tar_hdr)) + { + /* If the checksum is bad, skip 1 byte and try again. When + we try again we do not look for an EOF record (all zeros), + because when we start skipping bytes in a corrupted archive + the chances are pretty good that we might stumble across + 2 blocks of 512 zeros (that probably is not really the last + record) and it is better to miss the EOF and give the user + a "premature EOF" error than to give up too soon on a corrupted + archive. */ + if (!warned) + { + error (0, 0, _("invalid header: checksum error")); + warned = true; + } + memmove (&tar_rec, ((char *) &tar_rec) + 1, TARRECORDSIZE - 1); + tape_buffered_read (((char *) &tar_rec) + (TARRECORDSIZE - 1), in_des, 1); + ++bytes_skipped; + continue; + } + + if (archive_format != arf_ustar) + file_hdr->c_name = stash_tar_filename (NULL, tar_hdr->name); + else + file_hdr->c_name = stash_tar_filename (tar_hdr->prefix, tar_hdr->name); + file_hdr->c_nlink = 1; + file_hdr->c_mode = FROM_OCTAL (tar_hdr->mode); + file_hdr->c_mode = file_hdr->c_mode & 07777; + /* Debian hack: This version of cpio uses the -n flag also to extract + tar archives using the numeric UID/GID instead of the user/group + names in /etc/passwd and /etc/groups. (98/10/15) -BEM */ + if (archive_format == arf_ustar && !numeric_uid + && (uidp = getuidbyname (tar_hdr->uname))) + file_hdr->c_uid = *uidp; + else + file_hdr->c_uid = FROM_OCTAL (tar_hdr->uid); + + if (archive_format == arf_ustar && !numeric_uid + && (gidp = getgidbyname (tar_hdr->gname))) + file_hdr->c_gid = *gidp; + else + file_hdr->c_gid = FROM_OCTAL (tar_hdr->gid); + file_hdr->c_filesize = FROM_OCTAL (tar_hdr->size); + file_hdr->c_mtime = FROM_OCTAL (tar_hdr->mtime); + file_hdr->c_rdev_maj = FROM_OCTAL (tar_hdr->devmajor); + file_hdr->c_rdev_min = FROM_OCTAL (tar_hdr->devminor); + file_hdr->c_tar_linkname = NULL; + + switch (tar_hdr->typeflag) + { + case REGTYPE: + case CONTTYPE: /* For now, punt. */ + default: + file_hdr->c_mode |= CP_IFREG; + break; + case DIRTYPE: + file_hdr->c_mode |= CP_IFDIR; + break; + case CHRTYPE: + file_hdr->c_mode |= CP_IFCHR; + /* If a POSIX tar header has a valid linkname it's always supposed + to set typeflag to be LNKTYPE. System V.4 tar seems to + be broken, and for device files with multiple links it + puts the name of the link into linkname, but leaves typeflag + as CHRTYPE, BLKTYPE, FIFOTYPE, etc. */ + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + + /* Does POSIX say that the filesize must be 0 for devices? We + assume so, but HPUX's POSIX tar sets it to be 1 which causes + us problems (when reading an archive we assume we can always + skip to the next file by skipping filesize bytes). For + now at least, it's easier to clear filesize for devices, + rather than check everywhere we skip in copyin.c. */ + file_hdr->c_filesize = 0; + break; + case BLKTYPE: + file_hdr->c_mode |= CP_IFBLK; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; +#ifdef CP_IFIFO + case FIFOTYPE: + file_hdr->c_mode |= CP_IFIFO; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; +#endif + case SYMTYPE: +#ifdef CP_IFLNK + file_hdr->c_mode |= CP_IFLNK; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; + /* Else fall through. */ +#endif + case LNKTYPE: + file_hdr->c_mode |= CP_IFREG; + file_hdr->c_tar_linkname = stash_tar_linkname (tar_hdr->linkname); + file_hdr->c_filesize = 0; + break; + + case AREGTYPE: + /* Old tar format; if the last char in filename is '/' then it is + a directory, otherwise it's a regular file. */ + if (file_hdr->c_name[strlen (file_hdr->c_name) - 1] == '/') + file_hdr->c_mode |= CP_IFDIR; + else + file_hdr->c_mode |= CP_IFREG; + break; + } + break; + } + if (bytes_skipped > 0) + warn_junk_bytes (bytes_skipped); +} + +/* Return + 2 if BUF is a valid POSIX tar header (the checksum is correct + and it has the "ustar" magic string), + 1 if BUF is a valid old tar header (the checksum is correct), + 0 otherwise. */ + +int +is_tar_header (char *buf) +{ + struct tar_header *tar_hdr = (struct tar_header *) buf; + unsigned long chksum; + + chksum = FROM_OCTAL (tar_hdr->chksum); + + if (chksum != tar_checksum (tar_hdr)) + return 0; + + /* GNU tar 1.10 and previous set the magic field to be "ustar " instead + of "ustar\0". Only look at the first 5 characters of the magic + field so we can recognize old GNU tar ustar archives. */ + if (!strncmp (tar_hdr->magic, TMAGIC, TMAGLEN - 1)) + return 2; + return 1; +} + +/* Return true if the filename is too long to fit in a tar header. + For old tar headers, if the filename's length is less than or equal + to 100 then it will fit, otherwise it will not. For POSIX tar headers, + if the filename's length is less than or equal to 100 then it + will definitely fit, and if it is greater than 256 then it + will definitely not fit. If the length is between 100 and 256, + then the filename will fit only if it is possible to break it + into a 155 character "prefix" and 100 character "name". There + must be a slash between the "prefix" and the "name", although + the slash is not stored or counted in either the "prefix" or + the "name", and there must be at least one character in both + the "prefix" and the "name". If it is not possible to break down + the filename like this then it will not fit. */ + +int +is_tar_filename_too_long (char *name) +{ + int whole_name_len; + int prefix_name_len; + + whole_name_len = strlen (name); + if (whole_name_len <= TARNAMESIZE) + return false; + + if (archive_format != arf_ustar) + return true; + + if (whole_name_len > TARNAMESIZE + TARPREFIXSIZE + 1) + return true; + + /* See whether we can split up the name into acceptably-sized + `prefix' and `name' (`p') pieces. */ + prefix_name_len = split_long_name (name, whole_name_len); + + /* Interestingly, a name consisting of a slash followed by + TARNAMESIZE characters can't be stored, because the prefix + would be empty, and thus ignored. */ + if (prefix_name_len == 0 + || whole_name_len - prefix_name_len - 1 > TARNAMESIZE) + return true; + + return false; +} diff --git a/src/tar.h b/src/tar.h new file mode 100644 index 0000000..0047d6c --- /dev/null +++ b/src/tar.h @@ -0,0 +1,112 @@ +/* Extended tar format from POSIX.1. + Copyright (C) 1992, 2007, 2010 Free Software Foundation, Inc. + Written by David J. MacKenzie. + +This file is part of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 3 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General +Public License along with this library; see the file COPYING.LIB. +If not, write to the Free Software Foundation, Inc., 51 Franklin +Street, Fifth Floor, Boston, MA 02110-1301 USA. */ + +#ifndef _TAR_H + +#define _TAR_H 1 + + +/* A tar archive consists of 512-byte blocks. + Each file in the archive has a header block followed by 0+ data blocks. + Two blocks of NUL bytes indicate the end of the archive. */ + +/* The fields of header blocks: + All strings are stored as ISO 646 (approximately ASCII) strings. + + Fields are numeric unless otherwise noted below; numbers are ISO 646 + representations of octal numbers, with leading zeros as needed. + + linkname is only valid when typeflag==LNKTYPE. It doesn't use prefix; + files that are links to pathnames >100 chars long can not be stored + in a tar archive. + + If typeflag=={LNKTYPE,SYMTYPE,DIRTYPE} then size must be 0. + + devmajor and devminor are only valid for typeflag=={BLKTYPE,CHRTYPE}. + + chksum contains the sum of all 512 bytes in the header block, + treating each byte as an 8-bit unsigned value and treating the + 8 bytes of chksum as blank characters. + + uname and gname are used in preference to uid and gid, if those + names exist locally. + + Field Name Byte Offset Length in Bytes Field Type + name 0 100 NUL-terminated if NUL fits + mode 100 8 + uid 108 8 + gid 116 8 + size 124 12 + mtime 136 12 + chksum 148 8 + typeflag 156 1 see below + linkname 157 100 NUL-terminated if NUL fits + magic 257 6 must be TMAGIC (NUL term.) + version 263 2 must be TVERSION + uname 265 32 NUL-terminated + gname 297 32 NUL-terminated + devmajor 329 8 + devminor 337 8 + prefix 345 155 NUL-terminated if NUL fits + + If the first character of prefix is '\0', the file name is name; + otherwise, it is prefix/name. Files whose pathnames don't fit in that + length can not be stored in a tar archive. */ + +/* The bits in mode: */ +#define TSUID 04000 +#define TSGID 02000 +#define TSVTX 01000 +#define TUREAD 00400 +#define TUWRITE 00200 +#define TUEXEC 00100 +#define TGREAD 00040 +#define TGWRITE 00020 +#define TGEXEC 00010 +#define TOREAD 00004 +#define TOWRITE 00002 +#define TOEXEC 00001 + +/* The values for typeflag: + Values 'A'-'Z' are reserved for custom implementations. + All other values are reserved for future POSIX.1 revisions. */ + +#define REGTYPE '0' /* Regular file (preferred code). */ +#define AREGTYPE '\0' /* Regular file (alternate code). */ +#define LNKTYPE '1' /* Hard link. */ +#define SYMTYPE '2' /* Symbolic link (hard if not supported). */ +#define CHRTYPE '3' /* Character special. */ +#define BLKTYPE '4' /* Block special. */ +#define DIRTYPE '5' /* Directory. */ +#define FIFOTYPE '6' /* Named pipe. */ +#define CONTTYPE '7' /* Contiguous file */ + /* (regular file if not supported). */ + +/* Contents of magic field and its length. */ +#define TMAGIC "ustar" +#define TMAGLEN 6 + +/* Contents of the version field and its length. */ +#define TVERSION "00" +#define TVERSLEN 2 + + +#endif /* tar.h */ diff --git a/src/tarhdr.h b/src/tarhdr.h new file mode 100644 index 0000000..59d0060 --- /dev/null +++ b/src/tarhdr.h @@ -0,0 +1,63 @@ +/* Extended tar header from POSIX.1. + Copyright (C) 1992, 2007, 2010 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#ifndef _TARHDR_H + +#define _TARHDR_H 1 + +#include <tar.h> + +/* Size of `name' field. */ +#define TARNAMESIZE 100 + +/* Size of `linkname' field. */ +#define TARLINKNAMESIZE 100 + +/* Size of `prefix' field. */ +#define TARPREFIXSIZE 155 + +/* Size of entire tar header. */ +#define TARRECORDSIZE 512 + +struct tar_header +{ + char name[TARNAMESIZE]; + char mode[8]; + char uid[8]; + char gid[8]; + char size[12]; + char mtime[12]; + char chksum[8]; + char typeflag; + char linkname[TARLINKNAMESIZE]; + char magic[6]; + char version[2]; + char uname[32]; + char gname[32]; + char devmajor[8]; + char devminor[8]; + char prefix[TARPREFIXSIZE]; +}; + +union tar_record +{ + struct tar_header header; + char buffer[TARRECORDSIZE]; +}; + +#endif /* tarhdr.h */ diff --git a/src/userspec.c b/src/userspec.c new file mode 100644 index 0000000..8cffd65 --- /dev/null +++ b/src/userspec.c @@ -0,0 +1,233 @@ +/* userspec.c -- Parse a user and group string. + Copyright (C) 1989, 1990, 1991, 1992, 2001, 2004, 2005, 2007, 2010 + Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +/* Written by David MacKenzie <djm@gnu.ai.mit.edu>. */ + +#include <system.h> +#include <alloca.h> +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> + +#ifndef HAVE_ENDPWENT +# define endpwent() +#endif +#ifndef HAVE_ENDGRENT +# define endgrent() +#endif + +/* Perform the equivalent of the statement `dest = strdup (src);', + but obtaining storage via alloca instead of from the heap. */ + +#define V_STRDUP(dest, src) \ + do \ + { \ + int _len = strlen ((src)); \ + (dest) = (char *) alloca (_len + 1); \ + strcpy (dest, src); \ + } \ + while (0) + +/* Return nonzero if STR represents an unsigned decimal integer, + otherwise return 0. */ + +static int +isnumber_p (const char *str) +{ + for (; *str; str++) + if (!isdigit (*str)) + return 0; + return 1; +} + +/* Extract from NAME, which has the form "[user][:.][group]", + a USERNAME, UID U, GROUPNAME, and GID G. + Either user or group, or both, must be present. + If the group is omitted but the ":" or "." separator is given, + use the given user's login group. + + USERNAME and GROUPNAME will be in newly malloc'd memory. + Either one might be NULL instead, indicating that it was not + given and the corresponding numeric ID was left unchanged. + + Return NULL if successful, a static error message string if not. */ + +const char * +parse_user_spec (const char *spec_arg, uid_t *uid, gid_t *gid, + char **username_arg, char **groupname_arg) +{ + static const char *tired = "virtual memory exhausted"; + const char *error_msg; + char *spec; /* A copy we can write on. */ + struct passwd *pwd; + struct group *grp; + char *g, *u, *separator; + char *groupname; + + error_msg = NULL; + *username_arg = *groupname_arg = NULL; + groupname = NULL; + + V_STRDUP (spec, spec_arg); + + /* Find the separator if there is one. */ + separator = strchr (spec, ':'); + if (separator == NULL) + separator = strchr (spec, '.'); + + /* Replace separator with a NUL. */ + if (separator != NULL) + *separator = '\0'; + + /* Set U and G to non-zero length strings corresponding to user and + group specifiers or to NULL. */ + u = (*spec == '\0' ? NULL : spec); + + g = (separator == NULL || *(separator + 1) == '\0' + ? NULL + : separator + 1); + + if (u == NULL && g == NULL) + return "can not omit both user and group"; + + if (u != NULL) + { + pwd = getpwnam (u); + if (pwd == NULL) + { + + if (!isnumber_p (u)) + error_msg = _("invalid user"); + else + { + int use_login_group; + use_login_group = (separator != NULL && g == NULL); + if (use_login_group) + error_msg = _("cannot get the login group of a numeric UID"); + else + *uid = atoi (u); + } + } + else + { + *uid = pwd->pw_uid; + if (g == NULL && separator != NULL) + { + /* A separator was given, but a group was not specified, + so get the login group. */ + *gid = pwd->pw_gid; + grp = getgrgid (pwd->pw_gid); + if (grp == NULL) + { + /* This is enough room to hold the unsigned decimal + representation of any 32-bit quantity and the trailing + zero byte. */ + char uint_buf[21]; + sprintf (uint_buf, "%u", (unsigned) (pwd->pw_gid)); + V_STRDUP (groupname, uint_buf); + } + else + { + V_STRDUP (groupname, grp->gr_name); + } + endgrent (); + } + } + endpwent (); + } + + if (g != NULL && error_msg == NULL) + { + /* Explicit group. */ + grp = getgrnam (g); + if (grp == NULL) + { + if (!isnumber_p (g)) + error_msg = _("invalid group"); + else + *gid = atoi (g); + } + else + *gid = grp->gr_gid; + endgrent (); /* Save a file descriptor. */ + + if (error_msg == NULL) + V_STRDUP (groupname, g); + } + + if (error_msg == NULL) + { + if (u != NULL) + { + *username_arg = strdup (u); + if (*username_arg == NULL) + error_msg = tired; + } + + if (groupname != NULL && error_msg == NULL) + { + *groupname_arg = strdup (groupname); + if (*groupname_arg == NULL) + { + if (*username_arg != NULL) + { + free (*username_arg); + *username_arg = NULL; + } + error_msg = tired; + } + } + } + + return error_msg; +} + +#ifdef TEST + +#define NULL_CHECK(s) ((s) == NULL ? "(null)" : (s)) + +int +main (int argc, char **argv) +{ + int i; + + for (i = 1; i < argc; i++) + { + const char *e; + char *username, *groupname; + uid_t uid; + gid_t gid; + char *tmp; + + tmp = strdup (argv[i]); + e = parse_user_spec (tmp, &uid, &gid, &username, &groupname); + free (tmp); + printf ("%s: %u %u %s %s %s\n", + argv[i], + (unsigned int) uid, + (unsigned int) gid, + NULL_CHECK (username), + NULL_CHECK (groupname), + NULL_CHECK (e)); + } + + exit (0); +} + +#endif diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..00953d5 --- /dev/null +++ b/src/util.c @@ -0,0 +1,1620 @@ +/* util.c - Several utility routines for cpio. + Copyright (C) 1990, 1991, 1992, 2001, 2004, 2006, 2007, 2010 Free + Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public + License along with this program; if not, write to the Free + Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301 USA. */ + +#include <system.h> + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include "cpiohdr.h" +#include "dstring.h" +#include "extern.h" +#include <paxlib.h> +#include "filetypes.h" +#include <safe-read.h> +#include <full-write.h> +#include <rmt.h> +#include <hash.h> +#include <utimens.h> + +#ifdef HAVE_SYS_IOCTL_H +# include <sys/ioctl.h> +#endif + +#ifdef HAVE_SYS_MTIO_H +# ifdef HAVE_SYS_IO_TRIOCTL_H +# include <sys/io/trioctl.h> +# endif +# include <sys/mtio.h> +#endif + +#if !HAVE_DECL_ERRNO +extern int errno; +#endif + +/* Write `output_size' bytes of `output_buffer' to file + descriptor OUT_DES and reset `output_size' and `out_buff'. */ + +void +tape_empty_output_buffer (int out_des) +{ + int bytes_written; + +#ifdef BROKEN_LONG_TAPE_DRIVER + static long output_bytes_before_lseek = 0; + + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 2Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (output_is_special + && ( (output_bytes_before_lseek += output_size) >= 1073741824L) ) + { + lseek(out_des, 0L, SEEK_SET); + output_bytes_before_lseek = 0; + } +#endif + + bytes_written = rmtwrite (out_des, output_buffer, output_size); + if (bytes_written != output_size) + { + int rest_bytes_written; + int rest_output_size; + + if (output_is_special + && (bytes_written >= 0 + || (bytes_written < 0 + && (errno == ENOSPC || errno == EIO || errno == ENXIO)))) + { + get_next_reel (out_des); + if (bytes_written > 0) + rest_output_size = output_size - bytes_written; + else + rest_output_size = output_size; + rest_bytes_written = rmtwrite (out_des, output_buffer, + rest_output_size); + if (rest_bytes_written != rest_output_size) + error (1, errno, _("write error")); + } + else + error (1, errno, _("write error")); + } + output_bytes += output_size; + out_buff = output_buffer; + output_size = 0; +} + +static int sparse_write (int fildes, char *buf, unsigned int nbyte); + +/* Write `output_size' bytes of `output_buffer' to file + descriptor OUT_DES and reset `output_size' and `out_buff'. + If `swapping_halfwords' or `swapping_bytes' is set, + do the appropriate swapping first. Our callers have + to make sure to only set these flags if `output_size' + is appropriate (a multiple of 4 for `swapping_halfwords', + 2 for `swapping_bytes'). The fact that DISK_IO_BLOCK_SIZE + must always be a multiple of 4 helps us (and our callers) + insure this. */ + +void +disk_empty_output_buffer (int out_des) +{ + int bytes_written; + + if (swapping_halfwords || swapping_bytes) + { + if (swapping_halfwords) + { + int complete_words; + complete_words = output_size / 4; + swahw_array (output_buffer, complete_words); + if (swapping_bytes) + swab_array (output_buffer, 2 * complete_words); + } + else + { + int complete_halfwords; + complete_halfwords = output_size /2; + swab_array (output_buffer, complete_halfwords); + } + } + + if (sparse_flag) + bytes_written = sparse_write (out_des, output_buffer, output_size); + else + bytes_written = write (out_des, output_buffer, output_size); + + if (bytes_written != output_size) + { + error (1, errno, _("write error")); + } + output_bytes += output_size; + out_buff = output_buffer; + output_size = 0; +} + +/* Exchange the halfwords of each element of the array of COUNT longs + starting at PTR. PTR does not have to be aligned at a word + boundary. */ + +void +swahw_array (char *ptr, int count) +{ + char tmp; + + for (; count > 0; --count) + { + tmp = *ptr; + *ptr = *(ptr + 2); + *(ptr + 2) = tmp; + ++ptr; + tmp = *ptr; + *ptr = *(ptr + 2); + *(ptr + 2) = tmp; + ptr += 3; + } +} + +/* Read at most NUM_BYTES or `io_block_size' bytes, whichever is smaller, + into the start of `input_buffer' from file descriptor IN_DES. + Set `input_size' to the number of bytes read and reset `in_buff'. + Exit with an error if end of file is reached. */ + +#ifdef BROKEN_LONG_TAPE_DRIVER +static long input_bytes_before_lseek = 0; +#endif + +static void +tape_fill_input_buffer (int in_des, int num_bytes) +{ +#ifdef BROKEN_LONG_TAPE_DRIVER + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (input_is_special + && ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) ) + { + lseek(in_des, 0L, SEEK_SET); + input_bytes_before_lseek = 0; + } +#endif + in_buff = input_buffer; + num_bytes = (num_bytes < io_block_size) ? num_bytes : io_block_size; + input_size = rmtread (in_des, input_buffer, num_bytes); + if (input_size == 0 && input_is_special) + { + get_next_reel (in_des); + input_size = rmtread (in_des, input_buffer, num_bytes); + } + if (input_size < 0) + error (1, errno, _("read error")); + if (input_size == 0) + { + error (0, 0, _("premature end of file")); + exit (1); + } + input_bytes += input_size; +} + +/* Read at most NUM_BYTES or `DISK_IO_BLOCK_SIZE' bytes, whichever is smaller, + into the start of `input_buffer' from file descriptor IN_DES. + Set `input_size' to the number of bytes read and reset `in_buff'. + Exit with an error if end of file is reached. */ + +static int +disk_fill_input_buffer (int in_des, off_t num_bytes) +{ + in_buff = input_buffer; + num_bytes = (num_bytes < DISK_IO_BLOCK_SIZE) ? num_bytes : DISK_IO_BLOCK_SIZE; + input_size = read (in_des, input_buffer, num_bytes); + if (input_size < 0) + { + input_size = 0; + return (-1); + } + else if (input_size == 0) + return (1); + input_bytes += input_size; + return (0); +} + +/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full. + When `out_buff' fills up, flush it to file descriptor OUT_DES. */ + +void +tape_buffered_write (char *in_buf, int out_des, off_t num_bytes) +{ + off_t bytes_left = num_bytes; /* Bytes needing to be copied. */ + off_t space_left; /* Room left in output buffer. */ + + while (bytes_left > 0) + { + space_left = io_block_size - output_size; + if (space_left == 0) + tape_empty_output_buffer (out_des); + else + { + if (bytes_left < space_left) + space_left = bytes_left; + memcpy (out_buff, in_buf, (unsigned) space_left); + out_buff += space_left; + output_size += space_left; + in_buf += space_left; + bytes_left -= space_left; + } + } +} + +/* Copy NUM_BYTES of buffer IN_BUF to `out_buff', which may be partly full. + When `out_buff' fills up, flush it to file descriptor OUT_DES. */ + +void +disk_buffered_write (char *in_buf, int out_des, off_t num_bytes) +{ + off_t bytes_left = num_bytes; /* Bytes needing to be copied. */ + off_t space_left; /* Room left in output buffer. */ + + while (bytes_left > 0) + { + space_left = DISK_IO_BLOCK_SIZE - output_size; + if (space_left == 0) + disk_empty_output_buffer (out_des); + else + { + if (bytes_left < space_left) + space_left = bytes_left; + memcpy (out_buff, in_buf, (unsigned) space_left); + out_buff += space_left; + output_size += space_left; + in_buf += space_left; + bytes_left -= space_left; + } + } +} + +/* Copy NUM_BYTES of buffer `in_buff' into IN_BUF. + `in_buff' may be partly full. + When `in_buff' is exhausted, refill it from file descriptor IN_DES. */ + +void +tape_buffered_read (char *in_buf, int in_des, off_t num_bytes) +{ + off_t bytes_left = num_bytes; /* Bytes needing to be copied. */ + off_t space_left; /* Bytes to copy from input buffer. */ + + while (bytes_left > 0) + { + if (input_size == 0) + tape_fill_input_buffer (in_des, io_block_size); + if (bytes_left < input_size) + space_left = bytes_left; + else + space_left = input_size; + memcpy (in_buf, in_buff, (unsigned) space_left); + in_buff += space_left; + in_buf += space_left; + input_size -= space_left; + bytes_left -= space_left; + } +} + +/* Copy the the next NUM_BYTES bytes of `input_buffer' into PEEK_BUF. + If NUM_BYTES bytes are not available, read the next `io_block_size' bytes + into the end of `input_buffer' and update `input_size'. + + Return the number of bytes copied into PEEK_BUF. + If the number of bytes returned is less than NUM_BYTES, + then EOF has been reached. */ + +int +tape_buffered_peek (char *peek_buf, int in_des, int num_bytes) +{ + long tmp_input_size; + long got_bytes; + char *append_buf; + +#ifdef BROKEN_LONG_TAPE_DRIVER + /* Some tape drivers seem to have a signed internal seek pointer and + they lose if it overflows and becomes negative (e.g. when writing + tapes > 4Gb). Doing an lseek (des, 0, SEEK_SET) seems to reset the + seek pointer and prevent it from overflowing. */ + if (input_is_special + && ( (input_bytes_before_lseek += num_bytes) >= 1073741824L) ) + { + lseek(in_des, 0L, SEEK_SET); + input_bytes_before_lseek = 0; + } +#endif + + while (input_size < num_bytes) + { + append_buf = in_buff + input_size; + if ( (append_buf - input_buffer) >= input_buffer_size) + { + /* We can keep up to 2 "blocks" (either the physical block size + or 512 bytes(the size of a tar record), which ever is + larger) in the input buffer when we are peeking. We + assume that our caller will never be interested in peeking + ahead at more than 512 bytes, so we know that by the time + we need a 3rd "block" in the buffer we can throw away the + first block to make room. */ + int half; + half = input_buffer_size / 2; + memmove (input_buffer, input_buffer + half, half); + in_buff = in_buff - half; + append_buf = append_buf - half; + } + tmp_input_size = rmtread (in_des, append_buf, io_block_size); + if (tmp_input_size == 0) + { + if (input_is_special) + { + get_next_reel (in_des); + tmp_input_size = rmtread (in_des, append_buf, io_block_size); + } + else + break; + } + if (tmp_input_size < 0) + error (1, errno, _("read error")); + input_bytes += tmp_input_size; + input_size += tmp_input_size; + } + if (num_bytes <= input_size) + got_bytes = num_bytes; + else + got_bytes = input_size; + memcpy (peek_buf, in_buff, (unsigned) got_bytes); + return got_bytes; +} + +/* Skip the next NUM_BYTES bytes of file descriptor IN_DES. */ + +void +tape_toss_input (int in_des, off_t num_bytes) +{ + off_t bytes_left = num_bytes; /* Bytes needing to be copied. */ + off_t space_left; /* Bytes to copy from input buffer. */ + + while (bytes_left > 0) + { + if (input_size == 0) + tape_fill_input_buffer (in_des, io_block_size); + if (bytes_left < input_size) + space_left = bytes_left; + else + space_left = input_size; + + if (crc_i_flag && only_verify_crc_flag) + { + int k; + for (k = 0; k < space_left; ++k) + crc += in_buff[k] & 0xff; + } + + in_buff += space_left; + input_size -= space_left; + bytes_left -= space_left; + } +} + +void +write_nuls_to_file (off_t num_bytes, int out_des, + void (*writer) (char *in_buf, int out_des, off_t num_bytes)) +{ + off_t blocks; + off_t extra_bytes; + off_t i; + static char zeros_512[512]; + + blocks = num_bytes / sizeof zeros_512; + extra_bytes = num_bytes % sizeof zeros_512; + for (i = 0; i < blocks; ++i) + writer (zeros_512, out_des, sizeof zeros_512); + if (extra_bytes) + writer (zeros_512, out_des, extra_bytes); +} + +/* Copy a file using the input and output buffers, which may start out + partly full. After the copy, the files are not closed nor the last + block flushed to output, and the input buffer may still be partly + full. If `crc_i_flag' is set, add each byte to `crc'. + IN_DES is the file descriptor for input; + OUT_DES is the file descriptor for output; + NUM_BYTES is the number of bytes to copy. */ + +void +copy_files_tape_to_disk (int in_des, int out_des, off_t num_bytes) +{ + off_t size; + off_t k; + + while (num_bytes > 0) + { + if (input_size == 0) + tape_fill_input_buffer (in_des, io_block_size); + size = (input_size < num_bytes) ? input_size : num_bytes; + if (crc_i_flag) + { + for (k = 0; k < size; ++k) + crc += in_buff[k] & 0xff; + } + disk_buffered_write (in_buff, out_des, size); + num_bytes -= size; + input_size -= size; + in_buff += size; + } +} +/* Copy a file using the input and output buffers, which may start out + partly full. After the copy, the files are not closed nor the last + block flushed to output, and the input buffer may still be partly + full. If `crc_i_flag' is set, add each byte to `crc'. + IN_DES is the file descriptor for input; + OUT_DES is the file descriptor for output; + NUM_BYTES is the number of bytes to copy. */ + +void +copy_files_disk_to_tape (int in_des, int out_des, off_t num_bytes, + char *filename) +{ + off_t size; + off_t k; + int rc; + off_t original_num_bytes; + + original_num_bytes = num_bytes; + + while (num_bytes > 0) + { + if (input_size == 0) + if (rc = disk_fill_input_buffer (in_des, + num_bytes < DISK_IO_BLOCK_SIZE ? + num_bytes : DISK_IO_BLOCK_SIZE)) + { + if (rc > 0) + { + char buf[UINTMAX_STRSIZE_BOUND]; + error (0, 0, + ngettext ("File %s shrunk by %s byte, padding with zeros", + "File %s shrunk by %s bytes, padding with zeros", + num_bytes), + filename, STRINGIFY_BIGINT (num_bytes, buf)); + } + else + error (0, 0, _("Read error at byte %lld in file %s, padding with zeros"), + original_num_bytes - num_bytes, filename); + write_nuls_to_file (num_bytes, out_des, tape_buffered_write); + break; + } + size = (input_size < num_bytes) ? input_size : num_bytes; + if (crc_i_flag) + { + for (k = 0; k < size; ++k) + crc += in_buff[k] & 0xff; + } + tape_buffered_write (in_buff, out_des, size); + num_bytes -= size; + input_size -= size; + in_buff += size; + } +} +/* Copy a file using the input and output buffers, which may start out + partly full. After the copy, the files are not closed nor the last + block flushed to output, and the input buffer may still be partly + full. If `crc_i_flag' is set, add each byte to `crc'. + IN_DES is the file descriptor for input; + OUT_DES is the file descriptor for output; + NUM_BYTES is the number of bytes to copy. */ + +void +copy_files_disk_to_disk (int in_des, int out_des, off_t num_bytes, + char *filename) +{ + off_t size; + off_t k; + off_t original_num_bytes; + int rc; + + original_num_bytes = num_bytes; + while (num_bytes > 0) + { + if (input_size == 0) + if (rc = disk_fill_input_buffer (in_des, num_bytes)) + { + if (rc > 0) + { + char buf[UINTMAX_STRSIZE_BOUND]; + error (0, 0, + ngettext ("File %s shrunk by %s byte, padding with zeros", + "File %s shrunk by %s bytes, padding with zeros", + num_bytes), + filename, STRINGIFY_BIGINT (num_bytes, buf)); + } + else + error (0, 0, _("Read error at byte %lld in file %s, padding with zeros"), + original_num_bytes - num_bytes, filename); + write_nuls_to_file (num_bytes, out_des, disk_buffered_write); + break; + } + size = (input_size < num_bytes) ? input_size : num_bytes; + if (crc_i_flag) + { + for (k = 0; k < size; ++k) + crc += in_buff[k] & 0xff; + } + disk_buffered_write (in_buff, out_des, size); + num_bytes -= size; + input_size -= size; + in_buff += size; + } +} + +/* Warn if file changed while it was being copied. */ + +void +warn_if_file_changed (char *file_name, off_t old_file_size, + time_t old_file_mtime) +{ + struct stat new_file_stat; + if ((*xstat) (file_name, &new_file_stat) < 0) + { + stat_error (file_name); + return; + } + + /* Only check growth, shrinkage detected in copy_files_disk_to_{disk,tape}() + */ + if (new_file_stat.st_size > old_file_size) + error (0, 0, + ngettext ("File %s grew, %"PRIuMAX" new byte not copied", + "File %s grew, %"PRIuMAX" new bytes not copied", + (long)(new_file_stat.st_size - old_file_size)), + file_name, (uintmax_t) (new_file_stat.st_size - old_file_size)); + + else if (new_file_stat.st_mtime != old_file_mtime) + error (0, 0, _("File %s was modified while being copied"), file_name); +} + +/* Create all directories up to but not including the last part of NAME. + Do not destroy any nondirectories while creating directories. */ + +void +create_all_directories (char *name) +{ + char *dir; + int mode; +#ifdef HPUX_CDF + int cdf; +#endif + + dir = dir_name (name); + mode = 0700; +#ifdef HPUX_CDF + cdf = islastparentcdf (name); + if (cdf) + { + dir [strlen (dir) - 1] = '\0'; /* remove final + */ + mode = 04700; + } + +#endif + + if (dir == NULL) + error (2, 0, _("virtual memory exhausted")); + + if (dir[0] != '.' || dir[1] != '\0') + { + const char *fmt; + if (warn_option & CPIO_WARN_INTERDIR) + fmt = _("Creating intermediate directory `%s'"); + else + fmt = NULL; + make_path (dir, -1, -1, fmt); + } + + free (dir); +} + +/* Prepare to append to an archive. We have been in + process_copy_in, keeping track of the position where + the last header started in `last_header_start'. Now we + have the starting position of the last header (the TRAILER!!! + header, or blank record for tar archives) and we want to start + writing (appending) over the last header. The last header may + be in the middle of a block, so to keep the buffering in sync + we lseek back to the start of the block, read everything up + to but not including the last header, lseek back to the start + of the block, and then do a copy_buf_out of what we read. + Actually, we probably don't have to worry so much about keeping the + buffering perfect since you can only append to archives that + are disk files. */ + +void +prepare_append (int out_file_des) +{ + int start_of_header; + int start_of_block; + int useful_bytes_in_block; + char *tmp_buf; + + start_of_header = last_header_start; + /* Figure out how many bytes we will rewrite, and where they start. */ + useful_bytes_in_block = start_of_header % io_block_size; + start_of_block = start_of_header - useful_bytes_in_block; + + if (lseek (out_file_des, start_of_block, SEEK_SET) < 0) + error (1, errno, _("cannot seek on output")); + if (useful_bytes_in_block > 0) + { + tmp_buf = (char *) xmalloc (useful_bytes_in_block); + read (out_file_des, tmp_buf, useful_bytes_in_block); + if (lseek (out_file_des, start_of_block, SEEK_SET) < 0) + error (1, errno, _("cannot seek on output")); + /* fix juo -- is this copy_tape_buf_out? or copy_disk? */ + tape_buffered_write (tmp_buf, out_file_des, useful_bytes_in_block); + free (tmp_buf); + } + + /* We are done reading the archive, so clear these since they + will now be used for reading in files that we are appending + to the archive. */ + input_size = 0; + input_bytes = 0; + in_buff = input_buffer; +} + +/* Support for remembering inodes with multiple links. Used in the + "copy in" and "copy pass" modes for making links instead of copying + the file. */ + +struct inode_val +{ + ino_t inode; + unsigned long major_num; + unsigned long minor_num; + char *file_name; +}; + +/* Inode hash table. Allocated by first call to add_inode. */ +static Hash_table *hash_table = NULL; + +static size_t +inode_val_hasher (const void *val, size_t n_buckets) +{ + const struct inode_val *ival = val; + return ival->inode % n_buckets; +} + +static bool +inode_val_compare (const void *val1, const void *val2) +{ + const struct inode_val *ival1 = val1; + const struct inode_val *ival2 = val2; + return ival1->inode == ival2->inode + && ival1->major_num == ival2->major_num + && ival1->minor_num == ival2->minor_num; +} + +char * +find_inode_file (ino_t node_num, unsigned long major_num, + unsigned long minor_num) +{ + struct inode_val sample; + struct inode_val *ival; + + if (!hash_table) + return NULL; + + sample.inode = node_num; + sample.major_num = major_num; + sample.minor_num = minor_num; + ival = hash_lookup (hash_table, &sample); + return ival ? ival->file_name : NULL; +} + +/* Associate FILE_NAME with the inode NODE_NUM. (Insert into hash table.) */ + +void +add_inode (ino_t node_num, char *file_name, unsigned long major_num, + unsigned long minor_num) +{ + struct inode_val *temp; + struct inode_val *e; + + /* Create new inode record. */ + temp = (struct inode_val *) xmalloc (sizeof (struct inode_val)); + temp->inode = node_num; + temp->major_num = major_num; + temp->minor_num = minor_num; + temp->file_name = xstrdup (file_name); + + if (!((hash_table + || (hash_table = hash_initialize (0, 0, inode_val_hasher, + inode_val_compare, 0))) + && (e = hash_insert (hash_table, temp)))) + xalloc_die (); + /* FIXME: e is not used */ +} + + +/* Open FILE in the mode specified by the command line options + and return an open file descriptor for it, + or -1 if it can't be opened. */ + +int +open_archive (char *file) +{ + int fd; + void (*copy_in) (); /* Workaround for pcc bug. */ + + copy_in = process_copy_in; + + if (copy_function == copy_in) + fd = rmtopen (file, O_RDONLY | O_BINARY, MODE_RW, rsh_command_option); + else + { + if (!append_flag) + fd = rmtopen (file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, MODE_RW, + rsh_command_option); + else + fd = rmtopen (file, O_RDWR | O_BINARY, MODE_RW, rsh_command_option); + } + + return fd; +} + +/* Attempt to rewind the tape drive on file descriptor TAPE_DES + and take it offline. */ + +void +tape_offline (int tape_des) +{ +#if defined(MTIOCTOP) && defined(MTOFFL) + struct mtop control; + + control.mt_op = MTOFFL; + control.mt_count = 1; + rmtioctl (tape_des, MTIOCTOP, (char*) &control); /* Don't care if it fails. */ +#endif +} + +/* The file on file descriptor TAPE_DES is assumed to be magnetic tape + (or floppy disk or other device) and the end of the medium + has been reached. Ask the user for to mount a new "tape" to continue + the processing. If the user specified the device name on the + command line (with the -I, -O, -F or --file options), then we can + automatically re-open the same device to use the next medium. If the + user did not specify the device name, then we have to ask them which + device to use. */ + +void +get_next_reel (int tape_des) +{ + static int reel_number = 1; + FILE *tty_in; /* File for interacting with user. */ + FILE *tty_out; /* File for interacting with user. */ + int old_tape_des; + char *next_archive_name; + dynamic_string new_name; + char *str_res; + + ds_init (&new_name, 128); + + /* Open files for interactive communication. */ + tty_in = fopen (TTY_NAME, "r"); + if (tty_in == NULL) + error (2, errno, TTY_NAME); + tty_out = fopen (TTY_NAME, "w"); + if (tty_out == NULL) + error (2, errno, TTY_NAME); + + old_tape_des = tape_des; + tape_offline (tape_des); + rmtclose (tape_des); + + /* Give message and wait for carrage return. User should hit carrage return + only after loading the next tape. */ + ++reel_number; + if (new_media_message) + fprintf (tty_out, "%s", new_media_message); + else if (new_media_message_with_number) + fprintf (tty_out, "%s%d%s", new_media_message_with_number, reel_number, + new_media_message_after_number); + else if (archive_name) + fprintf (tty_out, _("Found end of tape. Load next tape and press RETURN. ")); + else + fprintf (tty_out, _("Found end of tape. To continue, type device/file name when ready.\n")); + + fflush (tty_out); + + if (archive_name) + { + int c; + + do + c = getc (tty_in); + while (c != EOF && c != '\n'); + + tape_des = open_archive (archive_name); + if (tape_des == -1) + open_error (archive_name); + } + else + { + do + { + if (tape_des < 0) + { + fprintf (tty_out, + _("To continue, type device/file name when ready.\n")); + fflush (tty_out); + } + + str_res = ds_fgets (tty_in, &new_name); + if (str_res == NULL || str_res[0] == '\0') + exit (1); + next_archive_name = str_res; + + tape_des = open_archive (next_archive_name); + if (tape_des == -1) + open_error (next_archive_name); + } + while (tape_des < 0); + } + + /* We have to make sure that `tape_des' has not changed its value even + though we closed it and reopened it, since there are local + copies of it in other routines. This works fine on Unix (even with + rmtread and rmtwrite) since open will always return the lowest + available file descriptor and we haven't closed any files (e.g., + stdin, stdout or stderr) that were opened before we originally opened + the archive. */ + + if (tape_des != old_tape_des) + error (1, 0, _("internal error: tape descriptor changed from %d to %d"), + old_tape_des, tape_des); + + free (new_name.ds_string); + fclose (tty_in); + fclose (tty_out); +} + +/* If MESSAGE does not contain the string "%d", make `new_media_message' + a copy of MESSAGE. If MESSAGES does contain the string "%d", make + `new_media_message_with_number' a copy of MESSAGE up to, but + not including, the string "%d", and make `new_media_message_after_number' + a copy of MESSAGE after the string "%d". */ + +void +set_new_media_message (char *message) +{ + char *p; + int prev_was_percent; + + p = message; + prev_was_percent = 0; + while (*p != '\0') + { + if (*p == 'd' && prev_was_percent) + break; + prev_was_percent = (*p == '%'); + ++p; + } + if (*p == '\0') + { + new_media_message = xstrdup (message); + } + else + { + int length = p - message - 1; + + new_media_message_with_number = xmalloc (length + 1); + strncpy (new_media_message_with_number, message, length); + new_media_message_with_number[length] = '\0'; + length = strlen (p + 1); + new_media_message_after_number = xmalloc (length + 1); + strcpy (new_media_message_after_number, p + 1); + } +} + +#ifdef SYMLINK_USES_UMASK +/* Most machines always create symlinks with rwxrwxrwx protection, + but some (HP/UX 8.07; maybe DEC's OSF on MIPS, too?) use the + umask when creating symlinks, so if your umask is 022 you end + up with rwxr-xr-x symlinks (although HP/UX seems to completely + ignore the protection). There doesn't seem to be any way to + manipulate the modes once the symlinks are created (e.g. + a hypothetical "lchmod"), so to create them with the right + modes we have to set the umask first. */ + +int +umasked_symlink (char *name1, char *name2, int mode) +{ + int old_umask; + int rc; + mode = ~(mode & 0777) & 0777; + old_umask = umask (mode); + rc = symlink (name1, name2); + umask (old_umask); + return rc; +} +#endif /* SYMLINK_USES_UMASK */ + +#ifdef HPUX_CDF +/* When we create a cpio archive we mark CDF's by putting an extra `/' + after their component name so we can distinguish the CDF's when we + extract the archive (in case the "hidden" directory's files appear + in the archive before the directory itself). E.g., in the path + "a/b+/c", if b+ is a CDF, we will write this path as "a/b+//c" in + the archive so when we extract the archive we will know that b+ + is actually a CDF, and not an ordinary directory whose name happens + to end in `+'. We also do the same thing internally in copypass.c. */ + + +/* Take an input pathname and check it for CDF's. Insert an extra + `/' in the pathname after each "hidden" directory. If we add + any `/'s, return a malloced string instead of the original input + string. + FIXME: This creates a memory leak. +*/ + +char * +add_cdf_double_slashes (char *input_name) +{ + static char *ret_name = NULL; /* re-usuable return buffer (malloc'ed) */ + static int ret_size = -1; /* size of return buffer. */ + char *p; + char *q; + int n; + struct stat dir_stat; + + /* Search for a `/' preceeded by a `+'. */ + + for (p = input_name; *p != '\0'; ++p) + { + if ( (*p == '+') && (*(p + 1) == '/') ) + break; + } + + /* If we didn't find a `/' preceeded by a `+' then there are + no CDF's in this pathname. Return the original pathname. */ + + if (*p == '\0') + return input_name; + + /* There was a `/' preceeded by a `+' in the pathname. If it is a CDF + then we will need to copy the input pathname to our return + buffer so we can insert the extra `/'s. Since we can't tell + yet whether or not it is a CDF we will just always copy the + string to the return buffer. First we have to make sure the + buffer is large enough to hold the string and any number of + extra `/'s we might add. */ + + n = 2 * (strlen (input_name) + 1); + if (n >= ret_size) + { + if (ret_size < 0) + ret_name = (char *) malloc (n); + else + ret_name = (char *)realloc (ret_name, n); + ret_size = n; + } + + /* Clear the `/' after this component, so we can stat the pathname + up to and including this component. */ + ++p; + *p = '\0'; + if ((*xstat) (input_name, &dir_stat) < 0) + { + stat_error (input_name); + return input_name; + } + + /* Now put back the `/' after this component and copy the pathname up to + and including this component and its trailing `/' to the return + buffer. */ + *p++ = '/'; + strncpy (ret_name, input_name, p - input_name); + q = ret_name + (p - input_name); + + /* If it was a CDF, add another `/'. */ + if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) ) + *q++ = '/'; + + /* Go through the rest of the input pathname, copying it to the + return buffer, and adding an extra `/' after each CDF. */ + while (*p != '\0') + { + if ( (*p == '+') && (*(p + 1) == '/') ) + { + *q++ = *p++; + + *p = '\0'; + if ((*xstat) (input_name, &dir_stat) < 0) + { + stat_error (input_name); + return input_name; + } + *p = '/'; + + if (S_ISDIR (dir_stat.st_mode) && (dir_stat.st_mode & 04000) ) + *q++ = '/'; + } + *q++ = *p++; + } + *q = '\0'; + + return ret_name; +} + +/* Is the last parent directory (e.g., c in a/b/c/d) a CDF? If the + directory name ends in `+' and is followed by 2 `/'s instead of 1 + then it is. This is only the case for cpio archives, but we don't + have to worry about tar because tar always has the directory before + its files (or else we lose). */ +int +islastparentcdf (char *path) +{ + char *newpath; + char *slash; + int slash_count; + int length; /* Length of result, not including NUL. */ + + slash = strrchr (path, '/'); + if (slash == 0) + return 0; + else + { + slash_count = 0; + while (slash > path && *slash == '/') + { + ++slash_count; + --slash; + } + + + if ( (*slash == '+') && (slash_count >= 2) ) + return 1; + } + return 0; +} +#endif + +#define DISKBLOCKSIZE (512) + +static int +buf_all_zeros (char *buf, int bufsize) +{ + int i; + for (i = 0; i < bufsize; ++i) + { + if (*buf++ != '\0') + return 0; + } + return 1; +} + +int delayed_seek_count = 0; + +/* Write NBYTE bytes from BUF to remote tape connection FILDES. + Return the number of bytes written on success, -1 on error. */ + +static int +sparse_write (int fildes, char *buf, unsigned int nbyte) +{ + int complete_block_count; + int leftover_bytes_count; + int seek_count; + int write_count; + char *cur_write_start; + int lseek_rc; + int write_rc; + int i; + enum { begin, in_zeros, not_in_zeros } state; + + complete_block_count = nbyte / DISKBLOCKSIZE; + leftover_bytes_count = nbyte % DISKBLOCKSIZE; + + if (delayed_seek_count != 0) + state = in_zeros; + else + state = begin; + + seek_count = delayed_seek_count; + + for (i = 0; i < complete_block_count; ++i) + { + switch (state) + { + case begin : + if (buf_all_zeros (buf, DISKBLOCKSIZE)) + { + seek_count = DISKBLOCKSIZE; + state = in_zeros; + } + else + { + cur_write_start = buf; + write_count = DISKBLOCKSIZE; + state = not_in_zeros; + } + buf += DISKBLOCKSIZE; + break; + + case in_zeros : + if (buf_all_zeros (buf, DISKBLOCKSIZE)) + { + seek_count += DISKBLOCKSIZE; + } + else + { + lseek (fildes, seek_count, SEEK_CUR); + cur_write_start = buf; + write_count = DISKBLOCKSIZE; + state = not_in_zeros; + } + buf += DISKBLOCKSIZE; + break; + + case not_in_zeros : + if (buf_all_zeros (buf, DISKBLOCKSIZE)) + { + write_rc = write (fildes, cur_write_start, write_count); + seek_count = DISKBLOCKSIZE; + state = in_zeros; + } + else + { + write_count += DISKBLOCKSIZE; + } + buf += DISKBLOCKSIZE; + break; + } + } + + switch (state) + { + case begin : + case in_zeros : + delayed_seek_count = seek_count; + break; + + case not_in_zeros : + write_rc = write (fildes, cur_write_start, write_count); + delayed_seek_count = 0; + break; + } + + if (leftover_bytes_count != 0) + { + if (delayed_seek_count != 0) + { + lseek_rc = lseek (fildes, delayed_seek_count, SEEK_CUR); + delayed_seek_count = 0; + } + write_rc = write (fildes, buf, leftover_bytes_count); + } + return nbyte; +} + +#define CPIO_UID(uid) (set_owner_flag ? set_owner : (uid)) +#define CPIO_GID(gid) (set_group_flag ? set_group : (gid)) + +void +stat_to_cpio (struct cpio_file_stat *hdr, struct stat *st) +{ + hdr->c_dev_maj = major (st->st_dev); + hdr->c_dev_min = minor (st->st_dev); + hdr->c_ino = st->st_ino; + /* For POSIX systems that don't define the S_IF macros, + we can't assume that S_ISfoo means the standard Unix + S_IFfoo bit(s) are set. So do it manually, with a + different name. Bleah. */ + hdr->c_mode = (st->st_mode & 07777); + if (S_ISREG (st->st_mode)) + hdr->c_mode |= CP_IFREG; + else if (S_ISDIR (st->st_mode)) + hdr->c_mode |= CP_IFDIR; +#ifdef S_ISBLK + else if (S_ISBLK (st->st_mode)) + hdr->c_mode |= CP_IFBLK; +#endif +#ifdef S_ISCHR + else if (S_ISCHR (st->st_mode)) + hdr->c_mode |= CP_IFCHR; +#endif +#ifdef S_ISFIFO + else if (S_ISFIFO (st->st_mode)) + hdr->c_mode |= CP_IFIFO; +#endif +#ifdef S_ISLNK + else if (S_ISLNK (st->st_mode)) + hdr->c_mode |= CP_IFLNK; +#endif +#ifdef S_ISSOCK + else if (S_ISSOCK (st->st_mode)) + hdr->c_mode |= CP_IFSOCK; +#endif +#ifdef S_ISNWK + else if (S_ISNWK (st->st_mode)) + hdr->c_mode |= CP_IFNWK; +#endif + hdr->c_nlink = st->st_nlink; + hdr->c_uid = CPIO_UID (st->st_uid); + hdr->c_gid = CPIO_GID (st->st_gid); + hdr->c_rdev_maj = major (st->st_rdev); + hdr->c_rdev_min = minor (st->st_rdev); + hdr->c_mtime = st->st_mtime; + hdr->c_filesize = st->st_size; + hdr->c_chksum = 0; + hdr->c_tar_linkname = NULL; +} + +void +cpio_to_stat (struct stat *st, struct cpio_file_stat *hdr) +{ + memset (st, 0, sizeof (*st)); + st->st_dev = makedev (hdr->c_dev_maj, hdr->c_dev_min); + st->st_ino = hdr->c_ino; + st->st_mode = hdr->c_mode & 0777; + if (hdr->c_mode & CP_IFREG) + st->st_mode |= S_IFREG; + else if (hdr->c_mode & CP_IFDIR) + st->st_mode |= S_IFDIR; +#ifdef S_IFBLK + else if (hdr->c_mode & CP_IFBLK) + st->st_mode |= S_IFBLK; +#endif +#ifdef S_IFCHR + else if (hdr->c_mode & CP_IFCHR) + st->st_mode |= S_IFCHR; +#endif +#ifdef S_IFFIFO + else if (hdr->c_mode & CP_IFIFO) + st->st_mode |= S_IFIFO; +#endif +#ifdef S_IFLNK + else if (hdr->c_mode & CP_IFLNK) + st->st_mode |= S_IFLNK; +#endif +#ifdef S_IFSOCK + else if (hdr->c_mode & CP_IFSOCK) + st->st_mode |= S_IFSOCK; +#endif +#ifdef S_IFNWK + else if (hdr->c_mode & CP_IFNWK) + st->st_mode |= S_IFNWK; +#endif + st->st_nlink = hdr->c_nlink; + st->st_uid = CPIO_UID (hdr->c_uid); + st->st_gid = CPIO_GID (hdr->c_gid); + st->st_rdev = makedev (hdr->c_rdev_maj, hdr->c_rdev_min); + st->st_mtime = hdr->c_mtime; + st->st_size = hdr->c_filesize; +} + +#ifndef HAVE_FCHOWN +# define HAVE_FCHOWN 0 +#endif +#ifndef HAVE_FCHMOD +# define HAVE_FCHMOD 0 +#endif + +int +fchown_or_chown (int fd, const char *name, uid_t uid, uid_t gid) +{ + if (HAVE_FCHOWN && fd != -1) + return fchown (fd, uid, gid); + else + return chown (name, uid, gid); +} + +int +fchmod_or_chmod (int fd, const char *name, mode_t mode) +{ + if (HAVE_FCHMOD && fd != -1) + return fchmod (fd, mode); + else + return chmod (name, mode); +} + +void +set_perms (int fd, struct cpio_file_stat *header) +{ + if (!no_chown_flag) + { + uid_t uid = CPIO_UID (header->c_uid); + gid_t gid = CPIO_GID (header->c_gid); + if ((fchown_or_chown (fd, header->c_name, uid, gid) < 0) + && errno != EPERM) + chown_error_details (header->c_name, uid, gid); + } + /* chown may have turned off some permissions we wanted. */ + if (fchmod_or_chmod (fd, header->c_name, header->c_mode) < 0) + chmod_error_details (header->c_name, header->c_mode); +#ifdef HPUX_CDF + if ((header->c_mode & CP_IFMT) && cdf_flag) + /* Once we "hide" the directory with the chmod(), + we have to refer to it using name+ instead of name. */ + file_hdr->c_name [cdf_char] = '+'; +#endif + if (retain_time_flag) + set_file_times (fd, header->c_name, header->c_mtime, header->c_mtime); +} + +void +set_file_times (int fd, + const char *name, unsigned long atime, unsigned long mtime) +{ + struct timespec ts[2]; + + memset (&ts, 0, sizeof ts); + + ts[0].tv_sec = atime; + ts[1].tv_sec = mtime; + + /* Silently ignore EROFS because reading the file won't have upset its + timestamp if it's on a read-only filesystem. */ + if (gl_futimens (fd, name, ts) < 0 && errno != EROFS) + utime_error (name); +} + +/* Do we have to ignore absolute paths, and if so, does the filename + have an absolute path? */ +void +cpio_safer_name_suffix (char *name, bool link_target, bool absolute_names, + bool strip_leading_dots) +{ + char *p = safer_name_suffix (name, link_target, absolute_names); + if (strip_leading_dots && strcmp (p, "./")) + /* strip leading `./' from the filename. */ + while (*p == '.' && *(p + 1) == '/') + { + ++p; + while (*p == '/') + ++p; + } + if (p != name) + memmove (name, p, (size_t)(strlen (p) + 1)); +} + + +/* This is a simplified form of delayed set_stat used by GNU tar. + With the time, both forms will merge and pass to paxutils + + List of directories whose statuses we need to extract after we've + finished extracting their subsidiary files. If you consider each + contiguous subsequence of elements of the form [D]?[^D]*, where [D] + represents an element where AFTER_LINKS is nonzero and [^D] + represents an element where AFTER_LINKS is zero, then the head + of the subsequence has the longest name, and each non-head element + in the prefix is an ancestor (in the directory hierarchy) of the + preceding element. */ + +struct delayed_set_stat + { + struct delayed_set_stat *next; + struct cpio_file_stat stat; + mode_t invert_permissions; + }; + +static struct delayed_set_stat *delayed_set_stat_head; + +void +delay_cpio_set_stat (struct cpio_file_stat *file_stat, + mode_t invert_permissions) +{ + size_t file_name_len = strlen (file_stat->c_name); + struct delayed_set_stat *data = + xmalloc (sizeof (struct delayed_set_stat) + file_name_len + 1); + data->next = delayed_set_stat_head; + memcpy (&data->stat, file_stat, sizeof data->stat); + data->stat.c_name = (char*) (data + 1); + strcpy (data->stat.c_name, file_stat->c_name); + data->invert_permissions = invert_permissions; + delayed_set_stat_head = data; +} + +void +delay_set_stat (char const *file_name, struct stat *st, + mode_t invert_permissions) +{ + struct cpio_file_stat fs; + + stat_to_cpio (&fs, st); + fs.c_name = (char*) file_name; + delay_cpio_set_stat (&fs, invert_permissions); +} + +/* Update the delayed_set_stat info for an intermediate directory + created within the file name of DIR. The intermediate directory turned + out to be the same as this directory, e.g. due to ".." or symbolic + links. *DIR_STAT_INFO is the status of the directory. */ +int +repair_inter_delayed_set_stat (struct stat *dir_stat_info) +{ + struct delayed_set_stat *data; + for (data = delayed_set_stat_head; data; data = data->next) + { + struct stat st; + if (stat (data->stat.c_name, &st) != 0) + { + stat_error (data->stat.c_name); + return -1; + } + + if (st.st_dev == dir_stat_info->st_dev + && st.st_ino == dir_stat_info->st_ino) + { + stat_to_cpio (&data->stat, dir_stat_info); + data->invert_permissions = + ((dir_stat_info->st_mode ^ st.st_mode) + & MODE_RWX & ~ newdir_umask); + return 0; + } + } + return 1; +} + +/* Update the delayed_set_stat info for a directory matching + FILE_HDR. + + Return 0 if such info was found, 1 otherwise. */ +int +repair_delayed_set_stat (struct cpio_file_stat *file_hdr) +{ + struct delayed_set_stat *data; + for (data = delayed_set_stat_head; data; data = data->next) + { + if (strcmp (file_hdr->c_name, data->stat.c_name) == 0) + { + data->invert_permissions = 0; + memcpy (&data->stat, file_hdr, + offsetof (struct cpio_file_stat, c_name)); + return 0; + } + } + return 1; +} + +void +apply_delayed_set_stat () +{ + while (delayed_set_stat_head) + { + struct delayed_set_stat *data = delayed_set_stat_head; + if (data->invert_permissions) + { + data->stat.c_mode ^= data->invert_permissions; + } + set_perms (-1, &data->stat); + delayed_set_stat_head = data->next; + free (data); + } +} + + +static int +cpio_mkdir (struct cpio_file_stat *file_hdr, int *setstat_delayed) +{ + int rc; + mode_t mode = file_hdr->c_mode; + + if (!(file_hdr->c_mode & S_IWUSR)) + { + rc = mkdir (file_hdr->c_name, mode | S_IWUSR); + if (rc == 0) + { + delay_cpio_set_stat (file_hdr, 0); + *setstat_delayed = 1; + } + } + else + { + rc = mkdir (file_hdr->c_name, mode); + *setstat_delayed = 0; + } + return rc; +} + +int +cpio_create_dir (struct cpio_file_stat *file_hdr, int existing_dir) +{ + int res; /* Result of various function calls. */ +#ifdef HPUX_CDF + int cdf_flag; /* True if file is a CDF. */ + int cdf_char; /* Index of `+' char indicating a CDF. */ +#endif + int setstat_delayed = 0; + + if (to_stdout_option) + return 0; + + /* Strip any trailing `/'s off the filename; tar puts + them on. We might as well do it here in case anybody + else does too, since they cause strange things to happen. */ + strip_trailing_slashes (file_hdr->c_name); + + /* Ignore the current directory. It must already exist, + and we don't want to change its permission, ownership + or time. */ + if (file_hdr->c_name[0] == '.' && file_hdr->c_name[1] == '\0') + { + return 0; + } + +#ifdef HPUX_CDF + cdf_flag = 0; +#endif + if (!existing_dir) + { +#ifdef HPUX_CDF + /* If the directory name ends in a + and is SUID, + then it is a CDF. Strip the trailing + from + the name before creating it. */ + cdf_char = strlen (file_hdr->c_name) - 1; + if ( (cdf_char > 0) && + (file_hdr->c_mode & 04000) && + (file_hdr->c_name [cdf_char] == '+') ) + { + file_hdr->c_name [cdf_char] = '\0'; + cdf_flag = 1; + } +#endif + res = cpio_mkdir (file_hdr, &setstat_delayed); + } + else + res = 0; + if (res < 0 && create_dir_flag) + { + create_all_directories (file_hdr->c_name); + res = cpio_mkdir (file_hdr, &setstat_delayed); + } + if (res < 0) + { + /* In some odd cases where the file_hdr->c_name includes `.', + the directory may have actually been created by + create_all_directories(), so the mkdir will fail + because the directory exists. If that's the case, + don't complain about it. */ + struct stat file_stat; + if (errno != EEXIST) + { + mkdir_error (file_hdr->c_name); + return -1; + } + if (lstat (file_hdr->c_name, &file_stat)) + { + stat_error (file_hdr->c_name); + return -1; + } + if (!(S_ISDIR (file_stat.st_mode))) + { + error (0, 0, _("%s is not a directory"), + quotearg_colon (file_hdr->c_name)); + return -1; + } + } + + if (!setstat_delayed && repair_delayed_set_stat (file_hdr)) + set_perms (-1, file_hdr); + return 0; +} + |