pax/.linked/mbsdcc.h010064400000000000000000000201661465033426200115370ustar00/*- * MirBSD ISO C compiler toolkit (macros for mbsdint.h and others) * * © mirabilos Ⓕ CC0 or The MirOS Licence (MirBSD) */ #ifndef SYSKERN_MBSDCC_H #define SYSKERN_MBSDCC_H "$MirOS: src/bin/pax/.linked/mbsdcc.h,v 1.10 2024/07/25 02:29:43 tg Exp $" /* * Note: this header uses the SIZE_MAX (or similar) definitions * from or if present, so either include * these before this header or include "mbsdint.h", which fixes * up the corresponding definition. * * Place the inclusion of this after or something * that pulls it, for example . * * Furthermore, either include for abort(3), or pro‐ * vide an mbccABEND macro (preferred). */ #if !defined(_KERNEL) && !defined(_STANDALONE) #include #else #include #endif /* mbccABEND should be provided by the user, called by mbsd*.h only */ #ifndef mbccABEND #define mbccABEND(reasonstr) abort() #endif /* monkey-patch known-bad offsetof versions */ #if (defined(__KLIBC__) || defined(__dietlibc__)) && \ ((defined(__GNUC__) && (__GNUC__ > 3)) || defined(__NWCC__)) #undef offsetof #define offsetof(struc,memb) __builtin_offsetof(struc, memb) #endif /* should be defined by already */ #ifndef __predict_true #if defined(__GNUC__) && (__GNUC__ >= 3) /* 2.96, but keep it simple here */ #define __predict_true(exp) __builtin_expect(!!(exp), 1) #define __predict_false(exp) __builtin_expect(!!(exp), 0) #else #define __predict_true(exp) (!!(exp)) #define __predict_false(exp) (!!(exp)) #endif /* !GCC 3.x */ #endif /* ndef(__predict_true) */ /* mbsdint.h replaces this with mbiSIZE_MAX */ #undef mbccSIZE_MAX #if defined(SIZE_MAX) #define mbccSIZE_MAX ((size_t)(SIZE_MAX) #elif defined(SIZE_T_MAX) #define mbccSIZE_MAX ((size_t)(SIZE_T_MAX) #elif defined(MAXSIZE_T) #define mbccSIZE_MAX ((size_t)(MAXSIZE_T) #else #define mbccSIZE_MAX ((size_t)~(size_t)0) #endif #ifdef __cplusplus #define mbcextern extern "C" /* for headers (BSD __BEGIN_DECLS/__END_DECLS) */ #define mbcextern_beg extern "C" { #define mbcextern_end } #else #define mbcextern extern #define mbcextern_beg #define mbcextern_end #endif /* compiler warning pragmas, NOP for other compilers */ #ifdef _MSC_VER /* push, suppress space-separated listed warning numbers */ #define mbmscWd(d) __pragma(warning(push)) \ __pragma(warning(disable: d)) /* suppress for only the next line */ #define mbmscWs(d) __pragma(warning(suppress: d)) /* pop from mbmscWd */ #define mbmscWpop __pragma(warning(pop)) #else #define mbmscWd(d) #define mbmscWs(d) #define mbmscWpop #endif /* in-expression compile-time check evaluating to 0 */ #ifdef __cplusplus template struct mbccChkExpr_sa; template<> struct mbccChkExpr_sa { typedef int Type; }; #define mbccChkExpr(test) (static_cast::Type>(0)) #else #define mbccChkExpr(test) mbmscWs(4116) \ (sizeof(struct { unsigned int (mbccChkExpr):((0+(test)) ? 1 : -1); }) * 0) #endif /* ensure value x is a constant expression */ #ifdef __cplusplus template struct mbccCEX_sa; template<> struct mbccCEX_sa { typedef int Type; }; template<> struct mbccCEX_sa { typedef int Type; }; #define mbccCEX(x) (static_cast::Type>(0) + (x)) #else #define mbccCEX(x) mbmscWs(4116) \ (sizeof(struct { unsigned int (mbccCEX):(((0+(x)) && 1) + 1); }) * 0 + (x)) #endif /* flexible array member */ #if defined(__cplusplus) #ifdef __cpp_flexible_array_members #define mbccFAMslot(type,memb) type memb[] #elif defined(__GNUC__) || defined(__clang__) || defined(_MSC_VER) #define mbccFAMslot(type,memb) type memb[0] mbmscWs(4200 4820) #else #define mbccFAMslot(type,memb) type memb[1] mbmscWs(4820) #endif #elif (defined(__STDC_VERSION__) && ((__STDC_VERSION__) >= 199901L)) #define mbccFAMslot(type,memb) type memb[] #elif defined(__GNUC__) #define mbccFAMslot(type,memb) type memb[0] mbmscWs(4200 4820) #else #define mbccFAMslot(type,memb) type memb[1] mbmscWs(4820) /* SOL */ #endif #define mbccFAMsz_i(t,memb,sz) /*private*/ (size_t)(offsetof(t, memb) + (size_t)sz) /* compile-time constant */ #define mbccFAMSZ(struc,memb,sz) ((size_t)( \ mbccChkExpr(mbccSIZE_MAX - sizeof(struc) > mbccCEX((size_t)(sz))) + \ (mbccFAMsz_i(struc, memb, (sz)) > sizeof(struc) ? \ mbccFAMsz_i(struc, memb, (sz)) : sizeof(struc)))) /* run-time */ #define mbccFAMsz(struc,memb,sz) ((size_t)( \ !(mbccSIZE_MAX - sizeof(struc) > (size_t)(sz)) ? \ (mbccABEND("mbccFAMsz: " mbccS(sz) " too large for " mbccS(struc) "." mbccS(memb)), 0) : \ (mbccFAMsz_i(struc, memb, (sz)) > sizeof(struc) ? \ mbccFAMsz_i(struc, memb, (sz)) : sizeof(struc)))) /* example: * * struct s { * int type; * mbccFAMslot(char, label); // like char label[…]; * }; * struct t { * int count; * mbccFAMslot(time_t, value); * }; * union fixed_s { * struct s s; * char storage[mbccFAMSZ(struct s, label, 28)]; * }; * struct s *sp = malloc(mbccFAMsz(struct s, label, strlen(labelvar) + 1U)); * struct t *tp = malloc(mbccFAMsz(struct t, value, sizeof(time_t[cnt]))); * tp->count = cnt; * struct s *np = malloc(mbccFAMSZ(struct s, label, sizeof("myname"))); * memcpy(np->label, "myname", sizeof("myname")); * static union fixed_s preallocated; * preallocated.s.label[0] = '\0'; * somefunc((struct s *)&preallocated); */ /* field sizeof */ #if defined(__cplusplus) /* TODO: add better way on CFrustFrust once it has one */ #define mbccFSZ(struc,memb) (sizeof(((struc *)0)->memb)) #elif (defined(__STDC_VERSION__) && ((__STDC_VERSION__) >= 199901L)) #define mbccFSZ(struc,memb) (sizeof(((struc){0}).memb)) #else #define mbccFSZ(struc,memb) (sizeof(((struc *)0)->memb)) #endif /* stringification with expansion */ #define mbccS(x) #x #define mbccS2(x) mbccS(x) /* compile-time assertions */ #undef mbccCTA #if defined(__WATCOMC__) && !defined(__WATCOM_CPLUSPLUS__) /* “Comparison result always 0” */ #define mbccCTA_wb _Pragma("warning 124 5") /*XXX no pop so set to visible info */ #define mbccCTA_we _Pragma("warning 124 4") #else #define mbccCTA_wb /* nothing */ #define mbccCTA_we /* nothing */ #endif #if defined(__cplusplus) #ifdef __cpp_static_assert #define mbccCTA(fldn,cond) static_assert(cond, mbccS(fldn)) #endif #elif (defined(__STDC_VERSION__) && ((__STDC_VERSION__) >= 202311L)) #define mbccCTA(fldn,cond) static_assert(cond, mbccS(fldn)) #elif (defined(__STDC_VERSION__) && ((__STDC_VERSION__) >= 201112L)) #define mbccCTA(fldn,cond) _Static_assert(cond, mbccS(fldn)) #elif defined(__GNUC__) && \ ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)) #define mbccCTA(fldn,cond) __extension__ _Static_assert(cond, mbccS(fldn)) #endif #ifndef mbccCTA /* single assertion for macros (with fldn prefixed, cond parenthesised) */ #define mbccCTA(fldn,cond) unsigned int fldn:(cond ? 1 : -1) /* begin/end assertion block */ #define mbCTA_BEG(name) struct ctassert_ ## name { mbccCTA_wb \ int _ctabeg /* plus user semicolon */ #define mbCTA_END(name) int _ctaend; \ }; \ struct ctassert2 ## name { \ char t[sizeof(struct ctassert_ ## name) > 1U ? 1 : -1]; \ mbccCTA_we } /* semicolon provided by user */ #else /* nothing, but syntax-check equally to manual compile-time assert macro */ #define mbCTA_BEG(name) struct ctassert_ ## name { mbccCTA_wb \ char t[2]; \ } /* semicolon provided by user */ #define mbCTA_END(name) struct ctassert2 ## name { \ char t[sizeof(struct ctassert_ ## name) > 1U ? 1 : -1]; \ mbccCTA_we } /* semicolon provided by user */ #endif /* single assertion */ #define mbCTA(name,cond) mbccCTA(cta_ ## name, (cond)) /* nil pointer constant */ #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ (defined(__STDC_VERSION__) && ((__STDC_VERSION__) >= 202311L)) #define mbnil nullptr #elif defined(__GNUG__) #define mbnil __null #elif defined(__cplusplus) #define mbnil (size_t)0UL /* wtf, why? */ #else #define mbnil (void *)0UL #endif /* præfix to avoid -Wunused-result when it really cannot be helped */ #ifdef _FORTIFY_SOURCE /* workaround via https://stackoverflow.com/a/64407070/2171120 */ #define SHIKATANAI (void)! #else #define SHIKATANAI (void) #endif #endif /* !SYSKERN_MBSDCC_H */ pax/.linked/strlfun.inc010064400000000000000000000126461372504413500123260ustar00#if 0 /* comment in gmake; next line ignored by gcc */ ifeq (0,gmake ignores from here) #endif /*- * Copyright (c) 2006, 2008, 2011 * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un- * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person's immediate fault when using the work as intended. *- * The original implementations of strlcpy(3) and strlcat(3) are from * Todd C. Miller; its licence is reproduced in the L_strlcpy part as * it applies to only that now; mirabilos rewrote strlcat from specs, * optimised both from suggestions by Bodo Eggert, aliased strlcpy as * just-copying strxfrm(3) and introduced wcslcat(3), wcslcpy(3) and, * symmetrically, wcsxfrm(3) as wide character variants. */ #include #if defined(MBSDPORT_H) #include MBSDPORT_H #elif !defined(OUTSIDE_OF_LIBKERN) #include #endif #ifndef __RCSID #define __RCSID(x) /* fallback */ static const char __rcsid[] = x #endif __RCSID("$MirOS: src/bin/pax/.linked/strlfun.inc,v 1.9 2020/09/06 02:24:02 tg Exp $"); #ifdef WIDEC #if defined(MBSDPORT_H) || defined(OUTSIDE_OF_LIBKERN) #ifdef __WCHAR_TYPE__ typedef __WCHAR_TYPE__ wchar_t; #else #include #endif #endif /* wide character string functions */ #define NUL L'\0' #define char_t wchar_t #define fn_len wcslen #define fn_cat wcslcat #define fn_cpy wcslcpy #else /* (multibyte) string functions */ #define NUL '\0' #define char_t char #define fn_len strlen #define fn_cat strlcat #define fn_cpy strlcpy #endif #ifdef L_strxfrm #define strlcpy strxfrm #define wcslcpy wcsxfrm #define L_strlcpy #endif #if defined(MBSDPORT_H) || defined(OUTSIDE_OF_LIBKERN) extern size_t fn_len(const char_t *); #ifdef NEED_STRLFUN_PROTOS size_t fn_cat(char_t *, const char_t *, size_t); size_t fn_cpy(char_t *, const char_t *, size_t); #endif #endif #ifndef __predict_true #define __predict_true(exp) (exp) #define __predict_false(exp) (exp) #endif #ifdef L_strlcat /* * Appends src to string dst of size dlen (unlike strncat, dlen is the * full size of dst, not space left). At most dlen-1 characters * will be copied. Always NUL terminates (unless dlen <= strlen(dst)). * Returns strlen(src) + MIN(dlen, strlen(initial dst)), without the * trailing NUL byte counted. If retval >= dlen, truncation occurred. */ size_t fn_cat(char_t *dst, const char_t *src, size_t dlen) { size_t n = 0, slen; slen = fn_len(src); while (__predict_true(n + 1 < dlen && dst[n] != NUL)) ++n; if (__predict_false(dlen == 0 || dst[n] != NUL)) return (dlen + slen); while (__predict_true((slen > 0) && (n < (dlen - 1)))) { dst[n++] = *src++; --slen; } dst[n] = NUL; return (n + slen); } #endif #ifdef L_strlcpy /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /*- * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t fn_cpy(char_t *dst, const char_t *src, size_t siz) { const char_t *s = src; if (__predict_false(siz == 0)) goto traverse_src; /* copy as many chars as will fit */ while (--siz && (*dst++ = *s++)) ; /* not enough room in dst */ if (__predict_false(siz == 0)) { /* safe to NUL-terminate dst since we copied <= siz-1 chars */ *dst = NUL; traverse_src: /* traverse rest of src */ while (*s++) ; } /* count does not include NUL */ return (s - src - 1); } #endif #if 0 /* gcc ignored from here; gmake stops ignoring */ endif USE_WIDEC?= 1 LIB= libstrlfun.a OBJS= strlcpy.o strlcat.o ifeq (1,$(strip $(USE_WIDEC))) OBJS+= wcslcpy.o wcslcat.o endif DEFS= -DOUTSIDE_OF_LIBKERN DEFS_strlcpy.o= -DL_strlcpy DEFS_strlcat.o= -DL_strlcat DEFS_wcslcpy.o= -DL_strlcpy -DWIDEC DEFS_wcslcat.o= -DL_strlcat -DWIDEC all: $(LIB) $(LIB): $(OBJS) ar rc $(LIB) $(OBJS) -ranlib $(LIB) $(OBJS): strlfun.c $(CC) $(CFLAGS) $(CPPFLAGS) $(DEFS) $(DEFS_$@) -c -o $@ strlfun.c #endif /* EOF for gmake and gcc */ pax/Build.sh010064400000000000000000001633111466023157000102010ustar00#!/bin/sh srcversion='$MirOS: src/bin/pax/Build.sh,v 1.27 2024/08/17 23:33:49 tg Exp $' set +evx #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, # 2020, 2021, 2023, 2024 # mirabilos # Copyright (c) 2018 # mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission # is granted to deal in this work without restriction, including un- # limited rights to use, publicly perform, distribute, sell, modify, # merge, give away, or sublicence. # # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to # the utmost extent permitted by applicable law, neither express nor # implied; without malicious intent or gross negligence. In no event # may a licensor, author or contributor be held liable for indirect, # direct, other damage, loss, or other issues arising in any way out # of dealing in the work, even if advised of the possibility of such # damage or existence of a defect, except proven that it results out # of said person's immediate fault when using the work as intended. #- # People analysing the output must whitelist conftest.c for any kind # of compiler warning checks (mirtoconf is by design not quiet). # # Used environment documentation is at the end of this file. LC_ALL=C; LANGUAGE=C export LC_ALL; unset LANGUAGE use_ach=x; unset use_ach case $ZSH_VERSION:$VERSION in :zsh*) ZSH_VERSION=2 ;; esac if test -n "${ZSH_VERSION+x}" && (emulate sh) >/dev/null 2>&1; then emulate sh NULLCMD=: fi if test -d /usr/xpg4/bin/. >/dev/null 2>&1; then # Solaris: some of the tools have weird behaviour, use portable ones PATH=/usr/xpg4/bin:$PATH export PATH fi test_tool() { x=`echo $2 | $3` y=$? test x"$y" = x"0" && test x"$x" = x"$4" && return echo >&2 "E: your $1 does not work correctly!" echo >&2 "N: 'echo $2 | $3' exited $y and returned '$x'; expected '$4'" echo >&2 'N: install a better one and prepend its location to $PATH' exit 1 } test_tool grep foobarbaz 'grep bar' foobarbaz test_tool sed abc 'sed y/ac/AC/' AbC test_tool tr abc 'tr ac AC' AbC sp=' ' ht=' ' nl=' ' safeIFS="$sp$ht$nl" IFS=$safeIFS allu=QWERTYUIOPASDFGHJKLZXCVBNM alll=qwertyuiopasdfghjklzxcvbnm alln=0123456789 alls=______________________________________________________________ test_n() { test x"$1" = x"" || return 0 return 1 } test_z() { test x"$1" = x"" } case `echo a | tr '\201' X` in X) # EBCDIC build system lfcr='\n\r' ;; *) lfcr='\012\015' ;; esac echo "For the build logs, demonstrate that /dev/null and /dev/tty exist:" ls -l /dev/null /dev/tty cat < - CFLAGS <$CFLAGS> - CPPFLAGS <$CPPFLAGS> - LDFLAGS <$LDFLAGS> - LIBS <$LIBS> - LDSTATIC <$LDSTATIC> - TARGET_OS <$TARGET_OS> TARGET_OSREV <$TARGET_OSREV> EOF v() { $e "$*" eval "$@" } vv() { _c=$1 shift $e "\$ $*" 2>&1 eval "$@" >vv.out 2>&1 sed "s^${_c} " $fd...$ao $ui$fr$ao$fx" fx= } # ac_cache label: sets f, fu, fv?=0 ac_cache() { f=$1 fu=`upper $f` eval fv=\$HAVE_$fu case $fv in 0|1) fx=' (cached)' return 0 ;; esac fv=0 return 1 } # ac_testinit label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput # returns 1 if value was cached/implied, 0 otherwise: call ac_testdone ac_testinit() { if ac_cache $1; then test x"$2" = x"!" && shift test x"$2" = x"" || shift fd=${3-$f} ac_testdone return 1 fi fc=0 if test x"$2" = x""; then ft=1 else if test x"$2" = x"!"; then fc=1 shift fi eval ft=\$HAVE_`upper $2` if test_z "$ft"; then echo >&2 echo >&2 "E: test $f depends on $2 which is not defined yet" exit 255 fi shift fi fd=${3-$f} if test $fc = "$ft"; then fv=$2 fx=' (implied)' ac_testdone return 1 fi $e ... $fd return 0 } cat_h_blurb() { echo '#ifdef MKSH_USE_AUTOCONF_H /* things that “should” have been on the command line */ #include "autoconf.h" #undef MKSH_USE_AUTOCONF_H #endif ' cat } # pipe .c | ac_test[n] [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput ac_testnndnd() { if test x"$1" = x"!"; then fr=1 shift else fr=0 fi cat_h_blurb >conftest.c ac_testinit "$@" || return 1 vv ']' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr" test $tcfn = no && test -f a.out && tcfn=a.out test $tcfn = no && test -f a.exe && tcfn=a.exe test $tcfn = no && test -f conftest.exe && tcfn=conftest.exe test $tcfn = no && test -f conftest && tcfn=conftest if test -f $tcfn; then test 1 = $fr || fv=1 else test 0 = $fr || fv=1 fi vscan= if test $phase = u; then case $ct in gcc*) vscan='unrecogni[sz]ed' ;; hpcc) vscan='unsupported' ;; pcc) vscan='unsupported' ;; sunpro) vscan='-e ignored -e turned.off' ;; esac fi test_n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr return 0 } ac_testn() { if ac_testnndnd "$@"; then rmf conftest.c conftest.o ${tcfn}* vv.out ac_testdone else rm -f conftest.c fi } ac_testnnd() { if ac_testnndnd "$@"; then ac_testdone fi } # ac_ifcpp cppexpr [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput ac_ifcpp() { expr=$1; shift ac_testn "$@" <<-EOF #include extern int thiswillneverbedefinedIhope(void); int main(void) { return (isatty(0) + #$expr 0 #else /* force a failure: expr is false */ thiswillneverbedefinedIhope() #endif ); } EOF test x"$1" = x"!" && shift f=$1 fu=`upper $f` eval fv=\$HAVE_$fu test x"$fv" = x"1" } addtoach() { if echo "$1" >>autoconf.h; then echo ">>> $1" else echo >&2 "E: could not write autoconf.h" exit 255 fi } # simple only (is IFS-split by shell) cpp_define() { case $use_ach in 0) add_cppflags "-D$1=$2" ;; 1) addtoach "#define $1 $2" ;; *) echo >&2 "E: cpp_define() called too early!" exit 255 ;; esac } add_cppflags() { CPPFLAGS="$CPPFLAGS $*" } ac_cppflags() { test x"$1" = x"" || fu=$1 fv=$2 test x"$2" = x"" && eval fv=\$HAVE_$fu cpp_define HAVE_$fu $fv } ac_test() { ac_testn "$@" ac_cppflags } # ac_flags [-] add varname cflags [text] [ldflags] ac_flags() { if test x"$1" = x"-"; then shift hf=1 else hf=0 fi fa=$1 vn=$2 f=$3 ft=$4 fl=$5 test x"$ft" = x"" && ft="if $f can be used" save_CFLAGS=$CFLAGS CFLAGS="$CFLAGS $f" save_LDFLAGS=$LDFLAGS test_z "$fl" || LDFLAGS="$LDFLAGS $fl" if test 1 = $hf; then ac_testn can_$vn '' "$ft" else ac_testn can_$vn '' "$ft" <<-'EOF' /* evil apo'stroph in comment test */ #include int main(void) { int t[2]; return (isatty(pipe(t))); } EOF #' fi eval fv=\$HAVE_CAN_`upper $vn` test_z "$fl" || test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS test 11 = $fa$fv || CFLAGS=$save_CFLAGS } # ac_header [!] header [prereq ...] ac_header() { if test x"$1" = x"!"; then na=1 shift else na=0 fi hf=$1; shift hv=`echo "$hf" | tr -d "$lfcr" | tr -c $alll$allu$alln $alls` echo "/* NeXTstep bug workaround */" >x for i do case $i in _time) echo '#if HAVE_BOTH_TIME_H' >>x echo '#include ' >>x echo '#include ' >>x echo '#elif HAVE_SYS_TIME_H' >>x echo '#include ' >>x echo '#elif HAVE_TIME_H' >>x echo '#include ' >>x echo '#endif' >>x ;; *) echo "#include <$i>" >>x ;; esac done echo "#include <$hf>" >>x echo '#include ' >>x echo 'int main(void) { return (isatty(0)); }' >>x ac_testn "$hv" "" "<$hf>" /dev/null` curdisp=. case x$curdir in x) curdir=. ;; *"$sp"*|*"$ht"*|*"$nl"*) echo >&2 Current directory should not contain space or tab or newline. echo >&2 Errors may occur. ;; *"'"*) echo >&2 Current directory should not contain single quotes. echo >&2 Errors may occur. ;; *) curdisp=$curdir ;; esac case x$srcdir in x) srcdir=. ;; *"$sp"*|*"$ht"*|*"$nl"*) echo >&2 Source directory should not contain space or tab or newline. echo >&2 Errors may occur. ;; *"'"*) echo >&2 Source directory must not contain single quotes. exit 1 ;; esac srcdisp=`cd "$srcdir" && pwd` || srcdisp= test_n "$srcdisp" || srcdisp=$srcdir if test x"$srcdisp" = x"$curdir"; then srcdisp= else srcdisp=$srcdir/ fi dstversion=`sed -n '/define MIRCPIO_VERSION/s/^.*"\([^"]*\)".*$/\1/p' "$srcdir/pax.h"` whatlong='MirCPIO (paxmirabilis)' whatshort=paxmirabilis e=echo r=0 eq=0 pm=0 cm=normal Cg= optflags=-std-compile-opts last= mans=0 for i do case $last:$i in c:dragonegg|c:llvm) cm=$i last= ;; c:*) echo "$me: Unknown option -c '$i'!" >&2 exit 1 ;; o:*) optflags=$i last= ;; :-A) rm -f autoconf.h addtoach '/* work around NeXTstep bug */' use_ach=1 add_cppflags -DMKSH_USE_AUTOCONF_H ;; :-c) last=c ;; :-g) # checker, debug, valgrind build add_cppflags -DDEBUG Cg=YES ;; :-j) pm=1 ;; :-M) cm=makefile ;; :-O) optflags=-std-compile-opts ;; :-o) last=o ;; :-Q) eq=1 ;; :-r) r=1 ;; :-tmir) mans=1 ;; :-tpax) mans=2 ;; :-v) echo "Build.sh $srcversion" echo "for $whatlong $dstversion" exit 0 ;; :*) echo "$me: Unknown option '$i'!" >&2 exit 1 ;; *) echo "$me: Unknown option -'$last' '$i'!" >&2 exit 1 ;; esac done if test_n "$last"; then echo "$me: Option -'$last' not followed by argument!" >&2 exit 1 fi tfn=paxpax case $mans in 0) paxname=pax cpioname=cpio tarname=tar ;; 1) paxname=mirpax cpioname=mircpio tarname=mirtar ;; 2) paxname=pax cpioname=paxcpio tarname=paxtar ;; esac if test -d $tfn || test -d $tfn.exe; then echo "$me: Error: ./$tfn is a directory!" >&2 exit 1 fi test x"$use_ach" = x"1" || use_ach=0 cpp_define MKSH_BUILDSH 1 rmf a.exe* a.out* conftest.* *core core.* ${tfn}* *.bc *.dbg *.ll *.o *.cat? \ *.gen Rebuild.sh Makefrag.inc lft no x vv.out rm -rf mans SRCS="ar.c ar_io.c ar_subs.c buf_subs.c compat.c cpio.c" SRCS="$SRCS file_subs.c ftree.c gen_subs.c getoldopt.c options.c" SRCS="$SRCS pat_rep.c pax.c sel_subs.c tables.c tar.c tty_subs.c" add_cppflags -DMBSDPORT_H=\\\"compat.h\\\" if test_z "$srcdisp"; then CPPFLAGS="-I. $CPPFLAGS" else CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS" fi test_z "$LDSTATIC" || if test_z "$LDFLAGS"; then LDFLAGS=$LDSTATIC else LDFLAGS="$LDFLAGS $LDSTATIC" fi if test_z "$TARGET_OS"; then x=`uname -s 2>/dev/null || uname` case $x in scosysv) # SVR4 Unix with uname -s = uname -n, whitelist TARGET_OS=$x ;; syllable) # other OS with uname -s = uname = uname -n, whitelist TARGET_OS=$x ;; *) test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x ;; esac fi if test_z "$TARGET_OS"; then echo "$me: Set TARGET_OS, your uname is broken!" >&2 exit 1 fi osnote= oswarn= ccpc=-Wc, ccpl=-Wl, tsts= ccpr='|| for _f in ${tcfn}*; do case $_f in cpio.[1ch]|pax.[1ch]|tar.[1ch]) ;; *) rm -f "$_f" ;; esac; done' # Evil hack if test x"$TARGET_OS" = x"Android"; then TARGET_OS=Linux fi # Evil OS if test x"$TARGET_OS" = x"Minix"; then echo >&2 " WARNING: additional checks before running Build.sh required! You can avoid these by calling Build.sh correctly, see below. " cat_h_blurb >conftest.c <<'EOF' #include const char * #ifdef _NETBSD_SOURCE ct="Ninix3" #else ct="Minix3" #endif ; EOF ct=unknown vv ']' "${CC-cc} -E $CFLAGS $Cg $CPPFLAGS $NOWARN conftest.c | grep ct= | tr -d \\\\015 >x" sed 's/^/[ /' x eval `cat x` rmf x vv.out case $ct in Minix3|Ninix3) echo >&2 " Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous. Please set it to either Minix3 or Ninix3, whereas the latter is all versions of Minix with even partial NetBSD(R) userland. The value determined from your compiler for the current compilation (which may be wrong) is: $ct " TARGET_OS=$ct ;; *) echo >&2 " Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous. Please set it to either Minix3 or Ninix3, whereas the latter is all versions of Minix with even partial NetBSD(R) userland. The proper value couldn't be determined, continue at your own risk. " ;; esac fi # Configuration depending on OS revision, on OSes that need them case $TARGET_OS in NEXTSTEP) test_n "$TARGET_OSREV" || TARGET_OSREV=`hostinfo 2>&1 | \ grep 'NeXT Mach [0-9][0-9.]*:' | \ sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'` ;; BeOS|HP-UX|QNX|SCO_SV) test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r` ;; esac # SVR4 (some) workaround int_as_ssizet() { cpp_define SSIZE_MIN INT_MIN cpp_define SSIZE_MAX INT_MAX cpp_define ssize_t int } cmplrflgs= # Configuration depending on OS name case $TARGET_OS in 386BSD) osnote='; it is untested' : "${HAVE_CAN_OTWO=0}" ;; 4.4BSD) osnote='; assuming BOW (BSD on Windows)' ;; A/UX) oswarn='; it is untested' add_cppflags -D_POSIX_SOURCE : "${CC=gcc}" : "${LIBS=-lposix}" # GCC defines AUX but cc nothing add_cppflags -D__A_UX__ ;; AIX) oswarn='; it is untested' add_cppflags -D_ALL_SOURCE ;; BeOS) oswarn='; it is untested' : "${CC=gcc}" ;; BSD/OS) osnote='; it is untested' ;; Coherent) oswarn='; it is untested, possibly has major issues' cpp_define MKSH__NO_SYMLINK 1 ;; CYGWIN*) # libc lacks dprintf but the headers declare it unless #define’d cpp_define dprintf rpl_dprintf ;; Darwin) osnote='; it is untested' add_cppflags -D_DARWIN_C_SOURCE ;; DragonFly) osnote='; it is untested' ;; FreeBSD) osnote='; it is untested' ;; FreeMiNT) oswarn='; it is untested' add_cppflags -D_GNU_SOURCE ;; GNU) osnote='; it is untested' case $CC in *tendracc*) ;; *) add_cppflags -D_GNU_SOURCE ;; esac ;; GNU/kFreeBSD) osnote='; it is untested' case $CC in *tendracc*) ;; *) add_cppflags -D_GNU_SOURCE ;; esac ;; Haiku) oswarn='; it is untested' ;; Harvey) oswarn='; it is not ported' add_cppflags -D_POSIX_SOURCE add_cppflags -D_LIMITS_EXTENSION add_cppflags -D_BSD_EXTENSION add_cppflags -D_SUSV2_SOURCE add_cppflags -D_GNU_SOURCE cpp_define MKSH__NO_SYMLINK 1 ;; HP-UX) osnote='; it is untested' case $TARGET_OSREV in B.09.*) : "${CC=c89}" add_cppflags -D_HPUX_SOURCE cpp_define MBSDINT_H_SMALL_SYSTEM 1 ;; esac ;; Interix) oswarn='; it is untested' ccpc='-X ' ccpl='-Y ' add_cppflags -D_ALL_SOURCE ;; IRIX*) osnote='; it is untested' ;; Jehanne) oswarn='; it is not ported' add_cppflags -D_POSIX_SOURCE add_cppflags -D_LIMITS_EXTENSION add_cppflags -D_BSD_EXTENSION add_cppflags -D_SUSV2_SOURCE add_cppflags -D_GNU_SOURCE cpp_define MKSH__NO_SYMLINK 1 ;; Linux) case $CC in *tendracc*) ;; *) add_cppflags -D_GNU_SOURCE ;; esac ;; LynxOS) osnote='; it is untested' ;; midipix) oswarn='; it is untested' add_cppflags -D_GNU_SOURCE ;; MidnightBSD) cpp_define _WITH_DPRINTF 1 cpp_define UT_NAMESIZE 32 ;; Minix-vmd) oswarn='; it is untested' add_cppflags -D_MINIX_SOURCE ;; Minix3) oswarn='; it is untested' add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX ;; Minoca) oswarn='; it is untested' : "${CC=gcc}" ;; MirBSD) add_cppflags -D_ALL_SOURCE ;; MSYS_*) osnote='; it is untested' # broken on this OE (from ir0nh34d) : "${HAVE_STDINT_H=0}" ;; NetBSD) add_cppflags -D_NETBSD_SOURCE add_cppflags -D_OPENBSD_SOURCE ;; NEXTSTEP) oswarn='; it is untested' add_cppflags -D_NEXT_SOURCE add_cppflags -D_POSIX_SOURCE : "${CC=cc -posix -traditional-cpp}" ;; Ninix3) oswarn='; it is untested' ;; OpenBSD) osnote='; it is untested' ;; OS/2) oswarn='; it is not ported' # cf. https://github.com/komh/pdksh-os2/commit/590f2b19b0ff92a9a373295bce914654f9f5bf22 HAVE_TERMIOS_H=0 : "${CC=gcc}" : "${SIZE=: size}" ;; OS/390) oswarn='; it is not ported' osnote='; EBCDIC support missing' : "${CC=xlc}" : "${SIZE=: size}" add_cppflags -D_ALL_SOURCE ;; OSF1) oswarn='; it is untested' add_cppflags -D_OSF_SOURCE add_cppflags -D_POSIX_C_SOURCE=200112L add_cppflags -D_XOPEN_SOURCE=600 add_cppflags -D_XOPEN_SOURCE_EXTENDED ;; Plan9) oswarn='; it is not ported' add_cppflags -D_POSIX_SOURCE add_cppflags -D_LIMITS_EXTENSION add_cppflags -D_BSD_EXTENSION add_cppflags -D_SUSV2_SOURCE cpp_define MKSH__NO_SYMLINK 1 # this is for detecting kencc cmplrflgs=-DMKSH_MAYBE_KENCC ;; PW32*) oswarn='; it is untested' ;; QNX) oswarn='; it is untested' add_cppflags -D__NO_EXT_QNX add_cppflags -D__EXT_UNIX_MISC ;; scosysv) oswarn='; it is untested' cmplrflgs=-DMKSH_MAYBE_QUICK_C int_as_ssizet ;; SCO_SV) oswarn='; it is untested' case $TARGET_OSREV in 3.2*) # SCO OpenServer 5 ;; 5*) # SCO OpenServer 6 ;; *) oswarn='; this is an unknown version of' oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do" ;; esac ;; SerenityOS) oswarn='; it is untested' ;; SINIX-Z) : "${CC=cc -Xa}" cmplrflgs=-DMKSH_MAYBE_SCDE ;; skyos) oswarn='; it is untested' ;; SunOS) oswarn='; it is untested' add_cppflags -D_BSD_SOURCE add_cppflags -D__EXTENSIONS__ ;; syllable) oswarn='; it is untested' add_cppflags -D_GNU_SOURCE ;; ULTRIX) oswarn='; it is untested' : "${CC=cc -YPOSIX}" int_as_ssizet ;; UnixWare|UNIX_SV) # SCO UnixWare oswarn='; it is untested' ;; UWIN*) oswarn='; it is untested' ccpc='-Yc,' ccpl='-Yl,' tsts=" 3<>/dev/tty" ;; XENIX) oswarn='; it is untested' # mostly when crosscompiling from scosysv cmplrflgs=-DMKSH_MAYBE_QUICK_C # this can barely do anything int_as_ssizet cpp_define MKSH__NO_SYMLINK 1 # these are broken HAVE_TERMIOS_H=0 ;; _svr4) # generic target for SVR4 Unix with uname -s = uname -n oswarn='; it may or may not work' : "${CC=cc -Xa}" cmplrflgs=-DMKSH_MAYBE_SCDE int_as_ssizet #XXX maybe not for *all* _svr4? here for Dell UNIX ;; *) oswarn='; it may or may not work' ;; esac test_n "$TARGET_OSREV" || TARGET_OSREV=`uname -r` : "${AWK=awk}${CC=cc}${NROFF=nroff}${SIZE=size}" test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \ echo | $NROFF -c >/dev/null 2>&1 && NROFF="$NROFF -c" # this aids me in tracing FTBFSen without access to the buildd $e "Hi from$ao $bi$srcversion$ao on:" case $TARGET_OS in AIX) vv '|' "oslevel >&2" vv '|' "uname -a >&2" ;; Darwin) vv '|' "hwprefs machine_type os_type os_class >&2" vv '|' "sw_vers >&2" vv '|' "system_profiler -detailLevel mini SPSoftwareDataType SPHardwareDataType >&2" vv '|' "/bin/sh --version >&2" vv '|' "xcodebuild -version >&2" vv '|' "uname -a >&2" vv '|' "sysctl kern.version hw.machine hw.model hw.memsize hw.availcpu hw.ncpu hw.cpufrequency hw.byteorder hw.cpu64bit_capable >&2" vv '|' "sysctl hw.cpufrequency hw.byteorder hw.cpu64bit_capable hw.ncpu >&2" ;; IRIX*) vv '|' "uname -a >&2" vv '|' "hinv -v >&2" ;; OSF1) vv '|' "uname -a >&2" vv '|' "/usr/sbin/sizer -v >&2" ;; scosysv|SCO_SV|UnixWare|UNIX_SV|XENIX) vv '|' "uname -a >&2" vv '|' "uname -X >&2" ;; *) vv '|' "uname -a >&2" ;; esac test_z "$oswarn" || echo >&2 " Warning: $whatshort has not yet been ported to or tested on your operating system '$TARGET_OS'$oswarn." test_z "$osnote" || echo >&2 " Note: $whatshort is not fully ported to or tested yet on your operating system '$TARGET_OS'$osnote." test_z "$osnote$oswarn" || echo >&2 " If you can provide a shell account to the developer, this may improve; please drop us a success or failure notice or even send patches for the remaining issues, or, at the very least, complete logs (Build.sh + test?) will help. " $e "$bi$me: Building $whatlong$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..." # # Start of mirtoconf checks # $e $bi$me: Scanning for functions... please ignore any errors.$ao # # Compiler: which one? # # notes: # - ICC defines __GNUC__ too # - GCC defines __hpux too # - LLVM+clang defines __GNUC__ too # - nwcc defines __GNUC__ too CPP="$CC -E" $e ... which compiler type seems to be used cat_h_blurb >conftest.c <<'EOF' const char * #if defined(__ICC) || defined(__INTEL_COMPILER) ct="icc" #elif defined(__xlC__) || defined(__IBMC__) ct="xlc" #elif defined(__SUNPRO_C) ct="sunpro" #elif defined(__neatcc__) ct="neatcc" #elif defined(__lacc__) ct="lacc" #elif defined(__ACK__) ct="ack" #elif defined(__BORLANDC__) ct="bcc" #elif defined(__WATCOMC__) ct="watcom" #elif defined(__MWERKS__) ct="metrowerks" #elif defined(__HP_cc) ct="hpcc" #elif defined(__DECC) || (defined(__osf__) && !defined(__GNUC__)) ct="dec" #elif defined(__PGI) ct="pgi" #elif defined(__DMC__) ct="dmc" #elif defined(_MSC_VER) ct="msc" #elif defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__) ct="adsp" #elif defined(__IAR_SYSTEMS_ICC__) ct="iar" #elif defined(SDCC) ct="sdcc" #elif defined(__PCC__) ct="pcc" #elif defined(__TenDRA__) ct="tendra" #elif defined(__TINYC__) ct="tcc" #elif defined(__llvm__) && defined(__clang__) ct="clang" #elif defined(__NWCC__) ct="nwcc" #elif defined(__GNUC__) && (__GNUC__ < 2) ct="gcc1" #elif defined(__GNUC__) ct="gcc" #elif defined(_COMPILER_VERSION) ct="mipspro" #elif defined(__sgi) ct="mipspro" #elif defined(__hpux) || defined(__hpua) ct="hpcc" #elif defined(__ultrix) ct="ucode" #elif defined(__USLC__) ct="uslc" #elif defined(__LCC__) ct="lcc" #elif defined(MKSH_MAYBE_QUICK_C) && defined(_M_BITFIELDS) ct="quickc" #elif defined(MKSH_MAYBE_KENCC) /* and none of the above matches */ ct="kencc" #elif defined(MKSH_MAYBE_SCDE) ct="tryscde" #else ct="unknown" #endif ; const char * #if defined(__KLIBC__) && !defined(__OS2__) et="klibc" #elif defined(__dietlibc__) et="dietlibc" #else et="unknown" #endif ; EOF ct=untested et=untested vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN $cmplrflgs conftest.c | \ sed -n '/^ *[ce]t *= */s/^ *\([ce]t\) *= */\1=/p' | tr -d \\\\015 >x" sed 's/^/[ /' x eval `cat x` rmf x vv.out cat_h_blurb >conftest.c <<'EOF' #include int main(void) { return (isatty(0)); } EOF test_z "$Cg" || Cg=-g # generic case $ct:$TARGET_OS in tryscde:*) case `LC_ALL=C; export LC_ALL; $CC -V 2>&1` in *'Standard C Development Environment'*) ct=scde ;; *) ct=unknown ;; esac ;; esac case $ct in ack) # work around "the famous ACK const bug" CPPFLAGS="-Dconst= $CPPFLAGS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; adsp) echo >&2 'Warning: Analog Devices C++ compiler for Blackfin, TigerSHARC and SHARC (21000) DSPs detected. This compiler has not yet been tested for compatibility with this. Continue at your own risk, please report success/failure to the developers.' ;; bcc) echo >&2 "Warning: Borland C++ Builder detected. This compiler might produce broken executables. Continue at your own risk, please report success/failure to the developers." ;; clang) # does not work with current "ccc" compiler driver vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS --version" # one of these two works, for now vv '|' "${CLANG-clang} -version" # ensure compiler and linker are in sync unless overridden case $CCC_CC:$CCC_LD in :*) ;; *:) CCC_LD=$CCC_CC; export CCC_LD ;; esac ;; dec) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V" vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; dmc) echo >&2 "Warning: Digital Mars Compiler detected. When running under" echo >&2 " UWIN, mksh tends to be unstable due to the limitations" echo >&2 " of this platform. Continue at your own risk," echo >&2 " please report success/failure to the developers." ;; gcc1) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" vv '|' 'eval echo "\`$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpmachine\`" \ "gcc\`$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpversion\`"' : "${HAVE_ATTRIBUTE_EXTENSION=0}" # false positive ;; gcc) test_z "$Cg" || Cg='-g3 -fno-builtin' vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" vv '|' 'eval echo "\`$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpmachine\`" \ "gcc\`$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -dumpversion\`"' ;; hpcc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" case $TARGET_OS,$TARGET_OSREV in HP-UX,B.09.*) : "${HAVE_ATTRIBUTE_EXTENSION=0}" ;; esac ;; iar) echo >&2 'Warning: IAR Systems (http://www.iar.com) compiler for embedded systems detected. This unsupported compiler has not yet been tested for compatibility with this. Continue at your own risk, please report success/failure to the developers.' ;; icc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V" ;; kencc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; lacc) # no version information ;; lcc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" cpp_define __inline__ __inline : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; metrowerks) echo >&2 'Warning: Metrowerks C compiler detected. This has not yet been tested for compatibility with this. Continue at your own risk, please report success/failure to the developers.' ;; mipspro) test_z "$Cg" || Cg='-g3' vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version" : "${HAVE_STDINT_H=0}" # broken unless building with __c99 : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; msc) ccpr= # errorlevels are not reliable case $TARGET_OS in Interix) if test_z "$C89_COMPILER"; then C89_COMPILER=CL.EXE else C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"` fi if test_z "$C89_LINKER"; then C89_LINKER=LINK.EXE else C89_LINKER=`ntpath2posix -c "$C89_LINKER"` fi vv '|' "$C89_COMPILER /HELP >&2" vv '|' "$C89_LINKER /LINK >&2" ;; esac ;; neatcc) cpp_define MKSH_DONT_EMIT_IDSTRING 1 vv '|' "$CC" ;; nwcc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version" ;; pcc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v" ;; pgi) echo >&2 'Warning: PGI detected. This unknown compiler has not yet been tested for compatibility with this. Continue at your own risk, please report success/failure to the developers.' ;; quickc) # no version information : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; scde) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; sdcc) echo >&2 'Warning: sdcc (http://sdcc.sourceforge.net), the small devices C compiler for embedded systems detected. This has not yet been tested for compatibility with this. Continue at your own risk, please report success/failure to the developers.' ;; sunpro) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" ;; tcc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v" ;; tendra) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \ grep -i -e version -e release" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # false positive ;; ucode) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V" vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; uslc) case $TARGET_OS:$TARGET_OSREV in SCO_SV:3.2*) # SCO OpenServer 5 CFLAGS="$CFLAGS -g" : "${HAVE_CAN_OTWO=0}${HAVE_CAN_OPTIMISE=0}" ;; esac vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" : "${HAVE_ATTRIBUTE_EXTENSION=0}" # skip checking as we know it absent ;; watcom) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" ;; xlc) vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion" vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion=verbose" vv '|' "ld -V" ;; *) test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed" ct=unknown vv '|' "$CC --version" vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS" vv '|' "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS" ;; esac case $cm in dragonegg|llvm) vv '|' "llc -version" ;; esac etd=" on $et" # still imake style but… can’t be helped case $et in dietlibc) # live, BSD, live❣ #add_cppflags -D_BSD_SOURCE # dietlibc has u_long as uint32_t in many versions, ouch, # but thankfully ifdef _BSD_SOURCE, so… ouch²… add_cppflags -U_BSD_SOURCE : "${HAVE_CAN_ULONG=0}" ;; klibc) ;; unknown) # nothing special detected, don’t worry etd= ;; *) # huh? ;; esac $e "$bi==> which compiler type seems to be used...$ao $ui$ct$etd$ao" rmf conftest.c conftest.o conftest a.out* a.exe* conftest.exe* vv.out # # Compiler: works as-is, with -Wno-error and -Werror # save_NOWARN=$NOWARN NOWARN= DOWARN= ac_flags 0 compiler_works '' 'if the compiler works' test 1 = $HAVE_CAN_COMPILER_WORKS || exit 1 HAVE_COMPILER_KNOWN=0 test $ct = unknown || HAVE_COMPILER_KNOWN=1 if ac_ifcpp 'if 0' compiler_fails '' \ 'if the compiler does not fail correctly'; then save_CFLAGS=$CFLAGS : "${HAVE_CAN_DELEXE=x}" case $ct in dec) CFLAGS="$CFLAGS ${ccpl}-non_shared" ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-'EOF' #include int main(void) { return (isatty(0)); } EOF ;; dmc) CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE" ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-'EOF' #include int main(void) { return (isatty(0)); } EOF ;; *) exit 1 ;; esac test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS ac_ifcpp 'if 0' compiler_still_fails \ 'if the compiler still does not fail correctly' && exit 1 fi if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \ 'if this could be tcc'; then ct=tcc CPP='cpp -D__TINYC__' HAVE_COMPILER_KNOWN=1 fi case $ct in bcc) save_NOWARN="${ccpc}-w" DOWARN="${ccpc}-w!" ;; dec) # -msg_* flags not used yet, or is -w2 correct? ;; dmc) save_NOWARN="${ccpc}-w" DOWARN="${ccpc}-wx" ;; hpcc) save_NOWARN= DOWARN=+We ;; kencc) save_NOWARN= DOWARN= ;; mipspro) save_NOWARN= DOWARN="-diag_error 1-10000" ;; msc) save_NOWARN="${ccpc}/w" DOWARN="${ccpc}/WX" ;; quickc) ;; scde) ;; sunpro) test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none' ac_flags 0 errwarnnone "$save_NOWARN" test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN= ac_flags 0 errwarnall "-errwarn=%all" test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all" ;; tendra) save_NOWARN=-w ;; ucode) save_NOWARN= DOWARN=-w2 ;; watcom) save_NOWARN= DOWARN=-Wc,-we ;; xlc) case $TARGET_OS in OS/390) save_NOWARN=-qflag=e DOWARN=-qflag=i ;; *) save_NOWARN=-qflag=i:e DOWARN=-qflag=i:i ;; esac ;; *) test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error ac_flags 0 wnoerror "$save_NOWARN" test 1 = $HAVE_CAN_WNOERROR || save_NOWARN= ac_flags 0 werror -Werror test 1 = $HAVE_CAN_WERROR && DOWARN=-Werror test $ct = icc && DOWARN="$DOWARN -wd1419" ;; esac NOWARN=$save_NOWARN # # Compiler: extra flags (-O2 -f* -W* etc.) # i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln` # optimisation: only if orig_CFLAGS is empty test_n "$i" || case $ct in hpcc) phase=u ac_flags 1 otwo +O2 phase=x ;; kencc|quickc|scde|tcc|tendra) # no special optimisation ;; sunpro) cat >x <<-'EOF' #include int main(void) { return (isatty(0)); } #define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p #define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p) #define pad void __IDSTRING_EXPAND(__LINE__,x)(void) { } EOF yes pad | head -n 256 >>x ac_flags - 1 otwo -xO2 x ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' not found. # CCN3944: Attribute "__foo__" is not supported and is ignored. # CCN3963: The attribute "foo" is not a valid variable attribute and is ignored. ac_flags 1 halton '-qhaltonmsg=CCN3296 -qhaltonmsg=CCN3944 -qhaltonmsg=CCN3963' # CCN3290: Unknown macro name FOO on #undef directive. # CCN4108: The use of keyword '__attribute__' is non-portable. ac_flags 1 supprss '-qsuppress=CCN3290 -qsuppress=CCN4108' ;; *) ac_flags 1 rodata '-qro -qroconst -qroptr' ac_flags 1 rtcheck -qcheck=all #ac_flags 1 rtchkc -qextchk # reported broken ac_flags 1 wformat '-qformat=all -qformat=nozln' ;; esac #ac_flags 1 wp64 -qwarn64 # too verbose for now ;; esac # flags common to a subset of compilers (run with -Werror on gcc) if test 1 = $i; then ac_flags 1 wall -Wall ac_flags 1 fwrapv -fwrapv fi phase=x # The following tests run with -Werror or similar (all compilers) if possible NOWARN=$DOWARN test $ct = pcc && phase=u # # Compiler: check for stuff that only generates warnings # : "${HAVE_ATTRIBUTE_EXTENSION=1}" # not a separate test but a dependency ac_test attribute_bounded attribute_extension 0 'for __attribute__((__bounded__))' <<-'EOF' #include #undef __attribute__ int xcopy(const void *, void *, size_t) __attribute__((__bounded__(__buffer__, 1, 3))) __attribute__((__bounded__(__buffer__, 2, 3))); int main(int ac, char *av[]) { return (xcopy(av[0], av[--ac], 1)); } int xcopy(const void *s, void *d, size_t n) { /* * if memmove does not exist, we are not on a system * with GCC with __bounded__ attribute either so poo */ memmove(d, s, n); return ((int)n); } EOF ac_test attribute_format attribute_extension 0 'for __attribute__((__format__))' <<-'EOF' #define fprintf printfoo #include #undef __attribute__ #undef fprintf extern int fprintf(FILE *, const char *format, ...) __attribute__((__format__(__printf__, 2, 3))); int main(int ac, char *av[]) { return (fprintf(stderr, "%s%d", *av, ac)); } EOF ac_test attribute_nonnull attribute_extension 0 'for __attribute__((__nonnull__))' <<-'EOF' #include #undef __attribute__ int fnord(const char *) __attribute__((__nonnull__(1))); int main(void) { return (fnord("x")); } int fnord(const char *x) { return (fputc(*x, stderr)); } EOF ac_test attribute_noreturn attribute_extension 0 'for __attribute__((__noreturn__))' <<-'EOF' #include #undef __attribute__ void fnord(void) __attribute__((__noreturn__)); int main(void) { fnord(); } void fnord(void) { exit(0); } EOF ac_test attribute_pure attribute_extension 0 'for __attribute__((__pure__))' <<-'EOF' #include #undef __attribute__ int foo(const char *) __attribute__((__pure__)); int main(int ac, char *av[]) { return (foo(av[ac - 1]) + isatty(0)); } int foo(const char *s) { return ((int)s[0]); } EOF ac_test attribute_unused attribute_extension 0 'for __attribute__((__unused__))' <<-'EOF' #include #undef __attribute__ int main(int ac __attribute__((__unused__)), char *av[] __attribute__((__unused__))) { return (isatty(0)); } EOF ac_test attribute_used attribute_extension 0 'for __attribute__((__used__))' <<-'EOF' #include #undef __attribute__ static const char fnord[] __attribute__((__used__)) = "42"; int main(void) { return (isatty(0)); } EOF # End of tests run with -Werror NOWARN=$save_NOWARN phase=x # # Environment: headers # ac_header sys/time.h sys/types.h ac_header time.h sys/types.h test "11" = "$HAVE_SYS_TIME_H$HAVE_TIME_H" || HAVE_BOTH_TIME_H=0 ac_test both_time_h '' 'whether and can both be included' <<-'EOF' #include #include #include #include int main(void) { struct tm tm; return ((int)sizeof(tm) + isatty(0)); } EOF ac_header sys/mkdev.h sys/types.h ac_header sys/mtio.h sys/types.h ac_header sys/resource.h sys/types.h _time ac_header sys/sysmacros.h ac_header grp.h sys/types.h ac_header paths.h ac_header stdint.h stdarg.h # include strings.h only if compatible with string.h ac_header strings.h sys/types.h string.h ac_header utime.h _time ac_header utmp.h _time ac_header utmpx.h _time ac_header vis.h stdlib.h # # Environment: definitions # echo '#include #include #include struct ctassert_offt { off_t min63bits:63; }; int main(void) { return ((int)sizeof(struct ctassert_offt)); }' >lft.c ac_testn can_lfs '' "for large file support" #include int main(int ac, char *av[]) { return ((uint32_t)(size_t)*av + (int32_t)ac); } EOF ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF' #include #include int main(int ac, char *av[]) { return ((u_int32_t)(size_t)*av + (int32_t)ac); } EOF ac_test can_ulong '' "for u_long" <<-'EOF' #include #include int main(int ac, char *av[]) { return ((u_long)(size_t)av[ac]); } EOF # # check whether whatever we use for the final link will succeed # if test $cm = makefile; then : nothing to check else HAVE_LINK_WORKS=x ac_testinit link_works '' 'if the final link command may succeed' fv=1 cat >conftest.c <<-EOF #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #if HAVE_SYS_MTIO_H #include #endif #if HAVE_SYS_RESOURCE_H #include #endif #include #include #include #include #include #include #include #if HAVE_GRP_H #include #endif #include #if HAVE_PATHS_H #include #endif #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #if HAVE_UTMP_H #include #elif HAVE_UTMPX_H #include #endif #if HAVE_VIS_H #include #endif #define EXTERN #define MIRTOCONF_EARLY #include "pax.h" #include "ftimes.h" #include "ar.h" #include "cpio.h" #include "tar.h" #include "extern.h" __RCSID("$srcversion"); int main(void) { printf("Hello, World!\\n"); return (isatty(0)); } EOF case $cm in llvm) v "$CC $CFLAGS $Cg $CPPFLAGS $NOWARN -emit-llvm -c conftest.c" || fv=0 rmf $tfn.s test $fv = 0 || v "llvm-link -o - conftest.o | opt $optflags | llc -o $tfn.s" || fv=0 test $fv = 0 || v "$CC $CFLAGS $Cg $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr" ;; dragonegg) v "$CC $CFLAGS $Cg $CPPFLAGS $NOWARN -S -flto conftest.c" || fv=0 test $fv = 0 || v "mv conftest.s conftest.ll" test $fv = 0 || v "llvm-as conftest.ll" || fv=0 rmf $tfn.s test $fv = 0 || v "llvm-link -o - conftest.bc | opt $optflags | llc -o $tfn.s" || fv=0 test $fv = 0 || v "$CC $CFLAGS $Cg $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr" ;; combine) v "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS -fwhole-program --combine $NOWARN -o $tcfn conftest.c $LIBS $ccpr" ;; lto|normal) cm=normal v "$CC $CFLAGS $Cg $CPPFLAGS $NOWARN -c conftest.c" || fv=0 test $fv = 0 || v "$CC $CFLAGS $Cg $LDFLAGS -o $tcfn conftest.o $LIBS $ccpr" ;; esac test -f $tcfn || fv=0 ac_testdone test $fv = 1 || exit 1 fi # # Environment: library functions # ac_test dprintf <<-'EOF' #include int main(void) { return (dprintf(1, "hi\n")); } EOF ac_test fchmodat <<-'EOF' #include #include #include #include int main(void) { return (fchmodat(AT_FDCWD, ".", 0, AT_SYMLINK_NOFOLLOW)); } EOF ac_test fchownat <<-'EOF' #include #include int main(void) { return (fchownat(AT_FDCWD, ".", 0, 0, AT_SYMLINK_NOFOLLOW)); } EOF ac_test futimens <<-'EOF' #include #include #include struct timespec ts[2] = {{0L, 0L}, {0L, UTIME_OMIT}}; int main(void) { return (futimens(0, ts)); } EOF ac_test lchmod '!' fchmodat 0 <<-'EOF' #include #include int main(void) { return (lchmod(".", 0)); } EOF ac_test lchown '!' fchownat 0 <<-'EOF' #include #include int main(void) { return (lchown(".", 0, 0)); } EOF ac_test linkat <<-'EOF' #include #include int main(int ac, char *av[]) { return (linkat(AT_FDCWD, av[1], AT_FDCWD, av[2], ac)); } EOF ac_test pledge <<-'EOF' #include int main(void) { return (pledge("", "")); } EOF ac_test reallocarray <<-'EOF' #include int main(void) { return ((void *)reallocarray(NULL, 3, 3) == (void *)0UL); } EOF ac_test setpgent grp_h 0 'for setpassent and setgroupent' <<-'EOF' #include #include #include int main(void) { return (setpassent(1) + setgroupent(1)); } EOF ac_test strlcpy <<-'EOF' #include int main(int ac, char *av[]) { return (strlcpy(*av, av[1], (size_t)ac)); } EOF ac_test strlcat strlcpy 0 <<-'EOF' #include int main(int ac, char *av[]) { return (strlcat(*av, av[1], (size_t)ac)); } EOF ac_test strmode <<-'EOF' #include #include int main(int ac, char *av[]) { strmode(ac, av[0]); return (*av[0]); } EOF ac_test strtonum <<-'EOF' #include #include int main(int ac, char *av[]) { return (ac == (int)strtonum(av[1], 0, 100, NULL)); } EOF ac_test ug_from_ugid grp_h 0 'for user_from_uid and group_from_gid' <<-'EOF' #include #include #include int main(void) { return (*user_from_uid(0, 0) ^ *group_from_gid(0, 0)); } EOF ac_test ugid_from_ug ug_from_ugid 0 'for uid_from_user and gid_from_group' <<-'EOF' #include #include #include int main(void) { uid_t uid; gid_t gid; uid_from_user("nobody", &uid); gid_from_group("nobody", &gid); return (uid ^ gid); } EOF ac_test utimensat <<-'EOF' #include #include #include struct timespec ts[2] = {{0L, 0L}, {0L, UTIME_OMIT}}; int main(void) { return (utimensat(AT_FDCWD, ".", ts, AT_SYMLINK_NOFOLLOW)); } EOF ac_test utimes '!' utimensat 0 <<-'EOF' #include struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; int main(void) { return (utimes(".", tv)); } EOF ac_test lutimes '!' utimensat 0 <<-'EOF' #include struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; int main(void) { return (lutimes(".", tv)); } EOF ac_test futimes '!' futimens 0 <<-'EOF' #include struct timeval tv[2] = {{0L, 0L}, {0L, 0L}}; int main(void) { return (futimes(0, tv)); } EOF # # check headers for declarations # # # other checks # ac_test offt_long '' 'whether off_t is as wide as long' <<-'EOF' #include #include #include #ifndef CHAR_BIT #define CHAR_BIT 0 #endif struct ctasserts { #define cta(name,assertion) char name[(assertion) ? 1 : -1] cta(char_is_8_bits, (CHAR_BIT) == 8); cta(off_t_is_long, sizeof(off_t) == sizeof(long)); }; int main(void) { return (sizeof(struct ctasserts)); } EOF ac_testn offt_llong '!' offt_long 0 'whether off_t is as wide as long long' <<-'EOF' #include #include #include #ifndef CHAR_BIT #define CHAR_BIT 0 #endif struct ctasserts { #define cta(name,assertion) char name[(assertion) ? 1 : -1] cta(char_is_8_bits, (CHAR_BIT) == 8); cta(off_t_is_llong, sizeof(off_t) == sizeof(long long)); }; int main(void) { return (sizeof(struct ctasserts)); } EOF case $HAVE_OFFT_LLONG$HAVE_OFFT_LONG in 10|01) ;; *) echo Cannot determine width of off_t exit 1 ;; esac ac_test timet_long '' 'whether time_t is as wide as long' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #ifndef CHAR_BIT #define CHAR_BIT 0 #endif struct ctasserts { #define cta(name,assertion) char name[(assertion) ? 1 : -1] cta(char_is_8_bits, (CHAR_BIT) == 8); cta(time_t_is_long, sizeof(time_t) == sizeof(long)); }; int main(void) { return (sizeof(struct ctasserts)); } EOF ac_testn timet_llong '!' timet_long 0 'whether time_t is as wide as long long' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #ifndef CHAR_BIT #define CHAR_BIT 0 #endif struct ctasserts { #define cta(name,assertion) char name[(assertion) ? 1 : -1] cta(char_is_8_bits, (CHAR_BIT) == 8); cta(time_t_is_llong, sizeof(time_t) == sizeof(long long)); }; int main(void) { return (sizeof(struct ctasserts)); } EOF case $HAVE_TIMET_LLONG$HAVE_TIMET_LONG in 10|01) ;; *) echo Cannot determine width of time_t exit 1 ;; esac ac_test timet_large '' 'whether time_t is wider than 32 bit' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #ifndef CHAR_BIT #define CHAR_BIT 0 #endif struct ctasserts { #define cta(name,assertion) char name[(assertion) ? 1 : -1] cta(char_is_8_bits, (CHAR_BIT) == 8); cta(time_t_is_large, sizeof(time_t) > 4); }; int main(void) { return (sizeof(struct ctasserts)); } EOF ac_testn st_mtimensec '' 'for struct stat.st_mtimensec' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); } EOF ac_testn st_mtimespec '!' st_mtimensec 0 'for struct stat.st_mtimespec.tv_nsec' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include int main(void) { struct stat sb; return (sizeof(sb.st_mtimespec.tv_nsec)); } EOF if test 1 = "$HAVE_ST_MTIMESPEC"; then cpp_define st_atimensec st_atimespec.tv_nsec cpp_define st_ctimensec st_ctimespec.tv_nsec cpp_define st_mtimensec st_mtimespec.tv_nsec HAVE_ST_MTIMENSEC=1 fi ac_testn st_mtim '!' st_mtimensec 0 'for struct stat.st_mtim.tv_nsec' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); } EOF if test 1 = "$HAVE_ST_MTIM"; then cpp_define st_atimensec st_atim.tv_nsec cpp_define st_ctimensec st_ctim.tv_nsec cpp_define st_mtimensec st_mtim.tv_nsec HAVE_ST_MTIMENSEC=1 fi ac_testn st_mtime_nsec '!' st_mtimensec 0 'for struct stat.st_mtime_nsec' <<-'EOF' #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include int main(void) { struct stat sb; return (sizeof(sb.st_mtime_nsec)); } EOF if test 1 = "$HAVE_ST_MTIME_NSEC"; then cpp_define st_atimensec st_atime_nsec cpp_define st_ctimensec st_ctime_nsec cpp_define st_mtimensec st_mtime_nsec HAVE_ST_MTIMENSEC=1 fi ac_cppflags ST_MTIMENSEC # # Compiler: Praeprocessor (only if needed) # # # End of mirtoconf checks # $e ... done. rmf vv.out addsrcs '!' HAVE_UGID_FROM_UG cache.c addsrcs '!' HAVE_REALLOCARRAY reallocarray.c addsrcs '!' HAVE_STRMODE strmode.c addsrcs '!' HAVE_STRTONUM strtonum.c test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose" $e $bi$me: Finished configuration testing, now producing output.$ao files= objs= fsp= case $tcfn in a.exe|conftest.exe) buildoutput=$tfn.exe paxexe=$paxname.exe cpioexe=$cpioname.exe tarexe=$tarname.exe cpp_define MKSH_EXE_EXT 1 ;; *) buildoutput=$tfn paxexe=$paxname cpioexe=$cpioname tarexe=$tarname ;; esac case $cm in dragonegg) emitbc="-S -flto" ;; llvm) emitbc="-emit-llvm -c" ;; *) emitbc=-c ;; esac echo ": # work around NeXTstep bug" >Rebuild.sh echo set -x >>Rebuild.sh for file in $SRCS; do op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'` test -f $file || file=$srcdir/$file files="$files$fsp$file" echo "$CC $CFLAGS $Cg $CPPFLAGS $emitbc $file || exit 1" >>Rebuild.sh if test $cm = dragonegg; then echo "mv ${op}s ${op}ll" >>Rebuild.sh echo "llvm-as ${op}ll || exit 1" >>Rebuild.sh objs="$objs$fsp${op}bc" else objs="$objs$fsp${op}o" fi fsp=$sp done case $cm in dragonegg|llvm) echo "rm -f $tfn.s" >>Rebuild.sh echo "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s" >>Rebuild.sh lobjs=$tfn.s ;; *) lobjs=$objs ;; esac echo tcfn=$buildoutput >>Rebuild.sh echo "$CC $CFLAGS $Cg $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh echo "rm -f $paxexe $cpioexe $tarexe" >>Rebuild.sh echo "for x in $paxexe $cpioexe $tarexe; do" >>Rebuild.sh echo " ln \$tcfn \$x || cp -p \$tcfn \$x || exit 1" >>Rebuild.sh echo "done" >>Rebuild.sh if test $cm = makefile; then extras='.linked/reallocarray.inc .linked/strlfun.inc .linked/strmode.inc .linked/strtonum.inc ar.h compat.h cpio.h extern.h ftimes.h pax.h tar.h' cat >Makefrag.inc < EOF $e $e Generated Makefrag.inc successfully. exit 0 fi if test $cm = combine; then objs="-o $buildoutput" for file in $SRCS; do test -f $file || file=$srcdir/$file objs="$objs $file" done emitbc="-fwhole-program --combine" v "$CC $CFLAGS $Cg $CPPFLAGS $LDFLAGS $emitbc $objs $LIBS $ccpr" elif test 1 = $pm; then for file in $SRCS; do test -f $file || file=$srcdir/$file v "$CC $CFLAGS $Cg $CPPFLAGS $emitbc $file" & done wait else for file in $SRCS; do test $cm = dragonegg && \ op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'` test -f $file || file=$srcdir/$file v "$CC $CFLAGS $Cg $CPPFLAGS $emitbc $file" || exit 1 if test $cm = dragonegg; then v "mv ${op}s ${op}ll" v "llvm-as ${op}ll" || exit 1 fi done fi case $cm in dragonegg|llvm) rmf $tfn.s v "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s" ;; esac tcfn=$buildoutput case $cm in combine) ;; *) v "$CC $CFLAGS $Cg $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr" ;; esac test -f $tcfn || exit 1 rm -f $paxexe $cpioexe $tarexe for x in $paxexe $cpioexe $tarexe; do ln $tcfn $x || cp -p $tcfn $x || exit 1 done rm -rf mans mkdir mans echo .nr g $mans | cat - "$srcdir/cpio.1" >mans/$cpioname.1 echo .nr g $mans | cat - "$srcdir/pax.1" >mans/$paxname.1 echo .nr g $mans | cat - "$srcdir/tar.1" >mans/$tarname.1 test 1 = $r || v "$NROFF -mdoc mans/$cpioname.cat1" || rmf mans/$cpioname.cat1 test 1 = $r || v "$NROFF -mdoc mans/$paxname.cat1" || rmf mans/$paxname.cat1 test 1 = $r || v "$NROFF -mdoc mans/$tarname.cat1" || rmf mans/$tarname.cat1 test 0 = $eq && v $SIZE $tcfn i=install test -f /usr/ucb/$i && i=/usr/ucb/$i test 1 = $eq && e=: $e $e Installing the executable: $e "# $i -c -s -o root -g bin -m 555 $paxexe /bin/$paxexe" for x in $cpioexe $tarexe; do $e "# ln -f /bin/$paxexe /bin/$x || cp -fp /bin/$paxexe /bin/$x" done $e $e Installing the manual: if test -f mans/$paxname.cat1; then $e "# $i -c -o root -g bin -m 444 mans/$cpioname.cat1" \ "/usr/share/man/cat1/$cpioname.0" $e "# $i -c -o root -g bin -m 444 mans/$paxname.cat1" \ "/usr/share/man/cat1/$paxname.0" $e "# $i -c -o root -g bin -m 444 mans/$tarname.cat1" \ "/usr/share/man/cat1/$tarname.0" $e or fi $e "# $i -c -o root -g bin -m 444 mans/$cpioname.1 mans/$paxname.1 mans/$tarname.1 /usr/share/man/man1/" $e $e Please also read the fine manual. exit 0 : <<'EOD' === Environment used === ==== build environment ==== CC default: cc CFLAGS if empty, defaults to -xO2 or +O2 or -O3 -qstrict or -O2, per compiler CPPFLAGS default empty LDFLAGS default empty; added before sources LDSTATIC set this to '-static'; default unset LIBS default empty; added after sources NOWARN -Wno-error or similar NROFF default: nroff TARGET_OS default: `uname -s || uname` TARGET_OSREV default: `uname -r` [only needed on some OS] ===== general format ===== HAVE_STRLEN ac_test HAVE_STRING_H ac_header HAVE_CAN_FSTACKPROTECTORALL ac_flags ==== cpp definitions ==== DEBUG don’t use in production, wants gcc MKSH_DONT_EMIT_IDSTRING omit RCS IDs from binary PAX_SAFE_PATH subprocess PATH, default "/bin:/usr/bin" SMALL for the MirBSD installer/rescue system === generic installation instructions === Set CC and possibly CFLAGS, CPPFLAGS, LDFLAGS, LIBS. If cross-compiling, also set TARGET_OS. To disable tests, set e.g. HAVE_STRLCPY=0; to enable them, set to a value other than 0 or 1 (e.g. to x). Normally, the following command is what you want to run, then: $ sh Build.sh -r 2>&1 | tee log Install as /bin/pax and hardlink to /bin/cpio and /bin/tar or install as $prefix/bin/mirpax and hardlink to $prefix/bin/mir{cpio,tar}; install the manpages, if omitting the -r flag catmanpages are made using $NROFF. Add -tmir to install as mir{pax,cpio,tar} or -tpax for pax{,cpio,tar}. EOD pax/Makefile010064400000000000000000000131461466023664500102560ustar00# $MirOS: src/bin/pax/Makefile,v 1.35 2024/08/18 00:17:14 tg Exp $ #- # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, # 2020, 2023, 2024 # mirabilos # Copyright (c) 2018 # mirabilos # # Provided that these terms and disclaimer and all copyright notices # are retained or reproduced in an accompanying document, permission # is granted to deal in this work without restriction, including un- # limited rights to use, publicly perform, distribute, sell, modify, # merge, give away, or sublicence. # # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to # the utmost extent permitted by applicable law, neither express nor # implied; without malicious intent or gross negligence. In no event # may a licensor, author or contributor be held liable for indirect, # direct, other damage, loss, or other issues arising in any way out # of dealing in the work, even if advised of the possibility of such # damage or existence of a defect, except proven that it results out # of said person's immediate fault when using the work as intended. #- # MirMakefile explicitly as part of MirBSD base; use Build.sh if you # wish to build paxmirabilis (MirCPIO) on anything else, these days. .ifdef cats .MAIN: cats .endif .include SRCDIR= ${.CURDIR} PROG= pax SRCS= ar.c ar_io.c ar_subs.c buf_subs.c compat.c cpio.c \ file_subs.c ftree.c gen_subs.c getoldopt.c options.c \ pat_rep.c pax.c sel_subs.c tables.c tar.c tty_subs.c SRCS+= cache.c MAN= cpio.1 pax.1 tar.1 LINKS+= ${BINDIR}/pax ${BINDIR}/cpio LINKS+= ${BINDIR}/pax ${BINDIR}/tar .if !make(test-build) # ^K/ mksh /usr/src/contrib/hosted/tg/retab CPPFLAGS+= \ -DHAVE_ATTRIBUTE_BOUNDED=1 -DHAVE_ATTRIBUTE_FORMAT=1 \ -DHAVE_ATTRIBUTE_NONNULL=1 -DHAVE_ATTRIBUTE_NORETURN=1 \ -DHAVE_ATTRIBUTE_PURE=1 -DHAVE_ATTRIBUTE_UNUSED=1 \ -DHAVE_ATTRIBUTE_USED=1 -DHAVE_SYS_TIME_H=1 -DHAVE_TIME_H=1 \ -DHAVE_BOTH_TIME_H=1 -DHAVE_SYS_MKDEV_H=0 -DHAVE_SYS_MTIO_H=1 \ -DHAVE_SYS_RESOURCE_H=1 -DHAVE_SYS_SYSMACROS_H=0 \ -DHAVE_GRP_H=1 -DHAVE_PATHS_H=1 -DHAVE_STDINT_H=1 \ -DHAVE_STRINGS_H=1 -DHAVE_UTIME_H=1 -DHAVE_UTMP_H=1 \ -DHAVE_UTMPX_H=0 -DHAVE_VIS_H=1 -DHAVE_CAN_INTTYPES=1 \ -DHAVE_CAN_UCBINTS=1 -DHAVE_CAN_ULONG=1 -DHAVE_DPRINTF=0 \ -DHAVE_FCHMODAT=0 -DHAVE_FCHOWNAT=0 -DHAVE_FUTIMENS=0 \ -DHAVE_LCHMOD=1 -DHAVE_LCHOWN=1 -DHAVE_LINKAT=0 \ -DHAVE_PLEDGE=0 -DHAVE_REALLOCARRAY=1 -DHAVE_SETPGENT=1 \ -DHAVE_STRLCPY=1 -DHAVE_STRLCAT=1 -DHAVE_STRMODE=1 \ -DHAVE_STRTONUM=1 -DHAVE_UG_FROM_UGID=1 -DHAVE_UGID_FROM_UG=0 \ -DHAVE_UTIMENSAT=0 -DHAVE_UTIMES=1 -DHAVE_LUTIMES=1 \ -DHAVE_FUTIMES=1 -DHAVE_OFFT_LONG=0 -DHAVE_TIMET_LONG=0 \ -DHAVE_TIMET_LARGE=1 -DHAVE_ST_MTIMENSEC=1 CPPFLAGS+= -I. CPPFLAGS+= -D_ALL_SOURCE COPTS+= -Wall .endif COPTS+= -std=c89 -U__STRICT_ANSI__ # for MirBSD only, for a while CPPFLAGS+= -DREALPATH_CAN_ALLOCATE=2 SAFE_PATH= /bin:/usr/bin:/usr/mpkg/bin:/usr/local/bin CPPFLAGS+= -DPAX_SAFE_PATH=\"${SAFE_PATH:Q}\" TEST_BUILD_ENV:= TARGET_OS= CPP= TEST_BUILD_ENV+= HAVE_PLEDGE=0 # for now, on MirBSD test-build: .PHONY -rm -rf build-dir mkdir -p build-dir cd build-dir; env CC=${CC:Q} CFLAGS=${CFLAGS:M*:Q} \ CPPFLAGS=${CPPFLAGS:M*:Q} LDFLAGS=${LDFLAGS:M*:Q} \ LIBS=${LDADD:Q} NOWARN=-Wno-error ${TEST_BUILD_ENV} \ /bin/sh ${SRCDIR:Q}/Build.sh -Q -r cleandir: clean-extra clean-extra: .PHONY -rm -rf build-dir CLEANFILES+= ${MANALL:S/.cat/.ps/} ${MAN:S/$/.pdf/} ${MANALL:S/$/.gz/} CLEANFILES+= ${MAN:S/$/.htm/} ${MAN:S/$/.htm.gz/} CLEANFILES+= ${MAN:S/$/.txt/} ${MAN:S/$/.txt.gz/} .include CLEANFILES+= cpio tar all: cpio tar cpio: ln -sf ${PROG} cpio tar: ln -sf ${PROG} tar .ifmake cats V_GROFF!= pkg_info -e 'groff-[0-9]*' V_GHOSTSCRIPT!= pkg_info -e 'ghostscript-[0-9]*' . if empty(V_GROFF) || empty(V_GHOSTSCRIPT) . error empty V_GROFF=${V_GROFF} or V_GHOSTSCRIPT=${V_GHOSTSCRIPT} . endif .endif CATS_KW= cpio, pax, tar CATS_TITLE_cpio_1=paxcpio - copy file archives in and out CATS_TITLE_pax_1=pax - read and write file archives and copy directory hierarchies CATS_TITLE_tar_1=paxtar - Unix tape archiver CATS_PAGES= cpio 1 pax 1 tar 1 catsall:= ${MANALL} .for _m _n in ${CATS_PAGES} catsall:= ${catsall:N${_m}.cat${_n}} .endfor .if "${catsall}" != "" . error Adjust here. .endif cats: ${MANALL} ${MANALL:S/.cat/.ps/} .for _m _n in ${CATS_PAGES} x=$$(ident ${SRCDIR:Q}/${_m}.${_n} | \ awk '/Mir''OS:/ { print $$4$$5; }' | \ tr -dc 0-9); (( $${#x} == 14 )) || exit 1; exec \ ${MKSH} ${BSDSRCDIR:Q}/contrib/hosted/tg/ps2pdfmir -p pa4 -c \ -o ${_m}.${_n}.pdf '[' /Author '(MirBSD)' \ /Title '('${CATS_TITLE_${_m}_${_n}:Q}')' \ /Subject '(BSD Reference Manual)' /ModDate "(D:$$x)" \ /Creator '(GNU groff version ${V_GROFF:S/groff-//} \(MirPorts\))' \ /Producer '(Artifex Ghostscript ${V_GHOSTSCRIPT:S/ghostscript-//:S/-artifex//} \(MirPorts\))' \ /Keywords '('${CATS_KW:Q}')' /DOCINFO pdfmark \ -f ${_m}.ps${_n} .endfor set -e; . ${BSDSRCDIR:Q}/scripts/roff2htm; set_target_absolute; \ for m in ${MANALL}; do \ bn=$${m%.*}; ext=$${m##*.cat}; \ [[ $$bn != $$m ]]; [[ $$ext != $$m ]]; \ gzip -n9 <"$$m" >"$$m.gz"; \ col -bx <"$$m" >"$$bn.$$ext.txt"; \ rm -f "$$bn.$$ext.txt.gz"; gzip -n9 "$$bn.$$ext.txt"; \ do_conversion_verbose "$$bn" "$$ext" "$$m" "$$bn.$$ext.htm"; \ rm -f "$$bn.$$ext.htm.gz"; gzip -n9 "$$bn.$$ext.htm"; \ done .ifdef cats . for _m _n in ${CATS_PAGES} mv -f ${_m}.cat${_n}.gz ${_m}-${cats}.cat${_n}.gz mv -f ${_m}.${_n}.htm.gz ${_m}-${cats}.htm.gz mv -f ${_m}.${_n}.pdf ${_m}-${cats}.pdf mv -f ${_m}.${_n}.txt.gz ${_m}-${cats}.txt.gz . endfor .endif pax/ar.c010064400000000000000000000166661343011625100073560ustar00/*- * Copyright © 2011, 2017, 2019 * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un‐ * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person’s immediate fault when using the work as intended. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "ar.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/ar.c,v 1.12 2019/02/10 21:50:06 tg Exp $"); /* * Routines for reading and writing Unix Archiver format libraries */ static const char magic[8] = { 0x21, 0x3C, 0x61, 0x72, 0x63, 0x68, 0x3E, 0x0A }; /* * initialisation for ar write * returns 0 if ok, -1 otherwise */ int uar_stwr(int is_app) { return (is_app ? 0 : wr_rdbuf(magic, 8)); } /* * check for ar magic * returns 0 if ok, -1 otherwise */ int uar_ismagic(char *buf) { return (memcmp(buf, magic, 8) ? -1 : 0); } /* * used during format identification, but we differ */ int uar_id(char *buf MKSH_A_UNUSED, int len MKSH_A_UNUSED) { errx(1, "internal error: %s should never have been called", "uar_id"); } /* internal parsing functions */ static uint64_t uar_atoi64(const char *buf, size_t len) { char c; uint64_t res = 0; loop: if (!len-- || (c = *buf++) < '0' || c > '9') return (res); res = (res * 10) + (c - '0'); goto loop; } static uint32_t uar_atoi32(const char *buf, size_t len) { char c; uint32_t res = 0; loop: if (!len-- || (c = *buf++) < '0' || c > '9') return (res); res = (res * 10) + (c - '0'); goto loop; } static uint32_t uar_otoi32(const char *buf, size_t len) { char c; uint32_t res = 0; loop: if (!len-- || (c = *buf++) < '0' || c > '7') return (res); res = (res << 3) | (c & 7); goto loop; } /* * parse header */ int uar_rd(ARCHD *arcn, char *buf) { HD_AR *h = (HD_AR *)buf; uint64_t i; if (h->ar_magic[0] != 0x60 || h->ar_magic[1] != 0x0A) return (-1); memset(arcn, 0, sizeof(*arcn)); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; arcn->type = PAX_REG; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime = uar_atoi64(h->ar_mtime, sizeof(h->ar_mtime)); arcn->sb.st_uid = uar_atoi32(h->ar_uid, sizeof(h->ar_uid)); arcn->sb.st_gid = uar_atoi32(h->ar_gid, sizeof(h->ar_gid)); arcn->sb.st_mode = uar_otoi32(h->ar_mode, sizeof(h->ar_mode)) | S_IFREG; i = uar_atoi64(h->ar_size, sizeof(h->ar_size)); arcn->pad = i & 1; if (h->ar_name[0] == 0x23 && h->ar_name[1] == 0x31 && h->ar_name[2] == 0x2F) { arcn->nlen = uar_atoi32(&(h->ar_name[3]), sizeof(h->ar_name) - 3); if (arcn->nlen < 0 || arcn->nlen > PAXPATHLEN) /*XXX just skip over this file */ return (-1); if (rd_wrbuf(arcn->name, arcn->nlen) != arcn->nlen) return (-1); i -= arcn->nlen; } else { register char c; /*arcn->nlen = 0;*/ while (arcn->nlen < (int)sizeof(h->ar_name)) { c = h->ar_name[arcn->nlen]; if (c == ' ' || c == '/' || c == '\0') break; arcn->name[arcn->nlen++] = c; } } arcn->name[arcn->nlen] = '\0'; arcn->sb.st_size = i; arcn->skip = i; return (0); } /* internal emission functions */ static char * uar_itoa64(char *dst, uint64_t num) { if (num >= 10) dst = uar_itoa64(dst, num / 10); *dst++ = '0' + (num % 10); return (dst); } static char * uar_itoa32(char *dst, uint32_t num) { if (num >= 10) dst = uar_itoa32(dst, num / 10); *dst++ = '0' + (num % 10); return (dst); } static char * uar_itoo32(char *dst, uint32_t num) { if (num & ~7) dst = uar_itoo32(dst, num >> 3); *dst++ = '0' | (num & 7); return (dst); } /* * write a header */ int uar_wr(ARCHD *arcn) { HD_AR h; u_long t_uid, t_gid; time_t t_mtime = 0; char *extname; size_t n; u_long t_mode[sizeof(arcn->sb.st_mode) <= sizeof(u_long) ? 1 : -1]; anonarch_init(); switch (arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* regular files, more or less */ break; case PAX_DIR: /* directory, ignore silently */ return (1); default: paxwarn(1, "ar can only archive regular files, which %s is not", arcn->org_name); return (1); } /* trim trailing slashes */ n = strlen(arcn->name) - 1; while (n && arcn->name[n] == '/') --n; arcn->name[++n] = '\0'; /* find out basename */ if ((extname = strrchr(arcn->name, '/')) == NULL) extname = arcn->name; else ++extname; t_uid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_uid; t_gid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_gid; t_mode[0] = arcn->sb.st_mode; if (!(anonarch & ANON_MTIME)) t_mtime = arcn->sb.st_mtime; #if HAVE_TIMET_LARGE if (t_mtime > (time_t)999999999999ULL) { paxwarn(1, "%s overflow for %s", "mtime", arcn->org_name); t_mtime = (time_t)999999999999ULL; } #endif if (t_uid > 999999UL) { paxwarn(1, "%s overflow for %s", "uid", arcn->org_name); t_uid = 999999UL; } if (t_gid > 999999UL) { paxwarn(1, "%s overflow for %s", "gid", arcn->org_name); t_gid = 999999UL; } if (t_mode[0] > 077777777UL) { paxwarn(1, "%s overflow for %s", "mode", arcn->org_name); t_mode[0] &= 077777777UL; } if ((uint64_t)arcn->sb.st_size > ((uint64_t)9999999999ULL)) { paxwarn(1, "%s overflow for %s", "size", arcn->org_name); return (1); } #ifndef SMALL if (anonarch & ANON_DEBUG) paxwarn(0, "writing mode %8lo user %ld:%ld " "mtime %08lX name '%s'", t_mode[0], t_uid, t_gid, (u_long)t_mtime, extname); #endif memset(&h, ' ', sizeof(HD_AR)); if ((n = strlen(extname)) <= sizeof(h.ar_name)) { while (n) if (extname[--n] == ' ') break; if (n == 0) { memcpy(h.ar_name, extname, strlen(extname)); extname = NULL; goto got_name; } } n = strlen(extname); /* assert: n <= PAXPATHLEN <= 9999999999999 */ h.ar_name[0] = 0x23; h.ar_name[1] = 0x31; h.ar_name[2] = 0x2F; uar_itoa32(&(h.ar_name[3]), n); got_name: uar_itoa64(h.ar_mtime, t_mtime); uar_itoa32(h.ar_uid, t_uid); uar_itoa32(h.ar_gid, t_gid); uar_itoo32(h.ar_mode, t_mode[0]); uar_itoa64(h.ar_size, arcn->sb.st_size + (extname ? strlen(extname) : 0)); h.ar_magic[0] = 0x60; h.ar_magic[1] = 0x0A; arcn->pad = (arcn->sb.st_size + (extname ? strlen(extname) : 0)) & 1; if (wr_rdbuf((void *)&h, sizeof(HD_AR)) < 0) return (-1); if (extname) { if (wr_rdbuf(extname, strlen(extname)) < 0) return (-1); } /* so let the data follow */ return (0); } /* * return size of trailer */ off_t uar_endrd(void) { return (0); } /* * another artefact of paxtar integration */ int uar_trail(ARCHD *ignore MKSH_A_UNUSED, char *buf MKSH_A_UNUSED, int in_resync MKSH_A_UNUSED, int *cnt MKSH_A_UNUSED) { errx(1, "internal error: %s should never have been called", "uar_trail"); } pax/ar.h010064400000000000000000000101471340424730400073540ustar00/*- * Copyright (c) 2011, 2016 * mirabilos * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef EXTERN __IDSTRING(rcsid_ar_h, "$MirOS: src/bin/pax/ar.h,v 1.4 2018/12/12 18:08:41 tg Exp $"); #endif /**************************************************** Libraries created by the Unix Archiver and compatible with the DEB file format consist of an initial eight- octet magic followed by a number of sections, per ar- chive member, comprised of a header and a data part. The magic is "\n" (21 3C 61 72 63 68 3E 0A). Each archive member section (header followed by data) is aligned to a multiple of two octets. The magic and member header are both of even size, so a padding oc- tet "\n" (0Ah) can be appended after the data part of that section; an archive always has an even length. A header is 60 octets long and structured as follows: +0 char ar_name[16]; +16 char ar_mtime[12]; +28 char ar_uid[6]; +34 char ar_gid[6]; +40 char ar_mode[8]; +48 char ar_size[10]; +58 char ar_magic[2]; All header fields are left-justified and space-padded at the end, if necessary. The composition for ar_name will be described later. ar_mtime, ar_uid, ar_gid and ar_size are the unsigned decimental representation of the mtime as time_t, numeric user and group ID values and the size of the data part, respectively. ar_mode, on the other hand, is the octal representation of its Unix file mode (permissions). ar_magic = { 60h, 0Ah } Archive memeber filenames are basenames, i.e. they do not contain a path. If the filename is not longer than 16 octets and does not contain a space, it is stored as ar_name directly (although some implementations would trim the part of the filename before a ".o" extension). Otherwise, the ar_name field consists of the string "#1/" (23 31 2F) followed by the length of the filename as decimal un- signed integer (again space-padded); the APT archival routines only support extended filenames of less than 300 octets. The actual filename is then stored as the first part of the data part consequently incrementing ar_size by its length. Since some versions of APT al- so support the SYSV property of ending ar_name with a slash "/" (2F), on encoding filenames containing them should be written as extended filenames; on decoding, a trailing slash in ar_name should be ignored. (Note, SYSV does not encode filenames that contain spaces as extended but BSD ar and APT truncate there then.) There are no trailers; an archive file ends after its last member section (including the padding). ****************************************************/ typedef struct { char ar_name[16]; char ar_mtime[12]; char ar_uid[6]; char ar_gid[6]; char ar_mode[8]; char ar_size[10]; char ar_magic[2]; } HD_AR; pax/ar_io.c010064400000000000000000001013121466023157100100350ustar00/* $OpenBSD: ar_io.c,v 1.62 2017/03/11 12:55:47 tb Exp $ */ /* $NetBSD: ar_io.c,v 1.5 1996/03/26 23:54:13 mrg Exp $ */ /*- * Copyright (c) 2012, 2016, 2017, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #if HAVE_SYS_MTIO_H #include #endif #include #include #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/ar_io.c,v 1.29 2024/08/17 23:33:50 tg Exp $"); #ifndef PAX_SAFE_PATH #define PAX_SAFE_PATH "/bin:/usr/bin" #endif /* * Routines which deal directly with the archive I/O device/file. */ #define DMOD 0666 /* default mode of created archives */ #define EXT_MODE O_RDONLY /* open mode for list/extract */ #define AR_MODE (O_WRONLY | O_CREAT | O_TRUNC) /* mode for archive */ #define APP_MODE O_RDWR /* mode for append */ #define STDO "" /* pseudo name for stdout */ #define STDN "" /* pseudo name for stdin */ int arfd = -1; /* archive file descriptor */ static char artyp = ISREG; /* archive type: file/FIFO/tape */ static int arvol = 1; /* archive volume number */ static int lstrval = -1; /* return value from last i/o */ static char io_ok; /* i/o worked on volume after resync */ static char did_io; /* did i/o ever occur on volume? */ static char done; /* set via tty termination */ static struct stat arsb; /* stat of archive device at open */ static char invld_rec; /* tape has out of spec record size */ static char wr_trail = 1; /* trailer was rewritten in append */ static char can_unlnk = 0; /* do we unlink null archives? */ const char *arcname; /* printable name of archive */ static char *arcname_alloc = NULL; /* this is so we can free(3) it */ const char *compress_program; /* name of compression program */ static pid_t zpid = -1; /* pid of child process */ char force_one_volume; /* 1 if we ignore volume changes */ #if HAVE_SYS_MTIO_H static int get_phys(void); #endif extern sigset_t s_mask; static void ar_start_compress(int, int); /* * ar_open() * Opens the next archive volume. Determines the type of the device and * sets up block sizes as required by the archive device and the format. * Note: we may be called with name == NULL on the first open only. * Return: * -1 on failure, 0 otherwise */ int ar_open(const char *name) { #if HAVE_SYS_MTIO_H struct mtget mb; #endif if (arfd != -1) (void)close(arfd); arfd = -1; can_unlnk = did_io = io_ok = invld_rec = 0; artyp = ISREG; flcnt = 0; /* * open based on overall operation mode */ switch (act) { case LIST: case EXTRACT: if (name == NULL) { arfd = STDIN_FILENO; arcname = STDN; } else if ((arfd = binopen3(0, name, EXT_MODE, DMOD)) < 0) syswarn(1, errno, "Failed open to read on %s", name); if (arfd != -1 && compress_program != NULL) ar_start_compress(arfd, 0); break; case ARCHIVE: if (name == NULL) { arfd = STDOUT_FILENO; arcname = STDO; } else if ((arfd = binopen3(0, name, AR_MODE, DMOD)) < 0) syswarn(1, errno, "Failed open to write on %s", name); else can_unlnk = 1; if (arfd != -1 && compress_program != NULL) ar_start_compress(arfd, 1); break; case APPND: if (name == NULL) { arfd = STDOUT_FILENO; arcname = STDO; } else if ((arfd = binopen3(0, name, APP_MODE, DMOD)) < 0) syswarn(1, errno, "Failed open to read/write on %s", name); break; case COPY: /* * arfd not used in COPY mode */ arcname = ""; lstrval = 1; return(0); } if (arfd < 0) return(-1); if (chdname != NULL) if (chdir(chdname) != 0) { syswarn(1, errno, "Failed chdir to %s", chdname); return(-1); } /* * set up is based on device type */ if (fstat(arfd, &arsb) < 0) { syswarn(1, errno, "Failed stat on %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return(-1); } if (S_ISDIR(arsb.st_mode)) { paxwarn(0, act == LIST || act == EXTRACT ? "Cannot read an archive from a directory %s" : "Cannot write an archive on top of a directory %s", arcname); (void)close(arfd); arfd = -1; can_unlnk = 0; return(-1); } if (S_ISCHR(arsb.st_mode)) #if HAVE_SYS_MTIO_H artyp = ioctl(arfd, MTIOCGET, &mb) ? ISCHR : ISTAPE; #else artyp = ISCHR; #endif else if (S_ISBLK(arsb.st_mode)) artyp = ISBLK; else if ((lseek(arfd, 0, SEEK_CUR) == -1) && (errno == ESPIPE)) artyp = ISPIPE; else artyp = ISREG; /* * make sure beyond any doubt that we can unlink only regular files * we created */ if (artyp != ISREG) can_unlnk = 0; /* * if we are writing, we are done */ if (act == ARCHIVE) { blksz = rdblksz = wrblksz; lstrval = 1; return(0); } /* * set default blksz on read. APPNDs writes rdblksz on the last volume * On all new archive volumes, we shift to wrblksz (if the user * specified one, otherwise we will continue to use rdblksz). We * must set blocksize based on what kind of device the archive is * stored. */ switch (artyp) { case ISTAPE: /* * Tape drives come in at least two flavors. Those that support * variable sized records and those that have fixed sized * records. They must be treated differently. For tape drives * that support variable sized records, we must make large * reads to make sure we get the entire record, otherwise we * will just get the first part of the record (up to size we * asked). Tapes with fixed sized records may or may not return * multiple records in a single read. We really do not care * what the physical record size is UNLESS we are going to * append. (We will need the physical block size to rewrite * the trailer). Only when we are appending do we go to the * effort to figure out the true PHYSICAL record size. */ blksz = rdblksz = MAXBLK; break; case ISPIPE: case ISBLK: case ISCHR: /* * Blocksize is not a major issue with these devices (but must * be kept a multiple of 512). If the user specified a write * block size, we use that to read. Under append, we must * always keep blksz == rdblksz. Otherwise we go ahead and use * the device optimal blocksize as (and if) returned by stat * and if it is within pax specs. */ if ((act == APPND) && wrblksz) { blksz = rdblksz = wrblksz; break; } if ((arsb.st_blksize > 0) && (arsb.st_blksize < MAXBLK) && ((arsb.st_blksize % BLKMULT) == 0)) rdblksz = arsb.st_blksize; else rdblksz = DEVBLK; /* * For performance go for large reads when we can without harm */ if ((act == APPND) || (artyp == ISCHR)) blksz = rdblksz; else blksz = MAXBLK; break; case ISREG: /* * if the user specified wrblksz works, use it. Under appends * we must always keep blksz == rdblksz */ if ((act == APPND) && wrblksz && ((arsb.st_size%wrblksz)==0)){ blksz = rdblksz = wrblksz; break; } /* * See if we can find the blocking factor from the file size */ for (rdblksz = MAXBLK; rdblksz > 0; rdblksz -= BLKMULT) if ((arsb.st_size % rdblksz) == 0) break; /* * When we cannot find a match, we may have a flawed archive. */ if (rdblksz <= 0) rdblksz = FILEBLK; /* * for performance go for large reads when we can */ if (act == APPND) blksz = rdblksz; else blksz = MAXBLK; break; default: /* * should never happen, worst case, slow... */ blksz = rdblksz = BLKMULT; break; } lstrval = 1; return(0); } /* * ar_close(int int_sig) * closes archive device, increments volume number, and prints i/o summary * If in_sig is set we're in a signal handler and can't flush stdio. */ void ar_close(int in_sig) { int status; if (arfd < 0) { did_io = io_ok = flcnt = 0; if (vfpart) { SHIKATANAI write(listfd, "\n", 1); vfpart = 0; } return; } if (!in_sig) fflush(listf); /* * Close archive file. This may take a LONG while on tapes (we may be * forced to wait for the rewind to complete) so tell the user what is * going on (this avoids the user hitting control-c thinking pax is * broken). */ if (vflag && (artyp == ISTAPE)) { (void)dprintf(listfd, "%s%s: Waiting for tape drive close to complete...", vfpart ? "\n" : "", argv0); } /* * if nothing was written to the archive (and we created it), we remove * it */ if (can_unlnk && (fstat(arfd, &arsb) == 0) && (S_ISREG(arsb.st_mode)) && (arsb.st_size == 0)) { (void)unlink(arcname); can_unlnk = 0; } /* * for a quick extract/list, pax frequently exits before the child * process is done */ if ((act == LIST || act == EXTRACT) && nflag && zpid > 0) { kill(zpid, SIGINT); zpid = -1; } (void)close(arfd); /* Do not exit before child to ensure data integrity */ if (zpid > 0) { waitpid(zpid, &status, 0); if (!WIFEXITED(status) || WEXITSTATUS(status)) exit_val = 1; } if (vflag && (artyp == ISTAPE)) { SHIKATANAI write(listfd, "done.\n", sizeof("done.\n")-1); vfpart = 0; } arfd = -1; if (!io_ok && !did_io) { flcnt = 0; return; } did_io = io_ok = 0; /* * The volume number is only increased when the last device has data * and we have already determined the archive format. */ if (frmt != NULL) ++arvol; /* Vflag can cause this to have been set */ if (vfpart) { SHIKATANAI write(listfd, "\n", 1); vfpart = 0; } /* nothing to do any more, unless vflag */ if (!vflag) { flcnt = 0; return; } /* * Print out a summary of I/O for this archive volume. */ /* * If we have not determined the format yet, we just say how many bytes * we have skipped over looking for a header to id. there is no way we * could have written anything yet. */ if (frmt == NULL) { (void)dprintf(listfd, "%s: unknown format, %" OT_FMT " bytes skipped.\n", argv0, rdcnt); flcnt = 0; return; } if (op_mode == OP_PAX) (void)dprintf(listfd, "%s: %s vol %d, %lu files," " %" OT_FMT " bytes read, %" OT_FMT " bytes written.\n", argv0, frmt->name, arvol-1, flcnt, rdcnt, wrcnt); else if (op_mode == OP_CPIO) (void)dprintf(listfd, "%" OT_FMT " blocks\n", (rdcnt ? rdcnt : wrcnt) / 5120); flcnt = 0; } /* * ar_drain() * drain any archive format independent padding from an archive read * from a socket or a pipe. This is to prevent the process on the * other side of the pipe from getting a SIGPIPE (pax will stop * reading an archive once a format dependent trailer is detected). */ void ar_drain(void) { int res; char drbuf[MAXBLK]; /* * we only drain from a pipe/socket. Other devices can be closed * without reading up to end of file. We sure hope that pipe is closed * on the other side so we will get an EOF. */ if ((artyp != ISPIPE) || (lstrval <= 0)) return; /* * keep reading until pipe is drained */ while ((res = read(arfd, drbuf, sizeof(drbuf))) > 0) continue; lstrval = res; } /* * ar_set_wr() * Set up device right before switching from read to write in an append. * device dependent code (if required) to do this should be added here. * For all archive devices we are already positioned at the place we want * to start writing when this routine is called. * Return: * 0 if all ready to write, -1 otherwise */ int ar_set_wr(void) { off_t cpos; /* * we must make sure the trailer is rewritten on append, ar_next() * will stop us if the archive containing the trailer was not written */ wr_trail = 0; /* * Add any device dependent code as required here */ if (artyp != ISREG) return(0); /* * Ok we have an archive in a regular file. If we were rewriting a * file, we must get rid of all the stuff after the current offset * (it was not written by pax). */ if (((cpos = lseek(arfd, 0, SEEK_CUR)) < 0) || (ftruncate(arfd, cpos) < 0)) { syswarn(1, errno, "Unable to truncate archive file"); return(-1); } return(0); } /* * ar_app_ok() * check if the last volume in the archive allows appends. We cannot check * this until we are ready to write since there is no spec that says all * volumes in a single archive have to be of the same type... * Return: * 0 if we can append, -1 otherwise. */ int ar_app_ok(void) { if (artyp == ISPIPE) { paxwarn(1, "Cannot append to an archive obtained from a pipe."); return(-1); } if (!invld_rec) return(0); paxwarn(1,"Cannot append, device record size %d does not support %s spec", rdblksz, argv0); return(-1); } /* * ar_read() * read up to a specified number of bytes from the archive into the * supplied buffer. When dealing with tapes we may not always be able to * read what we want. * Return: * Number of bytes in buffer. 0 for end of file, -1 for a read error. */ int ar_read(char *buf, int cnt) { int res = 0; /* * if last i/o was in error, no more reads until reset or new volume */ if (lstrval <= 0) return(lstrval); /* * how we read must be based on device type */ switch (artyp) { case ISTAPE: if ((res = read(arfd, buf, cnt)) > 0) { /* * CAUTION: tape systems may not always return the same * sized records so we leave blksz == MAXBLK. The * physical record size that a tape drive supports is * very hard to determine in a uniform and portable * manner. */ io_ok = 1; if (res != rdblksz) { /* * Record size changed. If this happens on * any record after the first, we probably have * a tape drive which has a fixed record size * (we are getting multiple records in a single * read). Watch out for record blocking that * violates pax spec (must be a multiple of * BLKMULT). */ rdblksz = res; if (rdblksz % BLKMULT) invld_rec = 1; } return(res); } break; case ISREG: case ISBLK: case ISCHR: case ISPIPE: default: /* * Files are so easy to deal with. These other things cannot * be trusted at all. So when we are dealing with character * devices and pipes we just take what they have ready for us * and return. Trying to do anything else with them runs the * risk of failure. */ if ((res = read(arfd, buf, cnt)) > 0) { io_ok = 1; return(res); } break; } /* * We are in trouble at this point, something is broken... */ lstrval = res; if (res < 0) syswarn(1, errno, "Failed read on archive volume %d", arvol); else if (!frmt || !frmt->is_uar) paxwarn(1, "End of archive volume %d reached", arvol); return(res); } /* * ar_write() * Write a specified number of bytes in supplied buffer to the archive * device so it appears as a single "block". Deals with errors and tries * to recover when faced with short writes. * Return: * Number of bytes written. 0 indicates end of volume reached and with no * flaws (as best that can be detected). A -1 indicates an unrecoverable * error in the archive occurred. */ int ar_write(char *buf, int bsz) { int res; off_t cpos; /* * do not allow pax to create a "bad" archive. Once a write fails on * an archive volume prevent further writes to it. */ if (lstrval <= 0) return(lstrval); if ((res = write(arfd, buf, bsz)) == bsz) { wr_trail = 1; io_ok = 1; return(bsz); } /* * write broke, see what we can do with it. We try to send any partial * writes that may violate pax spec to the next archive volume. */ if (res < 0) lstrval = res; else lstrval = 0; switch (artyp) { case ISREG: if ((res > 0) && (res % BLKMULT)) { /* * try to fix up partial writes which are not BLKMULT * in size by forcing the runt record to next archive * volume */ if ((cpos = lseek(arfd, 0, SEEK_CUR)) < 0) break; cpos -= res; if (ftruncate(arfd, cpos) < 0) break; res = lstrval = 0; break; } if (res >= 0) break; /* * if file is out of space, handle it like a return of 0 */ if ((errno == ENOSPC) || (errno == EFBIG) || (errno == EDQUOT)) res = lstrval = 0; break; case ISTAPE: case ISCHR: case ISBLK: if (res >= 0) break; if (errno == EACCES) { paxwarn(0, "Write failed, archive is write protected."); res = lstrval = 0; return(0); } /* * see if we reached the end of media, if so force a change to * the next volume */ if ((errno == ENOSPC) || (errno == EIO) || (errno == ENXIO)) res = lstrval = 0; break; case ISPIPE: default: /* * we cannot fix errors to these devices */ break; } /* * Better tell the user the bad news... * if this is a block aligned archive format, we may have a bad archive * if the format wants the header to start at a BLKMULT boundary. While * we can deal with the mis-aligned data, it violates spec and other * archive readers will likely fail. if the format is not block * aligned, the user may be lucky (and the archive is ok). */ if (res >= 0) { if (res > 0) wr_trail = 1; io_ok = 1; } /* * If we were trying to rewrite the trailer and it didn't work, we * must quit right away. */ if (!wr_trail && (res <= 0)) { paxwarn(1,"Unable to append, trailer re-write failed. Quitting."); return(res); } if (res == 0) paxwarn(0, "End of archive volume %d reached", arvol); else if (res < 0) syswarn(1, errno, "Failed write to archive volume: %d", arvol); else if (!frmt->blkalgn || ((res % frmt->blkalgn) == 0)) paxwarn(0,"WARNING: partial archive write. Archive MAY BE FLAWED"); else paxwarn(1,"WARNING: partial archive write. Archive IS FLAWED"); return(res); } /* * ar_rdsync() * Try to move past a bad spot on a flawed archive as needed to continue * I/O. Clears error flags to allow I/O to continue. * Return: * 0 when ok to try i/o again, -1 otherwise. */ int ar_rdsync(void) { long fsbz; off_t cpos; off_t mpos; #if HAVE_SYS_MTIO_H struct mtop mb; #endif /* * Fail resync attempts at user request (done) or if this is going to be * an update/append to a existing archive. if last i/o hit media end, * we need to go to the next volume not try a resync */ if ((done > 0) || (lstrval == 0)) return(-1); if ((act == APPND) || (act == ARCHIVE)) { paxwarn(1, "Cannot allow updates to an archive with flaws."); return(-1); } if (io_ok) did_io = 1; switch (artyp) { #if HAVE_SYS_MTIO_H case ISTAPE: /* * if the last i/o was a successful data transfer, we assume * the fault is just a bad record on the tape that we are now * past. If we did not get any data since the last resync try * to move the tape forward one PHYSICAL record past any * damaged tape section. Some tape drives are stubborn and need * to be pushed. */ if (io_ok) { io_ok = 0; lstrval = 1; break; } mb.mt_op = MTFSR; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) break; lstrval = 1; break; #endif case ISREG: case ISCHR: case ISBLK: /* * try to step over the bad part of the device. */ io_ok = 0; if (((fsbz = arsb.st_blksize) <= 0) || (artyp != ISREG)) fsbz = BLKMULT; if ((cpos = lseek(arfd, 0, SEEK_CUR)) < 0) break; mpos = fsbz - (cpos % fsbz); if (lseek(arfd, mpos, SEEK_CUR) < 0) break; lstrval = 1; break; case ISPIPE: default: /* * cannot recover on these archive device types */ io_ok = 0; break; } if (lstrval <= 0) { paxwarn(1, "Unable to recover from an archive read failure."); return(-1); } paxwarn(0, "Attempting to recover from an archive read failure."); return(0); } /* * ar_fow() * Move the I/O position within the archive forward the specified number of * bytes as supported by the device. If we cannot move the requested * number of bytes, return the actual number of bytes moved in skipped. * Return: * 0 if moved the requested distance, -1 on complete failure, 1 on * partial move (the amount moved is in skipped) */ int ar_fow(off_t sksz, off_t *skipped) { off_t cpos; off_t mpos; *skipped = 0; if (sksz <= 0) return(0); /* * we cannot move forward at EOF or error */ if (lstrval <= 0) return(lstrval); /* * Safer to read forward on devices where it is hard to find the end of * the media without reading to it. With tapes we cannot be sure of the * number of physical blocks to skip (we do not know physical block * size at this point), so we must only read forward on tapes! */ if (artyp != ISREG) return(0); /* * figure out where we are in the archive */ if ((cpos = lseek(arfd, 0, SEEK_CUR)) >= 0) { /* * we can be asked to move farther than there are bytes in this * volume, if so, just go to file end and let normal buf_fill() * deal with the end of file (it will go to next volume by * itself) */ if ((mpos = cpos + sksz) > (off_t)arsb.st_size) { *skipped = arsb.st_size - cpos; mpos = arsb.st_size; } else *skipped = sksz; if (lseek(arfd, mpos, SEEK_SET) >= 0) return(0); } syswarn(1, errno, "Forward positioning operation on archive failed"); lstrval = -1; return(-1); } /* * ar_rev() * move the i/o position within the archive backwards the specified byte * count as supported by the device. With tapes drives we RESET rdblksz to * the PHYSICAL blocksize. * NOTE: We should only be called to move backwards so we can rewrite the * last records (the trailer) of an archive (APPEND). * Return: * 0 if moved the requested distance, -1 on complete failure */ int ar_rev(off_t sksz) { off_t cpos; #if HAVE_SYS_MTIO_H struct mtop mb; int phyblk; #endif /* * make sure we do not have try to reverse on a flawed archive */ if (lstrval < 0) return(lstrval); switch (artyp) { case ISPIPE: if (sksz <= 0) break; /* * cannot go backwards on these critters */ paxwarn(1, "Reverse positioning on pipes is not supported."); lstrval = -1; return(-1); case ISREG: case ISBLK: case ISCHR: default: if (sksz <= 0) break; /* * For things other than files, backwards movement has a very * high probability of failure as we really do not know the * true attributes of the device we are talking to (the device * may not even have the ability to lseek() in any direction). * First we figure out where we are in the archive. */ if ((cpos = lseek(arfd, 0, SEEK_CUR)) < 0) { syswarn(1, errno, "Unable to obtain current archive byte offset"); lstrval = -1; return(-1); } /* * we may try to go backwards past the start when the archive * is only a single record. If this happens and we are on a * multi-volume archive, we need to go to the end of the * previous volume and continue our movement backwards from * there. */ if ((cpos -= sksz) < 0) { if (arvol > 1) { /* * this should never happen */ paxwarn(1,"Reverse position on previous volume."); lstrval = -1; return(-1); } cpos = 0; } if (lseek(arfd, cpos, SEEK_SET) < 0) { syswarn(1, errno, "Unable to seek archive backwards"); lstrval = -1; return(-1); } break; #if HAVE_SYS_MTIO_H case ISTAPE: /* * Calculate and move the proper number of PHYSICAL tape * blocks. If the sksz is not an even multiple of the physical * tape size, we cannot do the move (this should never happen). * (We also cannot handle trailers spread over two vols.) * get_phys() also makes sure we are in front of the filemark. */ if ((phyblk = get_phys()) <= 0) { lstrval = -1; return(-1); } /* * make sure future tape reads only go by physical tape block * size (set rdblksz to the real size). */ rdblksz = phyblk; /* * if no movement is required, just return (we must be after * get_phys() so the physical blocksize is properly set) */ if (sksz <= 0) break; /* * ok we have to move. Make sure the tape drive can do it. */ if (sksz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); lstrval = -1; return(-1); } /* * move backwards the requested number of bytes */ mb.mt_op = MTBSR; mb.mt_count = sksz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno, "Unable to backspace tape %d blocks.", mb.mt_count); lstrval = -1; return(-1); } break; #endif } lstrval = 1; return(0); } #if HAVE_SYS_MTIO_H /* * get_phys() * Determine the physical block size on a tape drive. We need the physical * block size so we know how many bytes we skip over when we move with * mtio commands. We also make sure we are BEFORE THE TAPE FILEMARK when * return. * This is one really SLOW routine... * Return: * physical block size if ok (ok > 0), -1 otherwise */ static int get_phys(void) { int padsz = 0; int res; int phyblk; struct mtop mb; char scbuf[MAXBLK]; /* * move to the file mark, and then back up one record and read it. * this should tell us the physical record size the tape is using. */ if (lstrval == 1) { /* * we know we are at file mark when we get back a 0 from * read() */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) padsz += res; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } } /* * move backwards over the file mark so we are at the end of the * last record. */ mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * move backwards so we are in front of the last record and read it to * get physical tape blocksize. */ mb.mt_op = MTBSR; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over last tape block."); return(-1); } if ((phyblk = read(arfd, scbuf, sizeof(scbuf))) <= 0) { syswarn(1, errno, "Cannot determine archive tape blocksize."); return(-1); } /* * read forward to the file mark, then back up in front of the filemark * (this is a bit paranoid, but should be safe to do). */ while ((res = read(arfd, scbuf, sizeof(scbuf))) > 0) continue; if (res < 0) { syswarn(1, errno, "Unable to locate tape filemark."); return(-1); } mb.mt_op = MTBSF; mb.mt_count = 1; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1, errno, "Unable to backspace over tape filemark."); return(-1); } /* * set lstrval so we know that the filemark has not been seen */ lstrval = 1; /* * return if there was no padding */ if (padsz == 0) return(phyblk); /* * make sure we can move backwards over the padding. (this should * never fail). */ if (padsz % phyblk) { paxwarn(1, "Tape drive unable to backspace requested amount"); return(-1); } /* * move backwards over the padding so the head is where it was when * we were first called (if required). */ mb.mt_op = MTBSR; mb.mt_count = padsz/phyblk; if (ioctl(arfd, MTIOCTOP, &mb) < 0) { syswarn(1,errno,"Unable to backspace tape over %d pad blocks", mb.mt_count); return(-1); } return(phyblk); } #endif /* * ar_next() * prompts the user for the next volume in this archive. For some devices * we may allow the media to be changed. Otherwise a new archive is * prompted for. By pax spec, if there is no controlling tty or an eof is * read on tty input, we must quit pax. * Return: * 0 when ready to continue, -1 when all done */ int ar_next(void) { char *buf; sigset_t o_mask; /* * WE MUST CLOSE THE DEVICE. A lot of devices must see last close, (so * things like writing EOF etc will be done) (Watch out ar_close() can * also be called via a signal handler, so we must prevent a race. */ if (sigprocmask(SIG_BLOCK, &s_mask, &o_mask) < 0) syswarn(0, errno, "Unable to set signal mask"); ar_close(0); if (sigprocmask(SIG_SETMASK, &o_mask, NULL) < 0) syswarn(0, errno, "Unable to restore signal mask"); if (done || !wr_trail || force_one_volume || op_mode == OP_TAR) return(-1); tty_prnt("\nATTENTION! %s archive volume change required.\n", argv0); /* * if i/o is on stdin or stdout, we cannot reopen it (we do not know * the name), the user will be forced to type it in. */ if (strcmp(arcname, STDO) && strcmp(arcname, STDN) && (artyp != ISREG) && (artyp != ISPIPE)) { if (artyp == ISTAPE) { tty_prnt("%s ready for archive tape volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT TAPE on the tape drive"); } else { tty_prnt("%s ready for archive volume: %d\n", arcname, arvol); tty_prnt("Load the NEXT STORAGE MEDIA (if required)"); } if ((act == ARCHIVE) || (act == APPND)) tty_prnt(" and make sure it is WRITE ENABLED.\n"); else tty_prnt("\n"); for (;;) { tty_prnt("Type \"y\" to continue, \".\" to quit %s,", argv0); tty_prnt(" or \"s\" to switch to new device.\nIf you"); tty_prnt(" cannot change storage media, type \"s\"\n"); tty_prnt("Is the device ready and online? > "); if ((buf = tty_rd()) == NULL || !strcmp(buf, ".")) { free(buf); done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if ((buf[0] == '\0') || (buf[1] != '\0')) goto eunknown; switch (buf[0]) { case 'y': case 'Y': /* * we are to continue with the same device */ free(buf); if (ar_open(arcname) >= 0) return (0); tty_prnt("Cannot re-open %s, try again\n", arcname); continue; case 's': case 'S': /* * user wants to open a different device */ free(buf); tty_prnt("Switching to a different archive\n"); break; default: eunknown: tty_prnt("%s unknown command, try again\n", buf); free(buf); continue; } break; } } else tty_prnt("Ready for archive volume: %d\n", arvol); /* * have to go to a different archive */ for (;;) { tty_prnt("Input archive name or \".\" to quit %s.\n", argv0); tty_prnt("Archive name > "); if ((buf = tty_rd()) == NULL || !strcmp(buf, ".")) { free(buf); done = 1; lstrval = -1; tty_prnt("Quitting %s!\n", argv0); vfpart = 0; return(-1); } if (buf[0] == '\0') { tty_prnt("Empty file name, try again\n"); free(buf); continue; } if (!strcmp(buf, "..")) { tty_prnt("Illegal file name '..', try again\n"); free(buf); continue; } if (strlen(buf) > PAXPATHLEN) { tty_prnt("File name too long, try again\n"); free(buf); continue; } /* * try to open new archive */ if (ar_open(buf) >= 0) { free(arcname_alloc); arcname = arcname_alloc = buf; break; } tty_prnt("Cannot open %s, try again\n", buf); free(buf); continue; } return (0); } /* * ar_start_compress() * starts the compression/decompression process as a child, using magic * to keep the fd the same in the calling function (parent). */ void ar_start_compress(int fd, int wr) { int fds[2]; const char *compress_flags; guess_compress_program(wr); if (compress_program == NULL) return; if (pipe(fds) < 0) err(1, "pipe"); zpid = fork(); if (zpid < 0) err(1, "fork"); /* parent */ if (zpid) { dup2(fds[wr ? 1 : 0], fd); close(fds[0]); close(fds[1]); #if HAVE_PLEDGE if (pmode == 0 || (act != EXTRACT && act != COPY)) { if (pledge("stdio rpath wpath cpath fattr dpath getpw proc tape", NULL) == -1) err(1, "pledge"); } #endif } else { if (wr) { dup2(fds[0], STDIN_FILENO); dup2(fd, STDOUT_FILENO); compress_flags = "-c"; } else { dup2(fds[1], STDOUT_FILENO); dup2(fd, STDIN_FILENO); compress_flags = "-dc"; } close(fds[0]); close(fds[1]); /* System compressors are more likely to use pledge(2) */ putenv("PATH=" PAX_SAFE_PATH); if (execlp(compress_program, compress_program, compress_flags, (char *)NULL) < 0) err(1, "exec(%s)", compress_program); /* NOTREACHED */ } } pax/ar_subs.c010064400000000000000000000776331340440266000104160ustar00/* $OpenBSD: ar_subs.c,v 1.48 2016/08/26 05:06:14 guenther Exp $ */ /* $NetBSD: ar_subs.c,v 1.5 1995/03/21 09:07:06 cgd Exp $ */ /*- * Copyright (c) 2008, 2011, 2012, 2016 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "ftimes.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/ar_subs.c,v 1.20 2018/12/13 07:09:09 tg Exp $"); static void wr_archive(ARCHD *, int is_app); static int get_arc(void); static int next_head(ARCHD *); extern sigset_t s_mask; /* * Routines which control the overall operation modes of pax as specified by * the user: list, append, read ... */ static char hdbuf[BLKMULT]; /* space for archive header on read */ u_long flcnt; /* number of files processed */ char ar_do_keepopen = 0; /* see append() below */ /* * list() * list the contents of an archive which match user supplied pattern(s) * (no pattern matches all). */ void list(void) { ARCHD *arcn; int res; ARCHD archd; arcn = &archd; /* * figure out archive type; pass any format specific options to the * archive option processing routine; call the format init routine. We * also save current time for ls_list() so we do not make a system * call for each file we need to print. If verbose (vflag) start up * the name and group caches. */ if ((get_arc() < 0) || ((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) return; #if !HAVE_UG_FROM_UGID if (vflag && ((uidtb_start() < 0) || (gidtb_start() < 0))) return; #endif /* * step through the archive until the format says it is done */ while (next_head(arcn) == 0) { if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { /* * we need to read, to get the real filename */ off_t cnt; if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF ? -1 : -2, &cnt)) (void)rd_skip(cnt + arcn->pad); continue; } /* * check for pattern, and user specified options match. * When all patterns are matched we are done. */ if ((res = pat_match(arcn)) < 0) break; if ((res == 0) && (sel_chk(arcn) == 0)) { /* * pattern resulted in a selected file */ if (pat_sel(arcn) < 0) break; /* * modify the name as requested by the user if name * survives modification, do a listing of the file */ if ((res = mod_name(arcn)) < 0) break; if (res == 0) ls_list(arcn, stdout); } /* * skip to next archive format header using values calculated * by the format header read routine */ if (rd_skip(arcn->skip + arcn->pad) == 1) break; } /* * all done, let format have a chance to cleanup, and make sure that * the patterns supplied by the user were all matched */ (void)(*frmt->end_rd)(); (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(0); pat_chk(); } static int cmp_file_times(int mtime_flag, int ctime_flag, ARCHD *arcn, struct stat *sbp) { struct stat sb; if (sbp == NULL) { if (lstat(arcn->name, &sb) != 0) return (0); sbp = &sb; } if (ctime_flag && mtime_flag) return (st_timecmp(m, &arcn->sb, sbp, <=) && st_timecmp(c, &arcn->sb, sbp, <=)); else if (ctime_flag) return (st_timecmp(c, &arcn->sb, sbp, <=)); else return (st_timecmp(m, &arcn->sb, sbp, <=)); } /* * extract() * extract the member(s) of an archive as specified by user supplied * pattern(s) (no patterns extracts all members) */ void extract(void) { ARCHD *arcn; int res; off_t cnt; ARCHD archd; int fd; sltab_start(); arcn = &archd; /* * figure out archive type; pass any format specific options to the * archive option processing routine; call the format init routine; * start up the directory modification time and access mode database */ if ((get_arc() < 0) || ((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0) || (dir_start() < 0)) return; /* * When we are doing interactive rename, we store the mapping of names * so we can fix up hard links files later in the archive. */ if (iflag && (name_start() < 0)) return; /* * step through each entry on the archive until the format read routine * says it is done */ while (next_head(arcn) == 0) { if (arcn->type == PAX_GLL || arcn->type == PAX_GLF) { /* * we need to read, to get the real filename */ if (!(*frmt->rd_data)(arcn, arcn->type == PAX_GLF ? -1 : -2, &cnt)) (void)rd_skip(cnt + arcn->pad); continue; } /* * check for pattern, and user specified options match. When * all the patterns are matched we are done */ if ((res = pat_match(arcn)) < 0) break; if ((res > 0) || (sel_chk(arcn) != 0)) { /* * file is not selected. skip past any file data and * padding and go back for the next archive member */ (void)rd_skip(arcn->skip + arcn->pad); continue; } /* * with -u or -D only extract when the archive member is newer * than the file with the same name in the filesystem (no * test of being the same type is required). * NOTE: this test is done BEFORE name modifications as * specified by pax. this operation can be confusing to the * user who might expect the test to be done on an existing * file AFTER the name mod. In honesty the pax spec is probably * flawed in this respect. */ if ((uflag || Dflag) && cmp_file_times(uflag, Dflag, arcn, NULL)) { (void)rd_skip(arcn->skip + arcn->pad); continue; } /* * this archive member is now been selected. modify the name. */ if ((pat_sel(arcn) < 0) || ((res = mod_name(arcn)) < 0)) break; if (res > 0) { /* * a bad name mod, skip and purge name from link table */ purg_lnk(arcn); (void)rd_skip(arcn->skip + arcn->pad); continue; } /* * Non standard -Y and -Z flag. When the existing file is * same age or newer skip */ if ((Yflag || Zflag) && cmp_file_times(Yflag, Zflag, arcn, NULL)) { (void)rd_skip(arcn->skip + arcn->pad); continue; } if (vflag) { if (vflag > 1) ls_list(arcn, listf); else { (void)safe_print(arcn->name, listf); vfpart = 1; } } else if (Vflag) { (void)putc('.', listf); (void)fflush(listf); vfpart = 1; } /* * if required, chdir around. */ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) if (!to_stdout && chdir(arcn->pat->chdname) != 0) syswarn(1, errno, "Cannot chdir to %s", arcn->pat->chdname); /* * all ok, extract this member based on type */ if (!PAX_IS_REG(arcn->type)) { /* * process archive members that are not regular files. * throw out padding and any data that might follow the * header (as determined by the format). */ if (!to_stdout) { if (PAX_IS_HARDLINK(arcn->type)) { res = lnk_creat(arcn, &fd); if (fd != -1) goto extdata; }else res = node_creat(arcn); } (void)rd_skip(arcn->skip + arcn->pad); if (res < 0) purg_lnk(arcn); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } goto popd; } /* * we have a file with data here. If we cannot create it, skip * over the data and purge the name from hard link table */ if (to_stdout) fd = STDOUT_FILENO; else if ((fd = file_creat(arcn)) < 0) { (void)rd_skip(arcn->skip + arcn->pad); purg_lnk(arcn); goto popd; } /* * extract the file from the archive and skip over padding and * any unprocessed data */ extdata: res = (*frmt->rd_data)(arcn, fd, &cnt); if (fd != STDOUT_FILENO) file_close(arcn, fd); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } if (!res) (void)rd_skip(cnt + arcn->pad); popd: /* * if required, chdir around. */ if ((arcn->pat != NULL) && (arcn->pat->chdname != NULL)) if (!to_stdout && fchdir(cwdfd) != 0) syswarn(1, errno, "Cannot fchdir to starting directory"); } /* * all done, restore directory modes and times as required; make sure * all patterns supplied by the user were matched; block off signals * to avoid chance for multiple entry into the cleanup code. */ (void)(*frmt->end_rd)(); (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(0); sltab_process(0); proc_dir(0); pat_chk(); } /* * wr_archive() * Write an archive. used in both creating a new archive and appends on * previously written archive. */ static void wr_archive(ARCHD *arcn, int is_app) { int res; int hlk; int wr_one; off_t cnt; int (*wrf)(ARCHD *); int fd = -1; /* * if this format supports hard link storage, start up the database * that detects them. */ if (((hlk = frmt->hlk) == 1) && (lnk_start() < 0)) return; /* * if this is not append, and there are no files, we do not write a * trailer */ wr_one = is_app; /* * start up the file traversal code and format specific write */ if (ftree_start() < 0) { if (is_app) goto trailer; return; } else if (((*frmt->st_wr)(is_app) < 0)) return; wrf = frmt->wr; /* * When we are doing interactive rename, we store the mapping of names * so we can fix up hard links files later in the archive. */ if (iflag && (name_start() < 0)) return; /* * while there are files to archive, process them one at at time */ while (next_file(arcn) == 0) { /* * check if this file meets user specified options match. */ if (sel_chk(arcn) != 0) continue; fd = -1; if (uflag) { /* * only archive if this file is newer than a file with * the same name that is already stored on the archive */ if ((res = chk_ftime(arcn)) < 0) break; if (res > 0) { ftree_skipped_newer(); continue; } } /* * this file is considered selected now. see if this is a hard * link to a file already stored */ ftree_sel(arcn); if (hlk && (chk_lnk(arcn) < 0)) break; if (PAX_IS_REG(arcn->type) || (arcn->type == PAX_HRG)) { /* * we will have to read this file. by opening it now we * can avoid writing a header to the archive for a file * we were later unable to read (we also purge it from * the link table). */ if ((fd = binopen3(0, arcn->org_name, O_RDONLY, 0)) < 0) { syswarn(1, errno, "Unable to open %s to read", arcn->org_name); purg_lnk(arcn); continue; } } /* * Now modify the name as requested by the user */ if ((res = mod_name(arcn)) < 0) { /* * name modification says to skip this file, close the * file and purge link table entry */ rdfile_close(arcn, &fd); purg_lnk(arcn); break; } if ((res > 0) || (docrc && (set_crc(arcn, fd) < 0))) { /* * unable to obtain the crc we need, close the file, * purge link table entry */ rdfile_close(arcn, &fd); purg_lnk(arcn); continue; } if (vflag) { if (vflag > 1) ls_list(arcn, listf); else { (void)safe_print(arcn->name, listf); vfpart = 1; } } else if (Vflag) { (void)putc('.', listf); (void)fflush(listf); vfpart = 1; } ++flcnt; /* * looks safe to store the file, have the format specific * routine write routine store the file header on the archive */ if ((res = (*wrf)(arcn)) < 0) { rdfile_close(arcn, &fd); break; } wr_one = 1; if (res > 0) { /* * format write says no file data needs to be stored * so we are done messing with this file */ if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } rdfile_close(arcn, &fd); continue; } /* * Add file data to the archive, quit on write error. if we * cannot write the entire file contents to the archive we * must pad the archive to replace the missing file data * (otherwise during an extract the file header for the file * which FOLLOWS this one will not be where we expect it to * be). */ res = (*frmt->wr_data)(arcn, fd, &cnt); rdfile_close(arcn, &fd); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } if (res < 0) break; /* * pad as required, cnt is number of bytes not written */ if (((cnt > 0) && (wr_skip(cnt) < 0)) || ((arcn->pad > 0) && (wr_skip(arcn->pad) < 0))) break; } trailer: /* * tell format to write trailer; pad to block boundary; reset directory * mode/access times, and check if all patterns supplied by the user * were matched. block off signals to avoid chance for multiple entry * into the cleanup code */ if (wr_one) { (*frmt->end_wr)(); wr_fin(); } (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(0); if (tflag) proc_dir(0); ftree_chk(); } /* * append() * Add file to previously written archive. Archive format specified by the * user must agree with archive. The archive is read first to collect * modification times (if -u) and locate the archive trailer. The archive * is positioned in front of the record with the trailer and wr_archive() * is called to add the new members. * PAX IMPLEMENTATION DETAIL NOTE: * -u is implemented by adding the new members to the end of the archive. * Care is taken so that these do not end up as links to the older * version of the same file already stored in the archive. It is expected * when extraction occurs these newer versions will over-write the older * ones stored "earlier" in the archive (this may be a bad assumption as * it depends on the implementation of the program doing the extraction). * It is really difficult to splice in members without either re-writing * the entire archive (from the point were the old version was), or having * assistance of the format specification in terms of a special update * header that invalidates a previous archive record. The posix spec left * the method used to implement -u unspecified. This pax is able to * over write existing files that it creates. */ void append(void) { ARCHD *arcn; int res; ARCHD archd; const FSUB *orgfrmt; int udev; off_t tlen; arcn = &archd; orgfrmt = frmt; /* * Do not allow an append operation if the actual archive is of a * different format than the user specified format. */ if (get_arc() < 0) return; if ((orgfrmt != NULL) && (orgfrmt != frmt)) { paxwarn(1, "Cannot mix current archive format %s with %s", frmt->name, orgfrmt->name); return; } /* * pass the format any options and start up format */ if (((*frmt->options)() < 0) || ((*frmt->st_rd)() < 0)) return; /* hack to allow appending to Unix Archiver libraries */ if (frmt->is_uar) ar_do_keepopen = 1; /* * if we only are adding members that are newer, we need to save the * mod times for all files we see. */ if (uflag && (ftime_start() < 0)) return; /* * some archive formats encode hard links by recording the device and * file serial number (inode) but copy the file anyway (multiple times) * to the archive. When we append, we run the risk that newly added * files may have the same device and inode numbers as those recorded * on the archive but during a previous run. If this happens, when the * archive is extracted we get INCORRECT hard links. We avoid this by * remapping the device numbers so that newly added files will never * use the same device number as one found on the archive. remapping * allows new members to safely have links among themselves. remapping * also avoids problems with file inode (serial number) truncations * when the inode number is larger than storage space in the archive * header. See the remap routines for more details. */ if ((udev = frmt->udev) && (dev_start() < 0)) return; /* * reading the archive may take a long time. If verbose tell the user */ if (vflag) { (void)fprintf(listf, "%s: Reading archive to position at the end...", argv0); vfpart = 1; } /* * step through the archive until the format says it is done */ while (next_head(arcn) == 0) { /* * check if this file meets user specified options. */ if (sel_chk(arcn) != 0) { if (rd_skip(arcn->skip + arcn->pad) == 1) break; continue; } if (uflag) { /* * see if this is the newest version of this file has * already been seen, if so skip. */ if ((res = chk_ftime(arcn)) < 0) break; if (res > 0) { if (rd_skip(arcn->skip + arcn->pad) == 1) break; continue; } } /* * Store this device number. Device numbers seen during the * read phase of append will cause newly appended files with a * device number seen in the old part of the archive to be * remapped to an unused device number. */ if ((udev && (add_dev(arcn) < 0)) || (rd_skip(arcn->skip + arcn->pad) == 1)) break; } /* * done, finish up read and get the number of bytes to back up so we * can add new members. The format might have used the hard link table, * purge it. */ tlen = (*frmt->end_rd)(); lnk_end(); /* * try to position for write, if this fails quit. if any error occurs, * we will refuse to write */ if (appnd_start(tlen) < 0) return; /* * tell the user we are done reading. */ if (vflag && vfpart) { (void)fputs("done.\n", listf); vfpart = 0; } /* * go to the writing phase to add the new members */ wr_archive(arcn, 1); } /* * archive() * write a new archive */ void archive(void) { ARCHD archd; /* * if we only are adding members that are newer, we need to save the * mod times for all files; set up for writing; pass the format any * options write the archive */ if ((uflag && (ftime_start() < 0)) || (wr_start() < 0)) return; if ((*frmt->options)() < 0) return; wr_archive(&archd, 0); } /* * copy() * copy files from one part of the filesystem to another. this does not * use any archive storage. The EFFECT OF THE COPY IS THE SAME as if an * archive was written and then extracted in the destination directory * (except the files are forced to be under the destination directory). */ void copy(void) { ARCHD *arcn; int res; int fddest; char *dest_pt; size_t dlen; size_t drem; int fdsrc = -1; struct stat sb; ARCHD archd; char dirbuf[PAXPATHLEN+1]; sltab_start(); arcn = &archd; /* * set up the destination dir path and make sure it is a directory. We * make sure we have a trailing / on the destination */ dlen = strlcpy(dirbuf, dirptr, sizeof(dirbuf)); if (dlen >= sizeof(dirbuf) || (dlen == sizeof(dirbuf) - 1 && dirbuf[dlen - 1] != '/')) { paxwarn(1, "directory name is too long %s", dirptr); return; } dest_pt = dirbuf + dlen; if (*(dest_pt-1) != '/') { *dest_pt++ = '/'; *dest_pt = '\0'; ++dlen; } drem = PAXPATHLEN - dlen; if (stat(dirptr, &sb) < 0) { syswarn(1, errno, "Cannot access destination directory %s", dirptr); return; } if (!S_ISDIR(sb.st_mode)) { paxwarn(1, "Destination is not a directory %s", dirptr); return; } /* * start up the hard link table; file traversal routines and the * modification time and access mode database */ if ((lnk_start() < 0) || (ftree_start() < 0) || (dir_start() < 0)) return; /* * When we are doing interactive rename, we store the mapping of names * so we can fix up hard links files later in the archive. */ if (iflag && (name_start() < 0)) return; /* * set up to cp file trees */ cp_start(); /* * while there are files to archive, process them */ while (next_file(arcn) == 0) { fdsrc = -1; /* * check if this file meets user specified options */ if (sel_chk(arcn) != 0) continue; /* * if there is already a file in the destination directory with * the same name and it is newer, skip the one stored on the * archive. * NOTE: this test is done BEFORE name modifications as * specified by pax. this can be confusing to the user who * might expect the test to be done on an existing file AFTER * the name mod. In honesty the pax spec is probably flawed in * this respect */ if (uflag || Dflag) { /* * create the destination name */ if (strlcpy(dest_pt, arcn->name + (*arcn->name == '/'), drem + 1) > drem) { paxwarn(1, "Destination pathname too long %s", arcn->name); continue; } /* * if existing file is same age or newer skip */ res = lstat(dirbuf, &sb); *dest_pt = '\0'; if (res == 0) { ftree_skipped_newer(); if (cmp_file_times(uflag, Dflag, arcn, &sb)) continue; } } /* * this file is considered selected. See if this is a hard link * to a previous file; modify the name as requested by the * user; set the final destination. */ ftree_sel(arcn); if ((chk_lnk(arcn) < 0) || ((res = mod_name(arcn)) < 0)) break; if ((res > 0) || (set_dest(arcn, dirbuf, dlen) < 0)) { /* * skip file, purge from link table */ purg_lnk(arcn); continue; } /* * Non standard -Y and -Z flag. When the existing file is * same age or newer skip */ if ((Yflag || Zflag) && cmp_file_times(Yflag, Zflag, arcn, NULL)) continue; if (vflag) { (void)safe_print(arcn->name, listf); vfpart = 1; } else if (Vflag) { (void)putc('.', listf); (void)fflush(listf); vfpart = 1; } ++flcnt; /* * try to create a hard link to the src file if requested * but make sure we are not trying to overwrite ourselves. */ if (lflag) res = cross_lnk(arcn); else res = chk_same(arcn); if (res <= 0) { if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } continue; } /* * have to create a new file */ if (!PAX_IS_REG(arcn->type)) { /* * create a link or special file */ if (PAX_IS_HARDLINK(arcn->type)) res = lnk_creat(arcn, NULL); else res = node_creat(arcn); if (res < 0) purg_lnk(arcn); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } continue; } /* * have to copy a regular file to the destination directory. * first open source file and then create the destination file */ if ((fdsrc = binopen3(0, arcn->org_name, O_RDONLY, 0)) < 0) { syswarn(1, errno, "Unable to open %s to read", arcn->org_name); purg_lnk(arcn); continue; } if ((fddest = file_creat(arcn)) < 0) { rdfile_close(arcn, &fdsrc); purg_lnk(arcn); continue; } /* * copy source file data to the destination file */ cp_file(arcn, fdsrc, fddest); file_close(arcn, fddest); rdfile_close(arcn, &fdsrc); if (vflag && vfpart) { (void)putc('\n', listf); vfpart = 0; } } /* * restore directory modes and times as required; make sure all * patterns were selected block off signals to avoid chance for * multiple entry into the cleanup code. */ (void)sigprocmask(SIG_BLOCK, &s_mask, NULL); ar_close(0); sltab_process(0); proc_dir(0); ftree_chk(); } /* * next_head() * try to find a valid header in the archive. Uses format specific * routines to extract the header and id the trailer. Trailers may be * located within a valid header or in an invalid header (the location * is format specific. The inhead field from the option table tells us * where to look for the trailer). * We keep reading (and resyncing) until we get enough contiguous data * to check for a header. If we cannot find one, we shift by a byte * add a new byte from the archive to the end of the buffer and try again. * If we get a read error, we throw out what we have (as we must have * contiguous data) and start over again. * ASSUMED: headers fit within a BLKMULT header. * Return: * 0 if we got a header, -1 if we are unable to ever find another one * (we reached the end of input, or we reached the limit on retries. see * the specs for rd_wrbuf() for more details) */ static int next_head(ARCHD *arcn) { int ret; char *hdend; int res; int shftsz; int hsz; int in_resync = 0; /* set when we are in resync mode */ int cnt = 0; /* counter for trailer function */ int first = 1; /* on 1st read, EOF isn't premature. */ /* * set up initial conditions, we want a whole frmt->hsz block as we * have no data yet. */ res = hsz = frmt->hsz; hdend = hdbuf; shftsz = hsz - 1; for (;;) { /* * keep looping until we get a contiguous FULL buffer * (frmt->hsz is the proper size) */ for (;;) { if ((ret = rd_wrbuf(hdend, res)) == res) break; /* * If we read 0 bytes (EOF) from an archive when we * expect to find a header, we have stepped upon * an archive without the customary block of zeroes * end marker. It's just stupid to error out on * them, so exit gracefully. */ if (first && ret == 0) return(-1); first = 0; /* * some kind of archive read problem, try to resync the * storage device, better give the user the bad news. */ if ((ret == 0) || (rd_sync() < 0) || frmt->is_uar) { no_header: paxwarn(1,"Premature end of file on archive read"); return(-1); } if (!in_resync) { if (act == APPND) { paxwarn(1, "Archive I/O error, cannot continue"); return(-1); } paxwarn(1,"Archive I/O error. Trying to recover."); ++in_resync; } /* * oh well, throw it all out and start over */ res = hsz; hdend = hdbuf; } /* * ok we have a contiguous buffer of the right size. Call the * format read routine. If this was not a valid header and this * format stores trailers outside of the header, call the * format specific trailer routine to check for a trailer. We * have to watch out that we do not mis-identify file data or * block padding as a header or trailer. Format specific * trailer functions must NOT check for the trailer while we * are running in resync mode. Some trailer functions may tell * us that this block cannot contain a valid header either, so * we then throw out the entire block and start over. */ if ((*frmt->rd)(arcn, hdbuf) == 0) break; if (frmt->is_uar) goto no_header; if (!frmt->inhead) { /* * this format has trailers outside of valid headers */ if ((ret = (*frmt->trail)(arcn,hdbuf,in_resync,&cnt)) == 0){ /* * valid trailer found, drain input as required */ ar_drain(); return(-1); } if (ret == 1) { /* * we are in resync and we were told to throw * the whole block out because none of the * bytes in this block can be used to form a * valid header */ res = hsz; hdend = hdbuf; continue; } } /* * Brute force section. * not a valid header. We may be able to find a header yet. So * we shift over by one byte, and set up to read one byte at a * time from the archive and place it at the end of the buffer. * We will keep moving byte at a time until we find a header or * get a read error and have to start over. */ if (!in_resync) { if (act == APPND) { paxwarn(1,"Unable to append, archive header flaw"); return(-1); } paxwarn(1,"Invalid header, starting valid header search."); ++in_resync; } memmove(hdbuf, hdbuf+1, shftsz); res = 1; hdend = hdbuf + shftsz; } /* * ok got a valid header, check for trailer if format encodes it in the * the header. NOTE: the parameters are different than trailer routines * which encode trailers outside of the header! */ if (frmt->inhead && ((*frmt->trail)(arcn,NULL,0,NULL) == 0)) { /* * valid trailer found, drain input as required */ ar_drain(); return(-1); } ++flcnt; return(0); } /* * get_arc() * Figure out what format an archive is. Handles archive with flaws by * brute force searches for a legal header in any supported format. The * format id routines have to be careful to NOT mis-identify a format. * ASSUMED: headers fit within a BLKMULT header. * Return: * 0 if archive found -1 otherwise */ static int get_arc(void) { int i; int hdsz = 0; int res; int minhd = BLKMULT; char *hdend; int notice = 0; /* * find the smallest header size in all archive formats and then set up * to read the archive. */ for (i = 0; ford[i] != FSUB_MAX; ++i) { if (fsub[ford[i]].hsz < minhd) minhd = fsub[ford[i]].hsz; } if (rd_start() < 0) return(-1); res = BLKMULT; hdsz = 0; hdend = hdbuf; #ifndef SMALL /* try to verify against ar first */ if (buf_fill_internal(8) == 8) { i = rd_wrbuf(hdend, 8); if (i == 8 && uar_ismagic(hdbuf) == 0) { frmt = &(fsub[FSUB_AR]); return (0); } if (i > 0) pback(hdend, i); } #endif for (;;) { for (;;) { /* * fill the buffer with at least the smallest header */ i = rd_wrbuf(hdend, res); if (i > 0) hdsz += i; if (hdsz >= minhd) break; /* * if we cannot recover from a read error quit */ if ((i == 0) || (rd_sync() < 0)) goto out; /* * when we get an error none of the data we already * have can be used to create a legal header (we just * got an error in the middle), so we throw it all out * and refill the buffer with fresh data. */ res = BLKMULT; hdsz = 0; hdend = hdbuf; if (!notice) { if (act == APPND) return(-1); paxwarn(1,"Cannot identify format. Searching..."); ++notice; } } /* * we have at least the size of the smallest header in any * archive format. Look to see if we have a match. The array * ford[] is used to specify the header id order to reduce the * chance of incorrectly id'ing a valid header (some formats * may be subsets of each other and the order would then be * important). */ for (i = 0; ford[i] != FSUB_MAX; ++i) { if ((*fsub[ford[i]].id)(hdbuf, hdsz) < 0) continue; frmt = &(fsub[ford[i]]); /* * yuck, to avoid slow special case code in the extract * routines, just push this header back as if it was * not seen. We have left extra space at start of the * buffer for this purpose. This is a bit ugly, but * adding all the special case code is far worse. */ pback(hdbuf, hdsz); return(0); } /* * We have a flawed archive, no match. we start searching, but * we never allow additions to flawed archives */ if (!notice) { if (act == APPND) return(-1); paxwarn(1, "Cannot identify format. Searching..."); ++notice; } /* * brute force search for a header that we can id. * we shift through byte at a time. this is slow, but we cannot * determine the nature of the flaw in the archive in a * portable manner */ if (--hdsz > 0) { memmove(hdbuf, hdbuf+1, hdsz); res = BLKMULT - hdsz; hdend = hdbuf + hdsz; } else { res = BLKMULT; hdend = hdbuf; hdsz = 0; } } out: /* * we cannot find a header, bow, apologise and quit */ paxwarn(1, "Sorry, unable to determine archive format."); return(-1); } pax/buf_subs.c010064400000000000000000000665731466023157100105770ustar00/* $OpenBSD: buf_subs.c,v 1.30 2016/12/20 21:29:08 kettenis Exp $ */ /* $NetBSD: buf_subs.c,v 1.5 1995/03/21 09:07:08 cgd Exp $ */ /*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/buf_subs.c,v 1.15 2024/08/17 23:33:50 tg Exp $"); /* * routines which implement archive and file buffering */ #define MINFBSZ 512 /* default block size for hole detect */ #define MAXFLT 10 /* default media read error limit */ /* * Need to change bufmem to dynamic allocation when the upper * limit on blocking size is removed (though that will violate pax spec) * MAXBLK define and tests will also need to be updated. */ static char bufmem[MAXBLK+BLKMULT]; /* i/o buffer + pushback id space */ static char *buf; /* normal start of i/o buffer */ static char *bufend; /* end or last char in i/o buffer */ static char *bufpt; /* read/write point in i/o buffer */ int blksz = MAXBLK; /* block input/output size in bytes */ int wrblksz; /* user spec output size in bytes */ int maxflt = MAXFLT; /* MAX consecutive media errors */ int rdblksz; /* first read blksize (tapes only) */ off_t wrlimit; /* # of bytes written per archive vol */ off_t wrcnt; /* # of bytes written on current vol */ off_t rdcnt; /* # of bytes read on current vol */ /* * wr_start() * set up the buffering system to operate in a write mode * Return: * 0 if ok, -1 if the user specified write block size violates pax spec */ int wr_start(void) { buf = &(bufmem[BLKMULT]); /* * Check to make sure the write block size meets pax specs. If the user * does not specify a blocksize, we use the format default blocksize. * We must be picky on writes, so we do not allow the user to create an * archive that might be hard to read elsewhere. If all ok, we then * open the first archive volume */ if (!wrblksz) wrblksz = frmt->bsz; if (wrblksz > MAXBLK) { paxwarn(1, "Write block size %d too large, maximum is: %d", wrblksz, MAXBLK); return(-1); } if (wrblksz % BLKMULT) { paxwarn(1, "Write block size %d is not a %d byte multiple", wrblksz, BLKMULT); return(-1); } if (wrblksz > MAXBLK_POSIX) { paxwarn(0, "Write block size %d larger than POSIX max %d, archive may not be portable", wrblksz, MAXBLK_POSIX); return(-1); } /* * we only allow wrblksz to be used with all archive operations */ blksz = rdblksz = wrblksz; if ((ar_open(arcname) < 0) && (ar_next() < 0)) return(-1); wrcnt = 0; bufend = buf + wrblksz; bufpt = buf; return(0); } /* * rd_start() * set up buffering system to read an archive * Return: * 0 if ok, -1 otherwise */ int rd_start(void) { /* * leave space for the header pushback (see get_arc()). If we are * going to append and user specified a write block size, check it * right away */ buf = &(bufmem[BLKMULT]); if ((act == APPND) && wrblksz) { if (wrblksz > MAXBLK) { paxwarn(1, "Write block size %d too large, maximum is: %d", wrblksz, MAXBLK); return (-1); } if (wrblksz % BLKMULT) { paxwarn(1, "Write block size %d is not a %d byte multiple", wrblksz, BLKMULT); return(-1); } } /* * open the archive */ if ((ar_open(arcname) < 0) && (ar_next() < 0)) return(-1); bufend = buf + rdblksz; bufpt = bufend; rdcnt = 0; return(0); } /* * cp_start() * set up buffer system for copying within the filesystem */ void cp_start(void) { buf = &(bufmem[BLKMULT]); rdblksz = blksz = MAXBLK; } /* * appnd_start() * Set up the buffering system to append new members to an archive that * was just read. The last block(s) of an archive may contain a format * specific trailer. To append a new member, this trailer has to be * removed from the archive. The first byte of the trailer is replaced by * the start of the header of the first file added to the archive. The * format specific end read function tells us how many bytes to move * backwards in the archive to be positioned BEFORE the trailer. Two * different position have to be adjusted, the O.S. file offset (e.g. the * position of the tape head) and the write point within the data we have * stored in the read (soon to become write) buffer. We may have to move * back several records (the number depends on the size of the archive * record and the size of the format trailer) to read up the record where * the first byte of the trailer is recorded. Trailers may span (and * overlap) record boundaries. * We first calculate which record has the first byte of the trailer. We * move the OS file offset back to the start of this record and read it * up. We set the buffer write pointer to be at this byte (the byte where * the trailer starts). We then move the OS file pointer back to the * start of this record so a flush of this buffer will replace the record * in the archive. * A major problem is rewriting this last record. For archives stored * on disk files, this is trivial. However, many devices are really picky * about the conditions under which they will allow a write to occur. * Often devices restrict the conditions where writes can be made, * so it may not be feasible to append archives stored on all types of * devices. * Return: * 0 for success, -1 for failure */ int appnd_start(off_t skcnt) { int res; off_t cnt; if (exit_val != 0) { paxwarn(0, "Cannot append to an archive that may have flaws."); return(-1); } /* * if the user did not specify a write blocksize, inherit the size used * in the last archive volume read. (If a is set we still use rdblksz * until next volume, cannot shift sizes within a single volume). */ if (!wrblksz) wrblksz = blksz = rdblksz; else blksz = rdblksz; /* * make sure that this volume allows appends */ if (ar_app_ok() < 0) return(-1); /* * Calculate bytes to move back and move in front of record where we * need to start writing from. Remember we have to add in any padding * that might be in the buffer after the trailer in the last block. We * travel skcnt + padding ROUNDED UP to blksize. */ skcnt += bufend - bufpt; if ((cnt = (skcnt/blksz) * blksz) < skcnt) cnt += blksz; if (ar_rev(cnt) < 0) goto out; /* * We may have gone too far if there is valid data in the block we are * now in front of, read up the block and position the pointer after * the valid data. */ if ((cnt -= skcnt) > 0) { /* * watch out for stupid tape drives. ar_rev() will set rdblksz * to be real physical blocksize so we must loop until we get * the old rdblksz (now in blksz). If ar_rev() fouls up the * determination of the physical block size, we will fail. */ bufpt = buf; bufend = buf + blksz; while (bufpt < bufend) { if ((res = ar_read(bufpt, rdblksz)) <= 0) goto out; bufpt += res; } if (ar_rev(bufpt - buf) < 0) goto out; bufpt = buf + cnt; bufend = buf + blksz; } else { /* * buffer is empty */ bufend = buf + blksz; bufpt = buf; } rdblksz = blksz; rdcnt -= skcnt; wrcnt = 0; /* * At this point we are ready to write. If the device requires special * handling to write at a point were previously recorded data resides, * that is handled in ar_set_wr(). From now on we operate under normal * ARCHIVE mode (write) conditions */ if (ar_set_wr() < 0) return(-1); act = ARCHIVE; return(0); out: paxwarn(1, "Unable to rewrite archive trailer, cannot append."); return(-1); } /* * rd_sync() * A read error occurred on this archive volume. Resync the buffer and * try to reset the device (if possible) so we can continue to read. Keep * trying to do this until we get a valid read, or we reach the limit on * consecutive read faults (at which point we give up). The user can * adjust the read error limit through a command line option. * Returns: * 0 on success, and -1 on failure */ int rd_sync(void) { int errcnt = 0; int res; /* * if the user says bail out on first fault, we are out of here... */ if (maxflt == 0) return(-1); if (act == APPND) { paxwarn(1, "Unable to append when there are archive read errors."); return(-1); } /* * poke at device and try to get past media error */ if (ar_rdsync() < 0) { if (ar_next() < 0) return(-1); else rdcnt = 0; } for (;;) { if ((res = ar_read(buf, blksz)) > 0) { /* * All right! got some data, fill that buffer */ bufpt = buf; bufend = buf + res; rdcnt += res; return(0); } /* * Oh well, yet another failed read... * if error limit reached, ditch. o.w. poke device to move past * bad media and try again. if media is badly damaged, we ask * the poor (and upset user at this point) for the next archive * volume. remember the goal on reads is to get the most we * can extract out of the archive. */ if ((maxflt > 0) && (++errcnt > maxflt)) paxwarn(0,"Archive read error limit (%d) reached",maxflt); else if (ar_rdsync() == 0) continue; if (ar_next() < 0) break; rdcnt = 0; errcnt = 0; } return(-1); } /* * pback() * push the data used during the archive id phase back into the I/O * buffer. This is required as we cannot be sure that the header does NOT * overlap a block boundary (as in the case we are trying to recover a * flawed archived). This was not designed to be used for any other * purpose. (What software engineering, HA!) * WARNING: do not even THINK of pback greater than BLKMULT, unless the * pback space is increased. */ void pback(const char *pt, int cnt) { bufpt -= cnt; memcpy(bufpt, pt, cnt); } /* * rd_skip() * skip forward in the archive during a archive read. Used to get quickly * past file data and padding for files the user did NOT select. * Return: * 0 if ok, -1 failure, and 1 when EOF on the archive volume was detected. */ int rd_skip(off_t skcnt) { off_t res; off_t cnt; off_t skipped = 0; if (skcnt < 0) { paxwarn(1, "Trying to skip backwards; corrupt archive likely"); sig_cleanup(0); } /* * consume what data we have in the buffer. If we have to move forward * whole records, we call the low level skip function to see if we can * move within the archive without doing the expensive reads on data we * do not want. */ if (skcnt == 0) return(0); res = MINIMUM((bufend - bufpt), skcnt); bufpt += res; skcnt -= res; /* * if skcnt is now 0, then no additional i/o is needed */ if (skcnt == 0) return(0); /* * We have to read more, calculate complete and partial record reads * based on rdblksz. we skip over "cnt" complete records */ res = skcnt%rdblksz; cnt = (skcnt/rdblksz) * rdblksz; /* * if the skip fails, we will have to resync. ar_fow will tell us * how much it can skip over. We will have to read the rest. */ if (ar_fow(cnt, &skipped) < 0) return(-1); res += cnt - skipped; rdcnt += skipped; /* * what is left we have to read (which may be the whole thing if * ar_fow() told us the device can only read to skip records); */ while (res > 0) { cnt = bufend - bufpt; /* * if the read fails, we will have to resync */ if ((cnt <= 0) && ((cnt = buf_fill()) < 0)) return(-1); if (cnt == 0) return(1); cnt = MINIMUM(cnt, res); bufpt += cnt; res -= cnt; } return(0); } /* * wr_fin() * flush out any data (and pad if required) the last block. We always pad * with zero (even though we do not have to). Padding with 0 makes it a * lot easier to recover if the archive is damaged. zero padding SHOULD * BE a requirement.... */ void wr_fin(void) { if (frmt->is_uar) { /*XXX breaks tape/file/stream abstraction */ extern int arfd; char *bufbt = buf; ssize_t n; while (bufpt > bufbt) { n = write(arfd, bufbt, bufpt - bufbt); if (n < 0) { syswarn(1, errno, "Could not finish writing"); return; } bufbt += n; } } else if (bufpt > buf) { memset(bufpt, 0, bufend - bufpt); bufpt = bufend; (void)buf_flush(blksz); } } /* * wr_rdbuf() * fill the write buffer from data passed to it in a buffer (usually used * by format specific write routines to pass a file header). On failure we * punt. We do not allow the user to continue to write flawed archives. * We assume these headers are not very large (the memory copy we use is * a bit expensive). * Return: * 0 if buffer was filled ok, -1 o.w. (buffer flush failure) */ int wr_rdbuf(const char *out, int outcnt) { int cnt; /* * while there is data to copy copy into the write buffer. when the * write buffer fills, flush it to the archive and continue */ while (outcnt > 0) { cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) return(-1); /* * only move what we have space for */ cnt = MINIMUM(cnt, outcnt); memcpy(bufpt, out, cnt); bufpt += cnt; out += cnt; outcnt -= cnt; } return(0); } /* * rd_wrbuf() * copy from the read buffer into a supplied buffer a specified number of * bytes. If the read buffer is empty fill it and continue to copy. * usually used to obtain a file header for processing by a format * specific read routine. * Return * number of bytes copied to the buffer, 0 indicates EOF on archive volume, * -1 is a read error */ int rd_wrbuf(char *in, int cpcnt) { int res; int cnt; int incnt = cpcnt; /* * loop until we fill the buffer with the requested number of bytes */ while (incnt > 0) { cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) { /* * read error, return what we got (or the error if * no data was copied). The caller must know that an * error occurred and has the best knowledge what to * do with it */ if ((res = cpcnt - incnt) > 0) return(res); return(cnt); } /* * calculate how much data to copy based on whats left and * state of buffer */ cnt = MINIMUM(cnt, incnt); memcpy(in, bufpt, cnt); bufpt += cnt; incnt -= cnt; in += cnt; } return(cpcnt); } /* * wr_skip() * skip forward during a write. In other words add padding to the file. * we add zero filled padding as it makes flawed archives much easier to * recover from. the caller tells us how many bytes of padding to add * This routine was not designed to add HUGE amount of padding, just small * amounts (a few 512 byte blocks at most) * Return: * 0 if ok, -1 if there was a buf_flush failure */ int wr_skip(off_t skcnt) { int cnt; /* * loop while there is more padding to add */ while (skcnt > 0) { cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) return(-1); cnt = MINIMUM(cnt, skcnt); memset(bufpt, 0, cnt); bufpt += cnt; skcnt -= cnt; } return(0); } /* * wr_rdfile() * fill write buffer with the contents of a file. We are passed an open * file descriptor to the file and the archive structure that describes * the file we are storing. The variable "left" is modified to contain the * number of bytes of the file we were NOT able to write to the archive. * it is important that we always write EXACTLY the number of bytes that * the format specific write routine told us to. The file can also get * bigger, so reading to the end of file would create an improper archive, * we just detect this case and warn the user. We never create a bad * archive if we can avoid it. Of course trying to archive files that are * active is asking for trouble. It we fail, we pass back how much we * could NOT copy and let the caller deal with it. * Return: * 0 ok, -1 if archive write failure. a short read of the file returns a * 0, but "left" is set to be greater than zero. */ int wr_rdfile(ARCHD *arcn, int ifd, off_t *left) { int cnt; int res = 0; off_t size = arcn->sb.st_size; struct stat sb; /* * while there are more bytes to write */ while (size > 0) { cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) { *left = size; return(-1); } cnt = MINIMUM(cnt, size); if ((res = read(ifd, bufpt, cnt)) <= 0) break; size -= res; bufpt += res; } /* * better check the file did not change during this operation * or the file read failed. */ if (res < 0) syswarn(1, errno, "Read fault on %s", arcn->org_name); else if (size != 0) paxwarn(1, "File changed size during read %s", arcn->org_name); else if (fstat(ifd, &sb) < 0) syswarn(1, errno, "Failed stat on %s", arcn->org_name); else if (st_timecmp(m, &arcn->sb, &sb, !=)) paxwarn(1, "File %s was modified during copy to %s", arcn->org_name, "archive"); *left = size; return(0); } /* * rd_wrfile() * extract the contents of a file from the archive. If we are unable to * extract the entire file (due to failure to write the file) we return * the numbers of bytes we did NOT process. This way the caller knows how * many bytes to skip past to find the next archive header. If the failure * was due to an archive read, we will catch that when we try to skip. If * the format supplies a file data crc value, we calculate the actual crc * so that it can be compared to the value stored in the header * NOTE: * We call a special function to write the file. This function attempts to * restore file holes (blocks of zeros) into the file. When files are * sparse this saves space, and is a LOT faster. For non sparse files * the performance hit is small. As of this writing, no archive supports * information on where the file holes are. * Return: * 0 ok, -1 if archive read failure. if we cannot write the entire file, * we return a 0 but "left" is set to be the amount unwritten */ int rd_wrfile(ARCHD *arcn, int ofd, off_t *left) { int cnt = 0; off_t size = arcn->sb.st_size; int res = 0; char *fnm = arcn->name; int isem = 1; int rem; int sz = MINFBSZ; struct stat sb; uint32_t crc = 0; /* * pass the blocksize of the file being written to the write routine, * if the size is zero, use the default MINFBSZ */ if (ofd < 0) sz = PAXPATHLEN + 1; /* GNU tar long link/file */ else if (fstat(ofd, &sb) == 0) { if (sb.st_blksize > 0) sz = (int)sb.st_blksize; } else syswarn(0,errno,"Unable to obtain block size for file %s",fnm); rem = sz; *left = 0; /* * Copy the archive to the file the number of bytes specified. We have * to assume that we want to recover file holes as none of the archive * formats can record the location of file holes. */ while (size > 0) { cnt = bufend - bufpt; /* * if we get a read error, we do not want to skip, as we may * miss a header, so we do not set left, but if we get a write * error, we do want to skip over the unprocessed data. */ if ((cnt <= 0) && ((cnt = buf_fill()) <= 0)) break; cnt = MINIMUM(cnt, size); if ((res = file_write(ofd,bufpt,cnt,&rem,&isem,sz,fnm)) <= 0) { *left = size; break; } if (docrc) { /* * update the actual crc value */ cnt = res; while (--cnt >= 0) crc += *bufpt++ & 0xff; } else bufpt += res; size -= res; } /* * if the last block has a file hole (all zero), we must make sure this * gets updated in the file. We force the last block of zeros to be * written. just closing with the file offset moved forward may not put * a hole at the end of the file. */ if (isem && (arcn->sb.st_size > 0)) file_flush(ofd, fnm, isem); /* * if we failed from archive read, we do not want to skip */ if ((size > 0) && (*left == 0)) return(-1); /* * some formats record a crc on file data. If so, then we compare the * calculated crc to the crc stored in the archive */ if (docrc && (size == 0) && (arcn->crc != crc)) paxwarn(1,"Actual crc does not match expected crc %s",arcn->name); return(0); } /* * cp_file() * copy the contents of one file to another. used during -rw phase of pax * just as in rd_wrfile() we use a special write function to write the * destination file so we can properly copy files with holes. */ void cp_file(ARCHD *arcn, int fd1, int fd2) { int cnt; off_t cpcnt = 0; int res = 0; char *fnm = arcn->name; int no_hole = 0; int isem = 1; int rem; int sz = MINFBSZ; struct stat sb; /* * check for holes in the source file. If none, we will use regular * write instead of file write. */ if (((off_t)(arcn->sb.st_blocks * BLKMULT)) >= (off_t)arcn->sb.st_size) ++no_hole; /* * pass the blocksize of the file being written to the write routine, * if the size is zero, use the default MINFBSZ */ if (fstat(fd2, &sb) == 0) { if (sb.st_blksize > 0) sz = sb.st_blksize; } else syswarn(0,errno,"Unable to obtain block size for file %s",fnm); rem = sz; /* * read the source file and copy to destination file until EOF */ for (;;) { if ((cnt = read(fd1, buf, blksz)) <= 0) break; if (no_hole) res = write(fd2, buf, cnt); else res = file_write(fd2, buf, cnt, &rem, &isem, sz, fnm); if (res != cnt) break; cpcnt += cnt; } /* * check to make sure the copy is valid. */ if (res < 0) syswarn(1, errno, "Failed write during copy of %s to %s", arcn->org_name, arcn->name); else if (cpcnt != (off_t)arcn->sb.st_size) paxwarn(1, "File %s changed size during copy to %s", arcn->org_name, arcn->name); else if (fstat(fd1, &sb) < 0) syswarn(1, errno, "Failed stat on %s", arcn->org_name); else if (st_timecmp(m, &arcn->sb, &sb, !=)) paxwarn(1, "File %s was modified during copy to %s", arcn->org_name, arcn->name); /* * if the last block has a file hole (all zero), we must make sure this * gets updated in the file. We force the last block of zeros to be * written. just closing with the file offset moved forward may not put * a hole at the end of the file. */ if (!no_hole && isem && (arcn->sb.st_size > 0)) file_flush(fd2, fnm, isem); } /* * buf_fill() * fill the read buffer with the next record (or what we can get) from * the archive volume. * Return: * Number of bytes of data in the read buffer, -1 for read error, and * 0 when finished (user specified termination in ar_next()). */ int buf_fill(void) { return (buf_fill_internal(blksz)); } /*XXX exposure of this breaks block alignment, use only in ar */ int buf_fill_internal(int numb) { int cnt; static int fini = 0; if (fini) return(0); for (;;) { /* * try to fill the buffer. on error the next archive volume is * opened and we try again. */ if ((cnt = ar_read(buf, numb)) > 0) { bufpt = buf; bufend = buf + cnt; rdcnt += cnt; return(cnt); } /* * errors require resync, EOF goes to next archive * but in case we have not determined yet the format, * this means that we have a very short file, so we * are done again. */ if (cnt < 0) break; if (ar_do_keepopen || frmt == NULL || ar_next() < 0) { fini = 1; return(0); } rdcnt = 0; } exit_val = 1; return(-1); } /* * buf_flush() * force the write buffer to the archive. We are passed the number of * bytes in the buffer at the point of the flush. When we change archives * the record size might change. (either larger or smaller). * Return: * 0 if all is ok, -1 when a write error occurs. */ int buf_flush(int bufcnt) { int cnt; int push = 0; int totcnt = 0; /* * if we have reached the user specified byte count for each archive * volume, prompt for the next volume. (The non-standard -R flag). * NOTE: If the wrlimit is smaller than wrcnt, we will always write * at least one record. We always round limit UP to next blocksize. */ if ((wrlimit > 0) && (wrcnt > wrlimit)) { paxwarn(0, "User specified archive volume byte limit reached."); if (ar_next() < 0) { wrcnt = 0; exit_val = 1; return(-1); } wrcnt = 0; /* * The new archive volume might have changed the size of the * write blocksize. if so we figure out if we need to write * (one or more times), or if there is now free space left in * the buffer (it is no longer full). bufcnt has the number of * bytes in the buffer, (the blocksize, at the point we were * CALLED). Push has the amount of "extra" data in the buffer * if the block size has shrunk from a volume change. */ bufend = buf + blksz; if (blksz > bufcnt) return(0); if (blksz < bufcnt) push = bufcnt - blksz; } /* * We have enough data to write at least one archive block */ for (;;) { /* * write a block and check if it all went out ok */ cnt = ar_write(buf, blksz); if (cnt == blksz) { /* * the write went ok */ wrcnt += cnt; totcnt += cnt; if (push > 0) { /* we have extra data to push to the front. * check for more than 1 block of push, and if * so we loop back to write again */ memcpy(buf, bufend, push); bufpt = buf + push; if (push >= blksz) { push -= blksz; continue; } } else bufpt = buf; return(totcnt); } else if (cnt > 0) { /* * Oh drat we got a partial write! * if format does not care about alignment let it go, * we warned the user in ar_write().... but this means * the last record on this volume violates pax spec.... */ totcnt += cnt; wrcnt += cnt; bufpt = buf + cnt; cnt = bufcnt - cnt; memcpy(buf, bufpt, cnt); bufpt = buf + cnt; if (!frmt->blkalgn || ((cnt % frmt->blkalgn) == 0)) return(totcnt); break; } /* * All done, go to next archive */ wrcnt = 0; if (ar_next() < 0) break; /* * The new archive volume might also have changed the block * size. if so, figure out if we have too much or too little * data for using the new block size */ bufend = buf + blksz; if (blksz > bufcnt) return(0); if (blksz < bufcnt) push = bufcnt - blksz; } /* * write failed, stop pax. we must not create a bad archive! */ exit_val = 1; return(-1); } #ifndef SMALL /* * wr_rdfile replacement for the Unix Archiver (padding) */ int uar_wr_data(ARCHD *arcn, int ifd, off_t *left) { int cnt; if (wr_rdfile(arcn, ifd, left) < 0) return (-1); if (!arcn->pad) return (0); /*XXX arcn->pad == 1 */ arcn->pad = 0; cnt = bufend - bufpt; if ((cnt <= 0) && ((cnt = buf_flush(blksz)) < 0)) { /* *left == 0 */ *left = 1; return (-1); } *bufpt++ = '\n'; return (0); } #endif pax/cache.c010064400000000000000000000265161340440266000100150ustar00/* $OpenBSD: cache.c,v 1.23 2016/08/26 04:08:18 guenther Exp $ */ /* $NetBSD: cache.c,v 1.4 1995/03/21 09:07:10 cgd Exp $ */ /*- * Copyright (c) 2018 mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #if HAVE_GRP_H #include #endif #include #include #include #include #if HAVE_STRINGS_H #include #endif #include "pax.h" #include "extern.h" #if HAVE_UGID_FROM_UG # error do not include this file if you do not need it #endif __RCSID("$MirOS: src/bin/pax/cache.c,v 1.11 2018/12/13 07:09:09 tg Exp $"); /* * Constants and data structures used to implement group and password file * caches. Traditional passwd/group cache routines perform quite poorly with * archives. The chances of hitting a valid lookup with an archive is quite a * bit worse than with files already resident on the filesystem. These misses * create a MAJOR performance cost. To address this problem, these routines * cache both hits and misses. * * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME */ #define UNMLEN 32 /* >= user name found in any protocol */ #define GNMLEN 32 /* >= group name found in any protocol */ #define UID_SZ 317 /* size of user_name/uid cache */ #define UNM_SZ 317 /* size of user_name/uid cache */ #define GID_SZ 251 /* size of gid cache */ #define GNM_SZ 317 /* size of group name cache */ #define VALID 1 /* entry and name are valid */ #define INVALID 2 /* entry valid, name NOT valid */ /* * Node structures used in the user, group, uid, and gid caches. */ typedef struct uidc { int valid; /* is this a valid or a miss entry */ char name[UNMLEN]; /* uid name */ uid_t uid; /* cached uid */ } UIDC; typedef struct gidc { int valid; /* is this a valid or a miss entry */ char name[GNMLEN]; /* gid name */ gid_t gid; /* cached gid */ } GIDC; /* * routines that control user, group, uid and gid caches (for the archive * member print routine). * IMPORTANT: * these routines cache BOTH hits and misses, a major performance improvement */ static int pwopn = 0; /* is password file open */ static int gropn = 0; /* is group file open */ #if !HAVE_UG_FROM_UGID static UIDC **uidtb = NULL; /* uid to name cache */ static GIDC **gidtb = NULL; /* gid to name cache */ #endif static UIDC **usrtb = NULL; /* user name to uid cache */ static GIDC **grptb = NULL; /* group name to gid cache */ #if !HAVE_UG_FROM_UGID /* * uidtb_start * creates an empty uidtb * Return: * 0 if ok, -1 otherwise */ int uidtb_start(void) { static int fail = 0; if (uidtb != NULL) return(0); if (fail) return(-1); if ((uidtb = calloc(UID_SZ, sizeof(UIDC *))) == NULL) { ++fail; paxwarn(1, "%s for %s", "Out of memory", "user id cache table"); return (-1); } return(0); } /* * gidtb_start * creates an empty gidtb * Return: * 0 if ok, -1 otherwise */ int gidtb_start(void) { static int fail = 0; if (gidtb != NULL) return(0); if (fail) return(-1); if ((gidtb = calloc(GID_SZ, sizeof(GIDC *))) == NULL) { ++fail; paxwarn(1, "%s for %s", "Out of memory", "group id cache table"); return (-1); } return(0); } #endif /* * usrtb_start * creates an empty usrtb * Return: * 0 if ok, -1 otherwise */ int usrtb_start(void) { static int fail = 0; if (usrtb != NULL) return(0); if (fail) return(-1); if ((usrtb = calloc(UNM_SZ, sizeof(UIDC *))) == NULL) { ++fail; paxwarn(1, "%s for %s", "Out of memory", "user name cache table"); return (-1); } return(0); } /* * grptb_start * creates an empty grptb * Return: * 0 if ok, -1 otherwise */ int grptb_start(void) { static int fail = 0; if (grptb != NULL) return(0); if (fail) return(-1); if ((grptb = calloc(GNM_SZ, sizeof(GIDC *))) == NULL) { ++fail; paxwarn(1,"%s for %s", "Out of memory", "group name cache table"); return (-1); } return(0); } #if !HAVE_UG_FROM_UGID /* * name_uid() * caches the name (if any) for the uid. If frc set, we always return the * the stored name (if valid or invalid match). We use a simple hash table. * Return * Pointer to stored name (or a empty string) */ const char * name_uid(uid_t uid, int frc) { struct passwd *pw; UIDC *ptr; if ((uidtb == NULL) && (uidtb_start() < 0)) return(""); /* * see if we have this uid cached */ ptr = uidtb[uid % UID_SZ]; if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) { /* * have an entry for this uid */ if (frc || (ptr->valid == VALID)) return(ptr->name); return(""); } /* * No entry for this uid, we will add it */ if (!pwopn) { #if HAVE_SETPGENT setpassent(1); #else setpwent(); #endif ++pwopn; } if (ptr == NULL) ptr = uidtb[uid % UID_SZ] = malloc(sizeof(UIDC)); if ((pw = getpwuid(uid)) == NULL) { /* * no match for this uid in the local password file * a string that is the uid in numeric format */ if (ptr == NULL) return(""); ptr->uid = uid; ptr->valid = INVALID; (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", (unsigned long)uid); if (frc == 0) return(""); } else { /* * there is an entry for this uid in the password file */ if (ptr == NULL) return(pw->pw_name); ptr->uid = uid; (void)strlcpy(ptr->name, pw->pw_name, sizeof(ptr->name)); ptr->valid = VALID; } return(ptr->name); } /* * name_gid() * caches the name (if any) for the gid. If frc set, we always return the * the stored name (if valid or invalid match). We use a simple hash table. * Return * Pointer to stored name (or a empty string) */ const char * name_gid(gid_t gid, int frc) { struct group *gr; GIDC *ptr; if ((gidtb == NULL) && (gidtb_start() < 0)) return(""); /* * see if we have this gid cached */ ptr = gidtb[gid % GID_SZ]; if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) { /* * have an entry for this gid */ if (frc || (ptr->valid == VALID)) return(ptr->name); return(""); } /* * No entry for this gid, we will add it */ if (!gropn) { #if HAVE_SETPGENT setgroupent(1); #else setgrent(); #endif ++gropn; } if (ptr == NULL) ptr = gidtb[gid % GID_SZ] = malloc(sizeof(GIDC)); if ((gr = getgrgid(gid)) == NULL) { /* * no match for this gid in the local group file, put in * a string that is the gid in numeric format */ if (ptr == NULL) return(""); ptr->gid = gid; ptr->valid = INVALID; (void)snprintf(ptr->name, sizeof(ptr->name), "%lu", (unsigned long)gid); if (frc == 0) return(""); } else { /* * there is an entry for this group in the group file */ if (ptr == NULL) return(gr->gr_name); ptr->gid = gid; (void)strlcpy(ptr->name, gr->gr_name, sizeof(ptr->name)); ptr->valid = VALID; } return(ptr->name); } #endif /* * uid_name() * caches the uid for a given user name. We use a simple hash table. * Return * the uid (if any) for a user name, or a -1 if no match can be found */ int uid_name(const char *name, uid_t *uid) { struct passwd *pw; UIDC *ptr; int namelen; /* * return -1 for mangled names */ if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) return(-1); if ((usrtb == NULL) && (usrtb_start() < 0)) return(-1); /* * look up in hash table, if found and valid return the uid, * if found and invalid, return a -1 */ ptr = usrtb[st_hash(name, namelen, UNM_SZ)]; if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { if (ptr->valid == INVALID) return(-1); *uid = ptr->uid; return(0); } if (!pwopn) { #if HAVE_SETPGENT setpassent(1); #else setpwent(); #endif ++pwopn; } if (ptr == NULL) ptr = usrtb[st_hash(name, namelen, UNM_SZ)] = malloc(sizeof(UIDC)); /* * no match, look it up, if no match store it as an invalid entry, * or store the matching uid */ if (ptr == NULL) { if ((pw = getpwnam(name)) == NULL) return(-1); *uid = pw->pw_uid; return(0); } (void)strlcpy(ptr->name, name, sizeof(ptr->name)); if ((pw = getpwnam(name)) == NULL) { ptr->valid = INVALID; return(-1); } ptr->valid = VALID; *uid = ptr->uid = pw->pw_uid; return(0); } /* * gid_name() * caches the gid for a given group name. We use a simple hash table. * Return * the gid (if any) for a group name, or a -1 if no match can be found */ int gid_name(const char *name, gid_t *gid) { struct group *gr; GIDC *ptr; int namelen; /* * return -1 for mangled names */ if (((namelen = strlen(name)) == 0) || (name[0] == '\0')) return(-1); if ((grptb == NULL) && (grptb_start() < 0)) return(-1); /* * look up in hash table, if found and valid return the uid, * if found and invalid, return a -1 */ ptr = grptb[st_hash(name, namelen, GID_SZ)]; if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) { if (ptr->valid == INVALID) return(-1); *gid = ptr->gid; return(0); } if (!gropn) { #if HAVE_SETPGENT setgroupent(1); #else setgrent(); #endif ++gropn; } if (ptr == NULL) ptr = grptb[st_hash(name, namelen, GID_SZ)] = malloc(sizeof(GIDC)); /* * no match, look it up, if no match store it as an invalid entry, * or store the matching gid */ if (ptr == NULL) { if ((gr = getgrnam(name)) == NULL) return(-1); *gid = gr->gr_gid; return(0); } (void)strlcpy(ptr->name, name, sizeof(ptr->name)); if ((gr = getgrnam(name)) == NULL) { ptr->valid = INVALID; return(-1); } ptr->valid = VALID; *gid = ptr->gid = gr->gr_gid; return(0); } int uid_uncached(const char *name, uid_t *uid) { struct passwd *pw; if ((pw = getpwnam(name)) == NULL) return (-1); *uid = pw->pw_uid; return (0); } int gid_uncached(const char *name, gid_t *gid) { struct group *gr; if ((gr = getgrnam(name)) == NULL) return (-1); *gid = gr->gr_gid; return (0); } pax/compat.c010064400000000000000000000102061372452640700102350ustar00/*- * Copyright © 2019, 2020 * mirabilos * Copyright © 2018 * mirabilos * The copyright notices and licences of the files in .linked/ inclu‐ * ded below shall be considered being part of this copyright notice. * Also contains material part of “jupp” (Joe’s Own Editor), © 2018 * mirabilos * Contains code from “mksh” (The MirBSD Korn Shell) © 2015 * mirabilos * KO Myung-Hun * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un‐ * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person’s immediate fault when using the work as intended. */ #include #include #include #include #include "compat.h" #define PAX_JUST_THE_WARNINGS #include "extern.h" __RCSID("$MirOS: src/bin/pax/compat.c,v 1.6 2020/09/04 21:09:00 tg Exp $"); int binopen3(int features, const char *path, int flags, mode_t mode) { int fd; #ifdef O_BINARY flags |= O_BINARY; #endif #ifdef O_CLOEXEC if (features & BO_CLEXEC) { flags |= O_CLOEXEC; features &= ~BO_CLEXEC; } #endif #ifdef O_DIRECTORY if (features & BO_MAYBE_DIR) flags |= O_DIRECTORY; #endif if ((fd = (features & BO__TWO) ? open(path, flags) : open(path, flags, mode)) != -1) { #ifdef __OS2__ setmode(fd, O_BINARY); #endif if ((features & BO_CLEXEC) && fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) syswarn(0, errno, "Failed to set the close-on-exec flag"); } return (fd); } ssize_t dwrite(int fd, const void *data, size_t size) { const unsigned char *buf = data; ssize_t rv = 0, z; while (size) { if ((z = write(fd, buf, size)) < 0) { if (errno == EINTR) continue; return (rv ? /* fucked up since we got some */ -2 : -1); } rv += z; buf += z; size -= z; } return (rv); } #if !HAVE_DPRINTF #include #include static char *dprintf_donum(char *, unsigned long long); /* replacement only as powerful as needed for this */ void dprintf(int fd, const char *fmt, ...) { /* %s %llu %lu %d */ const char *ccp; char *cp, buf[24]; unsigned long long uval; va_list ap; va_start(ap, fmt); loop: switch (*fmt) { case 0: goto out; case '%': break; default: ccp = fmt; do { ++ccp; } while (*ccp && *ccp != '%'); if (dwrite(fd, fmt, ccp - fmt) < 0) goto out; fmt = ccp; goto loop; } switch (*++fmt) { case 'd': { int ival; ival = va_arg(ap, int); cp = buf; if (ival < 0) { *cp++ = '-'; uval = -(long long)ival; } else uval = ival; goto num; } case 'l': switch (*++fmt) { case 'l': if (*++fmt != 'u') { errfmt: /* could do something about this? */ goto loop; } uval = va_arg(ap, unsigned long long); break; case 'u': uval = va_arg(ap, unsigned long); break; default: goto errfmt; } cp = buf; num: *dprintf_donum(cp, uval) = '\0'; cp = buf; if (0) /* FALLTHROUGH */ case 's': cp = va_arg(ap, char *); if (dwrite(fd, cp, strlen(cp)) < 0) goto out; break; default: goto errfmt; } ++fmt; goto loop; out: va_end(ap); } static char * dprintf_donum(char *cp, unsigned long long val) { unsigned long long subval = val / 10; if (subval) cp = dprintf_donum(cp, subval); *cp++ = '0' + (val % 10); return (cp); } #endif #if !HAVE_STRLCPY || !HAVE_STRLCAT #undef WIDEC #define OUTSIDE_OF_LIBKERN #if !HAVE_STRLCPY #define L_strlcat #endif #if !HAVE_STRLCAT #define L_strlcpy #endif #include ".linked/strlfun.inc" #endif pax/compat.h010064400000000000000000000147451466023157200102520ustar00/*- * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, * 2019, 2020 * mirabilos * Copyright © 2018 * mirabilos * * Provided that these terms and disclaimer and all copyright notices * are retained or reproduced in an accompanying document, permission * is granted to deal in this work without restriction, including un‐ * limited rights to use, publicly perform, distribute, sell, modify, * merge, give away, or sublicence. * * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to * the utmost extent permitted by applicable law, neither express nor * implied; without malicious intent or gross negligence. In no event * may a licensor, author or contributor be held liable for indirect, * direct, other damage, loss, or other issues arising in any way out * of dealing in the work, even if advised of the possibility of such * damage or existence of a defect, except proven that it results out * of said person’s immediate fault when using the work as intended. */ #ifdef MKSH_USE_AUTOCONF_H /* things that “should” have been on the command line */ #include "autoconf.h" #undef MKSH_USE_AUTOCONF_H #endif #ifndef PAX_COMPAT_H #define PAX_COMPAT_H #undef MBSDPORT_H #ifdef __MirBSD__ #include #else #include #endif #if HAVE_SYS_MKDEV_H #include #endif #if HAVE_SYS_SYSMACROS_H #include #endif #if HAVE_STDINT_H #include #endif #include ".linked/mbsdcc.h" #undef __attribute__ #if HAVE_ATTRIBUTE_BOUNDED #define MKSH_A_BOUNDED(x,y,z) __attribute__((__bounded__(x, y, z))) #else #define MKSH_A_BOUNDED(x,y,z) /* nothing */ #endif #if HAVE_ATTRIBUTE_FORMAT #define MKSH_A_FORMAT(x,y,z) __attribute__((__format__(x, y, z))) #else #define MKSH_A_FORMAT(x,y,z) /* nothing */ #endif #if HAVE_ATTRIBUTE_NONNULL #define MKSH_A_NONNULL(x) __attribute__((__nonnull__(x))) #else #define MKSH_A_NONNULL(x) /* nothing */ #endif #if HAVE_ATTRIBUTE_NORETURN #define MKSH_A_NORETURN __attribute__((__noreturn__)) #else #define MKSH_A_NORETURN /* nothing */ #endif #if HAVE_ATTRIBUTE_PURE #define MKSH_A_PURE __attribute__((__pure__)) #else #define MKSH_A_PURE /* nothing */ #endif #if HAVE_ATTRIBUTE_UNUSED #define MKSH_A_UNUSED __attribute__((__unused__)) #else #define MKSH_A_UNUSED /* nothing */ #endif #if HAVE_ATTRIBUTE_USED #define MKSH_A_USED __attribute__((__used__)) #else #define MKSH_A_USED /* nothing */ #endif #if defined(MirBSD) && (MirBSD >= 0x09A1) && \ defined(__ELF__) && defined(__GNUC__) && \ !defined(__llvm__) && !defined(__NWCC__) /* * We got usable __IDSTRING __COPYRIGHT __RCSID __SCCSID macros * which work for all cases; no need to redefine them using the * "portable" macros from below when we might have the "better" * gcc+ELF specific macros or other system dependent ones. */ #else #undef __IDSTRING #undef __IDSTRING_CONCAT #undef __IDSTRING_EXPAND #undef __COPYRIGHT #undef __RCSID #undef __SCCSID #define __IDSTRING_CONCAT(l,p) __LINTED__ ## l ## _ ## p #define __IDSTRING_EXPAND(l,p) __IDSTRING_CONCAT(l,p) #ifdef MKSH_DONT_EMIT_IDSTRING #define __IDSTRING(prefix,string) /* nothing */ #else #define __IDSTRING(prefix,string) \ static const char __IDSTRING_EXPAND(__LINE__,prefix) [] \ MKSH_A_USED = "@(""#)" #prefix ": " string #endif #define __COPYRIGHT(x) __IDSTRING(copyright,x) #define __RCSID(x) __IDSTRING(rcsid,x) #define __SCCSID(x) __IDSTRING(sccsid,x) #endif #ifdef EXTERN __IDSTRING(rcsid_compat_h, "$MirOS: src/bin/pax/compat.h,v 1.15 2024/08/17 23:33:51 tg Exp $"); __IDSTRING(rcsid_mbsdcc_h, SYSKERN_MBSDCC_H); #endif /* possibly missing types */ #if !HAVE_CAN_INTTYPES #if !HAVE_CAN_UCBINTS typedef signed int int32_t; typedef unsigned int uint32_t; #else typedef u_int32_t uint32_t; #endif #endif #if !HAVE_CAN_ULONG typedef unsigned long u_long; #endif /* boolean type (no deliberately) */ typedef unsigned char Wahr; #define Ja 1U #define Nee 0U #define isWahr(cond) ((cond) ? Ja : Nee) /* missing macros / header bug workarounds */ #if !defined(LLONG_MIN) && defined(LONG_LONG_MIN) #define LLONG_MIN LONG_LONG_MIN #define LLONG_MAX LONG_LONG_MAX #define ULLONG_MAX ULONG_LONG_MAX #endif /* macros dealing with types differing across systems */ #if HAVE_OFFT_LONG #define OT_FMT "lu" #else #define OT_FMT "llu" #endif /* macros dealing with struct stat.sb_[acm]time */ #if HAVE_ST_MTIMENSEC #define st_timecmp(x,sbpa,sbpb,op) ( \ ((sbpa)->st_ ## x ## time == (sbpb)->st_ ## x ## time) ? \ ((sbpa)->st_ ## x ## timensec op (sbpb)->st_ ## x ## timensec) : \ ((sbpa)->st_ ## x ## time op (sbpb)->st_ ## x ## time) \ ) #define st_timecpy(x,sbpd,sbps) do { \ (sbpd)->st_ ## x ## time = (sbps)->st_ ## x ## time; \ (sbpd)->st_ ## x ## timensec = (sbps)->st_ ## x ## timensec; \ } while (/* CONSTCOND */ 0) #define st_timexp(x,ts,sbp) do { \ (ts)->tv_sec = (sbp)->st_ ## x ## time; \ (ts)->tv_nsec = (sbp)->st_ ## x ## timensec; \ } while (/* CONSTCOND */ 0) #else #define st_timecmp(x,sbpa,sbpb,op) \ ((sbpa)->st_ ## x ## time op (sbpb)->st_ ## x ## time) #define st_timecpy(x,sbpd,sbps) do { \ (sbpd)->st_ ## x ## time = (sbps)->st_ ## x ## time; \ } while (/* CONSTCOND */ 0) #define st_timexp(x,ts,sbp) do { \ (ts)->tv_sec = (sbp)->st_ ## x ## time; \ (ts)->tv_nsec = 0; \ } while (/* CONSTCOND */ 0) #endif /* compat.c */ #define BO__TWO 0x01 /* (internal use only) */ #define BO_CLEXEC 0x02 /* set close-on-exec flag or warn */ #define BO_MAYBE_DIR 0x04 /* add O_DIRECTORY if defined */ #define binopen2(feat,path,flags) binopen3((feat) | BO__TWO, (path), (flags), 0) int binopen3(int, const char *, int, mode_t) MKSH_A_NONNULL(2); ssize_t dwrite(int, const void *, size_t) MKSH_A_BOUNDED(__buffer__, 2, 3); /* exclude system replacement functions in early link check */ #ifndef MIRTOCONF_EARLY #if !HAVE_DPRINTF /* replacement only as powerful as needed for this */ void dprintf(int, const char *, ...) MKSH_A_NONNULL(2) MKSH_A_FORMAT(__printf__, 2, 3); #endif #if !HAVE_REALLOCARRAY void *reallocarray(void *, size_t, size_t); #endif #if !HAVE_STRLCPY size_t strlcat(char *, const char *, size_t) MKSH_A_BOUNDED(__string__, 1, 3); size_t strlcpy(char *, const char *, size_t) MKSH_A_BOUNDED(__string__, 1, 3); #endif #if !HAVE_STRMODE void strmode(mode_t, char *); #endif #if !HAVE_STRTONUM long long strtonum(const char *, long long, long long, const char **); #endif #endif /* !MIRTOCONF_EARLY */ #endif pax/cpio.1010064400000000000000000000404321454566231300076250ustar00.\" $MirOS: src/bin/pax/cpio.1,v 1.45 2024/01/05 02:08:48 tg Exp $ .\" $OpenBSD: cpio.1,v 1.35 2014/09/08 01:27:54 schwarze Exp $ .\" .\" Copyright © 2005, 2009, 2011, 2012, 2014, 2015, 2016, .\" 2017, 2018 .\" mirabilos .\" Copyright (c) 1997 SigmaSoft, Th. Lockert .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\"- .\" $miros: contrib/samples/portmdoc,v 1.23 2024/01/04 22:52:50 tg Exp $ .\"- .\" Copyright © 2008, 2009, 2010, 2016, 2018, 2020, 2023 .\" mirabilos .\" .\" Glue GNU groff (BSD and GNU mdoc both) to AT&T nroff (UCB mdoc). .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en for a solo en dash .\" * and \*(EM for a correctly spaced em dash .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\"- .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 .. .\" .\" .Dd must come before most everything, because when called .\" with -mandoc it loads -mdoc via .so in .Dd (first macro). .\" .Dd $Mdocdate: January 5 2024 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if n .ss \n[.ss] 0 . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . if \*[.T]utf8 .tr \(la\*(Lt . if \*[.T]utf8 .tr \(ra\*(Gt . ds <= \(<= . ds >= \(>= . ds Rq \(rq . ds Lq \(lq . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em . ds tT ucb .\} .ie n \{\ . ds EM \ \(em\ \& .\} .el \{\ . ds EM \f(TR\|\(em\|\fP .\} .\" .\" Add UCB mdoc compatibility to GNU mdoc .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . ds sP \s0 . ds tN \*[Tn-font-size] . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*(tN\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . ds str-Mx1 \*(tN\%MirBSD\~#\*[arg\n[arg-ptr]]\*[str-Mx] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . de Aq . if !\n[arg-limit] \ . ds macro-name Aq . ie \n[in-authors-section] \{\ . ds quote-left \*(Lt . ds quote-right \*(Gt . \} . el \{\ . ds quote-left \[la] . ds quote-right \[ra] . \} . enclose-string \$@ .. . ec .\} .el .ie "\*(tT"gnp" \{\ . ds sP \s0 . ie t .ds tN \s[(\n[.ps]u-1z)] . el .ds tN . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*(tN\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . ds doc-str-Mx1 \*(tN\%MirBSD\~#\*[doc-arg\n[doc-arg-ptr]]\*[doc-str-Mx] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&\\*(tNMirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . nr xX 0 . nr xX 1+\\*(A\\n(aP . as b1 \&\\*(tNMirBSD\ \& . if \\n(xX>0 .as b1 #\& . as b1 \&\\*(A\\n(aP\\*(aa . rr xX . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&\\*(tNMirBSD\\*(aa . nR . \} . \} .. .\} .\" .\"- .ie \ng==1 \{\ . ds nc mircpio . ds np mirpax . ds nt mirtar . ds nm mircpio . Dt MIRCPIO 1 .\} .el .ie \ng==2 \{\ . ds nc paxcpio . ds np pax . ds nt paxtar . ds nm paxcpio . Dt PAXCPIO 1 .\} .el \{\ . ds nc cpio . ds np pax . ds nt tar . ds nm cpio . Dt CPIO 1 .\} .\"- .Os MirBSD .Sh NAME .ie \ng==1 \{\ .Nm mircpio .Nd copy file archives in and out .\} .el .ie \ng==2 \{\ .Nm paxcpio .Nd copy file archives in and out .\} .el \{\ .Nm cpio .Nd copy file archives in and out .\} .Sh SYNOPSIS .Bk -words .Nm \*(nm .Fl o .Op Fl 0AaBcJjLVvZz .Op Fl C Ar bytes .Op Fl F Ar archive .Op Fl H Ar format .Op Fl M Ar flag .Op Fl O Ar archive .No \*(Lt Ar name-list .Op \*(Gt Ar archive .Nm \*(nm .Fl i .Op Fl 06BbcdfJjmrSstuVvZz .Op Fl C Ar bytes .Op Fl E Ar file .Op Fl F Ar archive .Op Fl H Ar format .Op Fl I Ar archive .Op Fl M Ar flag .Op Ar pattern ... .Op \*(Lt Ar archive .Nm \*(nm .Fl p .Op Fl 0adLlmuVv .Ar destination-directory .No \*(Lt Ar name-list .Ek .Sh DESCRIPTION The .Nm command copies files to and from a .Nm cpio archive. .Pp The options are as follows: .Bl -tag -width Ds .It Fl 0 Use the NUL .Pq Ql \e0 character as a pathname terminator, instead of newline .Pq Ql \en . This applies only to the pathnames read from standard input in the write and copy modes, and to the pathnames written to standard output in list mode. This option is expected to be used in concert with the .Fl print0 function in .Xr find 1 , the .Fl d Ar \*(aq\*(aq option to the .Ic read built-in utility of .Xr mksh 1 or the .Fl 0 flag in .Xr xargs 1 . .It Fl o Create an archive. Reads the list of files to store in the archive from standard input, and writes the archive on standard output. .Bl -tag -width Ds .It Fl A Append to the specified archive. .It Fl a Reset the access times on files that have been copied to the archive. .It Fl B Set block size of output to 5120 bytes. .It Fl C Ar bytes Set the block size of output to .Ar bytes . .It Fl c Use ASCII format for .Nm cpio header for portability. .It Fl F Ar archive Use the specified file as the input for the archive. .It Fl H Ar format Write the archive in the specified format. Recognised formats are: .Pp .Bl -tag -width sv4cpio -compact .It Ar ar Unix Archiver. .It Ar bcpio Old binary .Nm cpio format. Selected by .Fl 6 . .It Ar cpio Old octal character .Nm cpio format. Selected by .Fl c . .It Ar sv4cpio SVR4 hex .Nm cpio format. .It Ar sv4crc SVR4 hex .Nm cpio format with checksums. This is the default format for creating new archives. .It Ar tar Old tar format. .It Ar ustar POSIX ustar format. .It "\ " .It Ar bin These four formats...\& .It Ar crc \&...are supported...\& .It Ar newc \&...for backwards...\& .It Ar odc \&...compatibility only. .El .It Fl J Use the xz utility to compress the archive. .It Fl j Use the bzip2 utility to compress the archive. .It Fl L Follow symbolic links. .It Fl M Ar flag Configure the archive normaliser. .Ar flag is either a numeric value compatible to .Xr strtonum 3 which is directly stored in the flags word, or one of the following values, optionally prefixed with .Dq no\- to turn them off: .Pp .Bl -tag -width xxxxxx -compact .It Ar inodes 0x0001: Serialise inodes, zero device info. .br (cpio, sv4cpio, sv4crc) .It Ar links 0x0002: Store content of hard links only once. .br (cpio, sv4cpio, sv4crc) .It Ar mtime 0x0004: Zero out the file modification time. .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar uidgid 0x0008: Set owner to 0:0 .Pq Li root Ns : Ns Li wheel . .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar verb 0x0010: Debug this option. .It Ar debug 0x0020: Debug file header storage. .It Ar lncp 0x0040: Extract hard links by copy if link fails. .It Ar numid 0x0080: Use only numeric uid and gid values. .br (ustar) .It Ar gslash 0x0100: Append a slash after directory names. .br (ustar) .It Ar set 0x0003: Keep ownership and mtime intact. .It Ar dist 0x008B: Clean everything except mtime. .It Ar norm 0x008F: Clean everything. .It Ar root 0x0089: Clean owner and device information. .El .Pp When creating an archive and verbosely listing output, these normalisation operations are not reflected in the output, because they are made only after the output has been shown. .Pp This option is only implemented for the ar, cpio, sv4cpio, sv4crc, and ustar file format writing routines. .It Fl O Ar archive Use the specified file name as the archive to write to. .It Fl V Print a dot .Pq Sq \&. for each file written to the archive. .It Fl v Be verbose about operations. List filenames as they are written to the archive. .It Fl Z Use the .Xr compress 1 utility to compress the archive. .It Fl z Use the .Xr gzip 1 utility to compress the archive. .El .It Fl i Restore files from an archive. Reads the archive file from standard input and extracts files matching the .Ar patterns that were specified on the command line. .Bl -tag -width Ds .It Fl 6 Process old-style .Nm cpio format archives. .It Fl B Set the block size of the archive being read to 5120 bytes. .It Fl b Do byte and word swapping after reading in data from the archive, for restoring archives created on systems with a different byte order. .It Fl C Ar bytes Read archive written with a block size of .Ar bytes . .It Fl c Expect the archive headers to be in ASCII format. .It Fl d Create any intermediate directories as needed during restore. .It Fl E Ar file Read list of file name patterns to extract or list from .Ar file . .It Fl F Ar archive , Fl I Ar archive Use the specified file as the input for the archive. .It Fl f Restore all files except those matching the .Ar patterns given on the command line. .It Fl H Ar format Read an archive of the specified format. Recognised formats are: .Pp .Bl -tag -width sv4cpio -compact .It Ar ar Unix Archiver. .It Ar bcpio Old binary .Nm cpio format. .It Ar cpio Old octal character .Nm cpio format. .It Ar sv4cpio SVR4 hex .Nm cpio format. .It Ar sv4crc SVR4 hex .Nm cpio format with checksums. .It Ar tar Old tar format. .It Ar ustar POSIX ustar format. .It "\ " .It Ar bin These four formats...\& .It Ar crc \&...are supported...\& .It Ar newc \&...for backwards...\& .It Ar odc \&...compatibility only. .El .It Fl J Use the xz utility to decompress the archive. .It Fl j Use the bzip2 utility to decompress the archive. .It Fl m Restore modification times on files. .It Fl r Rename restored files interactively. .It Fl S Swap words after reading data from the archive. .It Fl s Swap bytes after reading data from the archive. .It Fl t Only list the contents of the archive, no files or directories will be created. .It Fl u Overwrite files even when the file in the archive is older than the one that will be overwritten. .It Fl V Print a dot .Pq Sq \&. for each file read from the archive. .It Fl v Be verbose about operations. List filenames as they are copied in from the archive. .It Fl Z Use the .Xr compress 1 utility to decompress the archive. .It Fl z Use the .Xr gzip 1 utility to decompress the archive. .El .It Fl p Copy files from one location to another in a single pass. The list of files to copy are read from standard input and written out to a directory relative to the specified .Ar directory argument. .Bl -tag -width Ds .It Fl a Reset the access times on files that have been copied. .It Fl d Create any intermediate directories as needed to write the files at the new location. .It Fl L Follow symbolic links. .It Fl l When possible, link files rather than creating an extra copy. .It Fl m Restore modification times on files. .It Fl u Overwrite files even when the original file being copied is older than the one that will be overwritten. .It Fl V Print a dot .Pq Sq \&. for each file copied. .It Fl v Be verbose about operations. List filenames as they are copied. .El .El .Sh ENVIRONMENT .Bl -tag -width Fl .It Ev TMPDIR Path in which to store temporary files. .El .Sh EXIT STATUS The .Nm utility exits with one of the following values: .Pp .Bl -tag -width Ds -offset indent -compact .It 0 All files were processed successfully. .It 1 An error occurred. .El .Sh DIAGNOSTICS Whenever .Nm cannot create a file or a link when extracting an archive or cannot find a file while writing an archive, or cannot preserve the user ID, group ID, file mode, or access and modification times when the .Fl p option is specified, a diagnostic message is written to standard error and a non-zero exit value will be returned, but processing will continue. In the case where .Nm cannot create a link to a file, unless .Fl M Ar lncp is given, .Nm will not create a second copy of the file. .Pp If the extraction of a file from an archive is prematurely terminated by a signal or error, .Nm may have only partially extracted the file the user wanted. Additionally, the file modes of extracted files and directories may have incorrect file bits, and the modification and access times may be wrong. .Pp If the creation of an archive is prematurely terminated by a signal or error, .Nm may have only partially created the archive, which may violate the specific archive format specification. .Sh SEE ALSO .Xr ar 1 , .if \ng==1 \{\ .Xr cpio 1 , .Xr deb 5 , .Xr mirpax 1 , .Xr mirtar 1 , .\} .ie \ng==2 \{\ .Xr cpio 1 , .Xr deb 5 , .Xr pax 1 , .Xr paxtar 1 , .\} .el \{\ .Xr pax 1 , .\} .Xr tar 1 .Sh AUTHORS .An -nosplit .An Keith Muller at the University of California, San Diego. .Mx extensions by .An mirabilos Aq m@mirbsd.org . .Sh CAVEATS Different file formats have different maximum file sizes. It is recommended that a format such as cpio or ustar be used for larger files. .Bl -column "File format" "Maximum file size" -offset indent .It Sy "File format" Ta Sy "Maximum file size" .It ar Ta "10 Gigabytes \- 1 Byte" .It bcpio Ta "4 Gibibytes" .It sv4cpio Ta "4 Gibibytes" .It sv4crc Ta "4 Gibibytes" .It cpio Ta "8 Gibibytes" .It tar Ta "8 Gibibytes" .It ustar Ta "8 Gibibytes" .El .Pp The backwards-compatible format options are not available in the .Xr \*(np 1 front-end. .Pp The .Fl M option is a MirBSD extension, available starting with .Mx 8 . Archives written using these options are, however, compatible to the standard and should be readable on any other system. The only option whose behaviour is not explicitly allowed by the standard is hard link unification (write file contens only once) selected by .Fl M Ar 0x0002 . .Pp The .Fl V option is a GNU extension, available starting with .Mx 11 . .Pp The .Ar ar file format matches APT repositories and the BSD .Xr ar 1 specification, not GNU binutils (which can however read them) or SYSV systems. .Sh BUGS The .Fl s and .Fl S options are currently not implemented. .Pp The .Ar pax file format is not yet supported. pax/cpio.c010064400000000000000000000774341466023157200077200ustar00/* $OpenBSD: cpio.c,v 1.33 2017/09/16 07:42:34 otto Exp $ */ /* $NetBSD: cpio.c,v 1.5 1995/03/21 09:07:13 cgd Exp $ */ /*- * Copyright (c) 2005, 2012, 2016, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "cpio.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/cpio.c,v 1.29 2024/08/17 23:33:51 tg Exp $"); static int rd_nm(ARCHD *, int); static int rd_ln_nm(ARCHD *); static int com_rd(ARCHD *); /* * Routines which support the different cpio versions */ #ifndef SMALL static int swp_head; /* binary cpio header byte swap */ #endif /* * Routines common to all versions of cpio */ /* * cpio_strd() * Fire up the hard link detection code * Return: * 0 if ok -1 otherwise (the return values of lnk_start()) */ int cpio_strd(void) { return(lnk_start()); } /* * cpio_trail() * Called to determine if a header block is a valid trailer. We are * passed the block, the in_sync flag (which tells us we are in resync * mode; looking for a valid header), and cnt (which starts at zero) * which is used to count the number of empty blocks we have seen so far. * Return: * 0 if a valid trailer, -1 if not a valid trailer, */ int cpio_trail(ARCHD *arcn, char *notused MKSH_A_UNUSED, int notused2 MKSH_A_UNUSED, int *notused3 MKSH_A_UNUSED) { /* * look for trailer id in file we are about to process */ if ((strcmp(arcn->name, TRAILER) == 0) && (arcn->sb.st_size == 0)) return(0); return(-1); } /* * com_rd() * operations common to all cpio read functions. * Return: * 0 */ static int com_rd(ARCHD *arcn) { arcn->skip = 0; arcn->pat = NULL; arcn->org_name = arcn->name; switch (arcn->sb.st_mode & C_IFMT) { case C_ISFIFO: arcn->type = PAX_FIF; break; case C_ISDIR: arcn->type = PAX_DIR; break; case C_ISBLK: arcn->type = PAX_BLK; break; case C_ISCHR: arcn->type = PAX_CHR; break; case C_ISLNK: arcn->type = PAX_SLK; break; case C_ISOCK: arcn->type = PAX_SCK; break; case C_ISCTG: case C_ISREG: default: /* * we have file data, set up skip (pad is set in the format * specific sections) */ arcn->sb.st_mode = (arcn->sb.st_mode & 0xfff) | C_ISREG; arcn->type = PAX_REG; arcn->skip = arcn->sb.st_size; break; } if (chk_lnk(arcn) < 0) return(-1); return(0); } /* * cpio_endwr() * write the special file with the name trailer in the proper format * Return: * result of the write of the trailer from the cpio specific write func */ int cpio_endwr(void) { ARCHD last; /* * create a trailer request and call the proper format write function */ memset(&last, 0, sizeof(last)); last.nlen = sizeof(TRAILER) - 1; last.type = PAX_REG; last.sb.st_nlink = 1; (void)strlcpy(last.name, TRAILER, sizeof(last.name)); return((*frmt->wr)(&last)); } /* * rd_nm() * read in the file name which follows the cpio header * Return: * 0 if ok, -1 otherwise */ static int rd_nm(ARCHD *arcn, int nsz) { /* * do not even try bogus values */ if ((nsz == 0) || ((size_t)nsz > sizeof(arcn->name))) { paxwarn(1, "cpio file name length %d is out of range", nsz); return(-1); } /* * read the name and make sure it is not empty and is \0 terminated */ if ((rd_wrbuf(arcn->name,nsz) != nsz) || (arcn->name[nsz-1] != '\0') || (arcn->name[0] == '\0')) { paxwarn(1, "cpio file name in header is corrupted"); return(-1); } return(0); } /* * rd_ln_nm() * read in the link name for a file with links. The link name is stored * like file data (and is NOT \0 terminated!) * Return: * 0 if ok, -1 otherwise */ static int rd_ln_nm(ARCHD *arcn) { /* * check the length specified for bogus values */ if (((off_t)arcn->sb.st_size <= 0) || ((off_t)arcn->sb.st_size >= (off_t)sizeof(arcn->ln_name))) { paxwarn(1, "cpio link name length is invalid: %" OT_FMT, arcn->sb.st_size); return(-1); } /* * read in the link name and \0 terminate it */ if (rd_wrbuf(arcn->ln_name, (int)arcn->sb.st_size) != (int)arcn->sb.st_size) { paxwarn(1, "cpio link name read error"); return(-1); } arcn->ln_nlen = arcn->sb.st_size; arcn->ln_name[arcn->ln_nlen] = '\0'; /* * watch out for those empty link names */ if (arcn->ln_name[0] == '\0') { paxwarn(1, "cpio link name is corrupt"); return(-1); } return(0); } /* * Routines common to the extended byte oriented cpio format */ /* * cpio_id() * determine if a block given to us is a valid extended byte oriented * cpio header * Return: * 0 if a valid header, -1 otherwise */ int cpio_id(char *blk, int size) { if ((size < (int)sizeof(HD_CPIO)) || (strncmp(blk, AMAGIC, sizeof(AMAGIC) - 1) != 0)) return(-1); return(0); } /* * cpio_rd() * determine if a buffer is a byte oriented extended cpio archive entry. * convert and store the values in the ARCHD parameter. * Return: * 0 if a valid header, -1 otherwise. */ int cpio_rd(ARCHD *arcn, char *buf) { int nsz; unsigned long long val; HD_CPIO *hd; /* * check that this is a valid header, if not return -1 */ if (cpio_id(buf, sizeof(HD_CPIO)) < 0) return(-1); hd = (HD_CPIO *)buf; /* * byte oriented cpio (posix) does not have padding! extract the octal * ascii fields from the header */ arcn->pad = 0; arcn->sb.st_dev = (dev_t)asc_ul(hd->c_dev, sizeof(hd->c_dev), OCT); arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), OCT); arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), OCT); arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), OCT); arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), OCT); arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), OCT); arcn->sb.st_rdev = (dev_t)asc_ul(hd->c_rdev, sizeof(hd->c_rdev), OCT); val = asc_ull(hd->c_mtime, sizeof(hd->c_mtime), OCT); if (val > MAX_TIME_T) arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ else arcn->sb.st_mtime = val; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; #if HAVE_ST_MTIMENSEC arcn->sb.st_ctimensec = arcn->sb.st_atimensec = arcn->sb.st_mtimensec = 0; #endif arcn->sb.st_size = (off_t)asc_ull(hd->c_filesize,sizeof(hd->c_filesize), OCT); /* * check name size and if valid, read in the name of this entry (name * follows header in the archive) */ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),OCT)) < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { /* * no link name to read for this file */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; return(com_rd(arcn)); } /* * check link name size and read in the link name. Link names are * stored like file data. */ if (rd_ln_nm(arcn) < 0) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * cpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t cpio_endrd(void) { return ((off_t)(sizeof(HD_CPIO) + sizeof(TRAILER))); } /* * cpio_stwr() * start up the device mapping table * Return: * 0 if ok, -1 otherwise (what dev_start() returns) */ int cpio_stwr(int is_app MKSH_A_UNUSED) { if ((anonarch & ANON_INODES) && flnk_start()) return (-1); return (dev_start()); } #ifndef SMALL int dist_stwr(int is_app) { mircpio_deprecated("'dist' format", "the cpio format with 512 bytes blocksize and -M dist"); anonarch &= ANON_DEBUG | ANON_VERBOSE; anonarch |= ANON_UIDGID | ANON_INODES | ANON_HARDLINKS; return (cpio_stwr(is_app)); } #endif /* * cpio_wr() * copy the data in the ARCHD to buffer in extended byte oriented cpio * format. * Return * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int cpio_wr(ARCHD *arcn) { HD_CPIO *hd; int nsz; char hdblk[sizeof(HD_CPIO)]; u_long t_uid, t_gid, t_dev; ino_t t_ino; time_t t_mtime; anonarch_init(); /* * check and repair truncated device and inode fields in the header */ if (map_dev(arcn, CPIO_MASK, CPIO_MASK) < 0) return(-1); arcn->pad = 0; nsz = arcn->nlen + 1; hd = (HD_CPIO *)hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; t_uid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_uid; t_gid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_gid; t_mtime = (anonarch & ANON_MTIME) ? 0 : arcn->sb.st_mtime; t_ino = (anonarch & ANON_INODES) ? (ino_t)chk_flnk(arcn) : arcn->sb.st_ino; t_dev = (anonarch & ANON_INODES) ? 0UL : (u_long)arcn->sb.st_dev; if (!cpio_trail(arcn, NULL, 0, NULL)) t_ino = 0UL; if (t_ino == (ino_t)-1) { paxwarn(1, "Invalid inode number for file %s", arcn->org_name); return (1); } if (!(anonarch & ANON_HARDLINKS)) arcn->type &= ~PAX_LINKOR; switch (arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * set data size for file data */ if (ull_asc(arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), OCT)) { paxwarn(1, "File is too large for %s format %s", "cpio", arcn->org_name); return (1); } break; case PAX_SLK: /* * set data size to hold link name */ if (ul_asc(arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; default: /* * all other file types have no file data */ if (ul_asc(0, hd->c_filesize, sizeof(hd->c_filesize), OCT)) goto out; break; } #ifndef SMALL if (anonarch & ANON_DEBUG) paxwarn(0, "writing dev %lX inode %10lX mode %8lo user %ld:%ld" "\n\tnlink %3ld mtime %08llX name '%s'", t_dev, (u_long)t_ino, (u_long)arcn->sb.st_mode, t_uid, t_gid, (u_long)arcn->sb.st_nlink, (unsigned long long)t_mtime, arcn->name); #endif /* * copy the values to the header using octal ascii */ if (ul_asc(MAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc(t_dev, hd->c_dev, sizeof(hd->c_dev), OCT) || ul_asc((u_long)t_ino, hd->c_ino, sizeof(hd->c_ino), OCT) || ul_asc(arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), OCT) || ul_asc(t_uid, hd->c_uid, sizeof(hd->c_uid), OCT) || ul_asc(t_gid, hd->c_gid, sizeof(hd->c_gid), OCT) || ul_asc(arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), OCT) || ul_asc(arcn->sb.st_rdev, hd->c_rdev, sizeof(hd->c_rdev), OCT) || ull_asc(t_mtime < 0 ? 0ULL : (unsigned long long)t_mtime, hd->c_mtime, sizeof(hd->c_mtime), OCT) || ul_asc(nsz, hd->c_namesize, sizeof(hd->c_namesize), OCT)) goto out; /* * write the file name to the archive */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_CPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0)) { paxwarn(1, "Unable to write %s %s for %s", "cpio", "header", arcn->org_name); return (-1); } /* * if this file has data, we are done. The caller will write the file * data, if we are link tell caller we are done, go to next file */ if (PAX_IS_REG(arcn->type) || (arcn->type == PAX_HRG)) return (0); if (arcn->type & PAX_LINKOR) { arcn->type &= ~PAX_LINKOR; return (1); } if (arcn->type != PAX_SLK) return (1); /* * write the link name to the archive, tell the caller to go to the * next file as we are done. */ if (wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) { paxwarn(1, "Unable to write %s %s for %s", "cpio", "link name", arcn->org_name); return (-1); } return(1); out: /* * header field is out of range */ paxwarn(1, "%s header field is too small for file %s", "cpio", arcn->org_name); return (1); } /* * Routines common to the system VR4 version of cpio (with/without file CRC) */ /* * vcpio_id() * determine if a block given to us is a valid system VR4 cpio header * WITHOUT crc. WATCH it the magic cookies are in OCTAL, the header * uses HEX * Return: * 0 if a valid header, -1 otherwise */ int vcpio_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVMAGIC, sizeof(AVMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_id() * determine if a block given to us is a valid system VR4 cpio header * WITH crc. WATCH it the magic cookies are in OCTAL the header uses HEX * Return: * 0 if a valid header, -1 otherwise */ int crc_id(char *blk, int size) { if ((size < (int)sizeof(HD_VCPIO)) || (strncmp(blk, AVCMAGIC, sizeof(AVCMAGIC) - 1) != 0)) return(-1); return(0); } /* * crc_strd() w set file data CRC calculations. Fire up the hard link detection code * Return: * 0 if ok -1 otherwise (the return values of lnk_start()) */ int crc_strd(void) { docrc = 1; return(lnk_start()); } /* * vcpio_rd() * determine if a buffer is a system VR4 archive entry. (with/without CRC) * convert and store the values in the ARCHD parameter. * Return: * 0 if a valid header, -1 otherwise. */ int vcpio_rd(ARCHD *arcn, char *buf) { HD_VCPIO *hd; dev_t devminor; dev_t devmajor; int nsz; /* * during the id phase it was determined if we were using CRC, use the * proper id routine. */ if (docrc) { if (crc_id(buf, sizeof(HD_VCPIO)) < 0) return(-1); } else { if (vcpio_id(buf, sizeof(HD_VCPIO)) < 0) return(-1); } hd = (HD_VCPIO *)buf; arcn->pad = 0; /* * extract the hex ascii fields from the header */ arcn->sb.st_ino = (ino_t)asc_ul(hd->c_ino, sizeof(hd->c_ino), HEX); arcn->sb.st_mode = (mode_t)asc_ul(hd->c_mode, sizeof(hd->c_mode), HEX); arcn->sb.st_uid = (uid_t)asc_ul(hd->c_uid, sizeof(hd->c_uid), HEX); arcn->sb.st_gid = (gid_t)asc_ul(hd->c_gid, sizeof(hd->c_gid), HEX); arcn->sb.st_mtime = (time_t)asc_ul(hd->c_mtime,sizeof(hd->c_mtime),HEX); arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; #if HAVE_ST_MTIMENSEC arcn->sb.st_ctimensec = arcn->sb.st_atimensec = arcn->sb.st_mtimensec = 0; #endif arcn->sb.st_size = (off_t)asc_ull(hd->c_filesize, sizeof(hd->c_filesize), HEX); arcn->sb.st_nlink = (nlink_t)asc_ul(hd->c_nlink, sizeof(hd->c_nlink), HEX); devmajor = (dev_t)asc_ul(hd->c_maj, sizeof(hd->c_maj), HEX); devminor = (dev_t)asc_ul(hd->c_min, sizeof(hd->c_min), HEX); arcn->sb.st_dev = TODEV(devmajor, devminor); devmajor = (dev_t)asc_ul(hd->c_rmaj, sizeof(hd->c_rmaj), HEX); devminor = (dev_t)asc_ul(hd->c_rmin, sizeof(hd->c_rmin), HEX); arcn->sb.st_rdev = TODEV(devmajor, devminor); arcn->crc = asc_ul(hd->c_chksum, sizeof(hd->c_chksum), HEX); /* * check the length of the file name, if ok read it in, return -1 if * bogus */ if ((nsz = (int)asc_ul(hd->c_namesize,sizeof(hd->c_namesize),HEX)) < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); /* * skip padding. header + filename is aligned to 4 byte boundaries */ if (rd_skip(VCPIO_PAD(sizeof(HD_VCPIO) + nsz)) < 0) return(-1); /* * if not a link (or a file with no data), calculate pad size (for * padding which follows the file data), clear the link name and return */ if (((arcn->sb.st_mode&C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)) { /* * we have a valid header (not a link) */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->pad = VCPIO_PAD(arcn->sb.st_size); return(com_rd(arcn)); } /* * read in the link name and skip over the padding */ if ((rd_ln_nm(arcn) < 0) || (rd_skip(VCPIO_PAD(arcn->sb.st_size)) < 0)) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * vcpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t vcpio_endrd(void) { return sizeof(HD_VCPIO) + sizeof(TRAILER) + (VCPIO_PAD(sizeof(HD_VCPIO) + sizeof(TRAILER))); } /* * crc_stwr() * start up the device mapping table, enable crc file calculation * Return: * 0 if ok, -1 otherwise (what dev_start() returns) */ int crc_stwr(int is_app MKSH_A_UNUSED) { docrc = 1; if ((anonarch & ANON_INODES) && flnk_start()) return (-1); return(dev_start()); } #ifndef SMALL int v4root_stwr(int is_app) { mircpio_deprecated("'v4norm' format", "the sv4crc format with 512 bytes blocksize and -M root"); anonarch &= ANON_DEBUG | ANON_VERBOSE; anonarch |= ANON_UIDGID | ANON_INODES; return (crc_stwr(is_app)); } int v4norm_stwr(int is_app) { mircpio_deprecated("'v4norm' format", "the sv4crc format with 512 bytes blocksize and -M norm"); anonarch &= ANON_DEBUG | ANON_VERBOSE; anonarch |= ANON_UIDGID | ANON_INODES | ANON_MTIME | ANON_HARDLINKS; return (crc_stwr(is_app)); } #endif /* * vcpio_wr() * copy the data in the ARCHD to buffer in system VR4 cpio * (with/without crc) format. * Return * 0 if file has data to be written after the header, 1 if file has * NO data to write after the header, -1 if archive write failed */ int vcpio_wr(ARCHD *arcn) { HD_VCPIO *hd; unsigned int nsz; char hdblk[sizeof(HD_VCPIO)]; u_long t_uid, t_gid, t_major, t_minor; ino_t t_ino; time_t t_mtime; anonarch_init(); /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, VCPIO_MASK, VCPIO_MASK) < 0) return(-1); nsz = arcn->nlen + 1; hd = (HD_VCPIO *)hdblk; if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; /* * add the proper magic value depending whether we were asked for * file data crc's, and the crc if needed. */ if (docrc) { if (ul_asc(VCMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc(arcn->crc,hd->c_chksum,sizeof(hd->c_chksum), HEX)) goto out; } else { if (ul_asc(VMAGIC, hd->c_magic, sizeof(hd->c_magic), OCT) || ul_asc(0, hd->c_chksum, sizeof(hd->c_chksum),HEX)) goto out; } t_uid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_uid; t_gid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_gid; t_mtime = (anonarch & ANON_MTIME) ? 0 : arcn->sb.st_mtime; t_ino = (anonarch & ANON_INODES) ? (ino_t)chk_flnk(arcn) : arcn->sb.st_ino; t_major = (anonarch & ANON_INODES) ? 0UL : (u_long)MAJOR(arcn->sb.st_dev); t_minor = (anonarch & ANON_INODES) ? 0UL : (u_long)MINOR(arcn->sb.st_dev); if (!cpio_trail(arcn, NULL, 0, NULL)) t_ino = 0UL; if (t_ino == (ino_t)-1) { paxwarn(1, "Invalid inode number for file %s", arcn->org_name); return (1); } if (!(anonarch & ANON_HARDLINKS)) arcn->type &= ~PAX_LINKOR; switch (arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = VCPIO_PAD(arcn->sb.st_size); if (ull_asc(arcn->sb.st_size, hd->c_filesize, sizeof(hd->c_filesize), HEX)) { paxwarn(1, "File is too large for %s format %s", "sv4cpio", arcn->org_name); return (1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0; if (ul_asc(arcn->ln_nlen, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0; if (ul_asc(0, hd->c_filesize, sizeof(hd->c_filesize), HEX)) goto out; break; } #ifndef SMALL if (anonarch & ANON_DEBUG) paxwarn(0, "writing dev %lX:%lx inode %10lX mode %8lo user %ld:%ld" "\n\tnlink %3ld mtime %08llX name '%s'", t_major, t_minor, (u_long)t_ino, (u_long)arcn->sb.st_mode, t_uid, t_gid, (u_long)arcn->sb.st_nlink, (unsigned long long)t_mtime, arcn->name); #endif /* * set the other fields in the header */ if (ul_asc(t_ino, hd->c_ino, sizeof(hd->c_ino), HEX) || ul_asc(arcn->sb.st_mode, hd->c_mode, sizeof(hd->c_mode), HEX) || ul_asc(t_uid, hd->c_uid, sizeof(hd->c_uid), HEX) || ul_asc(t_gid, hd->c_gid, sizeof(hd->c_gid), HEX) || ul_asc(t_mtime < 0 ? 0 : t_mtime, hd->c_mtime, sizeof(hd->c_mtime), HEX) || ul_asc(arcn->sb.st_nlink, hd->c_nlink, sizeof(hd->c_nlink), HEX) || /* device major:minor of the device the file resides on */ ul_asc(t_major, hd->c_maj, sizeof(hd->c_maj), HEX) || ul_asc(t_minor, hd->c_min, sizeof(hd->c_min), HEX) || /* device major:minor of the file if it's a device node */ ul_asc(MAJOR(arcn->sb.st_rdev), hd->c_rmaj, sizeof(hd->c_rmaj), HEX) || ul_asc(MINOR(arcn->sb.st_rdev), hd->c_rmin, sizeof(hd->c_rmin), HEX) || ul_asc(nsz, hd->c_namesize, sizeof(hd->c_namesize), HEX)) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_VCPIO)) < 0) || (wr_rdbuf(arcn->name, (int)nsz) < 0) || (wr_skip(VCPIO_PAD(sizeof(HD_VCPIO) + nsz)) < 0)) { paxwarn(1, "Unable to write %s %s for %s", "sv4cpio", "header", arcn->org_name); return (-1); } /* * if we have file data, tell the caller we are done, copy the file */ if (PAX_IS_REG(arcn->type) || (arcn->type == PAX_HRG)) return(0); /* * if we are a detected hard link, we're done too, but no data written */ if (arcn->type & PAX_LINKOR) { arcn->type &= ~PAX_LINKOR; return (1); } /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip(VCPIO_PAD(arcn->ln_nlen)) < 0)) { paxwarn(1, "Unable to write %s %s for %s", "sv4cpio", "link name", arcn->org_name); return (-1); } return(1); out: /* * header field is out of range */ paxwarn(1, "%s header field is too small for file %s", "sv4cpio", arcn->org_name); return (1); } #ifndef SMALL /* * Routines common to the old binary header cpio */ /* * bcpio_id() * determine if a block given to us is a old binary cpio header * (with/without header byte swapping) * Return: * 0 if a valid header, -1 otherwise */ int bcpio_id(char *blk, int size) { if (size < (int)sizeof(HD_BCPIO)) return(-1); /* * check both normal and byte swapped magic cookies */ if (((unsigned short)SHRT_EXT(blk)) == MAGIC) return(0); if (((unsigned short)RSHRT_EXT(blk)) == MAGIC) { if (!swp_head) ++swp_head; return(0); } return(-1); } /* * bcpio_rd() * determine if a buffer is a old binary archive entry. (it may have byte * swapped header) convert and store the values in the ARCHD parameter. * This is a very old header format and should not really be used. * Return: * 0 if a valid header, -1 otherwise. */ int bcpio_rd(ARCHD *arcn, char *buf) { HD_BCPIO *hd; int nsz; /* * check the header */ if (bcpio_id(buf, sizeof(HD_BCPIO)) < 0) return(-1); arcn->pad = 0; hd = (HD_BCPIO *)buf; if (swp_head) { /* * header has swapped bytes on 16 bit boundaries */ arcn->sb.st_dev = (dev_t)(RSHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(RSHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(RSHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(RSHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(RSHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(RSHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(RSHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(RSHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(RSHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(RSHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(RSHRT_EXT(hd->h_filesize_2))); nsz = (int)(RSHRT_EXT(hd->h_namesize)); } else { arcn->sb.st_dev = (dev_t)(SHRT_EXT(hd->h_dev)); arcn->sb.st_ino = (ino_t)(SHRT_EXT(hd->h_ino)); arcn->sb.st_mode = (mode_t)(SHRT_EXT(hd->h_mode)); arcn->sb.st_uid = (uid_t)(SHRT_EXT(hd->h_uid)); arcn->sb.st_gid = (gid_t)(SHRT_EXT(hd->h_gid)); arcn->sb.st_nlink = (nlink_t)(SHRT_EXT(hd->h_nlink)); arcn->sb.st_rdev = (dev_t)(SHRT_EXT(hd->h_rdev)); arcn->sb.st_mtime = (time_t)(SHRT_EXT(hd->h_mtime_1)); arcn->sb.st_mtime = (arcn->sb.st_mtime << 16) | ((time_t)(SHRT_EXT(hd->h_mtime_2))); arcn->sb.st_size = (off_t)(SHRT_EXT(hd->h_filesize_1)); arcn->sb.st_size = (arcn->sb.st_size << 16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); nsz = (int)(SHRT_EXT(hd->h_namesize)); } arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; #if HAVE_ST_MTIMENSEC arcn->sb.st_ctimensec = arcn->sb.st_atimensec = arcn->sb.st_mtimensec = 0; #endif /* * check the file name size, if bogus give up. otherwise read the file * name */ if (nsz < 2) return(-1); arcn->nlen = nsz - 1; if (rd_nm(arcn, nsz) < 0) return(-1); /* * header + file name are aligned to 2 byte boundaries, skip if needed */ if (rd_skip(BCPIO_PAD(sizeof(HD_BCPIO) + nsz)) < 0) return(-1); /* * if not a link (or a file with no data), calculate pad size (for * padding which follows the file data), clear the link name and return */ if (((arcn->sb.st_mode & C_IFMT) != C_ISLNK)||(arcn->sb.st_size == 0)){ /* * we have a valid header (not a link) */ arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; arcn->pad = BCPIO_PAD(arcn->sb.st_size); return(com_rd(arcn)); } if ((rd_ln_nm(arcn) < 0) || (rd_skip(BCPIO_PAD(arcn->sb.st_size)) < 0)) return(-1); /* * we have a valid header (with a link) */ return(com_rd(arcn)); } /* * bcpio_endrd() * no cleanup needed here, just return size of the trailer (for append) * Return: * size of trailer header in this format */ off_t bcpio_endrd(void) { return sizeof(HD_BCPIO) + sizeof(TRAILER) + (BCPIO_PAD(sizeof(HD_BCPIO) + sizeof(TRAILER))); } /* * bcpio_wr() * copy the data in the ARCHD to buffer in old binary cpio format * There is a real chance of field overflow with this critter. So we * always check the conversion is ok. nobody in their right mind * should write an archive in this format... * Return * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int bcpio_wr(ARCHD *arcn) { HD_BCPIO *hd; int nsz; char hdblk[sizeof(HD_BCPIO)]; off_t t_offt; int t_int; time_t t_timet; /* * check and repair truncated device and inode fields in the cpio * header */ if (map_dev(arcn, BCPIO_MASK, BCPIO_MASK) < 0) return(-1); if ((arcn->type != PAX_BLK) && (arcn->type != PAX_CHR)) arcn->sb.st_rdev = 0; hd = (HD_BCPIO *)hdblk; switch (arcn->type) { case PAX_CTG: case PAX_REG: case PAX_HRG: /* * caller will copy file data to the archive. tell him how * much to pad. */ arcn->pad = BCPIO_PAD(arcn->sb.st_size); hd->h_filesize_1[0] = CHR_WR_0(arcn->sb.st_size); hd->h_filesize_1[1] = CHR_WR_1(arcn->sb.st_size); hd->h_filesize_2[0] = CHR_WR_2(arcn->sb.st_size); hd->h_filesize_2[1] = CHR_WR_3(arcn->sb.st_size); t_offt = (off_t)(SHRT_EXT(hd->h_filesize_1)); t_offt = (t_offt<<16) | ((off_t)(SHRT_EXT(hd->h_filesize_2))); /* sb.st_size is off_t in POSIX but not dietlibc */ if ((off_t)arcn->sb.st_size != t_offt) { paxwarn(1, "File is too large for %s format %s", "bcpio", arcn->org_name); return (1); } break; case PAX_SLK: /* * no file data for the caller to process, the file data has * the size of the link */ arcn->pad = 0; hd->h_filesize_1[0] = CHR_WR_0(arcn->ln_nlen); hd->h_filesize_1[1] = CHR_WR_1(arcn->ln_nlen); hd->h_filesize_2[0] = CHR_WR_2(arcn->ln_nlen); hd->h_filesize_2[1] = CHR_WR_3(arcn->ln_nlen); t_int = (int)(SHRT_EXT(hd->h_filesize_1)); t_int = (t_int << 16) | ((int)(SHRT_EXT(hd->h_filesize_2))); if (arcn->ln_nlen != t_int) goto out; break; default: /* * no file data for the caller to process */ arcn->pad = 0; hd->h_filesize_1[0] = (char)0; hd->h_filesize_1[1] = (char)0; hd->h_filesize_2[0] = (char)0; hd->h_filesize_2[1] = (char)0; break; } /* * build up the rest of the fields */ hd->h_magic[0] = CHR_WR_2(MAGIC); hd->h_magic[1] = CHR_WR_3(MAGIC); hd->h_dev[0] = CHR_WR_2(arcn->sb.st_dev); hd->h_dev[1] = CHR_WR_3(arcn->sb.st_dev); if (arcn->sb.st_dev != (dev_t)(SHRT_EXT(hd->h_dev))) goto out; hd->h_ino[0] = CHR_WR_2(arcn->sb.st_ino); hd->h_ino[1] = CHR_WR_3(arcn->sb.st_ino); if (arcn->sb.st_ino != (ino_t)(SHRT_EXT(hd->h_ino))) goto out; hd->h_mode[0] = CHR_WR_2(arcn->sb.st_mode); hd->h_mode[1] = CHR_WR_3(arcn->sb.st_mode); if (arcn->sb.st_mode != (mode_t)(SHRT_EXT(hd->h_mode))) goto out; hd->h_uid[0] = CHR_WR_2(arcn->sb.st_uid); hd->h_uid[1] = CHR_WR_3(arcn->sb.st_uid); if (arcn->sb.st_uid != (uid_t)(SHRT_EXT(hd->h_uid))) goto out; hd->h_gid[0] = CHR_WR_2(arcn->sb.st_gid); hd->h_gid[1] = CHR_WR_3(arcn->sb.st_gid); if (arcn->sb.st_gid != (gid_t)(SHRT_EXT(hd->h_gid))) goto out; hd->h_nlink[0] = CHR_WR_2(arcn->sb.st_nlink); hd->h_nlink[1] = CHR_WR_3(arcn->sb.st_nlink); if (arcn->sb.st_nlink != (nlink_t)(SHRT_EXT(hd->h_nlink))) goto out; hd->h_rdev[0] = CHR_WR_2(arcn->sb.st_rdev); hd->h_rdev[1] = CHR_WR_3(arcn->sb.st_rdev); if (arcn->sb.st_rdev != (dev_t)(SHRT_EXT(hd->h_rdev))) goto out; if (arcn->sb.st_mtime > 0) { hd->h_mtime_1[0] = CHR_WR_0(arcn->sb.st_mtime); hd->h_mtime_1[1] = CHR_WR_1(arcn->sb.st_mtime); hd->h_mtime_2[0] = CHR_WR_2(arcn->sb.st_mtime); hd->h_mtime_2[1] = CHR_WR_3(arcn->sb.st_mtime); t_timet = (time_t)SHRT_EXT(hd->h_mtime_1); t_timet = t_timet << 16 | (time_t)SHRT_EXT(hd->h_mtime_2); if (arcn->sb.st_mtime != t_timet) goto out; } else { hd->h_mtime_1[0] = hd->h_mtime_1[1] = 0; hd->h_mtime_2[0] = hd->h_mtime_2[1] = 0; } nsz = arcn->nlen + 1; hd->h_namesize[0] = CHR_WR_2(nsz); hd->h_namesize[1] = CHR_WR_3(nsz); if (nsz != (int)(SHRT_EXT(hd->h_namesize))) goto out; /* * write the header, the file name and padding as required. */ if ((wr_rdbuf(hdblk, (int)sizeof(HD_BCPIO)) < 0) || (wr_rdbuf(arcn->name, nsz) < 0) || (wr_skip(BCPIO_PAD(sizeof(HD_BCPIO) + nsz)) < 0)) { paxwarn(1, "Unable to write %s %s for %s", "bcpio", "header", arcn->org_name); return (-1); } /* * if we have file data, tell the caller we are done */ if (PAX_IS_REG(arcn->type) || (arcn->type == PAX_HRG)) return(0); /* * if we are not a link, tell the caller we are done, go to next file */ if (arcn->type != PAX_SLK) return(1); /* * write the link name, tell the caller we are done. */ if ((wr_rdbuf(arcn->ln_name, arcn->ln_nlen) < 0) || (wr_skip(BCPIO_PAD(arcn->ln_nlen)) < 0)) { paxwarn(1, "Unable to write %s %s for %s", "bcpio", "link name", arcn->org_name); return (-1); } return(1); out: /* * header field is out of range */ paxwarn(1, "%s header field is too small for file %s", "bcpio", arcn->org_name); return (1); } #endif pax/cpio.h010064400000000000000000000134321466022246600077130ustar00/* $OpenBSD: cpio.h,v 1.4 2003/06/02 23:32:08 millert Exp $ */ /* $NetBSD: cpio.h,v 1.3 1995/03/21 09:07:15 cgd Exp $ */ /*- * Copyright (c) 2005, 2016 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)cpio.h 8.1 (Berkeley) 5/31/93 */ #ifdef EXTERN __IDSTRING(rcsid_cpio_h, "$MirOS: src/bin/pax/cpio.h,v 1.7 2024/08/17 22:32:59 tg Exp $"); #endif /* * Defines common to all versions of cpio */ #define TRAILER "TRAILER!!!" /* name in last archive record */ /* * Header encoding of the different file types */ #define C_ISDIR 040000 /* Directory */ #define C_ISFIFO 010000 /* FIFO */ #define C_ISREG 0100000 /* Regular file */ #define C_ISBLK 060000 /* Block special file */ #define C_ISCHR 020000 /* Character special file */ #define C_ISCTG 0110000 /* Reserved for contiguous files */ #define C_ISLNK 0120000 /* Reserved for symbolic links */ #define C_ISOCK 0140000 /* Reserved for sockets */ #define C_IFMT 0170000 /* type of file */ /* * Data Interchange Format - Extended cpio header format - POSIX 1003.1-1990 */ typedef struct { char c_magic[6]; /* magic cookie */ char c_dev[6]; /* device number (device) */ char c_ino[6]; /* inode number */ char c_mode[6]; /* file type/access */ char c_uid[6]; /* owners uid */ char c_gid[6]; /* owners gid */ char c_nlink[6]; /* # of links at archive creation */ char c_rdev[6]; /* block/char major/minor # (node) */ char c_mtime[11]; /* modification time */ char c_namesize[6]; /* length of pathname */ char c_filesize[11]; /* length of file in bytes */ } HD_CPIO; #define MAGIC 070707 /* transportable archive id */ #ifdef _PAX_ #define AMAGIC "070707" /* ascii equivalent string of MAGIC */ #define CPIO_MASK 0x3ffff /* bits valid in the dev/ino fields */ /* used for dev/inode remaps */ #endif /* _PAX_ */ /* * Binary cpio header structure * * CAUTION! CAUTION! CAUTION! * Each field really represents a 16 bit short (NOT ASCII). Described as * an array of chars in an attempt to improve portability!! */ typedef struct { unsigned char h_magic[2]; unsigned char h_dev[2]; unsigned char h_ino[2]; unsigned char h_mode[2]; unsigned char h_uid[2]; unsigned char h_gid[2]; unsigned char h_nlink[2]; unsigned char h_rdev[2]; unsigned char h_mtime_1[2]; unsigned char h_mtime_2[2]; unsigned char h_namesize[2]; unsigned char h_filesize_1[2]; unsigned char h_filesize_2[2]; } HD_BCPIO; #ifdef _PAX_ /* * extraction and creation macros for binary cpio */ #define SHRT_EXT(ch) ((((unsigned)(ch)[0])<<8) | (((unsigned)(ch)[1])&0xff)) #define RSHRT_EXT(ch) ((((unsigned)(ch)[1])<<8) | (((unsigned)(ch)[0])&0xff)) #define CHR_WR_0(val) ((char)(((val) >> 24) & 0xff)) #define CHR_WR_1(val) ((char)(((val) >> 16) & 0xff)) #define CHR_WR_2(val) ((char)(((val) >> 8) & 0xff)) #define CHR_WR_3(val) ((char)((val) & 0xff)) /* * binary cpio masks and pads */ #define BCPIO_PAD(x) ((2 - ((x) & 1)) & 1) /* pad to next 2 byte word */ #define BCPIO_MASK 0xffff /* mask for dev/ino fields */ #endif /* _PAX_ */ /* * System VR4 cpio header structure (with/without file data crc) */ typedef struct { char c_magic[6]; /* magic cookie */ char c_ino[8]; /* inode number */ char c_mode[8]; /* file type/access */ char c_uid[8]; /* owners uid */ char c_gid[8]; /* owners gid */ char c_nlink[8]; /* # of links at archive creation */ char c_mtime[8]; /* modification time */ char c_filesize[8]; /* length of file in bytes */ char c_maj[8]; /* block/char major # (device) */ char c_min[8]; /* block/char minor # (device) */ char c_rmaj[8]; /* special file major # (node) */ char c_rmin[8]; /* special file minor # (node) */ char c_namesize[8]; /* length of pathname */ char c_chksum[8]; /* 0 OR CRC of bytes of FILE data */ } HD_VCPIO; #define VMAGIC 070701 /* sVr4 new portable archive id */ #define VCMAGIC 070702 /* sVr4 new portable archive id CRC */ #ifdef _PAX_ #define AVMAGIC "070701" /* ascii string of above */ #define AVCMAGIC "070702" /* ascii string of above */ #define VCPIO_PAD(x) ((4 - ((x) & 3)) & 3) /* pad to next 4 byte word */ #define VCPIO_MASK 0xffffffff /* mask for dev/ino fields */ #endif /* _PAX_ */ pax/extern.h010064400000000000000000000227541364306522600102740ustar00/* $OpenBSD: extern.h,v 1.59 2018/09/13 12:33:43 millert Exp $ */ /* $NetBSD: extern.h,v 1.5 1996/03/26 23:54:16 mrg Exp $ */ /*- * Copyright © 2013, 2015, 2016, 2018, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)extern.h 8.2 (Berkeley) 4/18/94 */ /* * External references from each source file */ #ifdef EXTERN __IDSTRING(rcsid_extern_h, "$MirOS: src/bin/pax/extern.h,v 1.40 2020/04/07 11:56:43 tg Exp $"); #endif #ifndef PAX_JUST_THE_WARNINGS /* * ar.c */ int uar_stwr(int); int uar_ismagic(char *); int uar_id(char *, int) MKSH_A_NORETURN; int uar_rd(ARCHD *, char *); int uar_wr(ARCHD *); int uar_wr_data(ARCHD *, int, off_t *); off_t uar_endrd(void); int uar_trail(ARCHD *, char *, int, int *) MKSH_A_NORETURN; /* * ar_io.c */ extern const char *arcname; extern const char *compress_program; extern char force_one_volume; int ar_open(const char *); void ar_close(int _in_sig); void ar_drain(void); int ar_set_wr(void); int ar_app_ok(void); int ar_read(char *, int); int ar_write(char *, int); int ar_rdsync(void); int ar_fow(off_t, off_t *); int ar_rev(off_t ); int ar_next(void); extern char ar_do_keepopen; int ar_next_keepopen(void); /* * ar_subs.c */ extern u_long flcnt; void list(void); void extract(void); void append(void); void archive(void); void copy(void); /* * buf_subs.c */ extern int blksz; extern int wrblksz; extern int maxflt; extern int rdblksz; extern off_t wrlimit; extern off_t rdcnt; extern off_t wrcnt; int wr_start(void); int rd_start(void); void cp_start(void); int appnd_start(off_t); int rd_sync(void); void pback(const char *, int); int rd_skip(off_t); void wr_fin(void); int wr_rdbuf(const char *, int); int rd_wrbuf(char *, int); int wr_skip(off_t); int wr_rdfile(ARCHD *, int, off_t *); int rd_wrfile(ARCHD *, int, off_t *); void cp_file(ARCHD *, int, int); int buf_fill(void); int buf_fill_internal(int); int buf_flush(int); /* * cache.c */ #if !HAVE_UG_FROM_UGID int uidtb_start(void); int gidtb_start(void); const char *name_uid(uid_t, int); const char *name_gid(gid_t, int); #else #define name_uid(u,frc) ((const char *)user_from_uid((u), !(frc))) #define name_gid(g,frc) ((const char *)group_from_gid((g), !(frc))) #endif #if !HAVE_UGID_FROM_UG int usrtb_start(void); int grptb_start(void); int uid_name(const char *, uid_t *); int gid_name(const char *, gid_t *); int uid_uncached(const char *, uid_t *); int gid_uncached(const char *, gid_t *); #else #define uid_name uid_from_user #define gid_name gid_from_group #define uid_uncached uid_from_user #define gid_uncached gid_from_group #endif /* * cpio.c */ int cpio_strd(void); int cpio_trail(ARCHD *, char *, int, int *); int cpio_endwr(void); int cpio_id(char *, int); int cpio_rd(ARCHD *, char *); off_t cpio_endrd(void); int cpio_stwr(int); int dist_stwr(int); int cpio_wr(ARCHD *); int vcpio_id(char *, int); int crc_id(char *, int); int crc_strd(void); int vcpio_rd(ARCHD *, char *); off_t vcpio_endrd(void); int crc_stwr(int); int v4root_stwr(int); int v4norm_stwr(int); int vcpio_wr(ARCHD *); int bcpio_id(char *, int); int bcpio_rd(ARCHD *, char *); off_t bcpio_endrd(void); int bcpio_wr(ARCHD *); /* * file_subs.c */ int file_creat(ARCHD *); void file_close(ARCHD *, int); int lnk_creat(ARCHD *, int *); int cross_lnk(ARCHD *); int chk_same(ARCHD *); int node_creat(ARCHD *); int unlnk_exist(char *, int); int chk_path(char *, uid_t, gid_t); void set_ftime(const char *, const struct stat *, int, int); int set_ids(char *, uid_t, gid_t, int); int fset_ids(char *, int, uid_t, gid_t); void set_pmode(char *, mode_t, int); void fset_pmode(char *, int, mode_t); struct file_times; /* avoid pulling time.h globally */ int set_attr(const struct file_times *, int _force_times, mode_t, int _do_mode, int _in_sig); int file_write(int, char *, int, int *, int *, int, char *); void file_flush(int, char *, int); void rdfile_close(ARCHD *, int *); int set_crc(ARCHD *, int); /* * ftree.c */ int ftree_start(void); int ftree_add(char *, int); void ftree_sel(ARCHD *); void ftree_skipped_newer(void); void ftree_chk(void); int next_file(ARCHD *); /* * gen_subs.c */ void ls_list(ARCHD *, FILE *); void ls_tty(ARCHD *); void safe_print(const char *, FILE *); u_long asc_ul(char *, int, int); int ul_asc(u_long, char *, int, int); unsigned long long asc_ull(char *, int, int); int ull_asc(unsigned long long, char *, int, int); size_t fieldcpy(char *, size_t, const char *, size_t); /* * getoldopt.c */ int getoldopt(int, char **, const char *); /* * options.c */ extern const FSUB fsub[]; extern const unsigned char ford[]; extern int anonarch; extern char to_stdout; void options(int, char **); OPLIST * opt_next(void); int opt_add(const char *); int bad_opt(void); void guess_compress_program(int); void anonarch_init(void); void mircpio_deprecated(const char *, const char *); extern char *chdname; /* * pat_rep.c */ int rep_add(char *); int pat_add(char *, char *); void pat_chk(void); int pat_sel(ARCHD *); int pat_match(ARCHD *); int mod_name(ARCHD *); int set_dest(ARCHD *, char *, int); int has_dotdot(const char *); /* * pax.c */ extern signed char act; extern const FSUB *frmt; extern signed char cflag; extern signed char cwdfd; extern signed char dflag; extern signed char iflag; extern signed char kflag; extern signed char lflag; extern signed char nflag; extern signed char tflag; extern signed char uflag; extern signed char Vflag; extern signed char vflag; extern signed char Dflag; extern signed char Hflag; extern signed char Lflag; extern signed char Xflag; extern signed char Yflag; extern signed char Zflag; extern signed char zeroflag; extern signed char vfpart; extern signed char patime; extern signed char pmtime; extern signed char nodirs; extern signed char pmode; extern signed char pids; extern signed char rmleadslash; extern signed char exit_val; extern signed char docrc; extern char *dirptr; extern const char *argv0; extern enum op_mode { OP_PAX, OP_TAR, OP_CPIO } op_mode; extern FILE *listf; extern int listfd; extern char *tempfile; extern char *tempbase; extern char havechd; extern time_t now; void sig_cleanup(int) MKSH_A_NORETURN; /* * sel_subs.c */ int sel_chk(ARCHD *); int grp_add(char *); int usr_add(char *); int trng_add(char *); /* * tables.c */ int lnk_start(void); int chk_lnk(ARCHD *); void purg_lnk(ARCHD *); void lnk_end(void); int ftime_start(void); int chk_ftime(ARCHD *); int sltab_start(void); int sltab_add_sym(const char *_path, const char *_value, mode_t _mode); int sltab_add_link(const char *, const struct stat *); void sltab_process(int _in_sig); int name_start(void); int add_name(char *, int, char *); void sub_name(char *, int *, int); int dev_start(void); int add_dev(ARCHD *); int map_dev(ARCHD *, u_long, u_long); int atdir_start(void); void atdir_end(void); void add_atdir(const char *, const struct stat *); int do_atdir(const char *, dev_t, ino_t); int dir_start(void); void add_dir(char *, struct stat *, int); void delete_dir(dev_t, ino_t); void proc_dir(int _in_sig); unsigned int st_hash(const char *, int, int); int flnk_start(void); int chk_flnk(ARCHD *); /* * tar.c */ #ifndef SMALL extern char tar_nodir; #endif extern char *gnu_name_string, *gnu_link_string; int tar_endwr(void); off_t tar_endrd(void); int tar_trail(ARCHD *, char *, int, int *); int tar_id(char *, int); #ifndef SMALL int tar_opt(void); #else #define tar_opt bad_opt #endif int tar_rd(ARCHD *, char *); int tar_wr(ARCHD *); int ustar_strd(void); int ustar_stwr(int); int ustar_id(char *, int); int ustar_rd(ARCHD *, char *); int ustar_wr(ARCHD *); /* * tty_subs.c */ extern char fdgetline_err; char *fdgetline(int); int tty_init(void); void tty_prnt(const char *, ...) MKSH_A_NONNULL(1) MKSH_A_FORMAT(__printf__, 1, 2); char *tty_rd(void); #endif void paxwarn(int, const char *, ...) MKSH_A_NONNULL(2) MKSH_A_FORMAT(__printf__, 2, 3); void syswarn(int, int, const char *, ...) MKSH_A_NONNULL(3) MKSH_A_FORMAT(__printf__, 3, 4); pax/file_subs.c010064400000000000000000001053201466023157200107230ustar00/* $OpenBSD: file_subs.c,v 1.53 2017/01/21 08:17:06 krw Exp $ */ /* $NetBSD: file_subs.c,v 1.4 1995/03/21 09:07:18 cgd Exp $ */ /*- * Copyright (c) 2007, 2008, 2009, 2012, 2014, 2016, 2018, 2019, 2024 * mirabilos * Copyright (c) 2018 * Jonathan de Boyne Pollard * mirabilos * Copyright (c) 2011 * Svante Signell * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #if HAVE_UTIME_H #include #endif #include "pax.h" #define EXTERN #include "ftimes.h" #undef EXTERN #include "extern.h" __RCSID("$MirOS: src/bin/pax/file_subs.c,v 1.33 2024/08/17 23:33:51 tg Exp $"); /* * routines that deal with file operations such as: creating, removing; * and setting access modes, uid/gid and times of files */ #if HAVE_FUTIMENS || HAVE_FUTIMES #define PAX_FSET_FTIME #endif static int mk_link(char *, struct stat *, char *, int); #ifdef PAX_FSET_FTIME static void fset_ftime(const char *, int, const struct stat *, int); #endif /* * file_creat() * Create and open a file. * Return: * file descriptor or -1 for failure */ int file_creat(ARCHD *arcn) { int fd = -1; mode_t file_mode; int oerrno; /* * Assume file doesn't exist, so just try to create it, most times this * works. We have to take special handling when the file does exist. To * detect this, we use O_EXCL. For example when trying to create a * file and a character device or FIFO exists with the same name, we * can accidently open the device by mistake (or block waiting to open). * If we find that the open has failed, then spend the effort to * figure out why. This strategy was found to have better average * performance in common use than checking the file (and the path) * first with lstat. */ file_mode = arcn->sb.st_mode & FILEBITS; if ((fd = binopen3(0, arcn->name, O_WRONLY | O_CREAT | O_EXCL, file_mode)) >= 0) return (fd); /* * the file seems to exist. First we try to get rid of it (found to be * the second most common failure when traced). If this fails, only * then we go to the expense to check and create the path to the file */ if (unlnk_exist(arcn->name, arcn->type) != 0) return(-1); for (;;) { /* * try to open it again, if this fails, check all the nodes in * the path and give it a final try. if chk_path() finds that * it cannot fix anything, we will skip the last attempt */ if ((fd = binopen3(0, arcn->name, O_WRONLY | O_CREAT | O_TRUNC, file_mode)) >= 0) break; oerrno = errno; if (nodirs || chk_path(arcn->name,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { syswarn(1, oerrno, "Unable to create %s", arcn->name); return(-1); } } return(fd); } /* * file_close() * Close file descriptor to a file just created by pax. Sets modes, * ownership and times as required. * Return: * 0 for success, -1 for failure */ void file_close(ARCHD *arcn, int fd) { int res = 0; if (fd < 0) return; /* * set owner/groups first as this may strip off mode bits we want * then set file permission modes. Then set file access and * modification times. */ if (pids) res = fset_ids(arcn->name, fd, arcn->sb.st_uid, arcn->sb.st_gid); /* * IMPORTANT SECURITY NOTE: * if not preserving mode or we cannot set uid/gid, then PROHIBIT * set uid/gid bits */ if (!pmode || res) arcn->sb.st_mode &= ~(SETBITS); if (pmode) fset_pmode(arcn->name, fd, arcn->sb.st_mode); #ifdef PAX_FSET_FTIME if (patime || pmtime) fset_ftime(arcn->name, fd, &arcn->sb, 0); #endif if (close(fd) < 0) syswarn(0, errno, "Unable to close file descriptor on %s", arcn->name); } /* * lnk_creat() * Create a hard link to arcn->ln_name from arcn->name. arcn->ln_name * must exist; * Return: * fd+2 if data should be extracted, * 0 if ok, 1 if we could not make the link, -1 otherwise */ int lnk_creat(ARCHD *arcn, int *fdp) { struct stat sb; int res; /* * we may be running as root, so we have to be sure that link target * is not a directory, so we lstat and check */ if (lstat(arcn->ln_name, &sb) < 0) { syswarn(1, errno, "Unable to link to %s from %s", arcn->ln_name, arcn->name); return (-1); } if (S_ISDIR(sb.st_mode)) { paxwarn(1, "A hard link to the directory %s is not allowed", arcn->ln_name); return(-1); } res = mk_link(arcn->ln_name, &sb, arcn->name, 0); if (res == 0) { /* check for a hardlink to a placeholder symlink */ res = sltab_add_link(arcn->name, &sb); if (res < 0) { /* arrgh, it failed, clean up */ unlink(arcn->name); } } if (fdp != NULL && res == 0 && sb.st_size == 0 && arcn->skip > 0) { /* request to write out file data late (broken archive) */ if (pmode) set_pmode(arcn->name, 0600, /*XXX I think */ 0); if ((*fdp = binopen2(0, arcn->name, O_WRONLY | O_TRUNC)) == -1) { res = errno; syswarn(1, res, "Unable to re-open %s", arcn->name); if (pmode) set_pmode(arcn->name, sb.st_mode, 0); } res = 0; } else if (fdp != NULL) *fdp = -1; return (res); } /* * cross_lnk() * Create a hard link to arcn->org_name from arcn->name. Only used in copy * with the -l flag. No warning or error if this does not succeed (we will * then just create the file) * Return: * 1 if copy() should try to create this file node * 0 if cross_lnk() ok, -1 for fatal flaw (like linking to self). */ int cross_lnk(ARCHD *arcn) { /* * try to make a link to original file (-l flag in copy mode). make * sure we do not try to link to directories in case we are running as * root (and it might succeed). */ if (arcn->type == PAX_DIR) return(1); return(mk_link(arcn->org_name, &(arcn->sb), arcn->name, 1)); } /* * chk_same() * In copy mode if we are not trying to make hard links between the src * and destinations, make sure we are not going to overwrite ourselves by * accident. This slows things down a little, but we have to protect all * those people who make typing errors. * Return: * 1 the target does not exist, go ahead and copy * 0 skip it file exists (-k) or may be the same as source file */ int chk_same(ARCHD *arcn) { struct stat sb; /* * if file does not exist, return. if file exists and -k, skip it * quietly */ if (lstat(arcn->name, &sb) < 0) return(1); if (kflag) return(0); /* * better make sure the user does not have src == dest by mistake */ if ((arcn->sb.st_dev == sb.st_dev) && (arcn->sb.st_ino == sb.st_ino)) { paxwarn(1, "Unable to copy %s, file would overwrite itself", arcn->name); return(0); } return(1); } /* * helper function to copy a symbolic link */ static int mk_link_symlink(const char *to, const char *from) { int cnt; char buf[PAXPATHLEN + 1]; if ((cnt = readlink(to, buf, PAXPATHLEN)) < 0) return (-1); /* cf. comment in ftree.c:next_file() */ buf[cnt] = '\0'; return (symlink(buf, from)); } /* * mk_link() * try to make a hard link between two files. if ign set, we do not * complain. * Return: * 0 if successful (or we are done with this file but no error, such as * finding the from file exists and the user has set -k). * 1 when ign was set to indicates we could not make the link but we * should try to copy/extract the file as that might work (and is an * allowed option). -1 an error occurred. */ static int mk_link(char *to, struct stat *to_sb, char *from, int ign) { struct stat sb; int oerrno; /* * if from file exists, it has to be unlinked to make the link. If the * file exists and -k is set, skip it quietly */ if (lstat(from, &sb) == 0) { if (kflag) return(0); /* * make sure it is not the same file, protect the user */ if ((to_sb->st_dev==sb.st_dev)&&(to_sb->st_ino == sb.st_ino)) { paxwarn(1, "Unable to link file %s to itself", to); return(-1); } /* * try to get rid of the file, based on the type */ if (S_ISDIR(sb.st_mode)) { if (rmdir(from) < 0) { syswarn(1, errno, "Unable to remove %s", from); return(-1); } delete_dir(sb.st_dev, sb.st_ino); } else if (unlink(from) < 0) { if (!ign) { syswarn(1, errno, "Unable to remove %s", from); return(-1); } return(1); } } /* * from file is gone (or did not exist), try to make the hard link. * if it fails, check the path and try it again (if chk_path() says to * try again) */ for (;;) { #if HAVE_LINKAT if (linkat(AT_FDCWD, to, AT_FDCWD, from, 0) == 0) #else if (link(to, from) == 0) #endif break; oerrno = errno; if (S_ISLNK(to_sb->st_mode)) { /* just copy the symlink */ if (mk_link_symlink(to, from) == 0) break; } if (!nodirs && chk_path(from, to_sb->st_uid, to_sb->st_gid) == 0) continue; /*- * non-standard (via -M lncp) cross-device link handling: * copy if hard link fails (but what if there are several * links for the same file mixed between several devices? * this code copies for all non-original devices, instead * of tracking them and linking between them on their re- * spective target device) */ if (oerrno == EXDEV && (anonarch & ANON_LNCP)) { int fdsrc, fddest; ARCHD tarcn; if ((fdsrc = binopen3(0, to, O_RDONLY, 0)) < 0) { if (!ign) syswarn(1, errno, "Unable to open %s to read", to); goto lncp_failed; } strlcpy(tarcn.name, from, sizeof(tarcn.name)); memcpy(&tarcn.sb, to_sb, sizeof(struct stat)); tarcn.type = PAX_REG; /* XXX */ tarcn.org_name = to; if ((fddest = file_creat(&tarcn)) < 0) { rdfile_close(&tarcn, &fdsrc); goto lncp_failed; } cp_file(&tarcn, fdsrc, fddest); file_close(&tarcn, fddest); rdfile_close(&tarcn, &fdsrc); /* file copied successfully, continue on */ break; } if (!ign) { lncp_failed: syswarn(1, oerrno, "Unable to link to %s from %s", to, from); return (-1); } return (1); } /* * all right the link was made */ return (0); } /* * node_creat() * create an entry in the filesystem (other than a file or hard link). * If successful, sets uid/gid modes and times as required. * Return: * 0 if ok, -1 otherwise */ int node_creat(ARCHD *arcn) { int res; int ign = 0; int oerrno; int pass = 0; mode_t file_mode; struct stat sb; char *allocd = NULL; char *nm = arcn->name; int len, defer_pmode = 0; /* * create node based on type, if that fails try to unlink the node and * try again. finally check the path and try again. As noted in the * file and link creation routines, this method seems to exhibit the * best performance in general use workloads. */ file_mode = arcn->sb.st_mode & FILEBITS; for (;;) { switch (arcn->type) { case PAX_DIR: /* * If -h (or -L) was given in tar-mode, follow the * potential symlink chain before trying to create the * directory. */ if (op_mode == OP_TAR && Lflag) { while (lstat(nm, &sb) == 0 && S_ISLNK(sb.st_mode)) { char *target = malloc(sb.st_size + 1); if (target == NULL) { free(allocd); oerrno = ENOMEM; syswarn(1, oerrno, "Out of memory"); return (-1); } len = readlink(nm, target, sb.st_size + 1); if (len == -1) { syswarn(0, errno, "cannot follow symlink %s in chain for %s", nm, arcn->name); res = -1; goto badlink; } target[len] = '\0'; nm = target; free(allocd); allocd = target; } } res = mkdir(nm, file_mode); badlink: if (ign) res = 0; break; case PAX_CHR: file_mode |= S_IFCHR; res = mknod(nm, file_mode, arcn->sb.st_rdev); break; case PAX_BLK: file_mode |= S_IFBLK; res = mknod(nm, file_mode, arcn->sb.st_rdev); break; case PAX_FIF: res = mkfifo(nm, file_mode); break; case PAX_SCK: /* * Skip sockets, operation has no meaning under BSD */ paxwarn(0, "%s skipped. Sockets cannot be copied or extracted", nm); free(allocd); return (-1); case PAX_SLK: if (arcn->ln_name[0] != '/' && !has_dotdot(arcn->ln_name)) res = symlink(arcn->ln_name, nm); else { /* * absolute symlinks and symlinks with ".." * have to be deferred to prevent the archive * from bootstrapping itself to outside the * working directory. */ res = sltab_add_sym(nm, arcn->ln_name, arcn->sb.st_mode); if (res == 0) defer_pmode = 1; } break; case PAX_CTG: case PAX_HLK: case PAX_HRG: case PAX_REG: default: /* * we should never get here */ paxwarn(0, "%s has an unknown file type, skipping", nm); free(allocd); return (-1); } /* * if we were able to create the node break out of the loop, * otherwise try to unlink the node and try again. if that * fails check the full path and try a final time. */ if (res == 0) break; /* * we failed to make the node */ oerrno = errno; if ((ign = unlnk_exist(nm, arcn->type)) < 0) { free(allocd); return (-1); } if (++pass <= 1) continue; if (nodirs || chk_path(nm,arcn->sb.st_uid,arcn->sb.st_gid) < 0) { syswarn(1, oerrno, "Unable to create %s", nm); free(allocd); return (-1); } } /* * we were able to create the node. set uid/gid, modes and times */ if (pids) res = set_ids(nm, arcn->sb.st_uid, arcn->sb.st_gid, arcn->type == PAX_SLK); else res = 0; /* * IMPORTANT SECURITY NOTE: * if not preserving mode or we cannot set uid/gid, then PROHIBIT any * set uid/gid bits */ if (!pmode || res) arcn->sb.st_mode &= ~(SETBITS); if (pmode && !defer_pmode) set_pmode(nm, arcn->sb.st_mode, arcn->type == PAX_SLK); if (arcn->type == PAX_DIR && op_mode != OP_CPIO) { /* * Dirs must be processed again at end of extract to set times * and modes to agree with those stored in the archive. However * to allow extract to continue, we may have to also set owner * rights. This allows nodes in the archive that are children * of this directory to be extracted without failure. Both time * and modes will be fixed after the entire archive is read and * before pax exits. To do that safely, we want the dev+ino * of the directory we created. */ if (lstat(nm, &sb) < 0) { syswarn(0, errno, "Unable to stat %s", nm); } else if (access(nm, R_OK | W_OK | X_OK) < 0) { /* * We have to add rights to the dir, so we make * sure to restore the mode. The mode must be * restored AS CREATED and not as stored if * pmode is not set. */ set_pmode(nm, ((sb.st_mode & FILEBITS) | S_IRWXU), 0); if (!pmode) arcn->sb.st_mode = sb.st_mode; /* * we have to force the mode to what was set * here, since we changed it from the default * as created. */ arcn->sb.st_dev = sb.st_dev; arcn->sb.st_ino = sb.st_ino; add_dir(nm, &(arcn->sb), 1); } else if (pmode || patime || pmtime) { arcn->sb.st_dev = sb.st_dev; arcn->sb.st_ino = sb.st_ino; add_dir(nm, &(arcn->sb), 0); } } else if (patime || pmtime) set_ftime(nm, &arcn->sb, 0, arcn->type == PAX_SLK); free(allocd); return (0); } /* * unlnk_exist() * Remove node from filesystem with the specified name. We pass the type * of the node that is going to replace it. When we try to create a * directory and find that it already exists, we allow processing to * continue as proper modes etc will always be set for it later on. * Return: * 0 is ok to proceed, no file with the specified name exists * -1 we were unable to remove the node, or we should not remove it (-k) * 1 we found a directory and we were going to create a directory. */ int unlnk_exist(char *name, int type) { struct stat sb; /* * the file does not exist, or -k we are done */ if (lstat(name, &sb) < 0) return(0); if (kflag) return(-1); if (S_ISDIR(sb.st_mode)) { /* * try to remove a directory, if it fails and we were going to * create a directory anyway, tell the caller (return a 1) */ if (rmdir(name) < 0) { if (type == PAX_DIR) return(1); syswarn(1,errno,"Unable to remove directory %s", name); return(-1); } delete_dir(sb.st_dev, sb.st_ino); return(0); } /* * try to get rid of all non-directory type nodes */ if (unlink(name) < 0) { syswarn(1, errno, "Unable to remove %s", name); return(-1); } return(0); } /* * chk_path() * We were trying to create some kind of node in the filesystem and it * failed. chk_path() makes sure the path up to the node exists and is * writeable. When we have to create a directory that is missing along the * path somewhere, the directory we create will be set to the same * uid/gid as the file has (when uid and gid are being preserved). * NOTE: this routine is a real performance loss. It is only used as a * last resort when trying to create entries in the filesystem. * Return: * -1 when it could find nothing it is allowed to fix. * 0 otherwise */ int chk_path(char *name, uid_t st_uid, gid_t st_gid) { char *spt = name; char *next; struct stat sb; int retval = -1; /* * watch out for paths with nodes stored directly in / (e.g. /bozo) */ while (*spt == '/') ++spt; for (;;) { /* * work forward from the first / and check each part of the path */ spt = strchr(spt, '/'); if (spt == NULL) break; /* * skip over duplicate slashes; stop if there're only * trailing slashes left */ next = spt + 1; while (*next == '/') next++; if (*next == '\0') break; *spt = '\0'; /* * if it exists we assume it is a directory, it is not within * the spec (at least it seems to read that way) to alter the * filesystem for nodes NOT EXPLICITLY stored on the archive. * If that assumption is changed, you would test the node here * and figure out how to get rid of it (probably like some * recursive unlink()) or fix up the directory permissions if * required (do an access()). */ if (lstat(name, &sb) == 0) { *spt = '/'; spt = next; continue; } /* * the path fails at this point, see if we can create the * needed directory and continue on */ if (mkdir(name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) { *spt = '/'; retval = -1; break; } /* * we were able to create the directory. We will tell the * caller that we found something to fix, and it is ok to try * and create the node again. */ retval = 0; if (pids) (void)set_ids(name, st_uid, st_gid, 0); /* * make sure the user doesn't have some strange umask that * causes this newly created directory to be unusable. We fix * the modes and restore them back to the creation default at * the end of pax */ if ((access(name, R_OK | W_OK | X_OK) < 0) && (lstat(name, &sb) == 0)) { set_pmode(name, ((sb.st_mode & FILEBITS) | S_IRWXU), 0); add_dir(name, &sb, 1); } *spt = '/'; spt = next; continue; } return(retval); } /* * set_ftime() * Set the access time and modification time for a named file. If frc * is non-zero we force these times to be set even if the user did not * request access and/or modification time preservation (this is also * used by -t to reset access times). * When ign is zero, only those times the user has asked for are set, the * other ones are left alone. We do not assume the un-documented feature * of many utimes() implementations that consider a 0 time value as a do * not set request. */ void set_ftime(const char *fnm, const struct stat *sbp, int frc, int issymlink MKSH_A_UNUSED) { #if HAVE_UTIMENSAT struct timespec ts[2]; #else struct { time_t tv_sec; long tv_nsec; } ts[2]; #if HAVE_UTIMES struct timeval tv[2]; #else struct utimbuf u; #endif struct stat sb; #endif int rv; /* pre-initialise values to set */ st_timexp(a, &ts[0], sbp); st_timexp(m, &ts[1], sbp); if (!frc && (!patime || !pmtime)) { /* * If we are not forcing, only set those times the user * wants set. We get the current values of the times if * we need them (utimensat does not). */ #if HAVE_UTIMENSAT if (!patime) ts[0].tv_nsec = UTIME_OMIT; if (!pmtime) ts[1].tv_nsec = UTIME_OMIT; #else if (lstat(fnm, &sb) == 0) { if (!patime) st_timexp(a, &ts[0], &sb); if (!pmtime) st_timexp(m, &ts[1], &sb); } else syswarn(0, errno, "Unable to stat %s", fnm); #endif } /* set the times */ #if HAVE_UTIMENSAT rv = utimensat(AT_FDCWD, fnm, ts, AT_SYMLINK_NOFOLLOW); #elif HAVE_UTIMES tv[0].tv_sec = ts[0].tv_sec; tv[0].tv_usec = ts[0].tv_nsec / 1000; tv[1].tv_sec = ts[1].tv_sec; tv[1].tv_usec = ts[1].tv_nsec / 1000; #if HAVE_LUTIMES rv = (issymlink ? lutimes : utimes)(fnm, tv); #else if (issymlink) /* no can do */ return; rv = utimes(fnm, tv); #endif if (rv < 0 && issymlink && (errno == ENOSYS || errno == ENOTSUP)) /* might be glibc */ return; #else if (issymlink) /* no can do */ return; u.actime = ts[0].tv_sec; u.modtime = ts[1].tv_sec; rv = utime(fnm, &u); #endif if (rv < 0) syswarn(1, errno, "Access/modification time set failed on: %s", fnm); } #ifdef PAX_FSET_FTIME static void fset_ftime(const char *fnm, int fd, const struct stat *sbp, int frc) { #if HAVE_FUTIMENS struct timespec ts[2]; #else struct { time_t tv_sec; long tv_nsec; } ts[2]; struct timeval tv[2]; struct stat sb; #endif int rv; /* pre-initialise values to set */ st_timexp(a, &ts[0], sbp); st_timexp(m, &ts[1], sbp); if (!frc && (!patime || !pmtime)) { /* * If we are not forcing, only set those times the user * wants set. We get the current values of the times if * we need them (futimens does not). */ #if HAVE_FUTIMENS if (!patime) ts[0].tv_nsec = UTIME_OMIT; if (!pmtime) ts[1].tv_nsec = UTIME_OMIT; #else if (fstat(fd, &sb) == 0) { if (!patime) st_timexp(a, &ts[0], &sb); if (!pmtime) st_timexp(m, &ts[1], &sb); } else syswarn(0, errno, "Unable to stat %s", fnm); #endif } /* set the times */ #if HAVE_FUTIMENS rv = futimens(fd, ts); #else tv[0].tv_sec = ts[0].tv_sec; tv[0].tv_usec = ts[0].tv_nsec / 1000; tv[1].tv_sec = ts[1].tv_sec; tv[1].tv_usec = ts[1].tv_nsec / 1000; rv = futimes(fd, tv); #endif if (rv < 0) syswarn(1, errno, "Access/modification time set failed on: %s", fnm); } #endif /* * set_ids() * set the uid and gid of a filesystem node * Return: * 0 when set, -1 on failure */ int set_ids(char *fnm, uid_t uid, gid_t gid, int issymlink MKSH_A_UNUSED) { int rv; #if HAVE_FCHOWNAT rv = fchownat(AT_FDCWD, fnm, uid, gid, AT_SYMLINK_NOFOLLOW); #elif HAVE_LCHOWN rv = (issymlink ? lchown : chown)(fnm, uid, gid); if (rv < 0 && issymlink && (errno == ENOSYS || errno == ENOTSUP)) /* might be glibc */ return (0); #else if (issymlink) /* no can do */ return (0); rv = chown(fnm, uid, gid); #endif if (rv < 0) { /* * ignore EPERM unless in verbose mode or being run by root. * if running as pax, POSIX requires a warning. */ if (/* portability, imake style though */ #ifndef __INTERIX errno != EPERM || vflag || geteuid() == 0 || #endif op_mode == OP_PAX) syswarn(1, errno, "Unable to set file uid/gid of %s", fnm); return (-1); } return (0); } int fset_ids(char *fnm, int fd, uid_t uid, gid_t gid) { if (fchown(fd, uid, gid) < 0) { /* * ignore EPERM unless in verbose mode or being run by root. * if running as pax, POSIX requires a warning. */ if (/* portability, imake style though */ #ifndef __INTERIX errno != EPERM || vflag || geteuid() == 0 || #endif op_mode == OP_PAX) syswarn(1, errno, "Unable to set file uid/gid of %s", fnm); return (-1); } return (0); } /* * set_pmode() * Set file access mode */ void set_pmode(char *fnm, mode_t mode, int issymlink MKSH_A_UNUSED) { int rv; mode &= ABITS; #if HAVE_FCHMODAT rv = fchmodat(AT_FDCWD, fnm, mode, AT_SYMLINK_NOFOLLOW); if (rv < 0 && (errno == ENOSYS || errno == ENOTSUP)) { /* glibc sucks */ if (issymlink) return; rv = chmod(fnm, mode); } #elif HAVE_LCHMOD rv = (issymlink ? lchmod : chmod)(fnm, mode); if (rv < 0 && issymlink && (errno == ENOSYS || errno == ENOTSUP)) /* similarly glibc */ return; #else if (issymlink) /* no can do */ return; rv = chmod(fnm, mode); #endif if (rv < 0) syswarn(1, errno, "Could not set permissions on %s", fnm); } void fset_pmode(char *fnm, int fd, mode_t mode) { mode &= ABITS; if (fchmod(fd, mode) < 0) syswarn(1, errno, "Could not set permissions on %s", fnm); } /* * set_attr() * Given a DIRDATA, restore the mode and times as indicated, but * only after verifying that it's the directory that we wanted. */ int set_attr(const struct file_times *ft, int force_times, mode_t mode, int do_mode, int in_sig) { struct stat sb; int fd, r; if (!do_mode && !force_times && !patime && !pmtime) return (0); /* * We could legitimately go through a symlink here, * so do *not* use O_NOFOLLOW. The dev+ino check will * protect us from evil. */ fd = binopen2(BO_MAYBE_DIR, ft->ft_name, O_RDONLY); if (fd == -1) { if (!in_sig) syswarn(1, errno, "Unable to restore mode and times" " for directory %s", ft->ft_name); return (-1); } if (fstat(fd, &sb) == -1) { if (!in_sig) syswarn(1, errno, "Unable to stat directory %s", ft->ft_name); r = -1; #ifndef O_DIRECTORY } else if (!S_ISDIR(sb.st_mode)) { if (!in_sig) syswarn(1, ENOTDIR, "Unable to restore mode and times" " for directory %s", ft->ft_name); r = -1; #endif } else if (ft->ft_ino != sb.st_ino || ft->ft_dev != sb.st_dev) { if (!in_sig) paxwarn(1, "Directory vanished before restoring" " mode and times: %s", ft->ft_name); r = -1; } else { /* Whew, it's a match! Is there anything to change? */ if (do_mode && (mode & ABITS) != (sb.st_mode & ABITS)) fset_pmode(ft->ft_name, fd, mode); #ifdef PAX_FSET_FTIME if (((force_times || patime) && st_timecmp(a, &ft->sb, &sb, !=)) || ((force_times || pmtime) && st_timecmp(m, &ft->sb, &sb, !=))) fset_ftime(ft->ft_name, fd, &ft->sb, force_times); #endif r = 0; } close(fd); return (r); } /* * file_write() * Write/copy a file (during copy or archive extract). This routine knows * how to copy files with lseek holes in it. (Which are read as file * blocks containing all 0's but do not have any file blocks associated * with the data). Typical examples of these are files created by dbm * variants (.pag files). While the file size of these files are huge, the * actual storage is quite small (the files are sparse). The problem is * the holes read as all zeros so are probably stored on the archive that * way (there is no way to determine if the file block is really a hole, * we only know that a file block of all zero's can be a hole). * At this writing, no major archive format knows how to archive files * with holes. However, on extraction (or during copy, -rw) we have to * deal with these files. Without detecting the holes, the files can * consume a lot of file space if just written to disk. This replacement * for write when passed the basic allocation size of a filesystem block, * uses lseek whenever it detects the input data is all 0 within that * file block. In more detail, the strategy is as follows: * While the input is all zero keep doing an lseek. Keep track of when we * pass over file block boundaries. Only write when we hit a non zero * input. once we have written a file block, we continue to write it to * the end (we stop looking at the input). When we reach the start of the * next file block, start checking for zero blocks again. Working on file * block boundaries significantly reduces the overhead when copying files * that are NOT very sparse. This overhead (when compared to a write) is * almost below the measurement resolution on many systems. Without it, * files with holes cannot be safely copied. It does has a side effect as * it can put holes into files that did not have them before, but that is * not a problem since the file contents are unchanged (in fact it saves * file space). (Except on paging files for diskless clients. But since we * cannot determine one of those file from here, we ignore them). If this * ever ends up on a system where CTG files are supported and the holes * are not desired, just do a conditional test in those routines that * call file_write() and have it call write() instead. BEFORE CLOSING THE * FILE, make sure to call file_flush() when the last write finishes with * an empty block. A lot of filesystems will not create an lseek hole at * the end. In this case we drop a single 0 at the end to force the * trailing 0's in the file. * ---Parameters--- * rem: how many bytes left in this filesystem block * isempt: have we written to the file block yet (is it empty) * sz: basic file block allocation size * cnt: number of bytes on this write * str: buffer to write * Return: * number of bytes written, -1 on write (or lseek) error. */ int file_write(int fd, char *str, int cnt, int *rem, int *isempt, int sz, char *name) { char *pt; char *end; int wcnt; char *st = str; char **strp; /* * while we have data to process */ while (cnt) { if (!*rem) { /* * We are now at the start of filesystem block again * (or what we think one is...). start looking for * empty blocks again */ *isempt = 1; *rem = sz; } /* * only examine up to the end of the current file block or * remaining characters to write, whatever is smaller */ wcnt = MINIMUM(cnt, *rem); cnt -= wcnt; *rem -= wcnt; if (*isempt) { /* * have not written to this block yet, so we keep * looking for zero's */ pt = st; end = st + wcnt; /* * look for a zero filled buffer */ while ((pt < end) && (*pt == '\0')) ++pt; if (pt == end) { /* * skip, buf is empty so far */ if (fd > -1 && lseek(fd, wcnt, SEEK_CUR) < 0) { if (errno == ESPIPE) goto isapipe; syswarn(1, errno, "Failed seek on file %s", name); return (-1); } st = pt; continue; } isapipe: /* * drat, the buf is not zero filled */ *isempt = 0; } /* * have non-zero data in this filesystem block, have to write */ switch (fd) { case -1: strp = &gnu_name_string; break; case -2: strp = &gnu_link_string; break; default: strp = NULL; break; } if (strp) { if (*strp) err(1, "WARNING! Major Internal Error! GNU hack Failing!"); *strp = malloc(wcnt + 1); if (*strp == NULL) { paxwarn(1, "Out of memory"); return(-1); } memcpy(*strp, st, wcnt); (*strp)[wcnt] = '\0'; break; } else if (write(fd, st, wcnt) != wcnt) { syswarn(1, errno, "Failed write to file %s", name); return(-1); } st += wcnt; } return(st - str); } /* * file_flush() * when the last file block in a file is zero, many filesystems will not * let us create a hole at the end. To get the last block with zeros, we * write the last BYTE with a zero (back up one byte and write a zero). */ void file_flush(int fd, char *fname, int isempt) { static char blnk[] = "\0"; /* * silly test, but make sure we are only called when the last block is * filled with all zeros. */ if (!isempt) return; /* * move back one byte and write a zero */ if (lseek(fd, -1, SEEK_CUR) < 0) { syswarn(1, errno, "Failed seek on file %s", fname); return; } if (write(fd, blnk, 1) < 0) syswarn(1, errno, "Failed write to file %s", fname); } /* * rdfile_close() * close a file we have been reading (to copy or archive). If we have to * reset access time (tflag) do so (the times are stored in arcn). */ void rdfile_close(ARCHD *arcn MKSH_A_UNUSED, int *fd) { /* * make sure the file is open */ if (*fd < 0) return; /* * user wants last access time reset */ #ifdef PAX_FSET_FTIME if (tflag) fset_ftime(arcn->org_name, *fd, &arcn->sb, 1); #endif (void)close(*fd); *fd = -1; } /* * set_crc() * read a file to calculate its crc. This is a real drag. Archive formats * that have this, end up reading the file twice (we have to write the * header WITH the crc before writing the file contents. Oh well... * Return: * 0 if was able to calculate the crc, -1 otherwise */ int set_crc(ARCHD *arcn, int fd) { int i; int res; off_t cpcnt = 0; size_t size; uint32_t crc = 0; char tbuf[FILEBLK]; struct stat sb; if (fd < 0) { /* * hmm, no fd, should never happen. well no crc then. */ arcn->crc = 0; return(0); } if ((size = arcn->sb.st_blksize) > sizeof(tbuf)) size = sizeof(tbuf); /* * read all the bytes we think that there are in the file. If the user * is trying to archive an active file, forget this file. */ for (;;) { if ((res = read(fd, tbuf, size)) <= 0) break; cpcnt += res; for (i = 0; i < res; ++i) crc += (tbuf[i] & 0xff); } /* * safety check. we want to avoid archiving files that are active as * they can create inconsistent archive copies. */ if (cpcnt != (off_t)arcn->sb.st_size) paxwarn(1, "File changed size %s", arcn->org_name); else if (fstat(fd, &sb) < 0) syswarn(1, errno, "Failed stat on %s", arcn->org_name); else if (st_timecmp(m, &arcn->sb, &sb, !=)) paxwarn(1, "File %s was modified during read", arcn->org_name); else if (lseek(fd, 0, SEEK_SET) < 0) syswarn(1, errno, "File rewind failed on: %s", arcn->org_name); else { arcn->crc = crc; return(0); } return(-1); } pax/ftimes.h010064400000000000000000000045571340424730700102540ustar00/*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pax.h 8.2 (Berkeley) 4/18/94 */ #ifdef EXTERN __IDSTRING(rcsid_ftimes_h, "$MirOS: src/bin/pax/ftimes.h,v 1.2 2018/12/12 18:08:44 tg Exp $"); #endif /* * Time data for a given file. This is usually embedded in a structure * indexed by dev+ino, by name, by order in the archive, etc. set_attr() * takes one of these and will only change the times or mode if the file * at the given name has the indicated dev+ino. */ struct file_times { char *ft_name; /* name of file to set the times on */ ino_t ft_ino; /* inode number to verify */ dev_t ft_dev; /* device number to verify */ struct stat sb; /* times to set (atime, mtime) */ }; pax/ftree.c010064400000000000000000000360521340440266100100540ustar00/* $OpenBSD: ftree.c,v 1.41 2017/09/16 07:42:34 otto Exp $ */ /* $NetBSD: ftree.c,v 1.4 1995/03/21 09:07:21 cgd Exp $ */ /*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/ftree.c,v 1.12 2018/12/13 07:09:10 tg Exp $"); /* * Data structure used to store the file args to be handed to fts(). * It keeps track of which args generated a "selected" member. */ typedef struct ftree { char *fname; /* file tree name */ int refcnt; /* has tree had a selected file? */ int newercnt; /* skipped due to -u/-D */ int chflg; /* change directory flag */ struct ftree *fow; /* pointer to next entry on list */ } FTREE; /* * routines to interface with the fts library function. * * file args supplied to pax are stored on a single linked list (of type FTREE) * and given to fts to be processed one at a time. pax "selects" files from * the expansion of each arg into the corresponding file tree (if the arg is a * directory, otherwise the node itself is just passed to pax). The selection * is modified by the -n and -u flags. The user is informed when a specific * file arg does not generate any selected files. -n keeps expanding the file * tree arg until one of its files is selected, then skips to the next file * arg. when the user does not supply the file trees as command line args to * pax, they are read from stdin */ static FTS *ftsp = NULL; /* current FTS handle */ static int ftsopts; /* options to be used on fts_open */ static char *farray[2]; /* array for passing each arg to fts */ static FTREE *fthead = NULL; /* head of linked list of file args */ static FTREE *fttail = NULL; /* tail of linked list of file args */ static FTREE *ftcur = NULL; /* current file arg being processed */ static FTSENT *ftent = NULL; /* current file tree entry */ static int ftree_skip; /* when set skip to next file arg */ static int ftree_arg(void); static char *getpathname(char *, int); /* * ftree_start() * initialise the options passed to fts_open() during this run of pax * options are based on the selection of pax options by the user * fts_start() also calls fts_arg() to open the first valid file arg. We * also attempt to reset directory access times when -t (tflag) is set. * Return: * 0 if there is at least one valid file arg to process, -1 otherwise */ int ftree_start(void) { /* * set up the operation mode of fts, open the first file arg. We must * use FTS_NOCHDIR, as the user may have to open multiple archives and * if fts did a chdir off into the boondocks, we may create an archive * volume in an place where the user did not expect to. */ ftsopts = FTS_NOCHDIR; /* * optional user flags that effect file traversal * -H command line symlink follow only (half follow) * -L follow sylinks (logical) * -P do not follow sylinks (physical). This is the default. * -X do not cross over mount points * -t preserve access times on files read. * -n select only the first member of a file tree when a match is found * -d do not extract subtrees rooted at a directory arg. */ if (Lflag) ftsopts |= FTS_LOGICAL; else ftsopts |= FTS_PHYSICAL; if (Hflag) ftsopts |= FTS_COMFOLLOW; if (Xflag) ftsopts |= FTS_XDEV; if ((fthead == NULL) && ((farray[0] = malloc(PAXPATHLEN+2)) == NULL)) { paxwarn(1, "%s for %s", "Out of memory", "file name buffer"); return (-1); } if (ftree_arg() < 0) return(-1); if (tflag && (atdir_start() < 0)) return(-1); return(0); } /* * ftree_add() * add the arg to the linked list of files to process. Each will be * processed by fts one at a time * Return: * 0 if added to the linked list, -1 if failed */ int ftree_add(char *str, int chflg) { FTREE *ft; int len; /* * simple check for bad args */ if ((str == NULL) || (*str == '\0')) { paxwarn(0, "Invalid file name argument"); return(-1); } /* * allocate FTREE node and add to the end of the linked list (args are * processed in the same order they were passed to pax). Get rid of any * trailing / the user may pass us. (watch out for / by itself). */ if ((ft = malloc(sizeof(FTREE))) == NULL) { paxwarn(0, "%s for %s", "Out of memory", "filename"); return (-1); } if (((len = strlen(str) - 1) > 0) && (str[len] == '/')) str[len] = '\0'; ft->fname = str; ft->refcnt = 0; ft->newercnt = 0; ft->chflg = chflg; ft->fow = NULL; if (fthead == NULL) { fttail = fthead = ft; return(0); } fttail->fow = ft; fttail = ft; return(0); } /* * ftree_sel() * this entry has been selected by pax. bump up reference count and handle * -n and -d processing. */ void ftree_sel(ARCHD *arcn) { /* * set reference bit for this pattern. This linked list is only used * when file trees are supplied pax as args. The list is not used when * the trees are read from stdin. */ if (ftcur != NULL) ftcur->refcnt = 1; /* * if -n we are done with this arg, force a skip to the next arg when * pax asks for the next file in next_file(). * if -d we tell fts only to match the directory (if the arg is a dir) * and not the entire file tree rooted at that point. */ if (nflag) ftree_skip = 1; if (!dflag || (arcn->type != PAX_DIR)) return; if (ftent != NULL) (void)fts_set(ftsp, ftent, FTS_SKIP); } /* * ftree_skipped_newer() * file has been skipped because a newer file exists and -u/-D given */ void ftree_skipped_newer(void) { /* skipped due to -u/-D, mark accordingly */ if (ftcur != NULL) ftcur->newercnt = 1; } /* * ftree_chk() * called at end on pax execution. Prints all those file args that did not * have a selected member (reference count still 0) */ void ftree_chk(void) { FTREE *ft; int wban = 0; /* * make sure all dir access times were reset. */ if (tflag) atdir_end(); /* * walk down list and check reference count. Print out those members * that never had a match */ for (ft = fthead; ft != NULL; ft = ft->fow) { if ((ft->refcnt > 0) || ft->newercnt > 0 || ft->chflg) continue; if (wban == 0) { paxwarn(1,"WARNING! These file names were not selected:"); ++wban; } (void)fprintf(stderr, "%s\n", ft->fname); } } /* * ftree_arg() * Get the next file arg for fts to process. Can be from either the linked * list or read from stdin when the user did not them as args to pax. Each * arg is processed until the first successful fts_open(). * Return: * 0 when the next arg is ready to go, -1 if out of file args (or EOF on * stdin). */ static int ftree_arg(void) { /* * close off the current file tree */ if (ftsp != NULL) { (void)fts_close(ftsp); ftsp = NULL; } /* * keep looping until we get a valid file tree to process. Stop when we * reach the end of the list (or get an eof on stdin) */ for (;;) { if (fthead == NULL) { /* * the user didn't supply any args, get the file trees * to process from stdin; */ if (getpathname(farray[0], PAXPATHLEN+1) == NULL) return(-1); } else { /* * the user supplied the file args as arguments to pax */ if (ftcur == NULL) ftcur = fthead; else if ((ftcur = ftcur->fow) == NULL) return(-1); if (ftcur->chflg) { /* First fchdir() back... */ if (fchdir(cwdfd) < 0) { syswarn(1, errno, "Cannot fchdir to starting directory"); return (-1); } if (chdir(ftcur->fname) < 0) { syswarn(1, errno, "Cannot chdir to %s", ftcur->fname); return (-1); } continue; } else farray[0] = ftcur->fname; } /* * watch it, fts wants the file arg stored in a array of char * ptrs, with the last one a null. we use a two element array * and set farray[0] to point at the buffer with the file name * in it. We cannot pass all the file args to fts at one shot * as we need to keep a handle on which file arg generates what * files (the -n and -d flags need this). If the open is * successful, return a 0. */ if ((ftsp = fts_open(farray, ftsopts, NULL)) != NULL) break; } return(0); } /* * next_file() * supplies the next file to process in the supplied archd structure. * Return: * 0 when contents of arcn have been set with the next file, -1 when done. */ int next_file(ARCHD *arcn) { int cnt; /* * ftree_sel() might have set the ftree_skip flag if the user has the * -n option and a file was selected from this file arg tree. (-n says * only one member is matched for each pattern) ftree_skip being 1 * forces us to go to the next arg now. */ if (ftree_skip) { /* * clear and go to next arg */ ftree_skip = 0; if (ftree_arg() < 0) return(-1); } /* * loop until we get a valid file to process */ for (;;) { if ((ftent = fts_read(ftsp)) == NULL) { if (errno) syswarn(1, errno, "next_file"); /* * out of files in this tree, go to next arg, if none * we are done */ if (ftree_arg() < 0) return(-1); continue; } /* * handle each type of fts_read() flag */ switch (ftent->fts_info) { case FTS_D: case FTS_DEFAULT: case FTS_F: case FTS_SL: case FTS_SLNONE: /* * these are all ok */ break; case FTS_DP: /* * already saw this directory. If the user wants file * access times reset, we use this to restore the * access time for this directory since this is the * last time we will see it in this file subtree * remember to force the time (this is -t on a read * directory, not a created directory). */ if (!tflag) continue; do_atdir(ftent->fts_path, ftent->fts_statp->st_dev, ftent->fts_statp->st_ino); continue; case FTS_DC: /* * fts claims a filesystem cycle */ paxwarn(1, "Filesystem cycle found at %s", ftent->fts_path); continue; case FTS_DNR: syswarn(1, ftent->fts_errno, "Unable to read directory %s", ftent->fts_path); continue; case FTS_ERR: syswarn(1, ftent->fts_errno, "Filesystem traversal error"); continue; case FTS_NS: case FTS_NSOK: syswarn(1, ftent->fts_errno, "Unable to access %s", ftent->fts_path); continue; } /* * ok got a file tree node to process. copy info into arcn * structure (initialise as required) */ arcn->skip = 0; arcn->pad = 0; arcn->ln_nlen = 0; arcn->ln_name[0] = '\0'; memcpy(&arcn->sb, ftent->fts_statp, sizeof(arcn->sb)); /* * file type based set up and copy into the arcn struct * SIDE NOTE: * we try to reset the access time on all files and directories * we may read when the -t flag is specified. files are reset * when we close them after copying. we reset the directories * when we are done with their file tree (we also clean up at * end in case we cut short a file tree traversal). However * there is no way to reset access times on symlinks. */ switch (S_IFMT & arcn->sb.st_mode) { case S_IFDIR: arcn->type = PAX_DIR; if (!tflag) break; add_atdir(ftent->fts_path, &arcn->sb); break; case S_IFCHR: arcn->type = PAX_CHR; break; case S_IFBLK: arcn->type = PAX_BLK; break; case S_IFREG: /* * only regular files with have data to store on the * archive. all others will store a zero length skip. * the skip field is used by pax for actual data it has * to read (or skip over). */ arcn->type = PAX_REG; arcn->skip = arcn->sb.st_size; break; case S_IFLNK: arcn->type = PAX_SLK; /* * have to read the symlink path from the file */ if ((cnt = readlink(ftent->fts_path, arcn->ln_name, PAXPATHLEN)) < 0) { syswarn(1, errno, "Unable to read symlink %s", ftent->fts_path); continue; } /* * set link name length, watch out readlink does not * NUL terminate the link path */ arcn->ln_name[cnt] = '\0'; arcn->ln_nlen = cnt; break; case S_IFSOCK: /* * under BSD storing a socket is senseless but we will * let the format specific write function make the * decision of what to do with it. */ arcn->type = PAX_SCK; break; case S_IFIFO: arcn->type = PAX_FIF; break; } break; } /* * copy file name, set file name length */ arcn->nlen = strlcpy(arcn->name, ftent->fts_path, sizeof(arcn->name)); if ((size_t)arcn->nlen >= sizeof(arcn->name)) arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */ arcn->org_name = ftent->fts_path; return(0); } /* * getpathname() * Reads a pathname from stdin, handling NUL- or newline-termination. * Return: * NULL at end of file, otherwise the NUL-terminated buffer. */ static char * getpathname(char *buf, int buflen) { char *bp, *ep; int ch, term; if (zeroflag) { /* * Read a NUL-terminated pathname, being especially * paranoid about proper termination and pathname length. */ for (bp = buf, ep = buf + buflen; bp < ep; bp++) { if ((ch = getchar()) == EOF) { if (bp != buf) paxwarn(1, "Ignoring unterminated " "pathname at EOF"); return(NULL); } if ((*bp = ch) == '\0') return(buf); } /* Too long - skip this path */ *--bp = '\0'; term = '\0'; } else { if (fgets(buf, buflen, stdin) == NULL) return(NULL); if ((bp = strchr(buf, '\n')) != NULL || feof(stdin)) { if (bp != NULL) *bp = '\0'; return(buf); } /* Too long - skip this path */ term = '\n'; } while ((ch = getchar()) != term && ch != EOF) continue; paxwarn(1, "Ignoring too-long pathname: %s", buf); return(NULL); } pax/gen_subs.c010064400000000000000000000233441412316724000105540ustar00/* $OpenBSD: gen_subs.c,v 1.32 2016/08/26 05:06:14 guenther Exp $ */ /* $NetBSD: gen_subs.c,v 1.5 1995/03/21 09:07:26 cgd Exp $ */ /*- * Copyright (c) 2012, 2015, 2016, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #if HAVE_GRP_H #include #endif #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #if HAVE_UTMP_H #include #elif HAVE_UTMPX_H #include #endif #if HAVE_VIS_H #include #endif #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/gen_subs.c,v 1.22 2021/09/23 21:15:49 tg Exp $"); /* * a collection of general purpose subroutines used by pax */ /* * constants used by ls_list() when printing out archive members */ #define MODELEN 20 #define DATELEN 64 #define SIXMONTHS (24 * 60 * 60 * 365 / 2) #define CURFRMT "%b %e %H:%M" #define OLDFRMT "%b %e %Y" #define NAME_WIDTH 8 #define TIMEFMT(t) \ (((t) + SIXMONTHS <= now || (t) > now) ? OLDFRMT : CURFRMT) /* * ls_list() * list the members of an archive in ls format */ void ls_list(ARCHD *arcn, FILE *fp) { struct stat *sbp; char f_mode[MODELEN]; char f_date[DATELEN]; int term; term = zeroflag ? '\0' : '\n'; /* path termination character */ /* * if not verbose, just print the file name */ if (!vflag) { if (zeroflag) (void)fputs(arcn->name, fp); else safe_print(arcn->name, fp); (void)putc(term, fp); (void)fflush(fp); return; } /* * user wants long mode */ sbp = &(arcn->sb); strmode(sbp->st_mode, f_mode); /* * print file mode, link count, uid, gid and time */ if (strftime(f_date, sizeof(f_date), TIMEFMT(sbp->st_mtime), localtime(&(sbp->st_mtime))) == 0) f_date[0] = '\0'; (void)fprintf(fp, "%s%2u %-*.*s %-*.*s ", f_mode, (unsigned int)sbp->st_nlink, NAME_WIDTH, UT_NAMESIZE, name_uid(sbp->st_uid, 1), NAME_WIDTH, UT_NAMESIZE, name_gid(sbp->st_gid, 1)); /* * print device id's for devices, or sizes for other nodes */ if ((arcn->type == PAX_CHR) || (arcn->type == PAX_BLK)) (void)fprintf(fp, "%4lu, %4lu ", (unsigned long)MAJOR(sbp->st_rdev), (unsigned long)MINOR(sbp->st_rdev)); else (void)fprintf(fp, "%9" OT_FMT " ", sbp->st_size); /* * print name and link info for hard and symbolic links */ (void)fputs(f_date, fp); (void)putc(' ', fp); safe_print(arcn->name, fp); if (PAX_IS_HARDLINK(arcn->type)) { fputs(" == ", fp); safe_print(arcn->ln_name, fp); } else if (arcn->type == PAX_SLK) { fputs(" -> ", fp); safe_print(arcn->ln_name, fp); } (void)putc(term, fp); (void)fflush(fp); } /* * tty_ls() * print a short summary of file to tty. */ void ls_tty(ARCHD *arcn) { char f_date[DATELEN]; char f_mode[MODELEN]; /* * convert time to string, and print */ if (strftime(f_date, DATELEN, TIMEFMT(arcn->sb.st_mtime), localtime(&(arcn->sb.st_mtime))) == 0) f_date[0] = '\0'; strmode(arcn->sb.st_mode, f_mode); tty_prnt("%s%s %s\n", f_mode, f_date, arcn->name); } void safe_print(const char *str, FILE *fp) { #if HAVE_VIS_H char visbuf[5]; const char *cp; /* * if printing to a tty, use vis(3) to print special characters. */ if (isatty(fileno(fp))) { for (cp = str; *cp; cp++) { (void)vis(visbuf, cp[0], VIS_CSTYLE, cp[1]); (void)fputs(visbuf, fp); } } else #endif (void)fputs(str, fp); } /* * asc_ul() * convert hex/octal character string into a u_long. We do not have to * check for overflow! (the headers in all supported formats are not large * enough to create an overflow). * NOTE: strings passed to us are NOT TERMINATED. * Return: * unsigned long value */ u_long asc_ul(char *str, int len, int base) { char *stop; u_long tval = 0; stop = str + len; /* * skip over leading blanks and zeros */ while ((str < stop) && ((*str == ' ') || (*str == '0'))) ++str; /* * for each valid digit, shift running value (tval) over to next digit * and add next digit */ if (base == HEX) { while (str < stop) { if ((*str >= '0') && (*str <= '9')) tval = (tval << 4) + (*str++ - '0'); else if ((*str >= 'A') && (*str <= 'F')) tval = (tval << 4) + 10 + (*str++ - 'A'); else if ((*str >= 'a') && (*str <= 'f')) tval = (tval << 4) + 10 + (*str++ - 'a'); else break; } } else { while ((str < stop) && (*str >= '0') && (*str <= '7')) tval = (tval << 3) + (*str++ - '0'); } return(tval); } /* * ul_asc() * convert an unsigned long into an hex/oct ascii string. pads with LEADING * ascii 0's to fill string completely * NOTE: the string created is NOT TERMINATED. */ int ul_asc(u_long val, char *str, int len, int base) { char *pt; u_long digit; /* * WARNING str is not '\0' terminated by this routine */ pt = str + len - 1; /* * do a tailwise conversion (start at right most end of string to place * least significant digit). Keep shifting until conversion value goes * to zero (all digits were converted) */ if (base == HEX) { while (pt >= str) { if ((digit = (val & 0xf)) < 10) *pt-- = '0' + (char)digit; else *pt-- = 'a' + (char)(digit - 10); val >>= 4; if (val == 0) break; } } else { while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); val >>= 3; if (val == 0) break; } } /* * pad with leading ascii ZEROS. We return -1 if we ran out of space. */ while (pt >= str) *pt-- = '0'; if (val != 0) return(-1); return(0); } /* * asc_ull() * Convert hex/octal character string into a unsigned long long. * We do not have to check for overflow! (The headers in all * supported formats are not large enough to create an overflow). * NOTE: strings passed to us are NOT TERMINATED. * Return: * unsigned long long value */ unsigned long long asc_ull(char *str, int len, int base) { char *stop; unsigned long long tval = 0; stop = str + len; /* * skip over leading blanks and zeros */ while ((str < stop) && ((*str == ' ') || (*str == '0'))) ++str; /* * for each valid digit, shift running value (tval) over to next digit * and add next digit */ if (base == HEX) { while (str < stop) { if ((*str >= '0') && (*str <= '9')) tval = (tval << 4) + (*str++ - '0'); else if ((*str >= 'A') && (*str <= 'F')) tval = (tval << 4) + 10 + (*str++ - 'A'); else if ((*str >= 'a') && (*str <= 'f')) tval = (tval << 4) + 10 + (*str++ - 'a'); else break; } } else { while ((str < stop) && (*str >= '0') && (*str <= '7')) tval = (tval << 3) + (*str++ - '0'); } return(tval); } /* * ull_asc() * Convert an unsigned long long into a hex/oct ascii string. * Pads with LEADING ascii 0's to fill string completely * NOTE: the string created is NOT TERMINATED. */ int ull_asc(unsigned long long val, char *str, int len, int base) { char *pt; unsigned long long digit; /* * WARNING str is not '\0' terminated by this routine */ pt = str + len - 1; /* * do a tailwise conversion (start at right most end of string to place * least significant digit). Keep shifting until conversion value goes * to zero (all digits were converted) */ if (base == HEX) { while (pt >= str) { if ((digit = (val & 0xf)) < 10) *pt-- = '0' + (char)digit; else *pt-- = 'a' + (char)(digit - 10); val >>= 4; if (val == 0) break; } } else { while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); val >>= 3; if (val == 0) break; } } /* * pad with leading ascii ZEROS. We return -1 if we ran out of space. */ while (pt >= str) *pt-- = '0'; if (val != 0) return(-1); return(0); } /* * Copy at max min(bufz, fieldsz) chars from field to buf, stopping * at the first NUL char. NUL terminate buf if there is room left. */ size_t fieldcpy(char *buf, size_t bufsz, const char *field, size_t fieldsz) { char *p = buf; const char *q = field; size_t i = 0; if (fieldsz > bufsz) fieldsz = bufsz; while (i < fieldsz && *q != '\0') { *p++ = *q++; i++; } if (i < bufsz) *p = '\0'; return(i); } pax/getoldopt.c010064400000000000000000000032621343011740100107370ustar00/* $OpenBSD: getoldopt.c,v 1.9 2009/10/27 23:59:22 deraadt Exp $ */ /* $NetBSD: getoldopt.c,v 1.3 1995/03/21 09:07:28 cgd Exp $ */ /* * Plug-compatible replacement for getopt() for parsing tar-like * arguments. If the first argument begins with "-", it uses getopt; * otherwise, it uses the old rules used by tar, dump, and ps. * * Written 25 August 1985 by John Gilmore (ihnp4!hoptoad!gnu) and * placed in the Public Domain for your edification and enjoyment, * https://creativecommons.org/publicdomain/zero/1.0/legalcode (CC0) * being an alternative licence, confirmed by eMail on 2019-02-10. */ #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/getoldopt.c,v 1.7 2019/02/10 22:00:06 tg Exp $"); int getoldopt(int argc, char **argv, const char *optstring) { static char *key; /* Points to next keyletter */ static char use_getopt; /* !=0 if argv[1][0] was '-' */ char c; char *place; optarg = NULL; if (key == NULL) { /* First time */ if (argc < 2) return (-1); key = argv[1]; if (*key == '-') use_getopt++; else optind = 2; } if (use_getopt) return (getopt(argc, argv, optstring)); c = *key++; if (c == '\0') { key--; return (-1); } place = strchr(optstring, c); if (place == NULL || c == ':') { fprintf(stderr, "%s: unknown option %c\n", argv[0], c); return ('?'); } place++; if (*place == ':') { if (optind < argc) { optarg = argv[optind]; optind++; } else { fprintf(stderr, "%s: %c argument missing\n", argv[0], c); return ('?'); } } return (c); } pax/options.c010064400000000000000000001314421412641344100104410ustar00/* $OpenBSD: options.c,v 1.102 2018/09/13 12:33:43 millert Exp $ */ /* $NetBSD: options.c,v 1.6 1996/03/26 23:54:18 mrg Exp $ */ /*- * Copyright (c) 2005, 2006, 2007, 2009, 2011, 2012, 2014, 2016, * 2019, 2020 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_SYS_MTIO_H #include #endif #include #include #include #include #include #if HAVE_PATHS_H #include #endif #include #include #include #if HAVE_STRINGS_H #include #endif #include #define EXTERN #include "pax.h" #include "ar.h" #include "cpio.h" #include "tar.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/options.c,v 1.76 2021/10/03 20:48:06 tg Exp $"); #ifndef _PATH_DEFTAPE #define _PATH_DEFTAPE "/dev/rmt0" #endif #define NELEM(a) (sizeof(a) / sizeof((a)[0])) #define SELEM(a) (sizeof((a)[0])) /* * argv[0] names. Used for tar and cpio emulation */ #define NM_TAR "tar" #define NM_CPIO "cpio" #define NM_PAX "pax" /* * Constants used to specify the legal sets of flags in pax. For each major * operation mode of pax, a set of illegal flags is defined. If any one of * those illegal flags are found set, we scream and exit */ /* * flags (one for each option). */ #define AF 0x00000001 #define BF 0x00000002 #define CF 0x00000004 #define DF 0x00000008 #define FF 0x00000010 #define IF 0x00000020 #define KF 0x00000040 #define LF 0x00000080 #define NF 0x00000100 #define OF 0x00000200 #define PF 0x00000400 #define RF 0x00000800 #define SF 0x00001000 #define TF 0x00002000 #define UF 0x00004000 #define VF 0x00008000 #define WF 0x00010000 #define XF 0x00020000 #define CBF 0x00040000 /* nonstandard extension */ #define CDF 0x00080000 /* nonstandard extension */ #define CEF 0x00100000 /* nonstandard extension */ #define CGF 0x00200000 /* nonstandard extension */ #define CHF 0x00400000 /* nonstandard extension */ #define CLF 0x00800000 /* nonstandard extension */ #define CPF 0x01000000 /* nonstandard extension */ #define CTF 0x02000000 /* nonstandard extension */ #define CUF 0x04000000 /* nonstandard extension */ #define CXF 0x08000000 #define CYF 0x10000000 /* nonstandard extension */ #define CZF 0x20000000 /* nonstandard extension */ #define C0F 0x40000000 /* nonstandard extension */ /* * ascii string indexed by bit position above (alter the above and you must * alter this string) used to tell the user what flags caused us to complain */ #define FLGCH "abcdfiklnoprstuvwxBDEGHLPTUXYZ0" /* * legal pax operation bit patterns */ #define ISLIST(x) (((x) & (RF|WF)) == 0) #define ISEXTRACT(x) (((x) & (RF|WF)) == RF) #define ISARCHIVE(x) (((x) & (AF|RF|WF)) == WF) #define ISAPPND(x) (((x) & (AF|RF|WF)) == (AF|WF)) #define ISCOPY(x) (((x) & (RF|WF)) == (RF|WF)) #define ISWRITE(x) (((x) & (RF|WF)) == WF) /* * Illegal option flag subsets based on pax operation */ #define BDEXTR (AF|BF|LF|TF|WF|XF|CBF|CHF|CLF|CPF|CXF) #define BDARCH (CF|KF|LF|NF|PF|RF|CDF|CEF|CYF|CZF) #define BDCOPY (AF|BF|FF|OF|XF|CBF|CEF) #define BDLIST (AF|BF|IF|KF|LF|OF|PF|RF|TF|UF|WF|XF|CBF|CDF|CHF|CLF|CPF|CXF|CYF|CZF) /* * Routines which handle command line options */ static char flgch[] = FLGCH; /* list of all possible flags */ static OPLIST *ophead = NULL; /* head for format specific options -x */ static OPLIST *optail = NULL; /* option tail */ #ifndef SMALL static int no_op(void); static int no_op_i(int); #endif static int mkpath(char *); static void process_M(const char *, void (*)(void)); static void printflg(unsigned int); static int c_frmt(const void *, const void *); static off_t str_offt(char *); static void pax_options(int, char **); static void pax_usage(void) MKSH_A_NORETURN; static void tar_set_action(int); static void tar_options(int, char **); static void tar_usage(void) MKSH_A_NORETURN; static void cpio_set_action(int); static void cpio_options(int, char **); static void cpio_usage(void) MKSH_A_NORETURN; #ifndef SMALL static int compress_id(char *_blk, int _size); static int gzip_id(char *_blk, int _size); static int bzip2_id(char *_blk, int _size); static int xz_id(char *_blk, int _size); #endif /* command to run as gzip */ static const char GZIP_CMD[] = "gzip"; #ifndef SMALL /* command to run as compress */ static const char COMPRESS_CMD[] = "compress"; /* command to run as bzip2 */ static const char BZIP2_CMD[] = "bzip2"; /* command to run as lzma and xz */ static const char XZ_CMD[] = "xz"; /* command used for creating lzma archives */ static const char LZMA_WRCMD[] = "lzma"; /* command to run as lzop */ static const char LZOP_CMD[] = "lzop"; #endif /* used as flag value */ #define COMPRESS_GUESS_CMD ((const void *)&compress_program) /* * Format specific routine table, MUST be in sorted order * and MUST match enum fsub_order in pax.h * (see pax.h for description of each function) * * name, blksz, hdsz, udev, hlk, blkagn, id, st_read, * read, end_read, st_write, write, end_write, trail, * rd_data, wr_data, options, inhead, is_uar */ const FSUB fsub[] = { #ifndef SMALL /* FSUB_AR: UNIX ARCHIVER */ {"ar", 512, sizeof(HD_AR), 0, 0, 0, uar_id, no_op, uar_rd, uar_endrd, uar_stwr, uar_wr, no_op, uar_trail, rd_wrfile, uar_wr_data, bad_opt, 0, 1}, /* FSUB_BCPIO: OLD BINARY CPIO */ {"bcpio", 5120, sizeof(HD_BCPIO), 1, 0, 0, bcpio_id, cpio_strd, bcpio_rd, bcpio_endrd, cpio_stwr, bcpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, #endif /* FSUB_CPIO: OLD OCTAL CHARACTER CPIO */ {"cpio", 5120, sizeof(HD_CPIO), 1, 0, 0, cpio_id, cpio_strd, cpio_rd, cpio_endrd, cpio_stwr, cpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, #ifndef SMALL /* FSUB_DIST: OLD OCTAL CHARACTER CPIO, UID/GID CLEARED (ANONYMISED) */ {"dist", 512, sizeof(HD_CPIO), 1, 0, 0, cpio_id, cpio_strd, cpio_rd, cpio_endrd, dist_stwr, cpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, #endif /* FSUB_SV4CPIO: SVR4 HEX CPIO */ {"sv4cpio", 5120, sizeof(HD_VCPIO), 1, 0, 0, vcpio_id, cpio_strd, vcpio_rd, vcpio_endrd, cpio_stwr, vcpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, /* FSUB_SV4CRC: SVR4 HEX CPIO WITH CRC */ {"sv4crc", 5120, sizeof(HD_VCPIO), 1, 0, 0, crc_id, crc_strd, vcpio_rd, vcpio_endrd, crc_stwr, vcpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, #ifndef SMALL /* FSUB_TAR: OLD TAR */ {"tar", 10240, BLKMULT, 0, 1, BLKMULT, tar_id, no_op, tar_rd, tar_endrd, no_op_i, tar_wr, tar_endwr, tar_trail, rd_wrfile, wr_rdfile, tar_opt, 0, 0}, #endif /* FSUB_USTAR: POSIX USTAR */ {"ustar", 10240, BLKMULT, 0, 1, BLKMULT, ustar_id, ustar_strd, ustar_rd, tar_endrd, ustar_stwr, ustar_wr, tar_endwr, tar_trail, rd_wrfile, wr_rdfile, tar_opt, 0, 0}, #ifndef SMALL /* FSUB_V4NORM: SVR4 HEX CPIO WITH CRC, UID/GID/MTIME CLEARED (NORMALISED) */ {"v4norm", 512, sizeof(HD_VCPIO), 1, 0, 0, crc_id, crc_strd, vcpio_rd, vcpio_endrd, v4norm_stwr, vcpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, /* FSUB_V4ROOT: SVR4 HEX CPIO WITH CRC, UID/GID CLEARED (ANONYMISED) */ {"v4root", 512, sizeof(HD_VCPIO), 1, 0, 0, crc_id, crc_strd, vcpio_rd, vcpio_endrd, v4root_stwr, vcpio_wr, cpio_endwr, cpio_trail, rd_wrfile, wr_rdfile, bad_opt, 1, 0}, /* FSUBFAIL_Z: compress, to detect failure to use -Z */ {NULL, 0, 4, 0, 0, 0, compress_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, /* FSUBFAIL_XZ: xz, to detect failure to decompress it */ {NULL, 0, 6, 0, 0, 0, xz_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, /* FSUBFAIL_BZ2: bzip2, to detect failure to use -j */ {NULL, 0, 4, 0, 0, 0, bzip2_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, /* FSUBFAIL_GZ: gzip, to detect failure to use -z */ {NULL, 0, 4, 0, 0, 0, gzip_id, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 0, 0}, #endif }; /* * ford is the archive search order used by get_arc() to determine what kind * of archive we are dealing with. This helps to properly id archive formats * some formats may be subsets of others.... */ const unsigned char ford[] = { FSUB_USTAR, #ifndef SMALL FSUB_TAR, FSUBFAIL_GZ, FSUBFAIL_BZ2, FSUBFAIL_XZ, FSUBFAIL_Z, #endif FSUB_SV4CRC, FSUB_SV4CPIO, FSUB_CPIO, #ifndef SMALL FSUB_BCPIO, #endif FSUB_MAX }; /* normalise archives */ int anonarch = 0; /* extract to standard output */ char to_stdout = 0; /* * Do we have -C anywhere and what is it? */ char havechd = 0; char *chdname = NULL; /* * options() * figure out if we are pax, tar or cpio. Call the appropriate options * parser */ void options(int argc, char **argv) { size_t n; /* * Are we acting like pax, tar or cpio (based on argv[0]) */ n = strlen(argv[0]); if (n >= 3 && !strcmp(argv[0] + (n - 3), NM_TAR)) { argv0 = NM_TAR; op_mode = OP_TAR; tar_options(argc, argv); } else if (n >= 4 && !strcmp(argv[0] + (n - 4), NM_CPIO)) { argv0 = NM_CPIO; op_mode = OP_CPIO; cpio_options(argc, argv); } else { /* assume pax as the default */ argv0 = NM_PAX; op_mode = OP_PAX; pax_options(argc, argv); } } static int gather_format(FSUB *fp, const char *name, int flag) { size_t i; if ((frmt = (const FSUB *)bsearch((const void *)fp, (const void *)fsub, NELEM(fsub), SELEM(fsub), c_frmt)) != NULL) return (0); paxwarn(1, "Unknown -%c format: %s", flag, optarg); fprintf(stderr, "%s: Known -%c formats are:", name, flag); for (i = 0; i < NELEM(fsub); ++i) if (fsub[i].name != NULL) fprintf(stderr, " %s", fsub[i].name); fputs("\n\n", stderr); return (1); } /* * pax_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void pax_options(int argc, char **argv) { int c; unsigned int flg = 0; unsigned int bflg = 0; const char *errstr; char *pt; FSUB tmp; /* * process option flags */ while ((c = getopt(argc, argv, "0aB:b:cDdE:f:G:HiJjkLlM:nOo:Pp:rs:T:tU:uvwXx:YZz")) != -1) { switch (c) { case '0': /* * Use \0 as pathname terminator. * (For use with the -print0 option of find(1).) */ zeroflag = 1; flg |= C0F; break; case 'a': /* * append */ flg |= AF; break; case 'B': /* * non-standard option on number of bytes written on a * single archive volume. */ if ((wrlimit = str_offt(optarg)) <= 0) { paxwarn(1, "Invalid write limit %s", optarg); pax_usage(); } if (wrlimit % BLKMULT) { paxwarn(1, "Write limit is not a %d byte multiple", BLKMULT); pax_usage(); } flg |= CBF; break; case 'b': /* * specify blocksize */ flg |= BF; if ((wrblksz = (int)str_offt(optarg)) <= 0) { paxwarn(1, "Invalid block size %s", optarg); pax_usage(); } break; case 'c': /* * inverse match on patterns */ cflag = 1; flg |= CF; break; case 'D': /* * On extraction check file inode change time before the * modification of the file name. Non standard option. */ Dflag = 1; flg |= CDF; break; case 'd': /* * match only dir on extract, not the subtree at dir */ dflag = 1; flg |= DF; break; case 'E': /* * non-standard limit on read faults * 0 indicates stop after first error, values * indicate a limit */ flg |= CEF; maxflt = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { paxwarn(1, "Error count value: %s", errstr); pax_usage(); } break; case 'f': /* * filename where the archive is stored */ arcname = optarg; flg |= FF; break; case 'G': /* * non-standard option for selecting files within an * archive by group (gid or name) */ if (grp_add(optarg) < 0) { pax_usage(); break; } flg |= CGF; break; case 'H': /* * follow command line symlinks only */ Hflag = 1; flg |= CHF; break; case 'i': /* * interactive file rename */ iflag = 1; flg |= IF; break; #ifndef SMALL case 'J': /* * use xz (non-standard option) */ compress_program = XZ_CMD; break; case 'j': /* * use bzip2 (non-standard option) */ compress_program = BZIP2_CMD; break; #endif case 'k': /* * do not clobber files that exist */ kflag = 1; flg |= KF; break; case 'L': /* * follow symlinks */ Lflag = 1; flg |= CLF; break; case 'l': /* * try to link src to dest with copy (-rw) */ lflag = 1; flg |= LF; break; case 'M': /* * MirOS extension: archive normaliser */ process_M(optarg, pax_usage); break; case 'n': /* * select first match for a pattern only */ nflag = 1; flg |= NF; break; case 'O': /* * Force one volume. Non standard option. */ force_one_volume = 1; break; case 'o': /* * pass format specific options */ flg |= OF; if (opt_add(optarg) < 0) pax_usage(); break; case 'P': /* * do NOT follow symlinks (default) */ Lflag = 0; flg |= CPF; break; case 'p': /* * specify file characteristic options */ for (pt = optarg; *pt != '\0'; ++pt) { switch (*pt) { case 'a': /* * do not preserve access time */ patime = 0; break; case 'e': /* * preserve user id, group id, file * mode, access/modification times */ pids = 1; pmode = 1; patime = 1; pmtime = 1; break; case 'm': /* * do not preserve modification time */ pmtime = 0; break; case 'o': /* * preserve uid/gid */ pids = 1; break; case 'p': /* * preserve file mode bits */ pmode = 1; break; default: paxwarn(1, "Invalid -p string: %c", *pt); pax_usage(); break; } } flg |= PF; break; case 'r': /* * read the archive */ flg |= RF; break; case 's': /* * file name substitution name pattern */ if (rep_add(optarg) < 0) { pax_usage(); break; } flg |= SF; break; case 'T': /* * non-standard option for selecting files within an * archive by modification time range (lower,upper) */ if (trng_add(optarg) < 0) { pax_usage(); break; } flg |= CTF; break; case 't': /* * preserve access time on filesystem nodes we read */ tflag = 1; flg |= TF; break; case 'U': /* * non-standard option for selecting files within an * archive by user (uid or name) */ if (usr_add(optarg) < 0) { pax_usage(); break; } flg |= CUF; break; case 'u': /* * ignore those older files */ uflag = 1; flg |= UF; break; case 'v': /* * verbose operation mode */ ++vflag; flg |= VF; break; case 'w': /* * write an archive */ flg |= WF; break; case 'X': /* * do not pass over mount points in the filesystem */ Xflag = 1; flg |= CXF; break; case 'x': /* * specify an archive format on write */ tmp.name = optarg; if (gather_format(&tmp, "pax", 'x')) pax_usage(); flg |= XF; break; case 'Y': /* * On extraction check file inode change time after the * modification of the file name. Non standard option. */ Yflag = 1; flg |= CYF; break; case 'Z': /* * On extraction check modification time after the * modification of the file name. Non standard option. */ Zflag = 1; flg |= CZF; break; case 'z': /* * use gzip (non-standard option) */ compress_program = GZIP_CMD; break; default: pax_usage(); break; } } /* * figure out the operation mode of pax read,write,extract,copy,append * or list. check that we have not been given a bogus set of flags * for the operation mode. */ if (ISLIST(flg)) { act = LIST; listf = stdout; bflg = flg & BDLIST; } else if (ISEXTRACT(flg)) { act = EXTRACT; bflg = flg & BDEXTR; } else if (ISARCHIVE(flg)) { act = ARCHIVE; bflg = flg & BDARCH; } else if (ISAPPND(flg)) { act = APPND; bflg = flg & BDARCH; } else if (ISCOPY(flg)) { act = COPY; bflg = flg & BDCOPY; } else pax_usage(); if (bflg) { printflg(flg); pax_usage(); } /* * if we are writing (ARCHIVE) we use the default format if the user * did not specify a format. when we write during an APPEND, we will * adopt the format of the existing archive if none was supplied. */ if (!(flg & XF) && (act == ARCHIVE)) frmt = &(fsub[FSUB_USTAR]); /* * process the args as they are interpreted by the operation mode */ switch (act) { case LIST: case EXTRACT: for (; optind < argc; optind++) if (pat_add(argv[optind], NULL) < 0) pax_usage(); break; case COPY: if (optind >= argc) { paxwarn(0, "Destination directory was not supplied"); pax_usage(); } --argc; dirptr = argv[argc]; /* FALLTHROUGH */ case ARCHIVE: case APPND: for (; optind < argc; optind++) if (ftree_add(argv[optind], 0) < 0) pax_usage(); /* * no read errors allowed on updates/append operation! */ maxflt = 0; break; } } /* * tar_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void tar_set_action(int op) { if (act != ERROR && act != op) tar_usage(); act = op; } static void tar_options(int argc, char **argv) { int c; int Oflag = FSUB_USTAR; int nincfiles = 0; int incfiles_max = 0; struct incfile { char *file; char *dir; }; struct incfile *incfiles = NULL; FSUB tmp; char *bp, *cp; /* * Set default values. */ rmleadslash = 1; /* * process option flags */ while ((c = getoldopt(argc, argv, "014578AaBb:C:cD:ef:HhI:JjLM:mNOoPpqRrSs:tuvwXxZz")) != -1) { switch (c) { case '0': arcname = DEV_0; break; case '1': arcname = DEV_1; break; case '4': arcname = DEV_4; break; case '5': arcname = DEV_5; break; case '7': arcname = DEV_7; break; case '8': arcname = DEV_8; break; #ifndef SMALL case 'A': Oflag = FSUB_AR; mircpio_deprecated("-A flag", "-D ar"); break; #endif case 'a': /* * use compression dependent on arcname * (non-standard option, gtar extension) */ compress_program = COMPRESS_GUESS_CMD; break; case 'B': /* * Nothing to do here, this is pax default */ break; case 'b': /* * specify blocksize in 512-byte blocks */ if ((wrblksz = (int)str_offt(optarg)) <= 0) { paxwarn(1, "Invalid block size %s", optarg); tar_usage(); } /* XXX - check for integer overflow */ wrblksz *= 512; break; case 'C': havechd++; chdname = optarg; break; case 'c': /* * create an archive */ tar_set_action(ARCHIVE); break; case 'D': /* * specify archive format and options */ if (!(bp = strdup(optarg))) { paxwarn(0, "out of memory"); exit(1); } if ((cp = strchr(bp, ','))) *cp++ = '\0'; tmp.name = bp; if (gather_format(&tmp, "tar", 'D')) tar_usage(); if (cp && opt_add(cp) < 0) tar_usage(); free(bp); Oflag = -666; /* "frmt already set" */ break; case 'e': /* * stop after first error */ maxflt = 0; break; case 'f': /* * filename where the archive is stored */ arcname = optarg; break; case 'H': /* * follow command line symlinks only */ Hflag = 1; break; case 'h': /* * follow symlinks */ Lflag = 1; break; case 'I': if (++nincfiles > incfiles_max) { size_t n = nincfiles + 3; struct incfile *p; p = reallocarray(incfiles, n, sizeof(*incfiles)); if (p == NULL) { paxwarn(0, "Unable to allocate space " "for option list"); exit(1); } incfiles = p; incfiles_max = n; } incfiles[nincfiles - 1].file = optarg; incfiles[nincfiles - 1].dir = chdname; break; #ifndef SMALL case 'J': /* * use xz (non-standard option) */ compress_program = XZ_CMD; break; case 'j': /* * use bzip2 (non-standard option) */ compress_program = BZIP2_CMD; break; #endif case 'L': /* * follow symlinks */ Lflag = 1; break; case 'M': /* * MirOS extension: archive normaliser */ process_M(optarg, tar_usage); break; case 'm': /* * do not preserve modification time */ pmtime = 0; break; #ifndef SMALL case 'N': /* numeric uid and gid only */ anonarch |= ANON_NUMID; mircpio_deprecated("-N flag", "-M numid"); break; #endif case 'O': #ifndef SMALL Oflag = FSUB_TAR; #else Oflag = FSUB_MAX; #endif to_stdout = 2; break; #ifndef SMALL case 'o': Oflag = FSUB_TAR; tar_nodir = 1; break; #endif case 'P': /* * do not remove leading '/' from pathnames */ rmleadslash = 0; break; case 'p': /* * preserve uid/gid and file mode, regardless of umask */ pmode = 1; pids = 1; break; case 'q': /* * select first match for a pattern only */ nflag = 1; break; #ifndef SMALL case 'R': Oflag = FSUB_SV4CPIO; anonarch |= ANON_INODES | ANON_HARDLINKS; mircpio_deprecated("-R flag", "-D sv4cpio -M set"); break; #endif case 'r': case 'u': /* * append to the archive */ tar_set_action(APPND); break; #ifndef SMALL case 'S': Oflag = FSUB_SV4CRC; anonarch |= ANON_INODES | ANON_HARDLINKS; mircpio_deprecated("-S flag", "-D sv4crc -M set"); break; #endif case 's': /* * file name substitution name pattern */ if (rep_add(optarg) < 0) { tar_usage(); break; } break; case 't': /* * list contents of the tape */ tar_set_action(LIST); break; /* u: see r */ case 'v': /* * verbose operation mode */ vflag++; break; case 'w': /* * interactive file rename */ iflag = 1; break; case 'X': /* * do not pass over mount points in the filesystem */ Xflag = 1; break; case 'x': /* * extract an archive, preserving mode, * and mtime if possible. */ tar_set_action(EXTRACT); pmtime = 1; break; #ifndef SMALL case 'Z': /* * use compress */ compress_program = COMPRESS_CMD; break; #endif case 'z': /* * use gzip (non-standard option) */ compress_program = GZIP_CMD; break; default: tar_usage(); break; } } argc -= optind; argv += optind; /* tar requires an action. */ if (act == ERROR) tar_usage(); if ((arcname == NULL) || (*arcname == '\0')) { arcname = getenv("TAPE"); if ((arcname == NULL) || (*arcname == '\0')) arcname = _PATH_DEFTAPE; } if ((arcname[0] == '-') && (arcname[1] == '\0')) arcname = NULL; /* * Traditional tar behaviour: list-like output goes to stdout unless * writing the archive there. (pax uses stderr unless in list mode) */ if (act == LIST || act == EXTRACT || arcname != NULL) listf = stdout; /* Traditional tar behaviour (pax wants to read file list from stdin) */ if ((act == ARCHIVE || act == APPND) && argc == 0 && nincfiles == 0) exit(0); /* * process the args as they are interpreted by the operation mode */ switch (act) { case EXTRACT: if (to_stdout == 2) to_stdout = 1; /* FALLTHROUGH */ case LIST: default: { int sawpat = 0; char *file, *dir = NULL; while (nincfiles || *argv != NULL) { /* * If we queued up any include files, * pull them in now. Otherwise, check * for -I and -C positional flags. * Anything else must be a file to * extract. */ if (nincfiles) { file = incfiles->file; dir = incfiles->dir; incfiles++; nincfiles--; } else if (strcmp(*argv, "-I") == 0) { if (*++argv == NULL) break; file = *argv++; dir = chdname; } else file = NULL; if (file != NULL) { int fd; char *str; if (strcmp(file, "-") == 0) fd = STDIN_FILENO; else if ((fd = binopen2(0, file, O_RDONLY)) == -1) { syswarn(1, errno, "Unable to open %s", file); tar_usage(); } while ((str = fdgetline(fd)) != NULL) { if (pat_add(str, dir) < 0) tar_usage(); sawpat = 1; } if (fd != STDIN_FILENO) close(fd); if (fdgetline_err) { paxwarn(1, "Unable to read from %s", strcmp(file, "-") ? file : "stdin"); tar_usage(); } } else if (strcmp(*argv, "-C") == 0) { if (*++argv == NULL) break; chdname = *argv++; havechd++; } else if (pat_add(*argv++, chdname) < 0) tar_usage(); else sawpat = 1; } /* * if patterns were added, we are doing chdir() * on a file-by-file basis, else, just one * global chdir (if any) after opening input. */ if (sawpat > 0) chdname = NULL; } break; case ARCHIVE: case APPND: if (Oflag == FSUB_MAX) tar_usage(); else if (Oflag != -666) frmt = &(fsub[Oflag]); if (chdname != NULL) { /* initial chdir() */ if (ftree_add(chdname, 1) < 0) tar_usage(); } while (nincfiles || *argv != NULL) { char *file, *dir = NULL; /* * If we queued up any include files, pull them in * now. Otherwise, check for -I and -C positional * flags. Anything else must be a file to include * in the archive. */ if (nincfiles) { file = incfiles->file; dir = incfiles->dir; incfiles++; nincfiles--; } else if (strcmp(*argv, "-I") == 0) { if (*++argv == NULL) break; file = *argv++; dir = NULL; } else file = NULL; if (file != NULL) { int fd; char *str; /* Set directory if needed */ if (dir) { if (ftree_add(dir, 1) < 0) tar_usage(); } if (strcmp(file, "-") == 0) fd = STDIN_FILENO; else if ((fd = binopen2(0, file, O_RDONLY)) == -1) { syswarn(1, errno, "Unable to open %s", file); tar_usage(); } while ((str = fdgetline(fd)) != NULL) { if (ftree_add(str, 0) < 0) tar_usage(); } if (fd != STDIN_FILENO) close(fd); if (fdgetline_err) { paxwarn(1, "Unable to read from %s", strcmp(file, "-") ? file : "stdin"); tar_usage(); } } else if (strcmp(*argv, "-C") == 0) { if (*++argv == NULL) break; if (ftree_add(*argv++, 1) < 0) tar_usage(); havechd++; } else if (ftree_add(*argv++, 0) < 0) tar_usage(); } /* * no read errors allowed on updates/append operation! */ maxflt = 0; break; } if (to_stdout != 1) to_stdout = 0; } static int mkpath(char *path) { struct stat sb; char *slash; int done = 0; slash = path; while (!done) { slash += strspn(slash, "/"); slash += strcspn(slash, "/"); done = (*slash == '\0'); *slash = '\0'; if (stat(path, &sb)) { if (errno != ENOENT || mkdir(path, 0777)) { paxwarn(1, "%s", path); return (-1); } } else if (!S_ISDIR(sb.st_mode)) { syswarn(1, ENOTDIR, "%s", path); return (-1); } if (!done) *slash = '/'; } return (0); } /* * cpio_options() * look at the user specified flags. set globals as required and check if * the user specified a legal set of flags. If not, complain and exit */ static void cpio_set_action(int op) { if ((act == APPND && op == ARCHIVE) || (act == ARCHIVE && op == APPND)) act = APPND; else if ((act == LIST && op == EXTRACT) || (act == EXTRACT && op == LIST)) act = LIST; else if (act != ERROR && act != op) cpio_usage(); else act = op; } static void cpio_options(int argc, char **argv) { const char *errstr; int c, list_only = 0; char *str; int fd; const char *optstr; FSUB tmp; kflag = 1; pids = 1; pmode = 1; pmtime = 0; arcname = NULL; dflag = 1; nodirs = 1; optstr = "iop"; opterr = 0; while ((c = getopt(argc, argv, optstr)) != -1) { switch (c) { case '0': /* * Use \0 as pathname terminator. * (For use with the -print0 option of find(1).) */ zeroflag = 1; break; #ifndef SMALL case '6': /* * process Version 6 cpio format */ frmt = &(fsub[FSUB_BCPIO]); break; #endif case 'A': /* * append mode */ cpio_set_action(APPND); break; case 'a': /* * preserve access time on files read */ tflag = 1; break; case 'B': /* * Use 5120 byte block size */ wrblksz = 5120; break; case 'b': /* * swap bytes and half-words when reading data */ break; case 'C': /* * set block size in bytes */ wrblksz = strtonum(optarg, 0, INT_MAX, &errstr); if (errstr) { paxwarn(1, "Invalid block size %s: %s", optarg, errstr); pax_usage(); } break; case 'c': /* * ASCII cpio header */ frmt = &(fsub[FSUB_CPIO]); break; case 'd': /* * create directories as needed */ nodirs = 0; break; case 'E': /* * file with patterns to extract or list */ if ((fd = binopen2(0, optarg, O_RDONLY)) == -1) { syswarn(1, errno, "Unable to open %s", optarg); cpio_usage(); } while ((str = fdgetline(fd)) != NULL) { pat_add(str, NULL); } close(fd); if (fdgetline_err) { paxwarn(1, "Unable to read from %s", optarg); cpio_usage(); } break; case 'F': case 'I': case 'O': /* * filename where the archive is stored */ if ((optarg[0] == '-') && (optarg[1]== '\0')) { /* * treat a - as stdin */ arcname = NULL; break; } arcname = optarg; break; case 'f': /* * invert meaning of pattern list */ cflag = 1; break; case 'H': /* * specify an archive format on write */ if (!strcmp(optarg, "bin")) { tmp.name = "bcpio"; } else if (!strcmp(optarg, "crc")) { tmp.name = "sv4crc"; } else if (!strcmp(optarg, "newc")) { tmp.name = "sv4cpio"; } else if (!strcmp(optarg, "odc")) { tmp.name = "cpio"; } else { tmp.name = optarg; } if (gather_format(&tmp, "cpio", 'H')) cpio_usage(); break; /* I: see F */ case 'i': /* * restore an archive */ cpio_set_action(EXTRACT); break; #ifndef SMALL case 'J': /* * use xz (non-standard option) */ compress_program = XZ_CMD; break; case 'j': /* * use bzip2 (non-standard option) */ compress_program = BZIP2_CMD; break; #endif case 'k': break; case 'L': /* * follow symbolic links */ Lflag = 1; break; case 'l': /* * use links instead of copies when possible */ lflag = 1; break; case 'M': /* * MirOS extension: archive normaliser */ process_M(optarg, cpio_usage); break; case 'm': /* * preserve modification time */ pmtime = 1; break; /* O: see F */ case 'o': /* * create an archive */ cpio_set_action(ARCHIVE); frmt = &(fsub[FSUB_SV4CRC]); break; case 'p': /* * copy-pass mode */ cpio_set_action(COPY); break; case 'r': /* * interactively rename files */ iflag = 1; break; case 'S': /* * swap halfwords after reading data */ break; case 's': /* * swap bytes after reading data */ break; case 't': /* * list contents of archive */ list_only = 1; break; case 'u': /* * replace newer files */ kflag = 0; break; case 'V': /* * print a dot for each file processed */ Vflag++; break; case 'v': /* * verbose operation mode */ ++vflag; break; #ifndef SMALL case 'Z': /* * use compress (non-standard option) */ compress_program = COMPRESS_CMD; break; #endif case 'z': /* * use gzip (non-standard option) */ compress_program = GZIP_CMD; break; case '?': default: if (opterr == 0) { paxwarn(1, "need -i or -o or -p option first"); } cpio_usage(); break; } if (opterr == 0) { optstr = "06AaBbC:cdE:F:fH:I:iJjkLlM:mO:oprSstuVvZz"; opterr = 1; } } argc -= optind; argv += optind; /* * process the args as they are interpreted by the operation mode */ switch (act) { case EXTRACT: if (list_only) { act = LIST; /* * cpio is like pax: list to stderr * unless in list mode */ listf = stdout; } while (*argv != NULL) if (pat_add(*argv++, NULL) < 0) cpio_usage(); break; case COPY: if (*argv == NULL) { paxwarn(0, "Destination directory was not supplied"); cpio_usage(); } dirptr = *argv; if (mkpath(dirptr) < 0) cpio_usage(); --argc; ++argv; /* FALLTHROUGH */ case ARCHIVE: case APPND: if (*argv != NULL) cpio_usage(); /* * no read errors allowed on updates/append operation! */ maxflt = 0; while ((str = fdgetline(STDIN_FILENO)) != NULL) { ftree_add(str, 0); } if (fdgetline_err) { paxwarn(1, "Unable to read from %s", "stdin"); cpio_usage(); } break; default: cpio_usage(); break; } } /* * printflg() * print out those invalid flag sets found to the user */ static void printflg(unsigned int flg) { int nxt; int pos = 0; (void)fprintf(stderr,"%s: Invalid combination of options:", argv0); while ((nxt = ffs(flg)) != 0) { flg >>= nxt; pos += nxt; (void)fprintf(stderr, " -%c", flgch[pos-1]); } (void)putc('\n', stderr); } /* * c_frmt() * comparison routine used by bsearch to find the format specified * by the user */ static int c_frmt(const void *a, const void *b) { if (!((const FSUB *)b)->name) return (-1); return(strcmp(((const FSUB *)a)->name, ((const FSUB *)b)->name)); } /* * opt_next() * called by format specific options routines to get each format specific * flag and value specified with -o * Return: * pointer to next OPLIST entry or NULL (end of list). */ OPLIST * opt_next(void) { OPLIST *opt; if ((opt = ophead) != NULL) ophead = ophead->fow; return(opt); } /* * bad_opt() * generic routine used to complain about a format specific options * when the format does not support options. */ int bad_opt(void) { OPLIST *opt; if (ophead == NULL) return(0); /* * print all we were given */ paxwarn(1,"These format options are not supported"); while ((opt = opt_next()) != NULL) (void)fprintf(stderr, "\t%s = %s\n", opt->name, opt->value); pax_usage(); return(0); } /* * opt_add() * breaks the value supplied to -o into a option name and value. * options are given to -o in the form -o name=value,name=value * or to -D in the form format,name=value[,name=value ...] * multiple -o may be specified. * Return: * 0 if format in name=value format, -1 if -o is passed junk */ int opt_add(const char *str) { OPLIST *opt; char *frpt; char *pt; char *endpt; char *dstr; if ((str == NULL) || (*str == '\0')) { paxwarn(0, "Invalid option name"); return(-1); } if ((dstr = strdup(str)) == NULL) { paxwarn(0, "Unable to allocate space for option list"); return(-1); } frpt = endpt = dstr; /* * break into name and values pieces and stuff each one into a * OPLIST structure. When we know the format, the format specific * option function will go through this list */ while ((frpt != NULL) && (*frpt != '\0')) { if ((endpt = strchr(frpt, ',')) != NULL) *endpt = '\0'; if ((pt = strchr(frpt, '=')) == NULL) { paxwarn(0, "Invalid options format"); free(dstr); return(-1); } if ((opt = malloc(sizeof(OPLIST))) == NULL) { paxwarn(0, "Unable to allocate space for option list"); free(dstr); return(-1); } /* parts of string going onto the OPLIST */ dstr = NULL; *pt++ = '\0'; opt->name = frpt; opt->value = pt; opt->fow = NULL; if (endpt != NULL) frpt = endpt + 1; else frpt = NULL; if (ophead == NULL) { optail = ophead = opt; continue; } optail->fow = opt; optail = opt; } free(dstr); return(0); } /* * str_offt() * Convert an expression of the following forms to an off_t > 0. * 1) A positive decimal number. * 2) A positive decimal number followed by a b (mult by 512). * 3) A positive decimal number followed by a k (mult by 1024). * 4) A positive decimal number followed by a m (mult by 512). * 5) A positive decimal number followed by a w (mult by sizeof int) * 6) Two or more positive decimal numbers (with/without k,b or w). * separated by x (also * for backwards compatibility), specifying * the product of the indicated values. * Return: * 0 for an error, a positive value o.w. */ #if HAVE_OFFT_LONG #define OT_MAX LONG_MAX #define strtoot strtol #else #define OT_MAX LLONG_MAX #define strtoot strtoll #endif static off_t str_offt(char *val) { char *expr; off_t num, t; num = strtoot(val, &expr, 0); if ((num == OT_MAX) || (num <= 0) || (expr == val)) return (0); switch (*expr) { case 'b': t = num; num *= 512; if (t > num) return (0); ++expr; break; case 'k': t = num; num *= 1024; if (t > num) return (0); ++expr; break; case 'm': t = num; num *= 1048576; if (t > num) return (0); ++expr; break; case 'w': t = num; num *= sizeof(int); if (t > num) return (0); ++expr; break; } switch (*expr) { case '\0': break; case '*': case 'x': t = num; num *= str_offt(expr + 1); if (t > num) return(0); break; default: return(0); } return (num); } #ifndef SMALL /* * no_op() * for those option functions where the archive format has nothing to do. * Return: * 0 */ static int no_op(void) { return (0); } static int no_op_i(int is_app MKSH_A_UNUSED) { return (0); } #endif /* * pax_usage() * print the usage summary to the user */ void pax_usage(void) { (void)fputs( #ifndef SMALL "paxmirabilis " MIRCPIO_VERSION "\n" #endif "usage: pax [-0cdJjnOvz] [-E limit] [-f archive] [-G group] [-s replstr]\n" " [-T range] [-U user] [pattern ...]\n" " pax -r [-0cDdiJjknOuvYZz] [-E limit] [-f archive] [-G group] [-M flag]\n" " [-o options] [-p string] [-s replstr] [-T range] [-U user]\n" " [pattern ...]\n" " pax -w [-0adHiJjLOPtuvXz] [-B bytes] [-b blocksize] [-f archive]\n" " [-G group] [-M flag] [-o options] [-s replstr] [-T range]\n" " [-U user] [-x format] [file ...]\n" " pax -rw [-0DdHikLlnOPtuvXYZ] [-G group] [-p string] [-s replstr]\n" " [-T range] [-U user] [file ...] directory\n", stderr); exit(1); } /* * tar_usage() * print the usage summary to the user */ void tar_usage(void) { (void)fputs( #ifndef SMALL "paxmirabilis " MIRCPIO_VERSION "\n" #endif "usage: tar {crtux}[014578AabefHhJjLmNOoPpqRSsvwXZz]\n" " [blocking-factor | archive | replstr] [-C directory] [-I file]\n" " [file ...]\n" " tar {-crtux} [-014578AaeHhJjLmNOoPpqRSvwXZz] [-b blocking-factor]\n" " [-C directory] [-D format-options] [-f archive] [-I file]\n" " [-M flag] [-s replstr] [file ...]\n", stderr); exit(1); } /* * cpio_usage() * print the usage summary to the user */ void cpio_usage(void) { (void)fputs( #ifndef SMALL "paxmirabilis " MIRCPIO_VERSION "\n" #endif "usage: cpio -o [-0AaBcJjLVvZz] [-C bytes] [-F archive] [-H format]\n" " [-M flag] [-O archive] archive]\n" " cpio -i [-06BbcdfJjmrSstuVvZz] [-C bytes] [-E file] [-F archive]\n" " [-H format] [-I archive] [-M flag] [pattern ...] [name); } } static void process_M(const char *arg, void (*call_usage)(void)) { int j, k = 0; if ((arg[0] >= '0') && (arg[0] <= '9')) { #if HAVE_STRTONUM const char *s; int64_t i = strtonum(arg, 0, ANON_MAXVAL, &s); if (s) errx(1, "%s M value: %s", s, arg); #else char *ep; long long i = strtoll(arg, &ep, 0); if ((ep == arg) || (*ep != '\0') || (i < 0) || (i > ANON_MAXVAL)) errx(1, "impossible M value:" " %s", arg); #endif anonarch = i; return; } if (!strncmp(arg, "no-", 3)) { j = 0; arg += 3; } else j = 1; if (!strncmp(arg, "uid", 3) || !strncmp(arg, "gid", 3)) { k = ANON_UIDGID; } else if (!strncmp(arg, "ino", 3)) { k = ANON_INODES; } else if (!strncmp(arg, "mtim", 4)) { k = ANON_MTIME; } else if (!strncmp(arg, "link", 4)) { k = ANON_HARDLINKS; } else if (!strncmp(arg, "norm", 4)) { k = ANON_UIDGID | ANON_INODES | ANON_NUMID | ANON_MTIME | ANON_HARDLINKS; } else if (!strncmp(arg, "root", 4)) { k = ANON_UIDGID | ANON_INODES | ANON_NUMID; } else if (!strncmp(arg, "dist", 4)) { k = ANON_UIDGID | ANON_INODES | ANON_NUMID | ANON_HARDLINKS; } else if (!strncmp(arg, "set", 3)) { k = ANON_INODES | ANON_HARDLINKS; } else if (!strncmp(arg, "v", 1)) { k = ANON_VERBOSE; } else if (!strncmp(arg, "debug", 5)) { k = ANON_DEBUG; } else if (!strncmp(arg, "lncp", 4)) { k = ANON_LNCP; } else if (!strncmp(arg, "numid", 5)) { k = ANON_NUMID; } else if (!strncmp(arg, "gslash", 6)) { k = ANON_DIRSLASH; } else call_usage(); if (j) anonarch |= k; else anonarch &= ~k; } void guess_compress_program(int wr __attribute__((__unused__))) { const char *ccp; if (compress_program != COMPRESS_GUESS_CMD) return; if (arcname == NULL || (ccp = strrchr(arcname, '.')) == NULL) { compress_program = NULL; return; } ++ccp; /* guess standard format gzip */ if (!strcmp(ccp, "gz") || !strcmp(ccp, "tgz") || !strcmp(ccp, "cgz") || !strcmp(ccp, "ngz") || !strcmp(ccp, "taz")) { compress_program = GZIP_CMD; return; } #ifndef SMALL /* guess extended format xz */ if (!strcmp(ccp, "xz") || !strcmp(ccp, "txz") || !strcmp(ccp, "cxz") || !strcmp(ccp, "nxz")) { compress_program = XZ_CMD; return; } /* guess extended format bzip2 (not bzip) */ if (!strcmp(ccp, "bz2") || !strcmp(ccp, "tbz") || !strcmp(ccp, "tz2") || !strcmp(ccp, "tbz2") || !strcmp(ccp, "cbz") || !strcmp(ccp, "nbz")) { compress_program = BZIP2_CMD; return; } /* guess standard format Unix compress */ if (!strcmp(ccp, "Z") || !strcmp(ccp, "mcz") || !strcmp(ccp, "taZ")) { compress_program = COMPRESS_CMD; return; } /* guess extended format lzma (using xz for decompression) */ if (!strcmp(ccp, "lzma")) { compress_program = wr ? LZMA_WRCMD : XZ_CMD; return; } /* guess extended format lzop */ if (!strcmp(ccp, "lzo")) { compress_program = LZOP_CMD; return; } #endif /* no sugar */ compress_program = NULL; } #ifndef SMALL static int compress_id(char *blk, int size) { if (size >= 2 && blk[0] == '\037' && blk[1] == '\235') { paxwarn(0, "input compressed with %s; use the -%c option" " to decompress it", "compress", 'Z'); exit(1); } return (-1); } static int gzip_id(char *blk, int size) { if (size >= 2 && blk[0] == '\037' && blk[1] == '\213') { paxwarn(0, "input compressed with %s; use the -%c option" " to decompress it", "gzip", 'z'); exit(1); } return (-1); } static int bzip2_id(char *blk, int size) { if (size >= 3 && blk[0] == 'B' && blk[1] == 'Z' && blk[2] == 'h') { paxwarn(0, "input compressed with %s; use the -%c option" " to decompress it", "bzip2", 'j'); exit(1); } return (-1); } static int xz_id(char *blk, int size) { if (size >= 6 && memcmp(blk, "\xFD\x37\x7A\x58\x5A", 6) == 0) { paxwarn(0, "input compressed with %s; use the -%c option" " to decompress it", "xz", 'J'); exit(1); } return (-1); } void mircpio_deprecated(const char *what, const char *with) { paxwarn(0, "the old MirCPIO %s is deprecated and will be removed soon; use %s instead", what, with); } #endif /* !SMALL */ pax/pat_rep.c010064400000000000000000000702131340440266200103770ustar00/* $OpenBSD: pat_rep.c,v 1.43 2017/09/16 07:42:34 otto Exp $ */ /* $NetBSD: pat_rep.c,v 1.4 1995/03/21 09:07:33 cgd Exp $ */ /*- * Copyright (c) 2018 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/pat_rep.c,v 1.17 2018/12/13 07:09:11 tg Exp $"); /* * data structure for storing user supplied replacement strings (-s) */ typedef struct replace { char *nstr; /* the new string we will substitute with */ regex_t rcmp; /* compiled regular expression used to match */ int flgs; /* print conversions? global in operation? */ #define PRNT 0x1 #define GLOB 0x2 struct replace *fow; /* pointer to next pattern */ } REPLACE; /* * routines to handle pattern matching, name modification (regular expression * substitution and interactive renames), and destination name modification for * copy (-rw). Both file name and link names are adjusted as required in these * routines. */ #define MAXSUBEXP 10 /* max subexpressions, DO NOT CHANGE */ static PATTERN *pathead = NULL; /* file pattern match list head */ static PATTERN *pattail = NULL; /* file pattern match list tail */ static REPLACE *rephead = NULL; /* replacement string list head */ static REPLACE *reptail = NULL; /* replacement string list tail */ static int rep_name(char *, size_t, int *, int); static int tty_rename(ARCHD *); static int fix_path(char *, int *, char *, int); static int fn_match(char *, char *, char **); static char * range_match(char *, int); static int resub(regex_t *, regmatch_t *, char *, char *, char *, char *); /* * rep_add() * parses the -s replacement string; compiles the regular expression * and stores the compiled value and it's replacement string together in * replacement string list. Input to this function is of the form: * /old/new/pg * The first char in the string specifies the delimiter used by this * replacement string. "Old" is a regular expression in "ed" format which * is compiled by regcomp() and is applied to filenames. "new" is the * substitution string; p and g are options flags for printing and global * replacement (over the single filename) * Return: * 0 if a proper replacement string and regular expression was added to * the list of replacement patterns; -1 otherwise. */ int rep_add(char *str) { char *pt1; char *pt2; REPLACE *rep; int res; char rebuf[BUFSIZ]; /* * throw out the bad parameters */ if ((str == NULL) || (*str == '\0')) { paxwarn(1, "Empty replacement string"); return(-1); } /* * first character in the string specifies what the delimiter is for * this expression */ for (pt1 = str+1; *pt1; pt1++) { if (*pt1 == '\\') { pt1++; continue; } if (*pt1 == *str) break; } if (*pt1 == '\0') { paxwarn(1, "%s %s", "Invalid replacement string", str); return (-1); } /* * allocate space for the node that handles this replacement pattern * and split out the regular expression and try to compile it */ if ((rep = malloc(sizeof(REPLACE))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "replacement string"); return (-1); } *pt1 = '\0'; if ((res = regcomp(&(rep->rcmp), str+1, 0)) != 0) { regerror(res, &(rep->rcmp), rebuf, sizeof(rebuf)); paxwarn(1, "%s while compiling regular expression %s", rebuf, str); free(rep); return(-1); } /* * put the delimiter back in case we need an error message and * locate the delimiter at the end of the replacement string * we then point the node at the new substitution string */ *pt1++ = *str; for (pt2 = pt1; *pt2; pt2++) { if (*pt2 == '\\') { pt2++; continue; } if (*pt2 == *str) break; } if (*pt2 == '\0') { regfree(&(rep->rcmp)); free(rep); paxwarn(1, "%s %s", "Invalid replacement string", str); return (-1); } *pt2 = '\0'; rep->nstr = pt1; pt1 = pt2++; rep->flgs = 0; /* * set the options if any */ while (*pt2 != '\0') { switch (*pt2) { case 'g': case 'G': rep->flgs |= GLOB; break; case 'p': case 'P': rep->flgs |= PRNT; break; default: regfree(&(rep->rcmp)); free(rep); *pt1 = *str; paxwarn(1, "%s option %s", "Invalid replacement string", str); return (-1); } ++pt2; } /* * all done, link it in at the end */ rep->fow = NULL; if (rephead == NULL) { reptail = rephead = rep; return(0); } reptail->fow = rep; reptail = rep; return(0); } /* * pat_add() * add a pattern match to the pattern match list. Pattern matches are used * to select which archive members are extracted. (They appear as * arguments to pax in the list and read modes). If no patterns are * supplied to pax, all members in the archive will be selected (and the * pattern match list is empty). * Return: * 0 if the pattern was added to the list, -1 otherwise */ int pat_add(char *str, char *chdirname) { PATTERN *pt; /* * throw out the junk */ if ((str == NULL) || (*str == '\0')) { paxwarn(1, "Empty pattern string"); return(-1); } /* * allocate space for the pattern and store the pattern. the pattern is * part of argv so do not bother to copy it, just point at it. Add the * node to the end of the pattern list */ if ((pt = malloc(sizeof(PATTERN))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "pattern string"); return (-1); } pt->pstr = str; pt->pend = NULL; pt->plen = strlen(str); pt->fow = NULL; pt->flgs = 0; pt->chdname = chdirname; if (pathead == NULL) { pattail = pathead = pt; return(0); } pattail->fow = pt; pattail = pt; return(0); } /* * pat_chk() * complain if any the user supplied pattern did not result in a match to * a selected archive member. */ void pat_chk(void) { PATTERN *pt; int wban = 0; /* * walk down the list checking the flags to make sure MTCH was set, * if not complain */ for (pt = pathead; pt != NULL; pt = pt->fow) { if (pt->flgs & MTCH) continue; if (!wban) { paxwarn(1, "WARNING! These patterns were not matched:"); ++wban; } (void)fprintf(stderr, "%s\n", pt->pstr); } } /* * pat_sel() * the archive member which matches a pattern was selected. Mark the * pattern as having selected an archive member. arcn->pat points at the * pattern that was matched. arcn->pat is set in pat_match() * * NOTE: When the -c option is used, we are called when there was no match * by pat_match() (that means we did match before the inverted sense of * the logic). Now this seems really strange at first, but with -c we * need to keep track of those patterns that cause an archive member to NOT * be selected (it found an archive member with a specified pattern) * Return: * 0 if the pattern pointed at by arcn->pat was tagged as creating a * match, -1 otherwise. */ int pat_sel(ARCHD *arcn) { PATTERN *pt; PATTERN **ppt; size_t len; /* * if no patterns just return */ if ((pathead == NULL) || ((pt = arcn->pat) == NULL)) return(0); /* * when we are NOT limited to a single match per pattern mark the * pattern and return */ if (!nflag) { pt->flgs |= MTCH; return(0); } /* * we reach this point only when we allow a single selected match per * pattern, if the pattern matches a directory and we do not have -d * (dflag) we are done with this pattern. We may also be handed a file * in the subtree of a directory. in that case when we are operating * with -d, this pattern was already selected and we are done */ if (pt->flgs & DIR_MTCH) return(0); if (!dflag && ((pt->pend != NULL) || (arcn->type == PAX_DIR))) { /* * ok we matched a directory and we are allowing * subtree matches but because of the -n only its children will * match. This is tagged as a DIR_MTCH type. * WATCH IT, the code assumes that pt->pend points * into arcn->name and arcn->name has not been modified. * If not we will have a big mess. Yup this is another kludge */ /* * if this was a prefix match, remove trailing part of path * so we can copy it. Future matches will be exact prefix match */ if (pt->pend != NULL) *pt->pend = '\0'; if ((pt->pstr = strdup(arcn->name)) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "pattern select"); if (pt->pend != NULL) *pt->pend = '/'; pt->pend = NULL; return(-1); } /* * put the trailing / back in the source string */ if (pt->pend != NULL) { *pt->pend = '/'; pt->pend = NULL; } pt->plen = strlen(pt->pstr); /* * strip off any trailing /, this should really never happen */ len = pt->plen - 1; if (*(pt->pstr + len) == '/') { *(pt->pstr + len) = '\0'; pt->plen = len; } pt->flgs = DIR_MTCH | MTCH; arcn->pat = pt; return(0); } /* * we are then done with this pattern, so we delete it from the list * because it can never be used for another match. * Seems kind of strange to do for a -c, but the pax spec is really * vague on the interaction of -c, -n and -d. We assume that when -c * and the pattern rejects a member (i.e. it matched it) it is done. * In effect we place the order of the flags as having -c last. */ pt = pathead; ppt = &pathead; while ((pt != NULL) && (pt != arcn->pat)) { ppt = &(pt->fow); pt = pt->fow; } if (pt == NULL) { /* * should never happen.... */ paxwarn(1, "Pattern list inconsistent"); return(-1); } *ppt = pt->fow; free(pt); arcn->pat = NULL; return(0); } /* * pat_match() * see if this archive member matches any supplied pattern, if a match * is found, arcn->pat is set to point at the potential pattern. Later if * this archive member is "selected" we process and mark the pattern as * one which matched a selected archive member (see pat_sel()) * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) */ int pat_match(ARCHD *arcn) { PATTERN *pt; arcn->pat = NULL; /* * if there are no more patterns and we have -n (and not -c) we are * done. otherwise with no patterns to match, matches all */ if (pathead == NULL) { if (nflag && !cflag) return(-1); return(0); } /* * have to search down the list one at a time looking for a match. */ pt = pathead; while (pt != NULL) { /* * check for a file name match unless we have DIR_MTCH set in * this pattern then we want a prefix match */ if (pt->flgs & DIR_MTCH) { /* * this pattern was matched before to a directory * as we must have -n set for this (but not -d). We can * only match CHILDREN of that directory so we must use * an exact prefix match (no wildcards). */ if ((arcn->name[pt->plen] == '/') && (strncmp(pt->pstr, arcn->name, pt->plen) == 0)) break; } else if (fn_match(pt->pstr, arcn->name, &pt->pend) == 0) break; pt = pt->fow; } /* * return the result, remember that cflag (-c) inverts the sense of a * match */ if (pt == NULL) return(cflag ? 0 : 1); /* * we had a match, now when we invert the sense (-c) we reject this * member. However we have to tag the pattern a being successful, (in a * match, not in selecting a archive member) so we call pat_sel() here. */ arcn->pat = pt; if (!cflag) return(0); if (pat_sel(arcn) < 0) return(-1); arcn->pat = NULL; return(1); } /* * fn_match() * Return: * 0 if this archive member should be processed, 1 if it should be * skipped and -1 if we are done with all patterns (and pax should quit * looking for more members) * Note: *pend may be changed to show where the prefix ends. */ static int fn_match(char *pattern, char *string, char **pend) { char c; char test; *pend = NULL; for (;;) { switch (c = *pattern++) { case '\0': /* * Ok we found an exact match */ if (*string == '\0') return(0); /* * Check if it is a prefix match */ if ((dflag == 1) || (*string != '/')) return(-1); /* * It is a prefix match, remember where the trailing * / is located */ *pend = string; return(0); case '?': if ((test = *string++) == '\0') return (-1); break; case '*': c = *pattern; /* * collapse multiple asterisks */ while (c == '*') c = *++pattern; /* * optimised hack for pattern with a * at the end */ if (c == '\0') return (0); /* * General case, use recursion. */ while ((test = *string) != '\0') { if (!fn_match(pattern, string, pend)) return (0); ++string; } return (-1); case '[': /* * range match */ if (((test = *string++) == '\0') || ((pattern = range_match(pattern, test)) == NULL)) return (-1); break; case '\\': if ((c = *pattern++) == '\0') return (-1); /* FALLTHROUGH */ default: if (c != *string++) return (-1); break; } } /* NOTREACHED */ } static char * range_match(char *pattern, int test) { char c; char c2; int negate; int ok = 0; if ((negate = (*pattern == '!')) != 0) ++pattern; while ((c = *pattern++) != ']') { /* * Illegal pattern */ if (c == '\0') return (NULL); if ((*pattern == '-') && ((c2 = pattern[1]) != '\0') && (c2 != ']')) { if ((c <= test) && (test <= c2)) ok = 1; pattern += 2; } else if (c == test) ok = 1; } return (ok == negate ? NULL : pattern); } /* * has_dotdot() * Returns true iff the supplied path contains a ".." component. */ int has_dotdot(const char *path) { const char *p = path; while ((p = strstr(p, "..")) != NULL) { if ((p == path || p[-1] == '/') && (p[2] == '/' || p[2] == '\0')) return (1); p += 2; } return (0); } /* * mod_name() * modify a selected file name. first attempt to apply replacement string * expressions, then apply interactive file rename. We apply replacement * string expressions to both filenames and file links (if we didn't the * links would point to the wrong place, and we could never be able to * move an archive that has a file link in it). When we rename files * interactively, we store that mapping (old name to user input name) so * if we spot any file links to the old file name in the future, we will * know exactly how to fix the file link. * Return: * 0 continue to process file, 1 skip this file, -1 pax is finished */ int mod_name(ARCHD *arcn) { int res = 0; if (rmleadslash) { /* CVE-2016-6321: completely skip names with dotdot in them */ const char *p = arcn->name; while ((p = strstr(p, ".."))) { if ((p == arcn->name || p[-1] == '/') && (p[2] == '/' || p[2] == '\0')) { paxwarn(1, "Skipping pathname with dotdot components: %s", arcn->name); return (1); } ++p; } } /* * Strip off leading '/' if appropriate. * Currently, this option is only set for the tar format. */ while (rmleadslash && arcn->name[0] == '/') { if (arcn->name[1] == '\0') { arcn->name[0] = '.'; } else { (void)memmove(arcn->name, &arcn->name[1], strlen(arcn->name)); arcn->nlen--; } if (rmleadslash < 2) { rmleadslash = 2; paxwarn(0, "Removing leading / from absolute path names in the archive"); } } while (rmleadslash && arcn->ln_name[0] == '/' && PAX_IS_HARDLINK(arcn->type)) { if (arcn->ln_name[1] == '\0') { arcn->ln_name[0] = '.'; } else { (void)memmove(arcn->ln_name, &arcn->ln_name[1], strlen(arcn->ln_name)); arcn->ln_nlen--; } if (rmleadslash < 2) { rmleadslash = 2; paxwarn(0, "Removing leading / from absolute path names in the archive"); } } /* * IMPORTANT: We have a problem. what do we do with symlinks? * Modifying a hard link name makes sense, as we know the file it * points at should have been seen already in the archive (and if it * wasn't seen because of a read error or a bad archive, we lose * anyway). But there are no such requirements for symlinks. On one * hand the symlink that refers to a file in the archive will have to * be modified to so it will still work at its new location in the * filesystem. On the other hand a symlink that points elsewhere (and * should continue to do so) should not be modified. There is clearly * no perfect solution here. So we handle them like hardlinks. Clearly * a replacement made by the interactive rename mapping is very likely * to be correct since it applies to a single file and is an exact * match. The regular expression replacements are a little harder to * justify though. We claim that the symlink name is only likely * to be replaced when it points within the file tree being moved and * in that case it should be modified. what we really need to do is to * call an oracle here. :) */ if (rephead != NULL) { /* * we have replacement strings, modify the name and the link * name if any. */ if ((res = rep_name(arcn->name, sizeof(arcn->name), &(arcn->nlen), 1)) != 0) return(res); if (PAX_IS_LINK(arcn->type)) { if ((res = rep_name(arcn->ln_name, sizeof(arcn->ln_name), &(arcn->ln_nlen), 0)) != 0) return(res); } } if (iflag) { /* * perform interactive file rename, then map the link if any */ if ((res = tty_rename(arcn)) != 0) return(res); if (PAX_IS_LINK(arcn->type)) sub_name(arcn->ln_name, &(arcn->ln_nlen), sizeof(arcn->ln_name)); } return(res); } /* * tty_rename() * Prompt the user for a replacement file name. A "." keeps the old name, * a empty line skips the file, and an EOF on reading the tty, will cause * pax to stop processing and exit. Otherwise the file name input, replaces * the old one. * Return: * 0 process this file, 1 skip this file, -1 we need to exit pax */ static int tty_rename(ARCHD *arcn) { char *tmpname; int res; /* * prompt user for the replacement name for a file, keep trying until * we get some reasonable input. Archives may have more than one file * on them with the same name (from updates etc). We print verbose info * on the file so the user knows what is up. */ tty_prnt("\nATTENTION: %s interactive file rename operation.\n", argv0); for (;;) { ls_tty(arcn); tty_prnt("Input new name, or a \".\" to keep the old name, "); tty_prnt("or a \"return\" to skip this file.\n"); tty_prnt("Input > "); if ((tmpname = tty_rd()) == NULL) return (-1); if (strcmp(tmpname, "..") == 0) { tty_prnt("Try again, illegal file name: ..\n"); free(tmpname); continue; } if (strlen(tmpname) > PAXPATHLEN) { tty_prnt("Try again, file name too long\n"); free(tmpname); continue; } break; } /* * empty file name, skips this file. a "." leaves it alone */ if (tmpname[0] == '\0') { tty_prnt("Skipping file.\n"); free(tmpname); return (1); } if ((tmpname[0] == '.') && (tmpname[1] == '\0')) { tty_prnt("Processing continues, name unchanged.\n"); free(tmpname); return (0); } /* * ok the name changed. We may run into links that point at this * file later. we have to remember where the user sent the file * in order to repair any links. */ tty_prnt("Processing continues, name changed to: %s\n", tmpname); res = add_name(arcn->name, arcn->nlen, tmpname); arcn->nlen = strlcpy(arcn->name, tmpname, sizeof(arcn->name)); if ((size_t)arcn->nlen >= sizeof(arcn->name)) arcn->nlen = sizeof(arcn->name) - 1; /* XXX truncate? */ free(tmpname); if (res < 0) return (-1); return (0); } /* * set_dest() * fix up the file name and the link name (if any) so this file will land * in the destination directory (used during copy() -rw). * Return: * 0 if ok, -1 if failure (name too long) */ int set_dest(ARCHD *arcn, char *dest_dir, int dir_len) { if (fix_path(arcn->name, &(arcn->nlen), dest_dir, dir_len) < 0) return(-1); /* * It is really hard to deal with symlinks here, we cannot be sure * if the name they point was moved (or will be moved). It is best to * leave them alone. */ if (!PAX_IS_HARDLINK(arcn->type)) return(0); if (fix_path(arcn->ln_name, &(arcn->ln_nlen), dest_dir, dir_len) < 0) return(-1); return(0); } /* * fix_path * concatenate dir_name and or_name and store the result in or_name (if * it fits). This is one ugly function. * Return: * 0 if ok, -1 if the final name is too long */ static int fix_path(char *or_name, int *or_len, char *dir_name, int dir_len) { char *src; char *dest; char *start; int len; /* * we shift the or_name to the right enough to tack in the dir_name * at the front. We make sure we have enough space for it all before * we start. since dest always ends in a slash, we skip of or_name * if it also starts with one. */ start = or_name; src = start + *or_len; dest = src + dir_len; if (*start == '/') { ++start; --dest; } if ((len = dest - or_name) > PAXPATHLEN) { paxwarn(1, "File name %s/%s, too long", dir_name, start); return(-1); } *or_len = len; /* * enough space, shift */ while (src >= start) *dest-- = *src--; src = dir_name + dir_len - 1; /* * splice in the destination directory name */ while (src >= dir_name) *dest-- = *src--; *(or_name + len) = '\0'; return(0); } /* * rep_name() * walk down the list of replacement strings applying each one in order. * when we find one with a successful substitution, we modify the name * as specified. if required, we print the results. if the resulting name * is empty, we will skip this archive member. We use the regexp(3) * routines (regexp() ought to win a price as having the most cryptic * library function manual page). * --Parameters-- * name is the file name we are going to apply the regular expressions to * (and may be modified) * nsize is the size of the name buffer. * nlen is the length of this name (and is modified to hold the length of * the final string). * prnt is a flag that says whether to print the final result. * Return: * 0 if substitution was successful, 1 if we are to skip the file (the name * ended up empty) */ static int rep_name(char *name, size_t nsize, int *nlen, int prnt) { REPLACE *pt; char *inpt; char *outpt; char *endpt; char *rpt; int found = 0; int res; regmatch_t pm[MAXSUBEXP]; char nname[PAXPATHLEN+1]; /* final result of all replacements */ char buf1[PAXPATHLEN+1]; /* where we work on the name */ /* * copy the name into buf1, where we will work on it. We need to keep * the orig string around so we can print out the result of the final * replacement. We build up the final result in nname. inpt points at * the string we apply the regular expression to. prnt is used to * suppress printing when we handle replacements on the link field * (the user already saw that substitution go by) */ pt = rephead; (void)strlcpy(buf1, name, sizeof(buf1)); inpt = buf1; outpt = nname; endpt = outpt + PAXPATHLEN; /* * try each replacement string in order */ while (pt != NULL) { do { char *oinpt = inpt; /* * check for a successful substitution, if not go to * the next pattern, or cleanup if we were global */ if (regexec(&(pt->rcmp), inpt, MAXSUBEXP, pm, 0) != 0) break; /* * ok we found one. We have three parts, the prefix * which did not match, the section that did and the * tail (that also did not match). Copy the prefix to * the final output buffer (watching to make sure we * do not create a string too long). */ found = 1; rpt = inpt + pm[0].rm_so; while ((inpt < rpt) && (outpt < endpt)) *outpt++ = *inpt++; if (outpt == endpt) break; /* * for the second part (which matched the regular * expression) apply the substitution using the * replacement string and place it the prefix in the * final output. If we have problems, skip it. */ if ((res = resub(&(pt->rcmp),pm,pt->nstr,oinpt,outpt,endpt)) < 0) { if (prnt) paxwarn(1, "Replacement name error %s", name); return(1); } outpt += res; /* * we set up to look again starting at the first * character in the tail (of the input string right * after the last character matched by the regular * expression (inpt always points at the first char in * the string to process). If we are not doing a global * substitution, we will use inpt to copy the tail to * the final result. Make sure we do not overrun the * output buffer */ inpt += pm[0].rm_eo - pm[0].rm_so; if ((outpt == endpt) || (*inpt == '\0')) break; /* * if the user wants global we keep trying to * substitute until it fails, then we are done. */ } while (pt->flgs & GLOB); if (found) break; /* * a successful substitution did NOT occur, try the next one */ pt = pt->fow; } if (found) { /* * we had a substitution, copy the last tail piece (if there is * room) to the final result */ while ((outpt < endpt) && (*inpt != '\0')) *outpt++ = *inpt++; *outpt = '\0'; if ((outpt == endpt) && (*inpt != '\0')) { if (prnt) paxwarn(1,"Replacement name too long %s >> %s", name, nname); return(1); } /* * inform the user of the result if wanted */ if (prnt && (pt->flgs & PRNT)) { (void)fprintf(stderr, "%s >> %s", name, *nname == '\0' ? "" : nname); (void)fputc('\n', stderr); } /* * if empty inform the caller this file is to be skipped * otherwise copy the new name over the orig name and return */ if (*nname == '\0') return(1); *nlen = strlcpy(name, nname, nsize); } return(0); } /* * resub() * apply the replacement to the matched expression. expand out the old * style ed(1) subexpression expansion. * Return: * -1 if error, or the number of characters added to the destination. */ static int resub(regex_t *rp, regmatch_t *pm, char *src, char *inpt, char *dest, char *destend) { char *spt; char *dpt; char c; regmatch_t *pmpt; int len; int subexcnt; spt = src; dpt = dest; subexcnt = rp->re_nsub; while ((dpt < destend) && ((c = *spt++) != '\0')) { /* * see if we just have an ordinary replacement character * or we refer to a subexpression. */ if (c == '&') { pmpt = pm; } else if ((c == '\\') && (*spt >= '0') && (*spt <= '9')) { /* * make sure there is a subexpression as specified */ if ((len = *spt++ - '0') > subexcnt) return(-1); pmpt = pm + len; } else { /* * Ordinary character, just copy it */ if ((c == '\\') && (*spt != '\0')) c = *spt++; *dpt++ = c; continue; } /* * continue if the subexpression is bogus */ if ((pmpt->rm_so < 0) || (pmpt->rm_eo < 0) || ((len = pmpt->rm_eo - pmpt->rm_so) <= 0)) continue; /* * copy the subexpression to the destination. * fail if we run out of space or the match string is damaged */ if (len > (destend - dpt)) return (-1); strncpy(dpt, inpt + pmpt->rm_so, len); dpt += len; } return(dpt - dest); } pax/pax.1010064400000000000000000001120611454566231300074610ustar00.\" $MirOS: src/bin/pax/pax.1,v 1.39 2024/01/05 02:08:48 tg Exp $ .\" $OpenBSD: pax.1,v 1.74 2018/07/23 19:02:49 kn Exp $ .\" $NetBSD: pax.1,v 1.3 1995/03/21 09:07:37 cgd Exp $ .\" .\" Copyright © 2005, 2009, 2011, 2012, 2014, 2016, 2017, 2018, 2021 .\" mirabilos .\" Copyright (c) 1992 Keith Muller. .\" Copyright (c) 1992, 1993 .\" The Regents of the University of California. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" Keith Muller of the University of California, San Diego. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" 3. Neither the name of the University nor the names of its contributors .\" may be used to endorse or promote products derived from this software .\" without specific prior written permission. .\" .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" .\" @(#)pax.1 8.4 (Berkeley) 4/18/94 .\" .\"- .\" $miros: contrib/samples/portmdoc,v 1.23 2024/01/04 22:52:50 tg Exp $ .\"- .\" Copyright © 2008, 2009, 2010, 2016, 2018, 2020, 2023 .\" mirabilos .\" .\" Glue GNU groff (BSD and GNU mdoc both) to AT&T nroff (UCB mdoc). .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en for a solo en dash .\" * and \*(EM for a correctly spaced em dash .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\"- .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 .. .\" .\" .Dd must come before most everything, because when called .\" with -mandoc it loads -mdoc via .so in .Dd (first macro). .\" .Dd $Mdocdate: January 5 2024 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if n .ss \n[.ss] 0 . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . if \*[.T]utf8 .tr \(la\*(Lt . if \*[.T]utf8 .tr \(ra\*(Gt . ds <= \(<= . ds >= \(>= . ds Rq \(rq . ds Lq \(lq . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em . ds tT ucb .\} .ie n \{\ . ds EM \ \(em\ \& .\} .el \{\ . ds EM \f(TR\|\(em\|\fP .\} .\" .\" Add UCB mdoc compatibility to GNU mdoc .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . ds sP \s0 . ds tN \*[Tn-font-size] . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*(tN\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . ds str-Mx1 \*(tN\%MirBSD\~#\*[arg\n[arg-ptr]]\*[str-Mx] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . de Aq . if !\n[arg-limit] \ . ds macro-name Aq . ie \n[in-authors-section] \{\ . ds quote-left \*(Lt . ds quote-right \*(Gt . \} . el \{\ . ds quote-left \[la] . ds quote-right \[ra] . \} . enclose-string \$@ .. . ec .\} .el .ie "\*(tT"gnp" \{\ . ds sP \s0 . ie t .ds tN \s[(\n[.ps]u-1z)] . el .ds tN . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*(tN\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . ds doc-str-Mx1 \*(tN\%MirBSD\~#\*[doc-arg\n[doc-arg-ptr]]\*[doc-str-Mx] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&\\*(tNMirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . nr xX 0 . nr xX 1+\\*(A\\n(aP . as b1 \&\\*(tNMirBSD\ \& . if \\n(xX>0 .as b1 #\& . as b1 \&\\*(A\\n(aP\\*(aa . rr xX . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&\\*(tNMirBSD\\*(aa . nR . \} . \} .. .\} .\" .\"- .ie \ng==1 \{\ . ds nc mircpio . ds np mirpax . ds nt mirtar . ds nm mirpax . Dt MIRPAX 1 .\} .el .ie \ng==2 \{\ . ds nc paxcpio . ds np pax . ds nt paxtar . ds nm pax . Dt PAX 1 .\} .el \{\ . ds nc cpio . ds np pax . ds nt tar . ds nm pax . Dt PAX 1 .\} .\"- .Os MirBSD .Sh NAME .ie \ng==1 \{\ .Nm mirpax .Nd read and write file archives and copy directory hierarchies .\} .el \{\ .Nm pax .Nd read and write file archives and copy directory hierarchies .\} .Sh SYNOPSIS .Bk -words .Nm \*(nm .Op Fl 0cdJjnOvz .Op Fl E Ar limit .Op Fl f Ar archive .Op Fl G Ar group .Op Fl s Ar replstr .Op Fl T Ar range .Op Fl U Ar user .Op Ar pattern ... .Nm \*(nm .Fl r .Op Fl 0cDdiJjknOuvYZz .Op Fl E Ar limit .Op Fl f Ar archive .Op Fl G Ar group .Op Fl M Ar flag .Op Fl o Ar options .Op Fl p Ar string .Op Fl s Ar replstr .Op Fl T Ar range .Op Fl U Ar user .Op Ar pattern ... .Nm \*(nm .Fl w .Op Fl 0adHiJjLOPtuvXz .Op Fl B Ar bytes .Op Fl b Ar blocksize .Op Fl f Ar archive .Op Fl G Ar group .Op Fl M Ar flag .Op Fl o Ar options .Op Fl s Ar replstr .Op Fl T Ar range .Op Fl U Ar user .Op Fl x Ar format .Op Ar .Nm \*(nm .Fl rw .Op Fl 0DdHiJjkLlnOPtuvXYZ .Op Fl G Ar group .Op Fl p Ar string .Op Fl s Ar replstr .Op Fl T Ar range .Op Fl U Ar user .Op Ar .Ar directory .Ek .Sh DESCRIPTION .Nm will read, write, and list the members of an archive file and will copy directory hierarchies. .Nm operation is independent of the specific archive format and supports a wide variety of different archive formats. A list of supported archive formats can be found under the description of the .Fl x option. .Pp The presence of the .Fl r and the .Fl w options specifies which of the following functional modes .Nm will operate under: .Em list , read , write , and .Em copy . .Bl -tag -width 6n .It Aq none .Em List . .Nm will write to standard output a table of contents of the members of the archive file read from standard input, whose pathnames match the specified .Ar pattern arguments. The table of contents contains one filename per line and is written using single line buffering. .It Fl r .Em Read . .Nm extracts the members of the archive file read from the standard input, with pathnames matching the specified .Ar pattern arguments. The archive format and blocking is automatically determined on input. When an extracted file is a directory, the entire file hierarchy rooted at that directory is extracted. All extracted files are created relative to the current file hierarchy. The setting of ownership, access and modification times, and file mode of the extracted files are discussed in more detail under the .Fl p option. .It Fl w .Em Write . .Nm writes an archive containing the .Ar file operands to standard output using the specified archive format. When no .Ar file operands are specified, a list of files to copy with one per line is read from standard input. When a .Ar file operand is also a directory, the entire file hierarchy rooted at that directory will be included. .It Fl rw .Em Copy . .Nm copies the .Ar file operands to the destination .Ar directory . When no .Ar file operands are specified, a list of files to copy with one per line is read from the standard input. When a .Ar file operand is also a directory the entire file hierarchy rooted at that directory will be included. The effect of the .Em copy is as if the copied files were written to an archive file and then subsequently extracted, except that there may be hard links between the original and the copied files (see the .Fl l option below). .Pp .Sy Warning : The destination .Ar directory must not be one of the .Ar file operands or a member of a file hierarchy rooted at one of the .Ar file operands. The result of a .Em copy under these conditions is unpredictable. .El .Pp While processing a damaged archive during a read or list operation, .Nm will attempt to recover from media defects and will search through the archive to locate and process the largest number of archive members possible (see the .Fl E option for more details on error handling). .Pp The .Ar directory operand specifies a destination directory pathname. If the .Ar directory operand does not exist, or it is not writable by the user, or it is not of type directory, .Nm will exit with a non-zero exit status. .Pp The .Ar pattern operand is used to select one or more pathnames of archive members. Archive members are selected using the pattern matching notation described by .Xr glob 7 . When the .Ar pattern operand is not supplied, all members of the archive will be selected. When a .Ar pattern matches a directory, the entire file hierarchy rooted at that directory will be selected. When a .Ar pattern operand does not select at least one archive member, .Nm will write these .Ar pattern operands in a diagnostic message to standard error and then exit with a non-zero exit status. .Pp The .Ar file operand specifies the pathname of a file to be copied or archived. When a .Ar file operand does not select at least one archive member, .Nm will write these .Ar file operand pathnames in a diagnostic message to standard error and then exit with a non-zero exit status. .Pp The options are as follows: .Bl -tag -width Ds .It Fl 0 Use the NUL .Pq Ql \e0 character as a pathname terminator, instead of newline .Pq Ql \en . This applies only to the pathnames read from standard input in the write and copy modes, and to the pathnames written to standard output in list mode. This option is expected to be used in concert with the .Fl print0 function in .Xr find 1 , the .Fl d Ar \*(aq\*(aq option to the .Ic read built-in utility of .Xr mksh 1 or the .Fl 0 flag in .Xr xargs 1 . .It Fl a Append the given .Ar file operands to the end of an archive that was previously written. If an archive format is not specified with a .Fl x option, the format currently being used in the archive will be selected. Any attempt to append to an archive in a format different from the format already used in the archive will cause .Nm to exit immediately with a non-zero exit status. The blocking size used in the archive volume where writing starts will continue to be used for the remainder of that archive volume. .Pp .Sy Warning : Many storage devices are not able to support the operations necessary to perform an append operation. Any attempt to append to an archive stored on such a device may damage the archive or have other unpredictable results. Tape drives in particular are more likely to not support an append operation. An archive stored in a regular filesystem file or on a disk device will usually support an append operation. .It Fl B Ar bytes Limit the number of bytes written to a single archive volume to .Ar bytes . The .Ar bytes limit can end with .Sq Li m , .Sq Li k , or .Sq Li b to specify multiplication by 1048576 (1M), 1024 (1K) or 512, respectively. A pair of .Ar bytes limits can be separated by .Sq Li x to indicate a product. .Pp .Sy Warning : Only use this option when writing an archive to a device which supports an end of file read condition based on last (or largest) write offset (such as a regular file or a tape drive). The use of this option with a floppy or hard disk is not recommended. .It Fl b Ar blocksize When writing an archive, block the output at a positive decimal integer number of bytes per write to the archive file. The .Ar blocksize must be a multiple of 512 bytes with a maximum of 64512 bytes. Archive block sizes larger than 32256 bytes violate the .Tn POSIX standard and will not be portable to all systems. A .Ar blocksize can end with .Sq Li k or .Sq Li b to specify multiplication by 1024 (1K) or 512, respectively. A pair of .Ar blocksize Ns s can be separated by .Sq Li x to indicate a product. A specific archive device may impose additional restrictions on the size of blocking it will support. When blocking is not specified, the default .Ar blocksize is dependent on the specific archive format being used (see the .Fl x option). .It Fl c Match all file or archive members .Em except those specified by the .Ar pattern and .Ar file operands. .It Fl D This option is the same as the .Fl u option, except that the file inode change time is checked instead of the file modification time. The file inode change time can be used to select files whose inode information (e.g., UID, GID, etc.) is newer than a copy of the file in the destination .Ar directory . .It Fl d Cause files of type directory being copied or archived, or archive members of type directory being extracted, to match only the directory file or archive member and not the file hierarchy rooted at the directory. .It Fl E Ar limit Limit the number of consecutive read faults while trying to read a flawed archive to .Ar limit . With a positive .Ar limit , .Nm will attempt to recover from an archive read error and will continue processing starting with the next file stored in the archive. A .Ar limit of 0 will cause .Nm to stop operation after the first read error is detected on an archive volume. The default .Ar limit is a small positive number of retries. .It Fl f Ar archive Specify .Ar archive as the pathname of the input or output archive, overriding the default standard input (for list and read) or standard output (for write). A single archive may span multiple files and different archive devices. When required, .Nm will prompt for the pathname of the file or device of the next volume in the archive. .It Fl G Ar group Select a file based on its .Ar group name, or when starting with a .Cm # , a numeric GID. A .Ql \e can be used to escape the .Cm # . Multiple .Fl G options may be supplied and checking stops with the first match. .It Fl H Follow only command-line symbolic links while performing a physical file system traversal. .It Fl i Interactively rename files or archive members. For each archive member matching a .Ar pattern operand or each file matching a .Ar file operand, .Nm will prompt to .Pa /dev/tty giving the name of the file, its file mode, and its modification time. .Nm will then read a line from .Pa /dev/tty . If this line is blank, the file or archive member is skipped. If this line consists of a single period, the file or archive member is processed with no modification to its name. Otherwise, its name is replaced with the contents of the line. .Nm will immediately exit with a non-zero exit status if .Dv EOF is encountered when reading a response or if .Pa /dev/tty cannot be opened for reading and writing. .It Fl J Use the xz utility to compress (decompress) the archive while writing (reading). Incompatible with .Fl a . .It Fl j Use the bzip2 utility to compress (decompress) the archive while writing (reading). Incompatible with .Fl a . .It Fl k Do not overwrite existing files. .It Fl L Follow all symbolic links to perform a logical filesystem traversal. .It Fl l (The lowercase letter .Dq ell . ) Link files. In copy mode .Pq Fl r Fl w , hard links are made between the source and destination file hierarchies whenever possible. .It Fl M Ar flag Configure the archive normaliser. .Ar flag is either a numeric value compatible to .Xr strtonum 3 which is directly stored in the flags word, or one of the following values, optionally prefixed with .Dq no\- to turn them off: .Pp .Bl -tag -width xxxxxx -compact .It Ar inodes 0x0001: Serialise inodes, zero device info. .br (cpio, sv4cpio, sv4crc) .It Ar links 0x0002: Store content of hard links only once. .br (cpio, sv4cpio, sv4crc) .It Ar mtime 0x0004: Zero out the file modification time. .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar uidgid 0x0008: Set owner to 0:0 .Pq Li root Ns : Ns Li wheel . .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar verb 0x0010: Debug this option. .It Ar debug 0x0020: Debug file header storage. .It Ar lncp 0x0040: Extract hard links by copy if link fails. .It Ar numid 0x0080: Use only numeric uid and gid values. .br (ustar) .It Ar gslash 0x0100: Append a slash after directory names. .br (ustar) .It Ar set 0x0003: Keep ownership and mtime intact. .It Ar dist 0x008B: Clean everything except mtime. .It Ar norm 0x008F: Clean everything. .It Ar root 0x0089: Clean owner and device information. .El .Pp When creating an archive and verbosely listing output, these normalisation operations are not reflected in the output, because they are made only after the output has been shown. .Pp This option is only implemented for the ar, cpio, sv4cpio, sv4crc, and ustar file format writing routines. .Pp TODO: The .Nm \*(nm frontend should be using the .Fl o option for handling this feature instead. .It Fl n Select the first archive member that matches each .Ar pattern operand. No more than one archive member is matched for each .Ar pattern . When members of type directory are matched, the file hierarchy rooted at that directory is also matched (unless .Fl d is also specified). .It Fl O Force the archive to be one volume. If a volume ends prematurely, .Nm will not prompt for a new volume. This option can be useful for automated tasks where error recovery cannot be performed by a human. .It Fl o Ar options Information to modify the algorithm for extracting or writing archive files which is specific to the archive format specified by .Fl x . In general, .Ar options take the form: .Ar name Ns = Ns Ar value . .Pp The following options are available for the .Cm ustar and old .Bx .Cm tar formats: .Pp .Bl -tag -width Ds -compact .It Cm write_opt=nodir When writing archives, omit the storage of directories. .El .It Fl P Do not follow symbolic links, perform a physical filesystem traversal. This is the default mode. .It Fl p Ar string Specify one or more file characteristic options (privileges). The .Ar string option-argument is a string specifying file characteristics to be retained or discarded on extraction. The string consists of the specification characters .Cm a , e , m , o , and .Cm p . Multiple characteristics can be concatenated within the same string and multiple .Fl p options can be specified. The meanings of the specification characters are as follows: .Bl -tag -width 2n .It Cm a Do not preserve file access times. By default, file access times are preserved whenever possible. .It Cm e .Dq Preserve everything , the user ID, group ID, file mode bits, file access time, and file modification time. This is intended to be used by root, someone with all the appropriate privileges, in order to preserve all aspects of the files as they are recorded in the archive. The .Cm e flag is the sum of the .Cm o and .Cm p flags. .It Cm m Do not preserve file modification times. By default, file modification times are preserved whenever possible. .It Cm o Preserve the user ID and group ID. .It Cm p .Dq Preserve the file mode bits. This is intended to be used by a user with regular privileges who wants to preserve all aspects of the file other than the ownership. The file times are preserved by default, but two other flags are offered to disable this and use the time of extraction instead. .El .Pp In the preceding list, .Sq preserve indicates that an attribute stored in the archive is given to the extracted file, subject to the permissions of the invoking process. Otherwise the attribute of the extracted file is determined as part of the normal file creation action. If neither the .Cm e nor the .Cm o specification character is specified, or the user ID and group ID are not preserved for any reason, .Nm will not set the .Dv S_ISUID (setuid) and .Dv S_ISGID (setgid) bits of the file mode. If the preservation of any of these items fails for any reason, .Nm will write a diagnostic message to standard error. Failure to preserve these items will affect the final exit status, but will not cause the extracted file to be deleted. If the file characteristic letters in any of the string option-arguments are duplicated or conflict with each other, the one(s) given last will take precedence. For example, if .Fl p Ar eme is specified, file modification times are still preserved. .It Fl r Read an archive file from standard input and extract the specified .Ar file operands. If any intermediate directories are needed in order to extract an archive member, these directories will be created as if .Xr mkdir 2 was called with the bitwise OR of .Dv S_IRWXU , S_IRWXG , and .Dv S_IRWXO as the mode argument. When the selected archive format supports the specification of linked files and these files cannot be linked while the archive is being extracted, .Nm will write a diagnostic message to standard error and exit with a non-zero exit status at the completion of operation. .It Fl s Ar replstr Modify the archive member names according to the substitution expression .Ar replstr , using the syntax of the .Xr ed 1 utility regular expressions. .Ar file or .Ar pattern arguments may be given to restrict the list of archive members to those specified. .Pp The format of these regular expressions is: .Pp .Dl /old/new/[gp] .Pp As in .Xr ed 1 , .Ar old is a basic regular expression (see .Xr re_format 7 ) and .Ar new can contain an ampersand .Pq Ql & , .Ql \e Ns Em n (where .Em n is a digit) back-references, or subexpression matching. The .Ar old string may also contain newline characters. Any non-null character can be used as a delimiter .Po .Ql / is shown here .Pc . Multiple .Fl s expressions can be specified. The expressions are applied in the order they are specified on the command line, terminating with the first successful substitution. .Pp The optional trailing .Cm g continues to apply the substitution expression to the pathname substring, which starts with the first character following the end of the last successful substitution. The first unsuccessful substitution stops the operation of the .Cm g option. The optional trailing .Cm p will cause the final result of a successful substitution to be written to standard error in the following format: .Pp .D1 Em original-pathname No \*(Gt\*(Gt Em new-pathname .Pp File or archive member names that substitute to the empty string are not selected and will be skipped. .It Fl T Ar range Allow files to be selected based on a file modification or inode change time falling within the specified time range. The range has the format: .Sm off .Bd -filled -offset indent .Oo Ar from_date Oc Oo , .Ar to_date Oc Oo / .Oo Cm c Oc Op Cm m Oc .Ed .Sm on .Pp The dates specified by .Ar from_date to .Ar to_date are inclusive. If only a .Ar from_date is supplied, all files with a modification or inode change time equal to or younger are selected. If only a .Ar to_date is supplied, all files with a modification or inode change time equal to or older will be selected. When the .Ar from_date is equal to the .Ar to_date , only files with a modification or inode change time of exactly that time will be selected. .Pp When .Nm is in write or copy mode, the optional trailing field .Oo Cm c Oc Ns Op Cm m can be used to determine which file time (inode change, file modification or both) are used in the comparison. If neither is specified, the default is to use file modification time only. The .Cm m specifies the comparison of file modification time (the time when the file was last written). The .Cm c specifies the comparison of inode change time (the time when the file inode was last changed; e.g., a change of owner, group, mode, etc). When .Cm c and .Cm m are both specified, then the modification and inode change times are both compared. .Pp The inode change time comparison is useful in selecting files whose attributes were recently changed or selecting files which were recently created and had their modification time reset to an older time (as what happens when a file is extracted from an archive and the modification time is preserved). Time comparisons using both file times is useful when .Nm is used to create a time based incremental archive (only files that were changed during a specified time range will be archived). .Pp A time range is made up of six different fields and each field must contain two digits. The format is: .Pp .Dl [[[[[cc]yy]mm]dd]HH]MM[.SS] .Pp Where .Ar cc is the first two digits of the year (the century), .Ar yy is the last two digits of the year, the first .Ar mm is the month (from 01 to 12), .Ar dd is the day of the month (from 01 to 31), .Ar HH is the hour of the day (from 00 to 23), .Ar MM is the minute (from 00 to 59), and .Ar SS is the seconds (from 00 to 59). The minute field .Ar MM is required, while the other fields are optional and must be added in the following order: .Ar HH , dd , mm , .Ar yy , cc . .Pp The .Ar SS field may be added independently of the other fields. Time ranges are relative to the current time, so .Ic \-T 1234/cm would select all files with a modification or inode change time of 12:34 PM today or later. Multiple .Fl T time range can be supplied and checking stops with the first match. .It Fl t Reset the access times of any file or directory read or accessed by .Nm to be the same as they were before being read or accessed by .Nm \*(nm . .It Fl U Ar user Select a file based on its .Ar user name, or when starting with a .Cm # , a numeric UID. A .Ql \e can be used to escape the .Cm # . Multiple .Fl U options may be supplied and checking stops with the first match. .It Fl u Ignore files that are older (having a less recent file modification time) than a pre-existing file or archive member with the same name. During read, an archive member with the same name as a file in the filesystem will be extracted if the archive member is newer than the file. During write, a filesystem member with the same name as an archive member will be written to the archive if it is newer than the archive member. During copy, the file in the destination hierarchy is replaced by the file in the source hierarchy or by a link to the file in the source hierarchy if the file in the source hierarchy is newer. .It Fl v During a list operation, produce a verbose table of contents using the format of the .Xr ls 1 utility with the .Fl l option. For pathnames representing a hard link to a previous member of the archive, the output has the format: .Pp .Dl Em ls \-l listing No == Em link-name .Pp For pathnames representing a symbolic link, the output has the format: .Pp .Dl Em ls \-l listing No -\*(Gt Em link-name .Pp Where .Em ls \-l listing is the output format specified by the .Xr ls 1 utility when used with the .Fl l option. Otherwise for all the other operational modes (read, write, and copy), pathnames are written and flushed to standard error without a trailing newline as soon as processing begins on that file or archive member. The trailing newline is not buffered and is written only after the file has been read or written. .It Fl w Write files to the standard output in the specified archive format. When no .Ar file operands are specified, standard input is read for a list of pathnames with one per line without any leading or trailing .Aq blanks . .It Fl X When traversing the file hierarchy specified by a pathname, do not descend into directories that have a different device ID. See the .Li st_dev field as described in .Xr stat 2 for more information about device IDs. .It Fl x Ar format Specify the output archive format, with the default format being .Cm ustar . .Nm currently supports the following formats: .Bl -tag -width "sv4cpio" .It Cm ar The Unix Archiver library format. This format matches APT repositories and the BSD .Xr ar 1 specification, not GNU binutils (which can however read them) or SYSV systems. See .Xr ar 5 on some operating systems for more information. .It Cm bcpio The old binary cpio format. The default blocksize for this format is 5120 bytes. This format is not very portable and should not be used when other formats are available. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm cpio The extended cpio interchange format specified in the .St -p1003.2 standard. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm sv4cpio The System V release 4 cpio. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm sv4crc The System V release 4 cpio with file CRC checksums. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm tar The old .Bx tar format as found in .Bx 4.3 . The default blocksize for this format is 10240 bytes. Pathnames stored by this format must be 100 characters or less in length. Only regular files, hard links, symlinks and directories will be archived (other filesystem types are not supported). For backwards compatibility with even older tar formats, a .Fl o option can be used when writing an archive to omit the storage of directories. This option takes the form: .Pp .Dl Fl o Cm write_opt=nodir .It Cm ustar The extended tar interchange format specified in the .St -p1003.2 standard. The default blocksize for this format is 10240 bytes. Filenames stored by this format must be 100 characters or less in length; the total pathname must be 256 characters or less. .El .Pp .Nm will detect and report any file that it is unable to store or extract as the result of any specific archive format restrictions. The individual archive formats may impose additional restrictions on use. Typical archive format restrictions include (but are not limited to): file pathname length, file size, link pathname length, and the type of the file. .It Fl Y This option is the same as the .Fl D option, except that the inode change time is checked using the pathname created after all the file name modifications have completed. .It Fl Z This option is the same as the .Fl u option, except that the modification time is checked using the pathname created after all the file name modifications have completed. .It Fl z Use the .Xr gzip 1 utility to compress (decompress) the archive while writing (reading). Incompatible with .Fl a . .El .Pp The options that operate on the names of files or archive members .Po Fl c , .Fl i , .Fl n , .Fl s , .Fl u , .Fl v , .Fl D , .Fl G , .Fl T , .Fl U , .Fl Y , and .Fl Z .Pc interact as follows. .Pp When extracting files during a read operation, archive members are .Sq selected , based only on the user specified pattern operands as modified by the .Fl c , .Fl n , .Fl u , .Fl D , .Fl G , .Fl T , .Fl U options. Then any .Fl s and .Fl i options will modify in that order, the names of these selected files. Then the .Fl Y and .Fl Z options will be applied based on the final pathname. Finally, the .Fl v option will write the names resulting from these modifications. .Pp When archiving files during a write operation, or copying files during a copy operation, archive members are .Sq selected , based only on the user specified pathnames as modified by the .Fl n , .Fl u , .Fl D , .Fl G , .Fl T , and .Fl U options (the .Fl D option only applies during a copy operation). Then any .Fl s and .Fl i options will modify in that order, the names of these selected files. Then during a copy operation the .Fl Y and the .Fl Z options will be applied based on the final pathname. Finally, the .Fl v option will write the names resulting from these modifications. .Pp When one or both of the .Fl u or .Fl D options are specified along with the .Fl n option, a file is not considered selected unless it is newer than the file to which it is compared. .Sh ENVIRONMENT .Bl -tag -width Fl .It Ev TMPDIR Path in which to store temporary files. .El .Sh EXIT STATUS .Ex -std pax .Sh EXAMPLES Copy the contents of the current directory to the device .Pa /dev/rst0 : .Pp .Dl $ \*(nm \-w \-f /dev/rst0 \&. .Pp Give the verbose table of contents for an archive stored in .Pa filename : .Pp .Dl $ \*(nm \-v \-f filename .Pp This sequence of commands will copy the entire .Pa olddir directory hierarchy to .Pa newdir : .Bd -literal -offset indent $ mkdir newdir $ cd olddir $ \*(nm \-rw . ../newdir .Ed .Pp Extract files from the archive .Pa a.pax . Files rooted in .Pa /usr are extracted relative to the current working directory; all other files are extracted to their unmodified path. .Pp .Dl $ \*(nm \-r \-s \*(aq,\*(ha/usr/,,\*(aq \-f a.pax .Pp This can be used to interactively select the files to copy from the current directory to .Pa dest_dir : .Pp .Dl $ \*(nm \-rw \-i \&. dest_dir .Pp Extract all files from the archive .Pa a.pax which are owned by .Em root with group .Em bin and preserve all file permissions: .Pp .Dl "$ \*(nm \-r \-pe \-U root \-G bin \-f a.pax" .Pp Update (and list) only those files in the destination directory .Pa /backup which are older (less recent inode change or file modification times) than files with the same name found in the source file tree .Pa home : .Pp .Dl "$ \*(nm \-r \-w \-v \-Y \-Z home /backup" .Sh DIAGNOSTICS Whenever .Nm cannot create a file or a link when reading an archive or cannot find a file when writing an archive, or cannot preserve the user ID, group ID, or file mode when the .Fl p option is specified, a diagnostic message is written to standard error and a non-zero exit status will be returned, but processing will continue. In the case where .Nm cannot create a link to a file, unless .Fl M Ar lncp is given, .Nm will not create a second copy of the file. .Pp If the extraction of a file from an archive is prematurely terminated by a signal or error, .Nm may have only partially extracted a file the user wanted. Additionally, the file modes of extracted files and directories may have incorrect file bits, and the modification and access times may be wrong. .Pp If the creation of an archive is prematurely terminated by a signal or error, .Nm may have only partially created the archive, which may violate the specific archive format specification. .Pp If while doing a copy, .Nm detects a file is about to overwrite itself, the file is not copied, a diagnostic message is written to standard error and when .Nm completes it will exit with a non-zero exit status. .Sh SEE ALSO .Xr ar 1 , .Xr cpio 1 , .if \ng==1 \{\ .Xr deb 5 , .Xr mircpio 1 , .Xr mirtar 1 , .Xr pax 1 , .\} .if \ng==2 \{\ .Xr deb 5 , .Xr paxcpio 1 , .Xr paxtar 1 , .\} .Xr tar 1 .Sh STANDARDS The .Nm utility is mostly compliant with an older version of the IEEE Std 1003.1 .Pq Dq Tn POSIX specification, except for the known .Sx BUGS listed below, and that the .Cm pax archive format and the .Cm listopt keyword are unsupported. .Pp The flags .Fl 0BDEGHJjLMOPTUYZz , the archive formats .Cm ar , .Cm bcpio , .Cm sv4cpio , .Cm sv4crc and .Cm tar , the .Cm b , k , and .Cm x additions to the .Fl b flag and the flawed archive handling during list and read operations are extensions to that specification. .Sh HISTORY A .Nm utility appeared in .Bx 4.4 . .Sh AUTHORS .An -nosplit .An Keith Muller at the University of California, San Diego. .Mx extensions by .An mirabilos Aq m@mirbsd.org . .Sh BUGS The pattern matching does not match either .Tn POSIX or this documentation completely. See also .Sx STANDARDS above. pax/pax.c010064400000000000000000000411641340440266200075400ustar00/* $OpenBSD: pax.c,v 1.52 2018/09/13 12:33:43 millert Exp $ */ /* $NetBSD: pax.c,v 1.5 1996/03/26 23:54:20 mrg Exp $ */ /*- * Copyright (c) 2012, 2015, 2016, 2017 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #if HAVE_SYS_RESOURCE_H #include #endif #include #include #include #include #if HAVE_GRP_H #include #endif #if HAVE_PATHS_H #include #endif #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/pax.c,v 1.29 2018/12/13 07:09:11 tg Exp $"); static int gen_init(void); /* * PAX main routines, general globals and some simple start up routines */ /* * Variables that can be accessed by any routine within pax */ signed char act = ERROR; /* read/write/append/copy */ const FSUB *frmt = NULL; /* archive format type */ signed char cflag; /* match all EXCEPT pattern/file */ signed char cwdfd; /* starting cwd */ signed char dflag; /* directory member match only */ signed char iflag; /* interactive file/archive rename */ signed char kflag; /* do not overwrite existing files */ signed char lflag; /* use hard links when possible */ signed char nflag; /* select first archive member match */ signed char tflag; /* restore access time after read */ signed char uflag; /* ignore older modification time files */ signed char Vflag = 0; /* print a dot for each file processed */ signed char vflag; /* produce verbose output */ signed char Dflag; /* same as uflag except inode change time */ signed char Hflag; /* follow command line symlinks (write only) */ signed char Lflag; /* follow symlinks when writing */ signed char Xflag; /* archive files with same device id only */ signed char Yflag; /* same as Dflag except after name mode */ signed char Zflag; /* same as uflag except after name mode */ signed char zeroflag; /* use \0 as pathname terminator */ signed char vfpart = 0; /* is partial verbose output in progress */ signed char patime = 1; /* preserve file access time */ signed char pmtime = 1; /* preserve file modification times */ signed char nodirs; /* do not create directories as needed */ signed char pmode; /* preserve file mode bits */ signed char pids; /* preserve file uid/gid */ signed char rmleadslash = 0; /* remove leading '/' from pathnames */ signed char exit_val; /* exit value */ signed char docrc; /* check/create file crc */ char *dirptr; /* destination dir in a copy */ const char *argv0; /* root of argv[0] */ enum op_mode op_mode; /* what program are we acting as? */ sigset_t s_mask; /* signal mask for cleanup critical sect */ FILE *listf; /* fp to print file list to (default stderr) */ int listfd = STDERR_FILENO; /* fd matching listf, for sighandler output */ char *tempfile; /* tempfile to use for mkstemp(3) */ char *tempbase; /* basename of tempfile to use for mkstemp(3) */ time_t now; /* time of program start */ /* * PAX - Portable Archive Interchange * * A utility to read, write, and write lists of the members of archive * files and copy directory hierarchies. A variety of archive formats * are supported (some are described in POSIX 1003.1 10.1): * * ustar - 10.1.1 extended tar interchange format * cpio - 10.1.2 extended cpio interchange format * tar - old BSD 4.3 tar format * binary cpio - old cpio with binary header format * sysVR4 cpio - with and without CRC * * This version is a superset of IEEE Std 1003.2b-d3 * * Summary of Extensions to the IEEE Standard: * * 1 READ ENHANCEMENTS * 1.1 Operations which read archives will continue to operate even when * processing archives which may be damaged, truncated, or fail to meet * format specs in several different ways. Damaged sections of archives * are detected and avoided if possible. Attempts will be made to resync * archive read operations even with badly damaged media. * 1.2 Blocksize requirements are not strictly enforced on archive read. * Tapes which have variable sized records can be read without errors. * 1.3 The user can specify via the non-standard option flag -E if error * resync operation should stop on a media error, try a specified number * of times to correct, or try to correct forever. * 1.4 Sparse files (lseek holes) stored on the archive (but stored with blocks * of all zeros will be restored with holes appropriate for the target * filesystem * 1.5 The user is notified whenever something is found during archive * read operations which violates spec (but the read will continue). * 1.6 Multiple archive volumes can be read and may span over different * archive devices * 1.7 Rigidly restores all file attributes exactly as they are stored on the * archive. * 1.8 Modification change time ranges can be specified via multiple -T * options. These allow a user to select files whose modification time * lies within a specific time range. * 1.9 Files can be selected based on owner (user name or uid) via one or more * -U options. * 1.10 Files can be selected based on group (group name or gid) via one o * more -G options. * 1.11 File modification time can be checked against existing file after * name modification (-Z) * * 2 WRITE ENHANCEMENTS * 2.1 Write operation will stop instead of allowing a user to create a flawed * flawed archive (due to any problem). * 2.2 Archives written by pax are forced to strictly conform to both the * archive and pax the specific format specifications. * 2.3 Blocking size and format is rigidly enforced on writes. * 2.4 Formats which may exhibit header overflow problems (they have fields * too small for large filesystems, such as inode number storage), use * routines designed to repair this problem. These techniques still * conform to both pax and format specifications, but no longer truncate * these fields. This removes any restrictions on using these archive * formats on large filesystems. * 2.5 Multiple archive volumes can be written and may span over different * archive devices * 2.6 A archive volume record limit allows the user to specify the number * of bytes stored on an archive volume. When reached the user is * prompted for the next archive volume. This is specified with the * non-standard -B flag. The limit is rounded up to the next blocksize. * 2.7 All archive padding during write use zero filled sections. This makes * it much easier to pull data out of flawed archive during read * operations. * 2.8 Access time reset with the -t applies to all file nodes (including * directories). * 2.9 Symbolic links can be followed with -L (optional in the spec). * 2.10 Modification or inode change time ranges can be specified via * multiple -T options. These allow a user to select files whose * modification or inode change time lies within a specific time range. * 2.11 Files can be selected based on owner (user name or uid) via one or more * -U options. * 2.12 Files can be selected based on group (group name or gid) via one o * more -G options. * 2.13 Symlinks which appear on the command line can be followed (without * following other symlinks; -H flag) * * 3 COPY ENHANCEMENTS * 3.1 Sparse files (lseek holes) can be copied without expanding the holes * into zero filled blocks. The file copy is created with holes which are * appropriate for the target filesystem * 3.2 Access time as well as modification time on copied file trees can be * preserved with the appropriate -p options. * 3.3 Access time reset with the -t applies to all file nodes (including * directories). * 3.4 Symbolic links can be followed with -L (optional in the spec). * 3.5 Modification or inode change time ranges can be specified via * multiple -T options. These allow a user to select files whose * modification or inode change time lies within a specific time range. * 3.6 Files can be selected based on owner (user name or uid) via one or more * -U options. * 3.7 Files can be selected based on group (group name or gid) via one o * more -G options. * 3.8 Symlinks which appear on the command line can be followed (without * following other symlinks; -H flag) * 3.9 File inode change time can be checked against existing file before * name modification (-D) * 3.10 File inode change time can be checked against existing file after * name modification (-Y) * 3.11 File modification time can be checked against existing file after * name modification (-Z) * * 4 GENERAL ENHANCEMENTS * 4.1 Internal structure is designed to isolate format dependent and * independent functions. Formats are selected via a format driver table. * This encourages the addition of new archive formats by only having to * write those routines which id, read and write the archive header. */ /* * main() * parse options, set up and operate as specified by the user. * any operational flaw will set exit_val to non-zero * Return: 0 if ok, 1 otherwise */ int main(int argc, char **argv) { const char *tmpdir; size_t tdlen; /* may not be a constant, thus initialising early */ listf = stderr; now = time(NULL); /* * Keep a reference to cwd, so we can always come back home. */ cwdfd = binopen2(BO_CLEXEC, ".", O_RDONLY); if (cwdfd < 0) { syswarn(1, errno, "Cannot open current working directory."); return(exit_val); } /* * Where should we put temporary files? */ if ((tmpdir = getenv("TMPDIR")) == NULL || *tmpdir == '\0') tmpdir = _PATH_TMP; tdlen = strlen(tmpdir); while (tdlen > 0 && tmpdir[tdlen - 1] == '/') tdlen--; tempfile = malloc(tdlen + 1 + sizeof(_TFILE_BASE)); if (tempfile == NULL) { paxwarn(1, "%s for %s", "Out of memory", "temp file name"); return (exit_val); } if (tdlen) memcpy(tempfile, tmpdir, tdlen); tempbase = tempfile + tdlen; *tempbase++ = '/'; #if HAVE_SETPGENT /* * keep passwd and group files open for faster lookups. */ setpassent(1); setgroupent(1); #endif /* * parse options, determine operational mode, general init */ options(argc, argv); if ((gen_init() < 0) || (tty_init() < 0)) return(exit_val); #if HAVE_PLEDGE /* * pmode needs to restore setugid bits when extracting or copying, * so can't pledge at all then. */ if (pmode == 0 || (act != EXTRACT && act != COPY)) { if (pledge("stdio rpath wpath cpath fattr dpath getpw proc exec tape", NULL) == -1) err(1, "pledge"); /* Copy mode, or no gzip -- don't need to fork/exec. */ if (compress_program == NULL || act == COPY) { if (pledge("stdio rpath wpath cpath fattr dpath getpw tape", NULL) == -1) err(1, "pledge"); } } #endif /* make list fd independent and line-buffered */ if ((listfd = dup(fileno(listf))) < 0 || !(listf = fdopen(listfd, "wb"))) { syswarn(1, errno, "Cannot open list file descriptor"); return (exit_val); } if (fcntl(listfd, F_SETFD, FD_CLOEXEC) == -1) syswarn(0, errno, "%s on list file descriptor", "Failed to set the close-on-exec flag"); setlinebuf(listf); /* * select a primary operation mode */ switch (act) { case EXTRACT: extract(); break; case ARCHIVE: archive(); break; case APPND: if (compress_program != NULL) errx(1, "cannot compress while appending"); append(); break; case COPY: copy(); break; default: /* for ar_io.c etc. */ act = LIST; /* FALLTHROUGH */ case LIST: list(); break; } return(exit_val); } /* * sig_cleanup() * when interrupted we try to do whatever delayed processing we can. * This is not critical, but we really ought to limit our damage when we * are aborted by the user. * Return: * never.... */ void sig_cleanup(int which_sig) { /* * Restore modes and times for any dirs we may have created * or any dirs we may have read. Set vflag and vfpart so the * user will clearly see the message on a line by itself. */ vflag = vfpart = 1; /* paxwarn() uses stdio; fake it as well as we can */ if (which_sig == SIGXCPU) dprintf(STDERR_FILENO, "\nCPU time limit reached, cleaning up.\n"); else if (!which_sig) dprintf(STDERR_FILENO, "\nCowardly giving up, trying to clean up.\n"); else dprintf(STDERR_FILENO, "\nSignal caught, cleaning up.\n"); ar_close(1); sltab_process(1); proc_dir(1); if (tflag) atdir_end(); _exit(1); } /* * setup_sig() * set a signal to be caught, but only if it isn't being ignored already */ static int setup_sig(int sig, const struct sigaction *n_hand) { struct sigaction o_hand; if (sigaction(sig, NULL, &o_hand) < 0) return (-1); if (o_hand.sa_handler == SIG_IGN) return (0); return (sigaction(sig, n_hand, NULL)); } /* * gen_init() * general setup routines. Not all are required, but they really help * when dealing with a medium to large sized archives. */ static int gen_init(void) { struct rlimit reslimit; struct sigaction n_hand; /* * Really needed to handle large archives. We can run out of memory for * internal tables really fast when we have a whole lot of files... */ if (getrlimit(RLIMIT_DATA , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_DATA , &reslimit); } /* * should file size limits be waived? if the os limits us, this is * needed if we want to write a large archive */ if (getrlimit(RLIMIT_FSIZE , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_FSIZE , &reslimit); } /* * increase the size the stack can grow to */ if (getrlimit(RLIMIT_STACK , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_STACK , &reslimit); } /* * not really needed, but doesn't hurt */ #ifdef RLIMIT_RSS if (getrlimit(RLIMIT_RSS , &reslimit) == 0){ reslimit.rlim_cur = reslimit.rlim_max; (void)setrlimit(RLIMIT_RSS , &reslimit); } #endif /* * signal handling to reset stored directory times and modes. Since * we deal with broken pipes via failed writes we ignore it. We also * deal with any file size limit through failed writes. CPU time * limits are caught and a cleanup is forced. */ if ((sigemptyset(&s_mask) < 0) || (sigaddset(&s_mask, SIGTERM) < 0) || (sigaddset(&s_mask,SIGINT) < 0)||(sigaddset(&s_mask,SIGHUP) < 0) || (sigaddset(&s_mask,SIGPIPE) < 0)||(sigaddset(&s_mask,SIGQUIT)<0) || (sigaddset(&s_mask,SIGXCPU) < 0)||(sigaddset(&s_mask,SIGXFSZ)<0)) { paxwarn(1, "Unable to set up signal mask"); return(-1); } /* snag the fd to be used from the signal handler */ listfd = fileno(listf); memset(&n_hand, 0, sizeof n_hand); n_hand.sa_mask = s_mask; n_hand.sa_flags = 0; n_hand.sa_handler = sig_cleanup; if (setup_sig(SIGHUP, &n_hand) || setup_sig(SIGTERM, &n_hand) || setup_sig(SIGINT, &n_hand) || setup_sig(SIGQUIT, &n_hand) || setup_sig(SIGXCPU, &n_hand)) goto out; n_hand.sa_handler = SIG_IGN; if ((sigaction(SIGPIPE, &n_hand, NULL) < 0) || (sigaction(SIGXFSZ, &n_hand, NULL) < 0)) goto out; return (0); out: syswarn(1, errno, "Unable to set up signal handler"); return (-1); } pax/pax.h010064400000000000000000000270741466023622600075570ustar00/* $OpenBSD: pax.h,v 1.29 2017/09/12 17:11:11 otto Exp $ */ /* $NetBSD: pax.h,v 1.3 1995/03/21 09:07:41 cgd Exp $ */ /*- * Copyright (c) 2006, 2009, 2011, 2012, 2016, 2017, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)pax.h 8.2 (Berkeley) 4/18/94 */ #include "compat.h" #ifdef EXTERN __IDSTRING(rcsid_pax_h, "$MirOS: src/bin/pax/pax.h,v 1.27 2024/08/18 00:12:43 tg Exp $"); #endif /* * BSD PAX global data structures and constants. */ #define MAXBLK 64512 /* MAX blocksize supported (posix SPEC) */ /* WARNING: increasing MAXBLK past 32256 */ /* will violate posix spec. */ #define MAXBLK_POSIX 32256 /* MAX blocksize supported as per POSIX */ #define BLKMULT 512 /* blocksize must be even mult of 512 bytes */ /* Don't even think of changing this */ #define DEVBLK 8192 /* default read blksize for devices */ #define FILEBLK 10240 /* default read blksize for files */ #define PAXPATHLEN 3072 /* maximum path length for pax. MUST be */ /* longer than the system PATH_MAX */ /* * pax modes of operation */ #define ERROR -1 /* nothing selected */ #define LIST 0 /* List the file in an archive */ #define EXTRACT 1 /* extract the files in an archive */ #define ARCHIVE 2 /* write a new archive */ #define APPND 3 /* append to the end of an archive */ #define COPY 4 /* copy files to destination dir */ /* * Device type of the current archive volume */ #define ISREG 0 /* regular file */ #define ISCHR 1 /* character device */ #define ISBLK 2 /* block device */ #define ISTAPE 3 /* tape drive */ #define ISPIPE 4 /* pipe/socket */ /* * Pattern matching structure * * Used to store command line patterns */ typedef struct pattern { char *pstr; /* pattern to match, user supplied */ char *pend; /* end of a prefix match */ char *chdname; /* the dir to change to if not NULL. */ size_t plen; /* length of pstr */ int flgs; /* processing/state flags */ #define MTCH 0x1 /* pattern has been matched */ #define DIR_MTCH 0x2 /* pattern matched a directory */ struct pattern *fow; /* next pattern */ } PATTERN; /* * General Archive Structure (used internal to pax) * * This structure is used to pass information about archive members between * the format independent routines and the format specific routines. When * new archive formats are added, they must accept requests and supply info * encoded in a structure of this type. The name fields are declared statically * here, as there is only ONE of these floating around, size is not a major * consideration. Eventually converting the name fields to a dynamic length * may be required if and when the supporting operating system removes all * restrictions on the length of pathnames it will resolve. */ typedef struct { int nlen; /* file name length */ char name[PAXPATHLEN+1]; /* file name */ int ln_nlen; /* link name length */ char ln_name[PAXPATHLEN+1]; /* name to link to (if any) */ char *org_name; /* orig name in filesystem */ PATTERN *pat; /* ptr to pattern match (if any) */ struct stat sb; /* stat buffer see stat(2) */ off_t pad; /* bytes of padding after file xfer */ off_t skip; /* bytes of real data after header */ /* IMPORTANT. The st_size field does */ /* not always indicate the amount of */ /* data following the header. */ uint32_t crc; /* file crc */ int type; /* type of file node */ #define PAX_DIR 1 /* directory */ #define PAX_CHR 2 /* character device */ #define PAX_BLK 3 /* block device */ #define PAX_REG 4 /* regular file */ #define PAX_SLK 5 /* symbolic link */ #define PAX_SCK 6 /* socket */ #define PAX_FIF 7 /* FIFO */ #define PAX_HLK 8 /* hard link */ #define PAX_HRG 9 /* hard link to a regular file */ #define PAX_CTG 10 /* high performance file */ #define PAX_GLL 11 /* GNU long symlink */ #define PAX_GLF 12 /* GNU long file */ #define PAX_LINKOR 0x80000000 /* hard link detection OR */ } ARCHD; #define PAX_IS_REG(type) ((type) == PAX_REG || (type) == PAX_CTG) #define PAX_IS_HARDLINK(type) ((type) == PAX_HLK || (type) == PAX_HRG) #define PAX_IS_LINK(type) ((type) == PAX_SLK || PAX_IS_HARDLINK(type)) /* * Format Specific Routine Table * * The format specific routine table allows new archive formats to be quickly * added. Overall pax operation is independent of the actual format used to * form the archive. Only those routines which deal directly with the archive * are tailored to the oddities of the specific format. All other routines are * independent of the archive format. Data flow in and out of the format * dependent routines pass pointers to ARCHD structure (described below). */ typedef struct { const char *name; /* name of format, this is the name the user */ /* gives to -x option to select it. */ int bsz; /* default block size. used when the user */ /* does not specify a blocksize for writing */ /* Appends continue to with the blocksize */ /* the archive is currently using. */ unsigned short hsz; /* Header size in bytes. this is the size of */ /* the smallest header this format supports. */ /* Headers are assumed to fit in a BLKMULT. */ /* If they are bigger, get_head() and */ /* get_arc() must be adjusted */ char udev; /* does append require unique dev/ino? some */ /* formats use the device and inode fields */ /* to specify hard links. when members in */ /* the archive have the same inode/dev they */ /* are assumed to be hard links. During */ /* append we may have to generate unique ids */ /* to avoid creating incorrect hard links */ char hlk; /* does archive store hard links info? if */ /* not, we do not bother to look for them */ /* during archive write operations */ int blkalgn; /* writes must be aligned to blkalgn boundary */ int (*id)(char *, /* checks if a buffer is a valid header */ int); /* returns 1 if it is, o.w. returns a 0 */ int (*st_rd)(void); /* initialise routine for read. so format */ /* can set up tables etc before it starts */ /* reading an archive */ int (*rd)(ARCHD *, /* read header routine. passed a pointer to */ char *); /* ARCHD. It must extract the info from the */ /* format and store it in the ARCHD struct. */ /* This routine is expected to fill all the */ /* fields in the ARCHD (including stat buf) */ /* 0 is returned when a valid header is */ /* found. -1 when not valid. This routine */ /* set the skip and pad fields so the format */ /* independent routines know the amount of */ /* padding and the number of bytes of data */ /* which follow the header. This info is */ /* used skip to the next file header */ off_t (*end_rd)(void); /* read cleanup. Allows format to clean up */ /* and MUST RETURN THE LENGTH OF THE TRAILER */ /* RECORD (so append knows how many bytes */ /* to move back to rewrite the trailer) */ int (*st_wr)(int); /* initialise routine for write operations */ int (*wr)(ARCHD *); /* write archive header. Passed an ARCHD */ /* filled with the specs on the next file to */ /* archived. Returns a 1 if no file data is */ /* is to be stored; 0 if file data is to be */ /* added. A -1 is returned if a write */ /* operation to the archive failed. this */ /* function sets the skip and pad fields so */ /* the proper padding can be added after */ /* file data. This routine must NEVER write */ /* a flawed archive header. */ int (*end_wr)(void); /* end write. write the trailer and do any */ /* other format specific functions needed */ /* at the end of an archive write */ int (*trail)(ARCHD *, /* returns 0 if a valid trailer, -1 if not */ char *, int, /* For formats which encode the trailer */ int *); /* outside of a valid header, a return value */ /* of 1 indicates that the block passed to */ /* it can never contain a valid header (skip */ /* this block, no point in looking at it) */ /* CAUTION: parameters to this function are */ /* different for trailers inside or outside */ /* of headers. See get_head() for details */ int (*rd_data)(ARCHD *, /* read/process file data from the archive */ int, off_t *); int (*wr_data)(ARCHD *, /* write/process file data to the archive */ int, off_t *); int (*options)(void); /* process format specific options (-o) */ char inhead; /* is the trailer encoded in a valid header? */ /* if not, trailers are assumed to be found */ /* in invalid headers (i.e like tar) */ char is_uar; /* is Unix Archiver (sequential, no trailer) */ } FSUB; /* * Format Specific Options List * * Used to pass format options to the format options handler */ typedef struct oplist { char *name; /* option variable name e.g. name= */ char *value; /* value for option variable */ struct oplist *fow; /* next option */ } OPLIST; /* * Archive manipulation code */ #define ANON_INODES 0x0001 #define ANON_HARDLINKS 0x0002 #define ANON_MTIME 0x0004 #define ANON_UIDGID 0x0008 #define ANON_VERBOSE 0x0010 #define ANON_DEBUG 0x0020 #define ANON_LNCP 0x0040 #define ANON_NUMID 0x0080 #define ANON_DIRSLASH 0x0100 #define ANON_MAXVAL 0x01FF /* format table, see FSUB fsub[] in options.c */ enum fsub_order { #ifndef SMALL FSUB_AR, FSUB_BCPIO, #endif FSUB_CPIO, #ifndef SMALL FSUB_DIST, #endif FSUB_SV4CPIO, FSUB_SV4CRC, #ifndef SMALL FSUB_TAR, #endif FSUB_USTAR, #ifndef SMALL FSUB_V4NORM, FSUB_V4ROOT, FSUBFAIL_Z, FSUBFAIL_XZ, FSUBFAIL_BZ2, FSUBFAIL_GZ, #endif FSUB_MAX }; /* * General Macros */ #define MINIMUM(a,b) (((a) < (b)) ? (a) : (b)) #define MAJOR(x) major(x) #define MINOR(x) minor(x) #ifdef __INTERIX #define TODEV mkdev #else #define TODEV makedev #endif #define FILEBITS (S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO) #define SETBITS (S_ISUID | S_ISGID) #define ABITS (FILEBITS | SETBITS) /* * General Defines */ #define HEX 16 #define OCT 8 #define _PAX_ 1 #define _TFILE_BASE "paxXXXXXXXXXX" #if HAVE_TIMET_LONG #define MAX_TIME_T LONG_MAX #else #define MAX_TIME_T LLONG_MAX #endif #define MIRCPIO_VERSION "2024-08-17" pax/reallocarray.c010064400000000000000000000030031372452222500114210ustar00/* $OpenBSD: reallocarray.c,v 1.3 2015/09/13 08:31:47 guenther Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #ifdef MBSDPORT_H #include MBSDPORT_H #endif __RCSID("$MirOS: src/bin/pax/reallocarray.c,v 1.2 2020/09/04 20:32:58 tg Exp $"); /* * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW */ #define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } #ifdef DEF_WEAK DEF_WEAK(reallocarray); #endif pax/sel_subs.c010064400000000000000000000353301364306522600105730ustar00/* $OpenBSD: sel_subs.c,v 1.27 2018/09/13 12:33:43 millert Exp $ */ /* $NetBSD: sel_subs.c,v 1.5 1995/03/21 09:07:42 cgd Exp $ */ /*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #include #if HAVE_GRP_H #include #endif #include #include #include #include #if HAVE_STRINGS_H #include #endif #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/sel_subs.c,v 1.11 2020/04/07 11:56:43 tg Exp $"); /* * data structure for storing uid/grp selects (-U, -G non standard options) */ #define USR_TB_SZ 317 /* user selection table size */ #define GRP_TB_SZ 317 /* user selection table size */ typedef struct usrt { uid_t uid; struct usrt *fow; /* next uid */ } USRT; typedef struct grpt { gid_t gid; struct grpt *fow; /* next gid */ } GRPT; /* * data structure for storing user supplied time ranges (-T option) */ #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; typedef struct time_rng { time_t low_time; /* lower inclusive time limit */ time_t high_time; /* higher inclusive time limit */ int flgs; /* option flags */ #define HASLOW 0x01 /* has lower time limit */ #define HASHIGH 0x02 /* has higher time limit */ #define CMPMTME 0x04 /* compare file modification time */ #define CMPCTME 0x08 /* compare inode change time */ #define CMPBOTH (CMPMTME | CMPCTME) /* compare inode and mod time */ struct time_rng *fow; /* next pattern */ } TIME_RNG; static int str_sec(const char *, time_t *); static int usr_match(ARCHD *); static int grp_match(ARCHD *); static int trng_match(ARCHD *); static TIME_RNG *trhead = NULL; /* time range list head */ static TIME_RNG *trtail = NULL; /* time range list tail */ static USRT **usrtb = NULL; /* user selection table */ static GRPT **grptb = NULL; /* group selection table */ /* * Routines for selection of archive members */ /* * sel_chk() * check if this file matches a specified uid, gid or time range * Return: * 0 if this archive member should be processed, 1 if it should be skipped */ int sel_chk(ARCHD *arcn) { if (((usrtb != NULL) && usr_match(arcn)) || ((grptb != NULL) && grp_match(arcn)) || ((trhead != NULL) && trng_match(arcn))) return(1); return(0); } /* * User/group selection routines * * Routines to handle user selection of files based on the file uid/gid. To * add an entry, the user supplies either the name or the uid/gid starting with * a # on the command line. A \# will escape the #. */ /* * usr_add() * add a user match to the user match hash table * Return: * 0 if added ok, -1 otherwise; */ int usr_add(char *str) { unsigned int indx; USRT *pt; uid_t uid; /* * create the table if it doesn't exist */ if ((str == NULL) || (*str == '\0')) return(-1); if ((usrtb == NULL) && ((usrtb = calloc(USR_TB_SZ, sizeof(USRT *))) == NULL)) { paxwarn(1, "%s for %s", "Out of memory", "user selection table"); return (-1); } /* * figure out user spec */ if (str[0] != '#') { /* * it is a user name, \# escapes # as first char in user name */ if ((str[0] == '\\') && (str[1] == '#')) ++str; if (uid_uncached(str, &uid) < 0) { paxwarn(1, "Unable to find uid for user %s", str); return (-1); } } else uid = (uid_t)strtoul(str+1, NULL, 10); endpwent(); /* * hash it and go down the hash chain (if any) looking for it */ indx = ((unsigned)uid) % USR_TB_SZ; if ((pt = usrtb[indx]) != NULL) { while (pt != NULL) { if (pt->uid == uid) return(0); pt = pt->fow; } } /* * uid is not yet in the table, add it to the front of the chain */ if ((pt = malloc(sizeof(USRT))) != NULL) { pt->uid = uid; pt->fow = usrtb[indx]; usrtb[indx] = pt; return(0); } paxwarn(1, "%s for %s", "Out of memory", "user selection table"); return (-1); } /* * usr_match() * check if this files uid matches a selected uid. * Return: * 0 if this archive member should be processed, 1 if it should be skipped */ static int usr_match(ARCHD *arcn) { USRT *pt; /* * hash and look for it in the table */ pt = usrtb[((unsigned)arcn->sb.st_uid) % USR_TB_SZ]; while (pt != NULL) { if (pt->uid == arcn->sb.st_uid) return(0); pt = pt->fow; } /* * not found */ return(1); } /* * grp_add() * add a group match to the group match hash table * Return: * 0 if added ok, -1 otherwise; */ int grp_add(char *str) { unsigned int indx; GRPT *pt; gid_t gid; /* * create the table if it doesn't exist */ if ((str == NULL) || (*str == '\0')) return(-1); if ((grptb == NULL) && ((grptb = calloc(GRP_TB_SZ, sizeof(GRPT *))) == NULL)) { paxwarn(1, "%s for %s", "Out of memory", "group selection table"); return (-1); } /* * figure out group spec */ if (str[0] != '#') { /* * it is a group name, \# escapes # as first char in group name */ if ((str[0] == '\\') && (str[1] == '#')) ++str; if (gid_uncached(str, &gid) < 0) { paxwarn(1, "Unable to find gid for group %s", str); return (-1); } } else gid = (gid_t)strtoul(str+1, NULL, 10); endgrent(); /* * hash it and go down the hash chain (if any) looking for it */ indx = ((unsigned)gid) % GRP_TB_SZ; if ((pt = grptb[indx]) != NULL) { while (pt != NULL) { if (pt->gid == gid) return(0); pt = pt->fow; } } /* * gid not in the table, add it to the front of the chain */ if ((pt = malloc(sizeof(GRPT))) != NULL) { pt->gid = gid; pt->fow = grptb[indx]; grptb[indx] = pt; return(0); } paxwarn(1, "%s for %s", "Out of memory", "group selection table"); return (-1); } /* * grp_match() * check if this files gid matches a selected gid. * Return: * 0 if this archive member should be processed, 1 if it should be skipped */ static int grp_match(ARCHD *arcn) { GRPT *pt; /* * hash and look for it in the table */ pt = grptb[((unsigned)arcn->sb.st_gid) % GRP_TB_SZ]; while (pt != NULL) { if (pt->gid == arcn->sb.st_gid) return(0); pt = pt->fow; } /* * not found */ return(1); } /* * Time range selection routines * * Routines to handle user selection of files based on the modification and/or * inode change time falling within a specified time range (the non-standard * -T flag). The user may specify any number of different file time ranges. * Time ranges are checked one at a time until a match is found (if at all). * If the file has a mtime (and/or ctime) which lies within one of the time * ranges, the file is selected. Time ranges may have a lower and/or a upper * value. These ranges are inclusive. When no time ranges are supplied to pax * with the -T option, all members in the archive will be selected by the time * range routines. When only a lower range is supplied, only files with a * mtime (and/or ctime) equal to or younger are selected. When only a upper * range is supplied, only files with a mtime (and/or ctime) equal to or older * are selected. When the lower time range is equal to the upper time range, * only files with a mtime (or ctime) of exactly that time are selected. */ /* * trng_add() * add a time range match to the time range list. * This is a non-standard pax option. Lower and upper ranges are in the * format: [[[[[cc]yy]mm]dd]HH]MM[.SS] and are comma separated. * Time ranges are based on current time, so 1234 would specify a time of * 12:34 today. * Return: * 0 if the time range was added to the list, -1 otherwise */ int trng_add(char *str) { TIME_RNG *pt; char *up_pt = NULL; char *stpt; char *flgpt; int dot = 0; /* * throw out the badly formed time ranges */ if ((str == NULL) || (*str == '\0')) { paxwarn(1, "Empty time range string"); return(-1); } /* * locate optional flags suffix /{cm}. */ if ((flgpt = strrchr(str, '/')) != NULL) *flgpt++ = '\0'; for (stpt = str; *stpt != '\0'; ++stpt) { if ((*stpt >= '0') && (*stpt <= '9')) continue; if ((*stpt == ',') && (up_pt == NULL)) { *stpt = '\0'; up_pt = stpt + 1; dot = 0; continue; } /* * allow only one dot per range (secs) */ if ((*stpt == '.') && (!dot)) { ++dot; continue; } paxwarn(1, "Improperly specified time range: %s", str); goto out; } /* * allocate space for the time range and store the limits */ if ((pt = malloc(sizeof(TIME_RNG))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "time range"); return (-1); } /* * by default we only will check file mtime, but user can specify * mtime, ctime (inode change time) or both. */ if ((flgpt == NULL) || (*flgpt == '\0')) pt->flgs = CMPMTME; else { pt->flgs = 0; while (*flgpt != '\0') { switch (*flgpt) { case 'M': case 'm': pt->flgs |= CMPMTME; break; case 'C': case 'c': pt->flgs |= CMPCTME; break; default: paxwarn(1, "Bad option %c with time range %s", *flgpt, str); free(pt); goto out; } ++flgpt; } } /* * start off with the current time */ pt->low_time = pt->high_time = time(NULL); if (*str != '\0') { /* * add lower limit */ if (str_sec(str, &(pt->low_time)) < 0) { paxwarn(1, "Illegal %ser time range %s", "low", str); free(pt); goto out; } pt->flgs |= HASLOW; } if ((up_pt != NULL) && (*up_pt != '\0')) { /* * add upper limit */ if (str_sec(up_pt, &(pt->high_time)) < 0) { paxwarn(1, "Illegal %ser time range %s", "upp", up_pt); free(pt); goto out; } pt->flgs |= HASHIGH; /* * check that the upper and lower do not overlap */ if (pt->flgs & HASLOW) { if (pt->low_time > pt->high_time) { paxwarn(1, "Upper %s and lower %s time overlap", up_pt, str); free(pt); return(-1); } } } pt->fow = NULL; if (trhead == NULL) { trtail = trhead = pt; return(0); } trtail->fow = pt; trtail = pt; return(0); out: paxwarn(1, "Time range format is: [[[[[cc]yy]mm]dd]HH]MM[.SS][/[c][m]]"); return(-1); } /* * trng_match() * check if this files mtime/ctime falls within any supplied time range. * Return: * 0 if this archive member should be processed, 1 if it should be skipped */ static int trng_match(ARCHD *arcn) { TIME_RNG *pt; /* * have to search down the list one at a time looking for a match. * remember time range limits are inclusive. */ pt = trhead; while (pt != NULL) { switch (pt->flgs & CMPBOTH) { case CMPBOTH: /* * user wants both mtime and ctime checked for this * time range */ if (((pt->flgs & HASLOW) && (arcn->sb.st_mtime < pt->low_time) && (arcn->sb.st_ctime < pt->low_time)) || ((pt->flgs & HASHIGH) && (arcn->sb.st_mtime > pt->high_time) && (arcn->sb.st_ctime > pt->high_time))) { pt = pt->fow; continue; } break; case CMPCTME: /* * user wants only ctime checked for this time range */ if (((pt->flgs & HASLOW) && (arcn->sb.st_ctime < pt->low_time)) || ((pt->flgs & HASHIGH) && (arcn->sb.st_ctime > pt->high_time))) { pt = pt->fow; continue; } break; case CMPMTME: default: /* * user wants only mtime checked for this time range */ if (((pt->flgs & HASLOW) && (arcn->sb.st_mtime < pt->low_time)) || ((pt->flgs & HASHIGH) && (arcn->sb.st_mtime > pt->high_time))) { pt = pt->fow; continue; } break; } break; } if (pt == NULL) return(1); return(0); } /* * str_sec() * Convert a time string in the format of [[[[[cc]yy]mm]dd]HH]MM[.SS] to * seconds UTC. Tval already has current time loaded into it at entry. * Return: * 0 if converted ok, -1 otherwise */ static int str_sec(const char *p, time_t *tval) { struct tm *lt; const char *dot, *t; size_t len; int bigyear; int yearset; yearset = 0; len = strlen(p); for (t = p, dot = NULL; *t; ++t) { if (isdigit((unsigned char)*t)) continue; if (*t == '.' && dot == NULL) { dot = t; continue; } return(-1); } lt = localtime(tval); if (dot != NULL) { /* .SS */ if (strlen(++dot) != 2) return(-1); lt->tm_sec = ATOI2(dot); if (lt->tm_sec > 61) return(-1); len -= 3; } else lt->tm_sec = 0; switch (len) { case 12: /* cc */ bigyear = ATOI2(p); lt->tm_year = (bigyear * 100LL) - 1900LL; yearset = 1; /* FALLTHROUGH */ case 10: /* yy */ if (yearset) { lt->tm_year += ATOI2(p); } else { lt->tm_year = ATOI2(p); if (lt->tm_year < 69) /* hack for 2000 ;-} */ lt->tm_year += (2000 - 1900); } /* FALLTHROUGH */ case 8: /* mm */ lt->tm_mon = ATOI2(p); if ((lt->tm_mon > 12) || !lt->tm_mon) return(-1); --lt->tm_mon; /* time struct is 0 - 11 */ /* FALLTHROUGH */ case 6: /* dd */ lt->tm_mday = ATOI2(p); if ((lt->tm_mday > 31) || !lt->tm_mday) return(-1); /* FALLTHROUGH */ case 4: /* HH */ lt->tm_hour = ATOI2(p); if (lt->tm_hour > 23) return(-1); /* FALLTHROUGH */ case 2: /* MM */ lt->tm_min = ATOI2(p); if (lt->tm_min > 59) return(-1); break; default: return(-1); } /* convert broken-down time to UTC clock time seconds */ if ((*tval = mktime(lt)) == -1) return(-1); return(0); } pax/strmode.c010064400000000000000000000066421372452222600104330ustar00/* $OpenBSD: strmode.c,v 1.8 2015/08/31 02:53:57 guenther Exp $ */ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #ifdef MBSDPORT_H #include MBSDPORT_H #endif __RCSID("$MirOS: src/bin/pax/strmode.c,v 1.5 2020/09/04 20:32:59 tg Exp $"); void strmode(mode_t mode, char *p) { /* print type */ switch (mode & S_IFMT) { case S_IFDIR: /* directory */ *p++ = 'd'; break; case S_IFCHR: /* character special device */ *p++ = 'c'; break; case S_IFBLK: /* block special device */ *p++ = 'b'; break; case S_IFREG: /* regular file */ *p++ = '-'; break; case S_IFLNK: /* symbolic link */ *p++ = 'l'; break; case S_IFSOCK: /* socket */ *p++ = 's'; break; #ifdef S_IFIFO case S_IFIFO: /* FIFO */ *p++ = 'p'; break; #endif default: /* unknown */ *p++ = '?'; break; } /* usr */ if (mode & S_IRUSR) *p++ = 'r'; else *p++ = '-'; if (mode & S_IWUSR) *p++ = 'w'; else *p++ = '-'; switch (mode & (S_IXUSR | S_ISUID)) { case 0: *p++ = '-'; break; case S_IXUSR: *p++ = 'x'; break; case S_ISUID: *p++ = 'S'; break; case S_IXUSR | S_ISUID: *p++ = 's'; break; } /* group */ if (mode & S_IRGRP) *p++ = 'r'; else *p++ = '-'; if (mode & S_IWGRP) *p++ = 'w'; else *p++ = '-'; switch (mode & (S_IXGRP | S_ISGID)) { case 0: *p++ = '-'; break; case S_IXGRP: *p++ = 'x'; break; case S_ISGID: *p++ = 'S'; break; case S_IXGRP | S_ISGID: *p++ = 's'; break; } /* other */ if (mode & S_IROTH) *p++ = 'r'; else *p++ = '-'; if (mode & S_IWOTH) *p++ = 'w'; else *p++ = '-'; switch (mode & (S_IXOTH | S_ISVTX)) { case 0: *p++ = '-'; break; case S_IXOTH: *p++ = 'x'; break; case S_ISVTX: *p++ = 'T'; break; case S_IXOTH | S_ISVTX: *p++ = 't'; break; } *p++ = ' '; /* will be a '+' if ACLs implemented */ *p = '\0'; } #ifdef DEF_WEAK DEF_WEAK(strmode); #endif pax/strtonum.c010064400000000000000000000036631363255147700106610ustar00/* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ /* * Copyright (c) 2004 Ted Unangst and Todd Miller * Copyright (c) 2005 mirabilos * All rights reserved. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #ifdef MBSDPORT_H #include MBSDPORT_H #endif __RCSID("$MirOS: src/bin/pax/strtonum.c,v 1.4 2020/03/13 00:26:12 tg Exp $"); #define INVALID 1 #define TOOSMALL 2 #define TOOLARGE 3 long long strtonum(const char *numstr, long long minval, long long maxval, const char **errstrp) { long long ll = 0; int error = 0; char *ep; struct errval { const char *errstr; int err; } ev[4] = { { NULL, 0 }, { "invalid", EINVAL }, { "too small", ERANGE }, { "too large", ERANGE }, }; ev[0].err = errno; errno = 0; if (minval > maxval) { error = INVALID; } else { ll = strtoll(numstr, &ep, 0); if (numstr == ep || *ep != '\0') error = INVALID; else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) error = TOOSMALL; else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) error = TOOLARGE; } if (errstrp != NULL) *errstrp = ev[error].errstr; errno = ev[error].err; if (error) ll = 0; return (ll); } #ifdef DEF_WEAK DEF_WEAK(strtonum); #endif pax/tables.c010064400000000000000000001513271466023157300102330ustar00/* $OpenBSD: tables.c,v 1.53 2017/09/16 07:42:34 otto Exp $ */ /* $NetBSD: tables.c,v 1.4 1995/03/21 09:07:45 cgd Exp $ */ /*- * Copyright (c) 2005, 2012, 2015, 2016, 2018, 2019 * mirabilos * Copyright (c) 2011 * Svante Signell * Guillem Jover * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #if HAVE_BOTH_TIME_H #include #include #elif HAVE_SYS_TIME_H #include #elif HAVE_TIME_H #include #endif #include #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "ftimes.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/tables.c,v 1.33 2024/08/17 23:33:52 tg Exp $"); #if (_POSIX_VERSION >= 200809L) || (defined(MirBSD) && (MirBSD > 0x0AB9)) #define REALPATH_CAN_ALLOCATE #endif /* * Routines for controlling the contents of all the different databases pax * keeps. Tables are dynamically created only when they are needed. The * goal was speed and the ability to work with HUGE archives. The databases * were kept simple, but do have complex rules for when the contents change. * As of this writing, the posix library functions were more complex than * needed for this application (pax databases have very short lifetimes and * do not survive after pax is finished). pax is required to handle very * large archives. These database routines carefully combine memory usage and * temporary file storage in ways which will not significantly impact runtime * performance while allowing the largest possible archives to be handled. * Trying to force the fit to the posix database routines was not considered * time well spent. */ /* * data structures and constants used by the different databases kept by pax */ /* * Hash Table Sizes MUST BE PRIME, if set too small performance suffers. * Probably safe to expect 500000 inodes per tape. Assuming good key * distribution (inodes) chains of under 50 long (worst case) is ok. */ #define L_TAB_SZ 2503 /* hard link hash table size */ #define F_TAB_SZ 50503 /* file time hash table size */ #define N_TAB_SZ 541 /* interactive rename hash table */ #define D_TAB_SZ 317 /* unique device mapping table */ #define A_TAB_SZ 317 /* ftree dir access time reset table */ #define SL_TAB_SZ 317 /* escape symlink tables */ #define MAXKEYLEN 64 /* max number of chars for hash */ #define DIRP_SIZE 64 /* initial size of created dir table */ /* * file hard link structure (hashed by dev/ino and chained) used to find the * hard links in a filesystem or with some archive formats (cpio) */ typedef struct hrdlnk { ino_t ino; /* files inode number */ char *name; /* name of first file seen with this ino/dev */ dev_t dev; /* files device number */ u_long nlink; /* expected link count */ struct hrdlnk *fow; } HRDLNK; /* * Archive write update file time table (the -u, -C flag), hashed by filename. * Filenames are stored in a scratch file at seek offset into the file. The * file time (mod time) and the file name length (for a quick check) are * stored in a hash table node. We were forced to use a scratch file because * with -u, the mtime for every node in the archive must always be available * to compare against (and this data can get REALLY large with big archives). * By being careful to read only when we have a good chance of a match, the * performance loss is not measurable (and the size of the archive we can * handle is greatly increased). */ typedef struct ftm { off_t seek; /* location in scratch file */ struct ftm *fow; int namelen; /* file name length */ struct stat sb; /* files last modification time */ } FTM; /* * Interactive rename table (-i flag), hashed by orig filename. * We assume this will not be a large table as this mapping data can only be * obtained through interactive input by the user. Nobody is going to type in * changes for 500000 files? We use chaining to resolve collisions. */ typedef struct namt { char *oname; /* old name */ char *nname; /* new name typed in by the user */ struct namt *fow; } NAMT; /* * Unique device mapping tables. Some protocols (e.g. cpio) require that the * pair will uniquely identify a file in an archive unless they * are links to the same file. Appending to archives can break this. For those * protocols that have this requirement we map c_dev to a unique value not seen * in the archive when we append. We also try to handle inode truncation with * this table. (When the inode field in the archive header are too small, we * remap the dev on writes to remove accidental collisions). * * The list is hashed by device number using chain collision resolution. Off of * each DEVT are linked the various remaps for this device based on those bits * in the inode which were truncated. For example if we are just remapping to * avoid a device number during an update append, off the DEVT we would have * only a single DLIST that has a truncation id of 0 (no inode bits were * stripped for this device so far). When we spot inode truncation we create * a new mapping based on the set of bits in the inode which were stripped off. * so if the top four bits of the inode are stripped and they have a pattern of * 0110...... (where . are those bits not truncated) we would have a mapping * assigned for all inodes that has the same 0110.... pattern (with this dev * number of course). This keeps the mapping sparse and should be able to store * close to the limit of files which can be represented by the optimal * combination of dev and inode bits, and without creating a fouled up archive. * Note we also remap truncated devs in the same way (an exercise for the * dedicated reader; always wanted to say that...:) */ typedef struct devt { dev_t dev; /* the orig device number we now have to map */ struct devt *fow; /* new device map list */ struct dlist *list; /* map list based on inode truncation bits */ } DEVT; typedef struct dlist { ino_t trunc_bits; /* truncation pattern for a specific map */ dev_t dev; /* the new device id we use */ struct dlist *fow; } DLIST; /* * ftree directory access time reset table. When we are done with a * subtree we reset the access and mod time of the directory when the tflag is * set. Not really explicitly specified in the pax spec, but easy and fast to * do (and this may have even been intended in the spec, it is not clear). * table is hashed by inode with chaining. */ typedef struct atdir { struct file_times ft; struct atdir *fow; } ATDIR; /* * created directory time and mode storage entry. After pax is finished during * extraction or copy, we must reset directory access modes and times that * may have been modified after creation (they no longer have the specified * times and/or modes). We must reset time in the reverse order of creation, * because entries are added from the top of the file tree to the bottom. * We MUST reset times from leaf to root (it will not work the other * direction). */ typedef struct dirdata { struct file_times ft; mode_t tr_mode; /* file mode to restore */ Wahr frc_mode; /* do we force mode settings? */ } DIRDATA; /* * file hard link structure (hashed by dev/ino and chained) for anonymisation */ typedef struct hrdflnk { dev_t dev; /* files device number */ ino_t ino; /* files inode number */ u_long nlink; /* expected link count */ ino_t newi; /* new inode number */ struct hrdflnk *fow; } HRDFLNK; static HRDLNK **ltab = NULL; /* hard link table for detecting hard links */ static HRDFLNK **fltab = NULL; /* hard link table for anonymisation */ static FTM **ftab = NULL; /* file time table for updating arch */ static NAMT **ntab = NULL; /* interactive rename storage table */ static DEVT **dtab = NULL; /* device/inode mapping tables */ static ATDIR **atab = NULL; /* file tree directory time reset table */ static DIRDATA *dirp = NULL; /* storage for setting created dir time/mode */ static size_t dirsize; /* size of dirp table */ static size_t dircnt = 0; /* entries in dir time/mode storage */ static int ffd = -1; /* tmp file for file time table name storage */ static DEVT *chk_dev(dev_t, int); #ifndef REALPATH_CAN_ALLOCATE static char realname[PATH_MAX]; #endif static char * xrealpath(const char *path) { #ifndef REALPATH_CAN_ALLOCATE char *rv; if ((rv = realpath(path, realname))) rv = strdup(rv); return (rv); #else return (realpath(path, NULL)); #endif } /* * hard link table routines * * The hard link table tries to detect hard links to files using the device and * inode values. We do this when writing an archive, so we can tell the format * write routine that this file is a hard link to another file. The format * write routine then can store this file in whatever way it wants (as a hard * link if the format supports that like tar, or ignore this info like cpio). * (Actually a field in the format driver table tells us if the format wants * hard link info. if not, we do not waste time looking for them). We also use * the same table when reading an archive. In that situation, this table is * used by the format read routine to detect hard links from stored dev and * inode numbers (like cpio). This will allow pax to create a link when one * can be detected by the archive format. */ /* * lnk_start * Creates the hard link table. * Return: * 0 if created, -1 if failure */ int lnk_start(void) { if (ltab != NULL) return (0); if ((ltab = calloc(L_TAB_SZ, sizeof(HRDLNK *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "hard link table"); return (-1); } return (0); } /* * chk_lnk() * Looks up entry in hard link hash table. If found, it copies the name * of the file it is linked to (we already saw that file) into ln_name. * lnkcnt is decremented and if goes to 1 the node is deleted from the * database. (We have seen all the links to this file). If not found, * we add the file to the database if it has the potential for having * hard links to other files we may process (it has a link count > 1) * Return: * if found returns 1; if not found returns 0; -1 on error */ int chk_lnk(ARCHD *arcn) { HRDLNK *pt; HRDLNK **ppt; unsigned int indx; if (ltab == NULL) return(-1); /* * ignore those nodes that cannot have hard links */ if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) return(0); /* * hash inode number and look for this file */ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; if ((pt = ltab[indx]) != NULL) { /* * its hash chain in not empty, walk down looking for it */ ppt = &(ltab[indx]); while (pt != NULL) { if ((pt->ino == arcn->sb.st_ino) && (pt->dev == arcn->sb.st_dev)) break; ppt = &(pt->fow); pt = pt->fow; } if (pt != NULL) { /* * found a link. set the node type and copy in the * name of the file it is to link to. we need to * handle hardlinks to regular files differently than * other links. */ arcn->ln_nlen = strlcpy(arcn->ln_name, pt->name, sizeof(arcn->ln_name)); /* XXX truncate? */ if ((size_t)arcn->nlen >= sizeof(arcn->name)) arcn->nlen = sizeof(arcn->name) - 1; if (arcn->type == PAX_REG) arcn->type = PAX_HRG; else arcn->type = PAX_HLK; /* * if we have found all the links to this file, remove * it from the database */ if (--pt->nlink <= 1) { *ppt = pt->fow; free(pt->name); free(pt); } return(1); } } /* * we never saw this file before. It has links so we add it to the * front of this hash chain */ if ((pt = malloc(sizeof(HRDLNK))) != NULL) { if ((pt->name = strdup(arcn->name)) != NULL) { pt->dev = arcn->sb.st_dev; pt->ino = arcn->sb.st_ino; pt->nlink = arcn->sb.st_nlink; pt->fow = ltab[indx]; ltab[indx] = pt; return(0); } free(pt); } paxwarn(1, "%s for %s", "Out of memory", "hard link table"); return (-1); } /* * purg_lnk * remove reference for a file that we may have added to the data base as * a potential source for hard links. We ended up not using the file, so * we do not want to accidently point another file at it later on. */ void purg_lnk(ARCHD *arcn) { HRDLNK *pt; HRDLNK **ppt; unsigned int indx; if (ltab == NULL) return; /* * do not bother to look if it could not be in the database */ if ((arcn->sb.st_nlink <= 1) || (arcn->type == PAX_DIR) || PAX_IS_HARDLINK(arcn->type)) return; /* * find the hash chain for this inode value, if empty return */ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; if ((pt = ltab[indx]) == NULL) return; /* * walk down the list looking for the inode/dev pair, unlink and * free if found */ ppt = &(ltab[indx]); while (pt != NULL) { if ((pt->ino == arcn->sb.st_ino) && (pt->dev == arcn->sb.st_dev)) break; ppt = &(pt->fow); pt = pt->fow; } if (pt == NULL) return; /* * remove and free it */ *ppt = pt->fow; free(pt->name); free(pt); } /* * lnk_end() * pull apart a existing link table so we can reuse it. We do this between * read and write phases of append with update. (The format may have * used the link table, and we need to start with a fresh table for the * write phase */ void lnk_end(void) { int i; HRDLNK *pt; HRDLNK *ppt; if (ltab == NULL) return; for (i = 0; i < L_TAB_SZ; ++i) { if (ltab[i] == NULL) continue; pt = ltab[i]; ltab[i] = NULL; /* * free up each entry on this chain */ while (pt != NULL) { ppt = pt; pt = ppt->fow; free(ppt->name); free(ppt); } } } /* * modification time table routines * * The modification time table keeps track of last modification times for all * files stored in an archive during a write phase when -u is set. We only * add a file to the archive if it is newer than a file with the same name * already stored on the archive (if there is no other file with the same * name on the archive it is added). This applies to writes and appends. * An append with an -u must read the archive and store the modification time * for every file on that archive before starting the write phase. It is clear * that this is one HUGE database. To save memory space, the actual file names * are stored in a scratch file and indexed by an in-memory hash table. The * hash table is indexed by hashing the file path. The nodes in the table store * the length of the filename and the lseek offset within the scratch file * where the actual name is stored. Since there are never any deletions from * this table, fragmentation of the scratch file is never a issue. Lookups * seem to not exhibit any locality at all (files in the database are rarely * looked up more than once...), so caching is just a waste of memory. The * only limitation is the amount of scratch file space available to store the * path names. */ /* * ftime_start() * create the file time hash table and open for read/write the scratch * file. (after created it is unlinked, so when we exit we leave * no witnesses). * Return: * 0 if the table and file was created ok, -1 otherwise */ int ftime_start(void) { if (ftab != NULL) return (0); if ((ftab = calloc(F_TAB_SZ, sizeof(FTM *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "file time table"); return (-1); } /* * get random name and create temporary scratch file, unlink name * so it will get removed on exit */ memcpy(tempbase, _TFILE_BASE, sizeof(_TFILE_BASE)); if ((ffd = mkstemp(tempfile)) < 0) { syswarn(1, errno, "Unable to create temporary file %s", tempfile); return (-1); } (void)unlink(tempfile); return(0); } /* * chk_ftime() * looks up entry in file time hash table. If not found, the file is * added to the hash table and the file named stored in the scratch file. * If a file with the same name is found, the file times are compared and * the most recent file time is retained. If the new file was younger (or * was not in the database) the new file is selected for storage. * Return: * 0 if file should be added to the archive, 1 if it should be skipped, * -1 on error */ int chk_ftime(ARCHD *arcn) { FTM *pt; int namelen; unsigned int indx; char ckname[PAXPATHLEN+1]; /* * no info, go ahead and add to archive */ if (ftab == NULL) return(0); /* * hash the pathname and look up in table */ namelen = arcn->nlen; indx = st_hash(arcn->name, namelen, F_TAB_SZ); if ((pt = ftab[indx]) != NULL) { /* * the hash chain is not empty, walk down looking for match * only read up the path names if the lengths match, speeds * up the search a lot */ while (pt != NULL) { if (pt->namelen == namelen) { /* * potential match, have to read the name * from the scratch file. */ if (lseek(ffd,pt->seek,SEEK_SET) != pt->seek) { syswarn(1, errno, "Failed %s on %s", "seek", "file time table"); return (-1); } if (read(ffd, ckname, namelen) != namelen) { syswarn(1, errno, "Failed %s on %s", "read", "file time table"); return (-1); } /* * if the names match, we are done */ if (!strncmp(ckname, arcn->name, namelen)) break; } /* * try the next entry on the chain */ pt = pt->fow; } if (pt != NULL) { /* * found the file, compare the times, save the newer */ if (st_timecmp(m, &arcn->sb, &pt->sb, >)) { /* * file is newer */ st_timecpy(m, &pt->sb, &arcn->sb); return(0); } /* * file is older */ return(1); } } /* * not in table, add it */ if ((pt = malloc(sizeof(FTM))) != NULL) { /* * add the name at the end of the scratch file, saving the * offset. add the file to the head of the hash chain */ if ((pt->seek = lseek(ffd, 0, SEEK_END)) >= 0) { if (write(ffd, arcn->name, namelen) == namelen) { st_timecpy(m, &pt->sb, &arcn->sb); pt->namelen = namelen; pt->fow = ftab[indx]; ftab[indx] = pt; return(0); } syswarn(1, errno, "Failed %s on %s", "write", "file time table"); } else syswarn(1, errno, "Failed %s on %s", "seek", "file time table"); } else paxwarn(1, "%s for %s", "Out of memory", "file time table"); if (pt != NULL) free(pt); return(-1); } /* * escaping (absolute or w/"..") symlink table routines * * By default, an archive shouldn't be able extract to outside of the * current directory. What should we do if the archive contains a symlink * whose value is either absolute or contains ".." components? What we'll * do is initially create the path as an empty file (to block attempts to * reference _through_ it) and instead record its path and desired * final value and mode. Then once all the other archive * members are created (but before the pass to set timestamps on * directories) we'll process those records, replacing the placeholder with * the correct symlink and setting them to the correct mode, owner, group, * and timestamps. * * Note: we also need to handle hardlinks to symlinks (barf) as well as * hardlinks whose target is replaced by a later entry in the archive (barf^2). * * So we track things by dev+ino of the placeholder file, associating with * that the value and mode of the final symlink and a list of paths that * should all be hardlinks of that. We'll 'store' the symlink's desired * timestamps, owner, and group by setting them on the placeholder file. * * The operations are: * a) create an escaping symlink: create the placeholder file and add an entry * for the new link * b) create a hardlink: do the link. If the target turns out to be a * zero-length file whose dev+ino are in the symlink table, then add this * path to the list of names for that link * c) perform deferred processing: for each entry, check each associated path: * if it's a zero-length file with the correct dev+ino then recreate it as * the specified symlink or hardlink to the first such */ struct slpath { char *sp_path; struct slpath *sp_next; }; struct slinode { ino_t sli_ino; char *sli_value; struct slpath sli_paths; struct slinode *sli_fow; /* hash table chain */ dev_t sli_dev; mode_t sli_mode; }; static struct slinode **slitab = NULL; /* * sltab_start() * create the hash table * Return: * 0 if the table and file was created ok, -1 otherwise */ int sltab_start(void) { if ((slitab = calloc(SL_TAB_SZ, sizeof *slitab)) == NULL) { syswarn(1, errno, "symlink table"); return(-1); } return(0); } /* * sltab_add_sym() * Create the placeholder and tracking info for an escaping symlink. * Return: * 0 on success, -1 otherwise */ int sltab_add_sym(const char *path0, const char *value0, mode_t mode) { struct stat sb; struct slinode *s; struct slpath *p; char *path, *value; unsigned int indx; int fd; /* create the placeholder */ fd = binopen3(BO_CLEXEC, path0, O_WRONLY | O_CREAT | O_EXCL, 0600); if (fd == -1) return (-1); if (fstat(fd, &sb) == -1) { unlink(path0); close(fd); return (-1); } close(fd); if (havechd && *path0 != '/') { if ((path = xrealpath(path0)) == NULL) { syswarn(1, errno, "Cannot canonicalise %s", path0); unlink(path0); return (-1); } } else if ((path = strdup(path0)) == NULL) { syswarn(1, errno, "%s %s", "deferred symlink", "path"); unlink(path0); return (-1); } if ((value = strdup(value0)) == NULL) { syswarn(1, errno, "%s value", "deferred symlink"); unlink(path); free(path); return (-1); } /* now check the hash table for conflicting entry */ indx = (sb.st_ino ^ sb.st_dev) % SL_TAB_SZ; for (s = slitab[indx]; s != NULL; s = s->sli_fow) { if (s->sli_ino != sb.st_ino || s->sli_dev != sb.st_dev) continue; /* * One of our placeholders got removed behind our back and * we've reused the inode. Weird, but clean up the mess. */ free(s->sli_value); free(s->sli_paths.sp_path); p = s->sli_paths.sp_next; while (p != NULL) { struct slpath *next_p = p->sp_next; free(p->sp_path); free(p); p = next_p; } goto set_value; } /* Normal case: create a new node */ if ((s = malloc(sizeof *s)) == NULL) { syswarn(1, errno, "deferred symlink"); unlink(path); free(path); free(value); return (-1); } s->sli_ino = sb.st_ino; s->sli_dev = sb.st_dev; s->sli_fow = slitab[indx]; slitab[indx] = s; set_value: s->sli_paths.sp_path = path; s->sli_paths.sp_next = NULL; s->sli_value = value; s->sli_mode = mode; return (0); } /* * sltab_add_link() * A hardlink was created; if it looks like a placeholder, handle the * tracking. * Return: * 0 if things are ok, -1 if something went wrong */ int sltab_add_link(const char *path, const struct stat *sb) { struct slinode *s; struct slpath *p; unsigned int indx; if (!S_ISREG(sb->st_mode) || sb->st_size != 0) return (1); /* find the hash table entry for this hardlink */ indx = (sb->st_ino ^ sb->st_dev) % SL_TAB_SZ; for (s = slitab[indx]; s != NULL; s = s->sli_fow) { if (s->sli_ino != sb->st_ino || s->sli_dev != sb->st_dev) continue; if ((p = malloc(sizeof *p)) == NULL) { syswarn(1, errno, "%s hardlink", "deferred symlink"); return (-1); } if (havechd && *path != '/') { if ((p->sp_path = xrealpath(path)) == NULL) { syswarn(1, errno, "Cannot canonicalise %s", path); free(p); return (-1); } } else if ((p->sp_path = strdup(path)) == NULL) { syswarn(1, errno, "%s hardlink path", "deferred symlink"); free(p); return (-1); } /* link it in */ p->sp_next = s->sli_paths.sp_next; s->sli_paths.sp_next = p; return (0); } /* not found */ return (1); } static int sltab_process_one(struct slinode *s, struct slpath *p, const char *first, int in_sig) { struct stat sb; char *path = p->sp_path; mode_t mode; int err; /* * is it the expected placeholder? This can fail legimately * if the archive overwrote the link with another, later entry, * so don't warn. */ if (stat(path, &sb) != 0 || !S_ISREG(sb.st_mode) || sb.st_size != 0 || sb.st_ino != s->sli_ino || sb.st_dev != s->sli_dev) return (0); if (unlink(path) && errno != ENOENT) { if (!in_sig) syswarn(1, errno, "%s removal", "deferred symlink"); return (0); } err = 0; if (first != NULL) { #if HAVE_LINKAT /* add another hardlink to the existing symlink */ if (linkat(AT_FDCWD, first, AT_FDCWD, path, 0) == 0) return (0); /* * Couldn't hardlink the symlink for some reason, so we'll * try creating it as its own symlink, but save the error * for reporting if that fails. */ err = errno; #else err = EOPNOTSUPP; #endif } if (symlink(s->sli_value, path)) { if (!in_sig) { const char *qualifier = ""; if (err) qualifier = " hardlink"; else err = errno; syswarn(1, err, "%s%s: %s", "deferred symlink", qualifier, path); } return (0); } /* success, so set the id, mode, and times */ mode = s->sli_mode; if (pids) { /* if can't set the ids, force the set[ug]id bits off */ if (set_ids(path, sb.st_uid, sb.st_gid, 1)) mode &= ~(SETBITS); } if (pmode) set_pmode(path, mode, 1); if (patime || pmtime) set_ftime(path, &sb, 0, 1); /* * If we tried to link to first but failed, then this new symlink * might be a better one to try in the future. Guess from the errno. */ if (err == 0 || err == ENOENT || err == EMLINK || err == EOPNOTSUPP) return (1); return (0); } /* * sltab_process() * Do all the delayed process for escape symlinks */ void sltab_process(int in_sig) { struct slinode *s; struct slpath *p; char *first; unsigned int indx; if (slitab == NULL) return; /* walk across the entire hash table */ for (indx = 0; indx < SL_TAB_SZ; indx++) { while ((s = slitab[indx]) != NULL) { /* pop this entry */ slitab[indx] = s->sli_fow; first = NULL; p = &s->sli_paths; while (1) { struct slpath *next_p; if (sltab_process_one(s, p, first, in_sig)) { if (!in_sig) free(first); first = p->sp_path; } else if (!in_sig) free(p->sp_path); if ((next_p = p->sp_next) == NULL) break; *p = *next_p; if (!in_sig) free(next_p); } if (!in_sig) { free(first); free(s->sli_value); free(s); } } } if (!in_sig) free(slitab); slitab = NULL; } /* * Interactive rename table routines * * The interactive rename table keeps track of the new names that the user * assigns to files from tty input. Since this map is unique for each file * we must store it in case there is a reference to the file later in archive * (a link). Otherwise we will be unable to find the file we know was * extracted. The remapping of these files is stored in a memory based hash * table (it is assumed since input must come from /dev/tty, it is unlikely to * be a very large table). */ /* * name_start() * create the interactive rename table * Return: * 0 if successful, -1 otherwise */ int name_start(void) { if (ntab != NULL) return (0); if ((ntab = calloc(N_TAB_SZ, sizeof(NAMT *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "interactive rename table"); return (-1); } return (0); } /* * add_name() * add the new name to old name mapping just created by the user. * If an old name mapping is found (there may be duplicate names on an * archive) only the most recent is kept. * Return: * 0 if added, -1 otherwise */ int add_name(char *oname, int onamelen, char *nname) { NAMT *pt; unsigned int indx; if (ntab == NULL) { /* * should never happen */ paxwarn(0, "No interactive rename table, links may fail"); return(0); } /* * look to see if we have already mapped this file, if so we * will update it */ indx = st_hash(oname, onamelen, N_TAB_SZ); if ((pt = ntab[indx]) != NULL) { /* * look down the has chain for the file */ while ((pt != NULL) && (strcmp(oname, pt->oname) != 0)) pt = pt->fow; if (pt != NULL) { /* * found an old mapping, replace it with the new one * the user just input (if it is different) */ if (strcmp(nname, pt->nname) == 0) return(0); free(pt->nname); if ((pt->nname = strdup(nname)) == NULL) { paxwarn(1, "Cannot update rename table"); return(-1); } return(0); } } /* * this is a new mapping, add it to the table */ if ((pt = malloc(sizeof(NAMT))) != NULL) { if ((pt->oname = strdup(oname)) != NULL) { if ((pt->nname = strdup(nname)) != NULL) { pt->fow = ntab[indx]; ntab[indx] = pt; return(0); } free(pt->oname); } free(pt); } paxwarn(1, "%s for %s", "Out of memory", "interactive rename table"); return (-1); } /* * sub_name() * look up a link name to see if it points at a file that has been * remapped by the user. If found, the link is adjusted to contain the * new name (oname is the link to name) */ void sub_name(char *oname, int *onamelen, int onamesize) { NAMT *pt; unsigned int indx; if (ntab == NULL) return; /* * look the name up in the hash table */ indx = st_hash(oname, *onamelen, N_TAB_SZ); if ((pt = ntab[indx]) == NULL) return; while (pt != NULL) { /* * walk down the hash chain looking for a match */ if (strcmp(oname, pt->oname) == 0) { /* * found it, replace it with the new name * and return (we know that oname has enough space) */ *onamelen = strlcpy(oname, pt->nname, onamesize); if (*onamelen >= onamesize) *onamelen = onamesize - 1; /* XXX truncate? */ return; } pt = pt->fow; } /* * no match, just return */ } /* * device/inode mapping table routines * (used with formats that store device and inodes fields) * * device/inode mapping tables remap the device field in a archive header. The * device/inode fields are used to determine when files are hard links to each * other. However these values have very little meaning outside of that. This * database is used to solve one of two different problems. * * 1) when files are appended to an archive, while the new files may have hard * links to each other, you cannot determine if they have hard links to any * file already stored on the archive from a prior run of pax. We must assume * that these inode/device pairs are unique only within a SINGLE run of pax * (which adds a set of files to an archive). So we have to make sure the * inode/dev pairs we add each time are always unique. We do this by observing * while the inode field is very dense, the use of the dev field is fairly * sparse. Within each run of pax, we remap any device number of a new archive * member that has a device number used in a prior run and already stored in a * file on the archive. During the read phase of the append, we store the * device numbers used and mark them to not be used by any file during the * write phase. If during write we go to use one of those old device numbers, * we remap it to a new value. * * 2) Often the fields in the archive header used to store these values are * too small to store the entire value. The result is an inode or device value * which can be truncated. This really can foul up an archive. With truncation * we end up creating links between files that are really not links (after * truncation the inodes are the same value). We address that by detecting * truncation and forcing a remap of the device field to split truncated * inodes away from each other. Each truncation creates a pattern of bits that * are removed. We use this pattern of truncated bits to partition the inodes * on a single device to many different devices (each one represented by the * truncated bit pattern). All inodes on the same device that have the same * truncation pattern are mapped to the same new device. Two inodes that * truncate to the same value clearly will always have different truncation * bit patterns, so they will be split from away each other. When we spot * device truncation we remap the device number to a non truncated value. * (for more info see table.h for the data structures involved). */ /* * dev_start() * create the device mapping table * Return: * 0 if successful, -1 otherwise */ int dev_start(void) { if (dtab != NULL) return (0); if ((dtab = calloc(D_TAB_SZ, sizeof(DEVT *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "device mapping table"); return (-1); } return (0); } /* * add_dev() * add a device number to the table. this will force the device to be * remapped to a new value if it be used during a write phase. This * function is called during the read phase of an append to prohibit the * use of any device number already in the archive. * Return: * 0 if added ok, -1 otherwise */ int add_dev(ARCHD *arcn) { if (chk_dev(arcn->sb.st_dev, 1) == NULL) return(-1); return(0); } /* * chk_dev() * check for a device value in the device table. If not found and the add * flag is set, it is added. This does NOT assign any mapping values, just * adds the device number as one that need to be remapped. If this device * is already mapped, just return with a pointer to that entry. * Return: * pointer to the entry for this device in the device map table. Null * if the add flag is not set and the device is not in the table (it is * not been seen yet). If add is set and the device cannot be added, null * is returned (indicates an error). */ static DEVT * chk_dev(dev_t dev, int add) { DEVT *pt; unsigned int indx; if (dtab == NULL) return(NULL); /* * look to see if this device is already in the table */ indx = ((unsigned)dev) % D_TAB_SZ; if ((pt = dtab[indx]) != NULL) { while ((pt != NULL) && (pt->dev != dev)) pt = pt->fow; /* * found it, return a pointer to it */ if (pt != NULL) return(pt); } /* * not in table, we add it only if told to as this may just be a check * to see if a device number is being used. */ if (add == 0) return(NULL); /* * allocate a node for this device and add it to the front of the hash * chain. Note we do not assign remaps values here, so the pt->list * list must be NULL. */ if ((pt = malloc(sizeof(DEVT))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "device mapping table"); return (NULL); } pt->dev = dev; pt->list = NULL; pt->fow = dtab[indx]; dtab[indx] = pt; return(pt); } /* * map_dev() * given an inode and device storage mask (the mask has a 1 for each bit * the archive format is able to store in a header), we check for inode * and device truncation and remap the device as required. Device mapping * can also occur when during the read phase of append a device number was * seen (and was marked as do not use during the write phase). WE ASSUME * that unsigned longs are the same size or bigger than the fields used * for ino_t and dev_t. If not the types will have to be changed. * Return: * 0 if all ok, -1 otherwise. */ int map_dev(ARCHD *arcn, u_long dev_mask, u_long ino_mask) { DEVT *pt; DLIST *dpt; static dev_t lastdev = 0; /* next device number to try */ int trc_ino = 0; int trc_dev = 0; ino_t trunc_bits = 0; ino_t nino; if (dtab == NULL) return(0); /* * check for device and inode truncation, and extract the truncated * bit pattern. */ if ((arcn->sb.st_dev & (dev_t)dev_mask) != arcn->sb.st_dev) ++trc_dev; if ((nino = arcn->sb.st_ino & (ino_t)ino_mask) != arcn->sb.st_ino) { ++trc_ino; trunc_bits = arcn->sb.st_ino & (ino_t)(~ino_mask); } /* * see if this device is already being mapped, look up the device * then find the truncation bit pattern which applies */ if ((pt = chk_dev(arcn->sb.st_dev, 0)) != NULL) { /* * this device is already marked to be remapped */ for (dpt = pt->list; dpt != NULL; dpt = dpt->fow) if (dpt->trunc_bits == trunc_bits) break; if (dpt != NULL) { /* * we are being remapped for this device and pattern * change the device number to be stored and return */ arcn->sb.st_dev = dpt->dev; arcn->sb.st_ino = nino; return(0); } } else { /* * this device is not being remapped YET. if we do not have any * form of truncation, we do not need a remap */ if (!trc_ino && !trc_dev) return(0); /* * we have truncation, have to add this as a device to remap */ if ((pt = chk_dev(arcn->sb.st_dev, 1)) == NULL) goto bad; /* * if we just have a truncated inode, we have to make sure that * all future inodes that do not truncate (they have the * truncation pattern of all 0's) continue to map to the same * device number. We probably have already written inodes with * this device number to the archive with the truncation * pattern of all 0's. So we add the mapping for all 0's to the * same device number. */ if (!trc_dev && (trunc_bits != 0)) { if ((dpt = malloc(sizeof(DLIST))) == NULL) goto bad; dpt->trunc_bits = 0; dpt->dev = arcn->sb.st_dev; dpt->fow = pt->list; pt->list = dpt; } } /* * look for a device number not being used. We must watch for wrap * around on lastdev (so we do not get stuck looking forever!) */ while (++lastdev > 0) { if (chk_dev(lastdev, 0) != NULL) continue; /* * found an unused value. If we have reached truncation point * for this format we are hosed, so we give up. Otherwise we * mark it as being used. */ if (((lastdev & ((dev_t)dev_mask)) != lastdev) || (chk_dev(lastdev, 1) == NULL)) goto bad; break; } if ((lastdev <= 0) || ((dpt = malloc(sizeof(DLIST))) == NULL)) goto bad; /* * got a new device number, store it under this truncation pattern. * change the device number this file is being stored with. */ dpt->trunc_bits = trunc_bits; dpt->dev = lastdev; dpt->fow = pt->list; pt->list = dpt; arcn->sb.st_dev = lastdev; arcn->sb.st_ino = nino; return(0); bad: paxwarn(1, "Unable to fix truncated inode/device field when storing %s", arcn->name); paxwarn(0, "Archive may create improper hard links when extracted"); return(0); } /* * directory access/mod time reset table routines (for directories READ by pax) * * The pax -t flag requires that access times of archive files be the same * before being read by pax. For regular files, access time is restored after * the file has been copied. This database provides the same functionality for * directories read during file tree traversal. Restoring directory access time * is more complex than files since directories may be read several times until * all the descendants in their subtree are visited by fts. Directory access * and modification times are stored during the fts pre-order visit (done * before any descendants in the subtree are visited) and restored after the * fts post-order visit (after all the descendants have been visited). In the * case of premature exit from a subtree (like from the effects of -n), any * directory entries left in this database are reset during final cleanup * operations of pax. Entries are hashed by inode number for fast lookup. */ /* * atdir_start() * create the directory access time database for directories READ by pax. * Return: * 0 is created ok, -1 otherwise. */ int atdir_start(void) { if (atab != NULL) return (0); if ((atab = calloc(A_TAB_SZ, sizeof(ATDIR *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "directory access time reset table"); return (-1); } return (0); } /* * atdir_end() * walk through the directory access time table and reset the access time * of any directory who still has an entry left in the database. These * entries are for directories READ by pax */ void atdir_end(void) { ATDIR *pt; int i; if (atab == NULL) return; /* * for each non-empty hash table entry reset all the directories * chained there. */ for (i = 0; i < A_TAB_SZ; ++i) { if ((pt = atab[i]) == NULL) continue; /* * remember to force the times, set_ftime() looks at pmtime * and patime, which only applies to things CREATED by pax, * not read by pax. Read time reset is controlled by -t. */ for (; pt != NULL; pt = pt->fow) set_attr(&pt->ft, 1, 0, 0, 0); } } /* * add_atdir() * add a directory to the directory access time table. Table is hashed * and chained by inode number. This is for directories READ by pax */ void add_atdir(const char *fname, const struct stat *sbp) { ATDIR *pt; sigset_t allsigs, savedsigs; unsigned int indx; if (atab == NULL) return; /* * make sure this directory is not already in the table, if so just * return (the older entry always has the correct time). The only * way this will happen is when the same subtree can be traversed by * different args to pax and the -n option is aborting fts out of a * subtree before all the post-order visits have been made. */ indx = ((unsigned)sbp->st_ino) % A_TAB_SZ; if ((pt = atab[indx]) != NULL) { while (pt != NULL) { if ((pt->ft.ft_ino == sbp->st_ino) && (pt->ft.ft_dev == sbp->st_dev)) break; pt = pt->fow; } /* * oops, already there. Leave it alone. */ if (pt != NULL) return; } /* * add it to the front of the hash chain */ sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, &savedsigs); if ((pt = malloc(sizeof *pt)) != NULL) { if ((pt->ft.ft_name = strdup(fname)) != NULL) { pt->ft.ft_dev = sbp->st_dev; pt->ft.ft_ino = sbp->st_ino; st_timecpy(m, &pt->ft.sb, sbp); st_timecpy(a, &pt->ft.sb, sbp); pt->fow = atab[indx]; atab[indx] = pt; sigprocmask(SIG_SETMASK, &savedsigs, NULL); return; } free(pt); } sigprocmask(SIG_SETMASK, &savedsigs, NULL); paxwarn(1, "%s for %s", "Out of memory", "directory access time reset table"); } /* * get_atdir() * look up a directory by inode and device number to obtain the access * and modification time you want to set to. If found, the modification * and access time parameters are set and the entry is removed from the * table (as it is no longer needed). These are for directories READ by * pax * Return: * 0 if found, -1 if not found. */ int do_atdir(const char *name, dev_t dev, ino_t ino) { ATDIR *pt; ATDIR **ppt; sigset_t allsigs, savedsigs; unsigned int indx; if (atab == NULL) return(-1); /* * hash by inode and search the chain for an inode and device match */ indx = ((unsigned)ino) % A_TAB_SZ; if ((pt = atab[indx]) == NULL) return(-1); ppt = &(atab[indx]); while (pt != NULL) { if ((pt->ft.ft_ino == ino) && (pt->ft.ft_dev == dev)) break; /* * no match, go to next one */ ppt = &(pt->fow); pt = pt->fow; } /* * return if we did not find it. */ if (pt == NULL || pt->ft.ft_name == NULL || strcmp(name, pt->ft.ft_name) == 0) return(-1); /* * found it. set the times and remove the entry from the table. */ set_attr(&pt->ft, 1, 0, 0, 0); sigfillset(&allsigs); sigprocmask(SIG_BLOCK, &allsigs, &savedsigs); *ppt = pt->fow; sigprocmask(SIG_SETMASK, &savedsigs, NULL); free(pt->ft.ft_name); free(pt); return(0); } /* * directory access mode and time storage routines (for directories CREATED * by pax). * * pax requires that extracted directories, by default, have their access/mod * times and permissions set to the values specified in the archive. During the * actions of extracting (and creating the destination subtree during -rw copy) * directories extracted may be modified after being created. Even worse is * that these directories may have been created with file permissions which * prohibits any descendants of these directories from being extracted. When * directories are created by pax, access rights may be added to permit the * creation of files in their subtree. Every time pax creates a directory, the * times and file permissions specified by the archive are stored. After all * files have been extracted (or copied), these directories have their times * and file modes reset to the stored values. The directory info is restored in * reverse order as entries were added from root to leaf: to restore atime * properly, we must go backwards. */ /* * dir_start() * set up the directory time and file mode storage for directories CREATED * by pax. * Return: * 0 if ok, -1 otherwise */ int dir_start(void) { if (dirp != NULL) return(0); dirsize = DIRP_SIZE; if ((dirp = reallocarray(NULL, dirsize, sizeof(DIRDATA))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "directory times"); return (-1); } return(0); } /* * add_dir() * add the mode and times for a newly CREATED directory * name is name of the directory, psb the stat buffer with the data in it, * frc_mode is a bool that says whether to force the setting of the mode * (ignoring the user-set values for preserving file mode). frc_mode is * for the case where we created a file and found that the resulting * directory was not writeable and the user asked for file modes to NOT * be preserved. (We have to preserve what was created by default, so we * have to force the setting at the end. This is stated explicitly in the * pax spec.) */ void add_dir(char *name, struct stat *psb, int frc_mode) { DIRDATA *dblk; sigset_t allsigs, savedsigs; char *rp = NULL; if (dirp == NULL) return; if (havechd && *name != '/') { #ifdef REALPATH_CAN_ALLOCATE if ((rp = realpath(name, NULL)) == NULL) #else if ((rp = realpath(name, realname)) == NULL) #endif { paxwarn(1, "Cannot canonicalise %s", name); return; } name = rp; } if (dircnt == dirsize) { dblk = reallocarray(dirp, dirsize * 2, sizeof(DIRDATA)); if (dblk == NULL) { paxwarn(1, "Unable to store mode and times for created" " directory %s", name); #ifdef REALPATH_CAN_ALLOCATE free(rp); #endif return; } sigprocmask(SIG_BLOCK, &allsigs, &savedsigs); dirp = dblk; dirsize *= 2; sigprocmask(SIG_SETMASK, &savedsigs, NULL); } dblk = &dirp[dircnt]; if ((dblk->ft.ft_name = strdup(name)) == NULL) { paxwarn(1, "Unable to store mode and times for created" " directory %s", name); #ifdef REALPATH_CAN_ALLOCATE free(rp); #endif return; } st_timecpy(m, &dblk->ft.sb, psb); st_timecpy(a, &dblk->ft.sb, psb); dblk->ft.ft_ino = psb->st_ino; dblk->ft.ft_dev = psb->st_dev; dblk->tr_mode = psb->st_mode & ABITS; dblk->frc_mode = isWahr(frc_mode); sigprocmask(SIG_BLOCK, &allsigs, &savedsigs); ++dircnt; sigprocmask(SIG_SETMASK, &savedsigs, NULL); #ifdef REALPATH_CAN_ALLOCATE free(rp); #endif } /* * delete_dir() * When we rmdir a directory, we may want to make sure we don't * later warn about being unable to set its mode and times. */ void delete_dir(dev_t dev, ino_t ino) { DIRDATA *dblk; char *name; size_t i; if (dirp == NULL) return; for (i = 0; i < dircnt; i++) { dblk = &dirp[i]; if (dblk->ft.ft_name == NULL) continue; if (dblk->ft.ft_dev == dev && dblk->ft.ft_ino == ino) { name = dblk->ft.ft_name; dblk->ft.ft_name = NULL; free(name); break; } } } /* * proc_dir(int in_sig) * process all file modes and times stored for directories CREATED * by pax. If in_sig is set, we're in a signal handler and can't * free stuff. */ void proc_dir(int in_sig) { DIRDATA *dblk; size_t cnt; if (dirp == NULL) return; /* * read backwards through the file and process each directory */ cnt = dircnt; while (cnt-- > 0) { dblk = &dirp[cnt]; /* * If we remove a directory we created, we replace the * ft_name with NULL. Ignore those. */ if (dblk->ft.ft_name == NULL) continue; /* * frc_mode set, make sure we set the file modes even if * the user didn't ask for it (see file_subs.c for more info) */ set_attr(&dblk->ft, 0, dblk->tr_mode, pmode || dblk->frc_mode, in_sig); if (!in_sig) free(dblk->ft.ft_name); } if (!in_sig) free(dirp); dirp = NULL; dircnt = 0; } /* * database independent routines */ /* * st_hash() * hashes filenames to an unsigned int for hashing into a table. It looks * at the tail end of the file, as this provides far better distribution * than any other part of the name. For performance reasons we only care * about the last MAXKEYLEN chars (should be at LEAST large enough to pick * off the filename). It was tested on a 500'000 name file tree traversal * from the root and gave almost a perfectly uniform distribution of keys * when used with prime-sized tables (MAXKEYLEN was 128 in the test). * Hashes sizeof(int) chars at a time and pads with 0 for last addition. * Return: * the hash value of the string MOD (%) the table size. */ unsigned int st_hash(const char *name, int len, int tabsz) { const char *pt; char *dest; const char *end; int i; unsigned int key = 0; int steps; int res; unsigned int val; /* * only look at the tail up to MAXKEYLEN, we do not need to waste * time here (remember these are pathnames, the tail is what will * spread out the keys) */ if (len > MAXKEYLEN) { pt = &(name[len - MAXKEYLEN]); len = MAXKEYLEN; } else pt = name; /* * calculate the number of unsigned int size steps in the string * and if there is a runt to deal with */ steps = len / sizeof(unsigned int); res = len % sizeof(unsigned int); /* * add up the value of the string in unsigned integer sized pieces * too bad we cannot have unsigned int aligned strings, then we * could avoid the expensive copy. */ for (i = 0; i < steps; ++i) { end = pt + sizeof(unsigned int); dest = (char *)&val; while (pt < end) *dest++ = *pt++; key += val; } /* * add in the runt padded with zero to the right */ if (res) { val = 0; end = pt + res; dest = (char *)&val; while (pt < end) *dest++ = *pt++; key += val; } /* * return the result mod the table size */ return(key % tabsz); } /* Forward hard link anonymisation routines */ /* * flnk_start * Creates the hard link table. * Return: * 0 if created, -1 if failure */ int flnk_start(void) { if (fltab != NULL) return (0); if ((fltab = (HRDFLNK **)calloc(L_TAB_SZ, sizeof(HRDFLNK *))) == NULL) { paxwarn(1, "%s for %s", "Out of memory", "hard link anonymisation table"); return (-1); } return (0); } /* * chk_flnk() * Looks up entry in hard link hash table. If found, it copies the name * of the file it is linked to (we already saw that file) into ln_name. * lnkcnt is decremented and if goes to 1 the node is deleted from the * database. (We have seen all the links to this file). If not found, * we add the file to the database if it has the potential for having * hard links to other files we may process (it has a link count > 1) * Return: * if found returns the new inode number; -1 on error */ int chk_flnk(ARCHD *arcn) { HRDFLNK *pt; HRDFLNK **ppt; unsigned int indx; static ino_t running = 3; if (fltab == NULL) return (-1); /* * ignore those nodes that cannot have hard links */ if ((arcn->type == PAX_DIR) || (arcn->sb.st_nlink <= 1)) return (running++); /* * hash inode number and look for this file */ indx = ((unsigned)arcn->sb.st_ino) % L_TAB_SZ; if ((pt = fltab[indx]) != NULL) { /* * it's hash chain in not empty, walk down looking for it */ ppt = &(fltab[indx]); while (pt != NULL) { if ((pt->ino == arcn->sb.st_ino) && (pt->dev == arcn->sb.st_dev)) break; ppt = &(pt->fow); pt = pt->fow; } if (pt != NULL) { /* found a link */ ino_t rv = pt->newi; /* so cpio doesn't write file data twice */ arcn->type |= PAX_LINKOR; /* * if we have found all the links to this file, remove * it from the database */ if (--pt->nlink <= 1) { *ppt = pt->fow; (void)free((char *)pt); } return (rv); } } /* * we never saw this file before. It has links so we add it to the * front of this hash chain */ if ((pt = (HRDFLNK *)malloc(sizeof(HRDFLNK))) != NULL) { pt->dev = arcn->sb.st_dev; pt->ino = arcn->sb.st_ino; pt->nlink = arcn->sb.st_nlink; pt->fow = fltab[indx]; pt->newi = running++; fltab[indx] = pt; return (pt->newi); } paxwarn(1, "%s for %s", "Out of memory", "hard link anonymisation table"); return (-1); } pax/tar.1010064400000000000000000000507761454566231400074760ustar00.\" $MirOS: src/bin/pax/tar.1,v 1.49 2024/01/05 02:08:49 tg Exp $ .\" $OpenBSD: tar.1,v 1.61 2018/07/23 19:02:49 kn Exp $ .\" .\" Copyright © 2005, 2008, 2009, 2011, 2012, 2014, 2016, .\" 2017, 2018, 2019, 2020, 2021 .\" mirabilos .\" Copyright (c) 1996 SigmaSoft, Th. Lockert .\" All rights reserved. .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions .\" are met: .\" 1. Redistributions of source code must retain the above copyright .\" notice, this list of conditions and the following disclaimer. .\" 2. Redistributions in binary form must reproduce the above copyright .\" notice, this list of conditions and the following disclaimer in the .\" documentation and/or other materials provided with the distribution. .\" .\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR .\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES .\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. .\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, .\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT .\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, .\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY .\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\"- .\" $miros: contrib/samples/portmdoc,v 1.23 2024/01/04 22:52:50 tg Exp $ .\"- .\" Copyright © 2008, 2009, 2010, 2016, 2018, 2020, 2023 .\" mirabilos .\" .\" Glue GNU groff (BSD and GNU mdoc both) to AT&T nroff (UCB mdoc). .\" * ` generates ‘ in gnroff, so use \` .\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq .\" * - generates ‐ in gnroff, \- generates −, so .tr it to - .\" thus use - for hyphens and \- for minus signs and option dashes .\" * ~ is size-reduced and placed atop in groff, so use \*(TI .\" * ^ is size-reduced and placed atop in groff, so use \*(ha .\" * \(en does not work in nroff, so use \*(en for a solo en dash .\" * and \*(EM for a correctly spaced em dash .\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba .\" Also make sure to use \& *before* a punctuation char that is to not .\" be interpreted as punctuation, and especially with two-letter words .\" but also (after) a period that does not end a sentence (“e.g.\&”). .\"- .\" .\" Implement .Dd with the Mdocdate RCS keyword .\" .rn Dd xD .de Dd .ie \\$1$Mdocdate: \{\ . xD \\$2 \\$3, \\$4 .\} .el .xD \\$1 \\$2 \\$3 .. .\" .\" .Dd must come before most everything, because when called .\" with -mandoc it loads -mdoc via .so in .Dd (first macro). .\" .Dd $Mdocdate: January 5 2024 $ .\" .\" Check which macro package we use, and do other -mdoc setup. .\" .ie \n(.g \{\ . if n .ss \n[.ss] 0 . if \*[.T]ascii .tr \-\N'45' . if \*[.T]latin1 .tr \-\N'45' . if \*[.T]utf8 .tr \-\N'45' . if \*[.T]utf8 .tr \(la\*(Lt . if \*[.T]utf8 .tr \(ra\*(Gt . ds <= \(<= . ds >= \(>= . ds Rq \(rq . ds Lq \(lq . ds sL \(aq . ds sR \(aq . if \*[.T]utf8 .ds sL ` . if \*[.T]ps .ds sL ` . if \*[.T]utf8 .ds sR ' . if \*[.T]ps .ds sR ' . ds aq \(aq . ds TI \(ti . ds ha \(ha . ds en \(en . ie d volume-ds-1 .ds tT gnu . el .ie d doc-volume-ds-1 .ds tT gnp . el .ds tT bsd .\} .el \{\ . ds aq ' . ds TI ~ . ds ha ^ . ds en \(em . ds tT ucb .\} .ie n \{\ . ds EM \ \(em\ \& .\} .el \{\ . ds EM \f(TR\|\(em\|\fP .\} .\" .\" Add UCB mdoc compatibility to GNU mdoc .\" Implement .Mx (MirBSD) .\" .ie "\*(tT"gnu" \{\ . ds sP \s0 . ds tN \*[Tn-font-size] . eo . de Mx . nr curr-font \n[.f] . nr curr-size \n[.ps] . ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u] . ds str-Mx1 \*(tN\%MirBSD\*[str-Mx] . if !\n[arg-limit] \ . if \n[.$] \{\ . ds macro-name Mx . parse-args \$@ . \} . if (\n[arg-limit] > \n[arg-ptr]) \{\ . nr arg-ptr +1 . ie (\n[type\n[arg-ptr]] == 2) \ . ds str-Mx1 \*(tN\%MirBSD\~#\*[arg\n[arg-ptr]]\*[str-Mx] . el \ . nr arg-ptr -1 . \} . ds arg\n[arg-ptr] "\*[str-Mx1] . nr type\n[arg-ptr] 2 . ds space\n[arg-ptr] "\*[space] . nr num-args (\n[arg-limit] - \n[arg-ptr]) . nr arg-limit \n[arg-ptr] . if \n[num-args] \ . parse-space-vector . print-recursive .. . de Aq . if !\n[arg-limit] \ . ds macro-name Aq . ie \n[in-authors-section] \{\ . ds quote-left \*(Lt . ds quote-right \*(Gt . \} . el \{\ . ds quote-left \[la] . ds quote-right \[ra] . \} . enclose-string \$@ .. . ec .\} .el .ie "\*(tT"gnp" \{\ . ds sP \s0 . ie t .ds tN \s[(\n[.ps]u-1z)] . el .ds tN . eo . de Mx . nr doc-curr-font \n[.f] . nr doc-curr-size \n[.ps] . ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u] . ds doc-str-Mx1 \*(tN\%MirBSD\*[doc-str-Mx] . if !\n[doc-arg-limit] \ . if \n[.$] \{\ . ds doc-macro-name Mx . doc-parse-args \$@ . \} . if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\ . nr doc-arg-ptr +1 . ie (\n[doc-type\n[doc-arg-ptr]] == 2) \ . ds doc-str-Mx1 \*(tN\%MirBSD\~#\*[doc-arg\n[doc-arg-ptr]]\*[doc-str-Mx] . el \ . nr doc-arg-ptr -1 . \} . ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1] . nr doc-type\n[doc-arg-ptr] 2 . ds doc-space\n[doc-arg-ptr] "\*[doc-space] . nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr]) . nr doc-arg-limit \n[doc-arg-ptr] . if \n[doc-num-args] \ . doc-parse-space-vector . doc-print-recursive .. . ec .\} .el \{\ . de Mx . nr cF \\n(.f . nr cZ \\n(.s . ds aa \&\f\\n(cF\s\\n(cZ . if \\n(aC==0 \{\ . ie \\n(.$==0 \&\\*(tNMirBSD\\*(aa . el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 . \} . if \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . ie \\n(C\\n(aP==2 \{\ . nr xX 0 . nr xX 1+\\*(A\\n(aP . as b1 \&\\*(tNMirBSD\ \& . if \\n(xX>0 .as b1 #\& . as b1 \&\\*(A\\n(aP\\*(aa . rr xX . ie \\n(aC>\\n(aP \{\ . nr aP \\n(aP+1 . nR . \} . el .aZ . \} . el \{\ . as b1 \&\\*(tNMirBSD\\*(aa . nR . \} . \} .. .\} .\" .\"- .ie \ng==1 \{\ . ds nc mircpio . ds np mirpax . ds nt mirtar . ds nm mirtar . Dt MIRTAR 1 .\} .el .ie \ng==2 \{\ . ds nc paxcpio . ds np pax . ds nt paxtar . ds nm paxtar . Dt PAXTAR 1 .\} .el \{\ . ds nc cpio . ds np pax . ds nt tar . ds nm tar . Dt TAR 1 .\} .\"- .Os MirBSD .Sh NAME .ie \ng==1 \{\ .Nm mirtar .Nd tape archiver .\} .el .ie \ng==2 \{\ .Nm paxtar .Nd tape archiver .\} .el \{\ .Nm tar .Nd tape archiver .\} .Sh SYNOPSIS .Nm \*(nm .Sm off .No { Cm crtux No } Op Cm 014578abefHhJjLmOoPpqsvwXZz .Sm on .Op Ar blocking-factor \*(Ba archive \*(Ba replstr .Op Fl C Ar directory .Op Fl I Ar file .Op Ar .Pp .Nm \*(nm .No { Ns Fl crtux Ns } .Bk -words .Op Fl 014578aeHhJjLmOoPpqvwXZz .Op Fl b Ar blocking-factor .Op Fl C Ar directory .Op Fl D Ar format-options .Op Fl f Ar archive .Op Fl I Ar file .Op Fl M Ar flag .Op Fl s Ar replstr .Op Ar .Ek .Sh DESCRIPTION The .Nm command creates, adds files to, or extracts files from an archive file in .Dq tar .Pq strictly speaking, Cm ustar format. A tar archive is often stored on a magnetic tape, but can be stored equally well on a floppy, CD-ROM, or in a regular disk file. .Pp In the first (legacy) form, all option flags except for .Fl C and .Fl I must be contained within the first argument to .Nm and must not be prefixed by a hyphen .Pq Sq \-\& . Option arguments, if any, are processed as subsequent arguments to .Nm and are processed in the order in which their corresponding option flags have been presented on the command line. .Pp In the second and preferred form, option flags may be given in any order and are immediately followed by their corresponding option argument values. .Pp One of the following flags must be present: .Bl -tag -width Ds .It Fl c Create new archive, or overwrite an existing archive, adding the specified files to it. .It Fl r Append the named new files to existing archive. Note that this will only work on media on which an end-of-file mark can be overwritten. .It Fl t List contents of archive. If any files are named on the command line, only those files will be listed. The .Ar file arguments may be specified as glob patterns (see .Xr glob 7 for more information), in which case .Nm will list all archive members that match each pattern. .It Fl u Alias for .Fl r . .It Fl x Extract files from archive. If any files are named on the command line, only those files will be extracted from the archive. The .Ar file arguments may be specified as glob patterns (see .Xr glob 7 for more information), in which case .Nm will extract all archive members that match each pattern. .Pp If more than one copy of a file exists in the archive, later copies will overwrite earlier copies during extraction. The file mode and modification time are preserved if possible. The file mode is subject to modification by the .Xr umask 2 . .El .Pp In addition to the flags mentioned above, any of the following flags may be used: .Bl -tag -width Ds .It Fl a Guess the compression utility based on the archive filename. Inability to guess will result in quietly not using any compression. This option only exists for semi-compatibility with .Tn GNU .Nm tar ; it is strongly recommended to archive to stdout and pipe into an external compression utility with appropriate arguments instead: .Pp .Dl tar \-cf \- foo \*(Ba xz \-2e \*(Gtfoo.txz .It Fl b Ar blocking-factor Set blocking factor to use for the archive. .Nm uses 512-byte blocks. The default is 20, the maximum is 126. Archives with a blocking factor larger than 63 violate the .Tn POSIX standard and will not be portable to all systems. .It Fl C Ar directory This is a positional argument which sets the working directory for the following files. When extracting, files will be extracted into the specified directory; when creating, the specified files will be matched from the directory. .It Fl D Ar format-options Specify the archive format and format options, separated by comma. .Nm currently supports the following formats and options: .Bl -tag -width "sv4cpio" .It Cm ar The Unix Archiver library format. This format matches APT repositories and the BSD .Xr ar 1 specification, not GNU binutils (which can however read them) or SYSV systems. See .Xr ar 5 on some operating systems for more information. .It Cm bcpio The old binary cpio format. The default blocksize for this format is 5120 bytes. This format is not very portable and should not be used when other formats are available. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm cpio The extended cpio interchange format specified in the .St -p1003.2 standard. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm sv4cpio The System V release 4 cpio. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm sv4crc The System V release 4 cpio with file CRC checksums. The default blocksize for this format is 5120 bytes. Inode and device information about a file (used for detecting file hard links by this format), which may be truncated by this format, is detected by .Nm and is repaired. .It Cm tar The old .Bx tar format as found in .Bx 4.3 . The default blocksize for this format is 10240 bytes. Pathnames stored by this format must be 100 characters or less in length. Only regular files, hard links, symlinks and directories will be archived (other filesystem types are not supported). .Pp For backwards compatibility with even older tar formats, the .Cm write_opt=nodir option can be used when writing an archive to omit the storage of directories. .It Cm ustar The extended tar interchange format specified in the .St -p1003.2 standard. The default blocksize for this format is 10240 bytes. Filenames stored by this format must be 100 characters or less in length; the total pathname must be 256 characters or less. .El .Pp .Nm will detect and report any file that it is unable to store or extract as the result of any specific archive format restrictions. The individual archive formats may impose additional restrictions on use. Typical archive format restrictions include (but are not limited to): file pathname length, file size, link pathname length, and the type of the file. .It Fl e Stop after the first error. .It Fl f Ar archive Filename where the archive is stored. Defaults to .Pa /dev/rst0 . If set to hyphen .Pq Sq \-\& standard output is used. See also the .Ev TAPE environment variable. .It Fl H Follow symlinks given on the command line only. .It Fl h Follow symbolic links as if they were normal files or directories. In extract mode this means that a directory entry in the archive will not overwrite an existing symbolic link, but rather what the link ultimately points to. .It Fl I Ar file This is a positional argument which reads the names of files to archive or extract from the given file, one per line. .It Fl J Use the xz utility to compress the archive. .It Fl j Use the bzip2 utility to compress the archive. .It Fl L Synonym for the .Fl h option. .It Fl M Ar flag Configure the archive normaliser. .Ar flag is either a numeric value compatible to .Xr strtonum 3 which is directly stored in the flags word, or one of the following values, optionally prefixed with .Dq no\- to turn them off: .Pp .Bl -tag -width xxxxxx -compact .It Ar inodes 0x0001: Serialise inodes, zero device info. .br (cpio, sv4cpio, sv4crc) .It Ar links 0x0002: Store content of hard links only once. .br (cpio, sv4cpio, sv4crc) .It Ar mtime 0x0004: Zero out the file modification time. .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar uidgid 0x0008: Set owner to 0:0 .Pq Li root Ns : Ns Li wheel . .br (ar, cpio, sv4cpio, sv4crc, ustar) .It Ar verb 0x0010: Debug this option. .It Ar debug 0x0020: Debug file header storage. .It Ar lncp 0x0040: Extract hard links by copy if link fails. .It Ar numid 0x0080: Use only numeric uid and gid values. .br (ustar) .It Ar gslash 0x0100: Append a slash after directory names. .br (ustar) .It Ar set 0x0003: Keep ownership and mtime intact. .It Ar dist 0x008B: Clean everything except mtime. .It Ar norm 0x008F: Clean everything. .It Ar root 0x0089: Clean owner and device information. .El .Pp When creating an archive and verbosely listing output, these normalisation operations are not reflected in the output, because they are made only after the output has been shown. .Pp This option is only implemented for the ar, cpio, sv4cpio, sv4crc, and ustar file format writing routines. .It Fl m Do not preserve modification time. .It Fl O If reading, extract files to standard output. .br If writing, write old-style (non-POSIX) archives. .It Fl o If writing, write old-style (non-POSIX) archives. .br Don't write directory information that the older (V7) style .Nm tar is unable to decode. Same as .Fl D Ar tar,write_opt=nodir . .It Fl P For security reasons, .Nm skips pathnames containing dotdot .Pq Dq ..\& components and strips leading slashes .Pq Sq / from pathnames by default; this option disables that behaviour. .It Fl p Preserve user and group ID as well as file mode regardless of the current .Xr umask 2 . The setuid and setgid bits are only preserved if the user and group ID could be preserved. Only meaningful in conjunction with the .Fl x flag. .It Fl q Select the first archive member that matches each .Ar file operand. No more than one archive member is matched for each .Ar file . When members of type directory are matched, the file hierarchy rooted at that directory is also matched. .It Fl s Ar replstr Modify the archive member names according to the substitution expression .Ar replstr , using the syntax of the .Xr ed 1 utility regular expressions. .Ar file arguments may be given to restrict the list of archive members to those specified. .Pp The format of these regular expressions is .Pp .Dl /old/new/[gp] .Pp As in .Xr ed 1 , .Va old is a basic regular expression (see .Xr re_format 7 ) and .Va new can contain an ampersand .Pq Ql & , .Ql \e Ns Em n (where .Em n is a digit) back-references, or subexpression matching. The .Va old string may also contain newline characters. Any non-null character can be used as a delimiter .Po .Ql / is shown here .Pc . Multiple .Fl s expressions can be specified. The expressions are applied in the order they are specified on the command line, terminating with the first successful substitution. .Pp The optional trailing .Cm g continues to apply the substitution expression to the pathname substring, which starts with the first character following the end of the last successful substitution. The first unsuccessful substitution stops the operation of the .Cm g option. The optional trailing .Cm p will cause the final result of a successful substitution to be written to standard error in the following format: .Pp .D1 Em original-pathname No \*(Gt\*(Gt Em new-pathname .Pp File or archive member names that substitute to the empty string are not selected and will be skipped. .It Fl v Verbose operation mode. If .Fl v is specified multiple times or if the .Fl t option is also specified, .Nm will use a long format for listing files, similar to .Xr ls 1 .Fl l . .It Fl w Interactively rename files. This option causes .Nm to prompt the user for the filename to use when storing or extracting files in an archive. .It Fl X Do not cross mount points in the filesystem. .It Fl Z Use the .Xr compress 1 utility to compress the archive. .It Fl z Use the .Xr gzip 1 utility to compress the archive. .El .Pp The options .Op Fl 014578 can be used to select one of the compiled-in backup devices, .Pa /dev/rstN . .Sh ENVIRONMENT .Bl -tag -width Fl .It Ev TMPDIR Path in which to store temporary files. .It Ev TAPE Default tape device to use instead of .Pa /dev/rst0 . If set to hyphen .Pq Sq \-\& standard output is used. .El .Sh FILES .Bl -tag -width "/dev/rst0" .It Pa /dev/rst0 default archive name .El .Sh EXIT STATUS The .Nm utility exits with one of the following values: .Pp .Bl -tag -width Ds -offset indent -compact .It 0 All files were processed successfully. .It 1 An error occurred. .El .Sh EXAMPLES Create an archive on the default tape drive, containing the files named .Pa bonvole and .Pa sekve : .Pp .Dl $ \*(nm c bonvole sekve .Pp Output a .Xr gzip 1 compressed archive containing the files .Pa bonvole and .Pa sekve to a file called .Pa foriru.tar.gz : .Pp .Dl $ \*(nm zcf foriru.tar.gz bonvole sekve .Pp Verbosely create an archive, called .Pa backup.tar.gz , of all files matching the shell .Xr glob 7 function .Pa *.c : .Pp .Dl $ \*(nm zcvf backup.tar.gz *.c .Pp Verbosely list, but do not extract, all files ending in .Pa .jpeg from a compressed archive named .Pa backup.tar.gz . Note that the glob pattern has been quoted to avoid expansion by the shell: .Pp .Dl $ \*(nm tvzf backup.tar.gz \*(aq*.jpeg\*(aq .Pp For more detailed examples, see .Xr \*(np 1 . .Sh DIAGNOSTICS Whenever .Nm cannot create a file or a link when extracting an archive or cannot find a file while writing an archive, or cannot preserve the user ID, group ID, file mode, or access and modification times when the .Fl p option is specified, a diagnostic message is written to standard error and a non-zero exit value will be returned, but processing will continue. In the case where .Nm cannot create a link to a file, unless .Fl M Ar lncp is given, .Nm will not create a second copy of the file. .Pp If the extraction of a file from an archive is prematurely terminated by a signal or error, .Nm may have only partially extracted the file the user wanted. Additionally, the file modes of extracted files and directories may have incorrect file bits, and the modification and access times may be wrong. .Pp If the creation of an archive is prematurely terminated by a signal or error, .Nm may have only partially created the archive, which may violate the specific archive format specification. .Sh SEE ALSO .Xr ar 1 , .Xr cpio 1 , .ie \ng==1 \{\ .Xr mircpio 1 , .Xr mirpax 1 , .Xr pax 1 , .Xr tar 1 , .Xr deb 5 .\} .el .ie \ng==2 \{\ .Xr pax 1 , .Xr paxcpio 1 , .Xr tar 1 , .Xr deb 5 .\} .el \{\ .Xr pax 1 .\} .Sh HISTORY A .Nm tar command first appeared in .At v7 . .Sh AUTHORS .An -nosplit .An Keith Muller at the University of California, San Diego. .Mx extensions by .An mirabilos Aq m@mirbsd.org . .Sh CAVEATS The flags .Fl aDJjLMo are not portable to other implementations of .Nm tar where they may have a different meaning or not exist at all. .Pp This implementation may have support for other non-standard options that are undocumented because removal-inducing deprecation was issued. .Sh BUGS The .Ar pax file format is not yet supported. pax/tar.c010064400000000000000000001052471343434567000075510ustar00/* $OpenBSD: tar.c,v 1.67 2018/09/13 12:33:43 millert Exp $ */ /* $NetBSD: tar.c,v 1.5 1995/03/21 09:07:49 cgd Exp $ */ /*- * Copyright (c) 2006, 2012, 2016, 2017, 2019 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #if HAVE_GRP_H #include #endif #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" #include "tar.h" __RCSID("$MirOS: src/bin/pax/tar.c,v 1.24 2019/02/23 22:39:25 tg Exp $"); /* * Routines for reading, writing and header identify of various versions of tar */ static size_t expandname(char *, size_t, char **, const char *, size_t); static u_long tar_chksm(char *, int); static char *name_split(char *, int); static int ul_oct(u_long, char *, int, int); static int ull_oct(unsigned long long, char *, int, int); #ifndef SMALL static void tar_dbgfld(const char *, const char *, size_t); static int rd_xheader(ARCHD *arcn, int, off_t); #endif static uid_t uid_nobody; static uid_t uid_warn; static gid_t gid_nobody; static gid_t gid_warn; /* * Routines common to all versions of tar */ #ifndef SMALL char tar_nodir; /* do not write dirs under old tar */ #endif char *gnu_name_string; /* GNU ././@LongLink hackery name */ char *gnu_link_string; /* GNU ././@LongLink hackery link */ /* * tar_endwr() * add the tar trailer of two null blocks * Return: * 0 if ok, -1 otherwise (what wr_skip returns) */ int tar_endwr(void) { return (wr_skip(NULLCNT * BLKMULT)); } /* * tar_endrd() * no cleanup needed here, just return size of trailer (for append) * Return: * size of trailer (2 * BLKMULT) */ off_t tar_endrd(void) { return (NULLCNT * BLKMULT); } /* * tar_trail() * Called to determine if a header block is a valid trailer. We are passed * the block, the in_sync flag (which tells us we are in resync mode; * looking for a valid header), and cnt (which starts at zero) which is * used to count the number of empty blocks we have seen so far. * Return: * 0 if a valid trailer, -1 if not a valid trailer, or 1 if the block * could never contain a header. */ int tar_trail(ARCHD *ignore MKSH_A_UNUSED, char *buf, int in_resync, int *cnt) { int i; /* * look for all zero, trailer is two consecutive blocks of zero */ for (i = 0; i < BLKMULT; ++i) { if (buf[i] != '\0') break; } /* * if not all zero it is not a trailer, but MIGHT be a header. */ if (i != BLKMULT) return(-1); /* * When given a zero block, we must be careful! * If we are not in resync mode, check for the trailer. Have to watch * out that we do not mis-identify file data as the trailer, so we do * NOT try to id a trailer during resync mode. During resync mode we * might as well throw this block out since a valid header can NEVER be * a block of all 0 (we must have a valid file name). */ if (!in_resync && (++*cnt >= NULLCNT)) return(0); return(1); } /* * ul_oct() * convert an unsigned long to an octal string. many oddball field * termination characters are used by the various versions of tar in the * different fields. term selects which kind to use. str is '0' padded * at the front to len. we are unable to use only one format as many old * tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */ static int ul_oct(u_long val, char *str, int len, int term) { char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch (term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); val >>= 3; if (val == 0) break; } while (pt >= str) *pt-- = '0'; if (val != 0) return(-1); return(0); } /* * ull_oct() * Convert an unsigned long long to an octal string. One of many oddball * field termination characters are used by the various versions of tar * in the different fields. term selects which kind to use. str is * '0' padded at the front to len. We are unable to use only one format * as many old tar readers are very cranky about this. * Return: * 0 if the number fit into the string, -1 otherwise */ static int ull_oct(unsigned long long val, char *str, int len, int term) { char *pt; /* * term selects the appropriate character(s) for the end of the string */ pt = str + len - 1; switch (term) { case 3: *pt-- = '\0'; break; case 2: *pt-- = ' '; *pt-- = '\0'; break; case 1: *pt-- = ' '; break; case 0: default: *pt-- = '\0'; *pt-- = ' '; break; } /* * convert and blank pad if there is space */ while (pt >= str) { *pt-- = '0' + (char)(val & 0x7); val >>= 3; if (val == 0) break; } while (pt >= str) *pt-- = '0'; if (val != 0) return(-1); return(0); } /* * tar_chksm() * calculate the checksum for a tar block counting the checksum field as * all blanks (BLNKSUM is that value pre-calculated, the sum of 8 blanks). * NOTE: we use len to short circuit summing 0's on write since we ALWAYS * pad headers with 0. * Return: * unsigned long checksum */ static u_long tar_chksm(char *blk, int len) { char *stop; char *pt; u_long chksm = BLNKSUM; /* initial value is checksum field sum */ /* * add the part of the block before the checksum field */ pt = blk; stop = blk + CHK_OFFSET; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); /* * move past the checksum field and keep going, spec counts the * checksum field as the sum of 8 blanks (which is pre-computed as * BLNKSUM). * ASSUMED: len is greater than CHK_OFFSET. (len is where our 0 padding * starts, no point in summing zero's) */ pt += CHK_LEN; stop = blk + len; while (pt < stop) chksm += (u_long)(*pt++ & 0xff); return(chksm); } #ifndef SMALL /* * Routines for old BSD style tar (also made portable to sysV tar) */ /* * tar_id() * determine if a block given to us is a valid tar header (and not a USTAR * header). We have to be on the lookout for those pesky blocks of all * zero's. * Return: * 0 if a tar header, -1 otherwise */ int tar_id(char *blk, int size) { HD_TAR *hd; HD_USTAR *uhd; if (size < BLKMULT) return (-1); hd = (HD_TAR *)blk; uhd = (HD_USTAR *)blk; /* * check for block of NULs first, a simple and fast test, then make * sure this is not a ustar header by looking for the ustar magic * cookie. We should use TMAGLEN, but some USTAR archive programs are * wrong and create archives missing the \0. Last we check the * checksum. If this is ok we have to assume it is a valid header. */ if (hd->name[0] == '\0') return (-1); if (memcmp(uhd->magic, TMAGIC, TMAGLEN - 1) == 0) return (-1); if (asc_ul(hd->chksum, sizeof(hd->chksum), OCT) != tar_chksm(blk, BLKMULT)) return (-1); force_one_volume = 1; return (0); } /* * tar_opt() * handle tar format specific -o options * Return: * 0 if ok -1 otherwise */ int tar_opt(void) { OPLIST *opt; while ((opt = opt_next()) != NULL) { if (strcmp(opt->name, TAR_OPTION) || strcmp(opt->value, TAR_NODIR)) { paxwarn(1, "Unknown tar format -o option/value pair %s=%s", opt->name, opt->value); paxwarn(1,"%s=%s is the only supported tar format option", TAR_OPTION, TAR_NODIR); return(-1); } /* * we only support one option, and only when writing */ if ((act != APPND) && (act != ARCHIVE)) { paxwarn(1, "%s=%s is only supported when writing.", opt->name, opt->value); return(-1); } tar_nodir = 1; } return(0); } /* * tar_rd() * extract the values out of block already determined to be a tar header. * store the values in the ARCHD parameter. * Return: * 0 */ int tar_rd(ARCHD *arcn, char *buf) { HD_TAR *hd; unsigned long long val; char *pt; /* * we only get proper sized buffers passed to us */ if (tar_id(buf, BLKMULT) < 0) return(-1); memset(arcn, 0, sizeof(*arcn)); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; /* * copy out the name and values in the stat buffer */ hd = (HD_TAR *)buf; if (hd->linkflag != LONGLINKTYPE && hd->linkflag != LONGNAMETYPE) { arcn->nlen = expandname(arcn->name, sizeof(arcn->name), &gnu_name_string, hd->name, sizeof(hd->name)); arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), &gnu_link_string, hd->linkname, sizeof(hd->linkname)); } arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode,sizeof(hd->mode),OCT) & 0xfff); arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); arcn->sb.st_size = (off_t)asc_ull(hd->size, sizeof(hd->size), OCT); val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT); if (val > MAX_TIME_T) arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ else arcn->sb.st_mtime = val; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * have to look at the last character, it may be a '/' and that is used * to encode this as a directory */ pt = arcn->nlen > 0 ? &(arcn->name[arcn->nlen - 1]) : NULL; arcn->pad = 0; arcn->skip = 0; switch (hd->linkflag) { case SYMTYPE: /* * symbolic link, need to get the link name and set the type in * the st_mode so -v printing will look correct. */ arcn->type = PAX_SLK; arcn->sb.st_mode |= S_IFLNK; break; case LNKTYPE: /* * hard link, need to get the link name, set the type in the * st_mode and st_nlink so -v printing will look better. */ arcn->type = PAX_HLK; arcn->sb.st_nlink = 2; /* * no idea of what type this thing really points at, but * we set something for printing only. */ arcn->sb.st_mode |= S_IFREG; break; case LONGLINKTYPE: case LONGNAMETYPE: /* * GNU long link/file; we tag these here and let the * pax internals deal with it -- too ugly otherwise. */ arcn->type = hd->linkflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; break; case DIRTYPE: /* * It is a directory, set the mode for -v printing */ arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; break; case AREGTYPE: case REGTYPE: default: /* * If we have a trailing / this is a directory and NOT a file. */ arcn->ln_name[0] = '\0'; arcn->ln_nlen = 0; if (pt && *pt == '/') { /* * it is a directory, set the mode for -v printing */ arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; } else { /* * have a file that will be followed by data. Set the * skip value to the size field and calculate the size * of the padding. */ arcn->type = PAX_REG; arcn->sb.st_mode |= S_IFREG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; } break; } /* * strip off any trailing slash. */ if (pt && *pt == '/') { *pt = '\0'; --arcn->nlen; } return (0); } /* * tar_wr() * write a tar header for the file specified in the ARCHD to the archive. * Have to check for file types that cannot be stored and file names that * are too long. Be careful of the term (last arg) to ul_oct, each field * of tar has it own spec for the termination character(s). * ASSUMED: space after header in header block is zero filled * Return: * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int tar_wr(ARCHD *arcn) { HD_TAR *hd; int len; char hdblk[sizeof(HD_TAR)]; /* * check for those filesystem types which tar cannot store */ switch (arcn->type) { case PAX_DIR: #ifndef SMALL /* * user asked that dirs not be written to the archive */ if (tar_nodir) return(1); #endif break; case PAX_CHR: paxwarn(1, "%s cannot archive a %s %s", "tar", "character device", arcn->org_name); return (1); case PAX_BLK: paxwarn(1, "%s cannot archive a %s %s", "tar", "block device", arcn->org_name); return (1); case PAX_SCK: paxwarn(1, "%s cannot archive a %s %s", "tar", "socket", arcn->org_name); return (1); case PAX_FIF: paxwarn(1, "%s cannot archive a %s %s", "tar", "FIFO", arcn->org_name); return (1); case PAX_SLK: case PAX_HLK: case PAX_HRG: if ((size_t)arcn->ln_nlen > sizeof(hd->linkname)) { paxwarn(1, "%s name too long for %s %s", "Link", "tar", arcn->ln_name); return (1); } break; case PAX_REG: case PAX_CTG: default: break; } /* * check file name len, remember extra char for dirs (the / at the end) */ len = arcn->nlen; if (arcn->type == PAX_DIR) ++len; if ((size_t)len > sizeof(hd->name)) { paxwarn(1, "%s name too long for %s %s", "File", "tar", arcn->name); return (1); } /* * Copy the data out of the ARCHD into the tar header based on the type * of the file. Remember, many tar readers want all fields to be * padded with zero so we zero the header first. We then set the * linkflag field (type), the linkname, the size, and set the padding * (if any) to be added after the file data (0 for all other types, * as they only have a header). */ memset(hdblk, 0, sizeof(hdblk)); hd = (HD_TAR *)hdblk; fieldcpy(hd->name, sizeof(hd->name), arcn->name, sizeof(arcn->name)); arcn->pad = 0; if (arcn->type == PAX_DIR) { /* * directories are the same as files, except have a filename * that ends with a /, we add the slash here. No data follows * dirs, so no pad. */ hd->linkflag = AREGTYPE; hd->name[len-1] = '/'; if (ul_oct(0, hd->size, sizeof(hd->size), 1)) goto out; } else if (arcn->type == PAX_SLK) { /* * no data follows this file, so no pad */ hd->linkflag = SYMTYPE; fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name, sizeof(arcn->ln_name)); if (ul_oct(0, hd->size, sizeof(hd->size), 1)) goto out; } else if (PAX_IS_HARDLINK(arcn->type)) { /* * no data follows this file, so no pad */ hd->linkflag = LNKTYPE; fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name, sizeof(arcn->ln_name)); if (ul_oct(0, hd->size, sizeof(hd->size), 1)) goto out; } else { /* * data follows this file, so set the pad */ hd->linkflag = AREGTYPE; if (ull_oct(arcn->sb.st_size, hd->size, sizeof(hd->size), 1)) { paxwarn(1, "File is too large for %s format %s", "tar", arcn->org_name); return (1); } arcn->pad = TAR_PAD(arcn->sb.st_size); } /* * copy those fields that are independent of the type */ if (ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 0) || ull_oct(arcn->sb.st_mtime < 0 ? 0 : arcn->sb.st_mtime, hd->mtime, sizeof(hd->mtime), 1) || ul_oct(arcn->sb.st_uid, hd->uid, sizeof(hd->uid), 0) || ul_oct(arcn->sb.st_gid, hd->gid, sizeof(hd->gid), 0)) goto out; /* * calculate and add the checksum, then write the header. A return of * 0 tells the caller to now write the file data, 1 says no data needs * to be written */ if (ul_oct(tar_chksm(hdblk, sizeof(HD_TAR)), hd->chksum, sizeof(hd->chksum), 3)) goto out; if (wr_rdbuf(hdblk, sizeof(HD_TAR)) < 0) return(-1); if (wr_skip(BLKMULT - sizeof(HD_TAR)) < 0) return(-1); if (PAX_IS_REG(arcn->type)) return(0); return(1); out: /* * header field is out of range */ paxwarn(1, "%s header field is too small for file %s", "tar", arcn->org_name); return (1); } #endif /* * Routines for POSIX ustar */ /* * ustar_strd() * initialization for ustar read * Return: * 0 if ok, -1 otherwise */ int ustar_strd(void) { #if !HAVE_UGID_FROM_UG if ((usrtb_start() < 0) || (grptb_start() < 0)) return(-1); #endif return(0); } /* * ustar_stwr() * initialization for ustar write * Return: * 0 if ok, -1 otherwise */ int ustar_stwr(int is_app MKSH_A_UNUSED) { #if !HAVE_UG_FROM_UGID if ((uidtb_start() < 0) || (gidtb_start() < 0)) return(-1); #endif return(0); } /* * ustar_id() * determine if a block given to us is a valid ustar header. We have to * be on the lookout for those pesky blocks of all zero's * Return: * 0 if a ustar header, -1 otherwise */ int ustar_id(char *blk, int size) { HD_USTAR *hd; if (size < BLKMULT) return (-1); hd = (HD_USTAR *)blk; /* * check for block of NULs first, a simple and fast test, then check * ustar magic cookie. We should use TMAGLEN, but some USTAR archive * programs are fouled up and create archives missing the \0. Last we * check the checksum. If ok we have to assume it is a valid header. */ if (hd->prefix[0] == '\0' && hd->name[0] == '\0') return (-1); if (memcmp(hd->magic, TMAGIC, TMAGLEN - 1) != 0) return (-1); if (asc_ul(hd->chksum, sizeof(hd->chksum), OCT) != tar_chksm(blk, BLKMULT)) return (-1); return (0); } /* * ustar_rd() * extract the values out of block already determined to be a ustar header. * store the values in the ARCHD parameter. * Return: * 0 */ int ustar_rd(ARCHD *arcn, char *buf) { HD_USTAR *hd = (HD_USTAR *)buf; char *dest; int cnt = 0; dev_t devmajor; dev_t devminor; unsigned long long val; /* * we only get proper sized buffers */ if (ustar_id(buf, BLKMULT) < 0) return(-1); #ifndef SMALL reset: #endif memset(arcn, 0, sizeof(*arcn)); arcn->org_name = arcn->name; arcn->sb.st_nlink = 1; #ifndef SMALL /* Process Extended headers. */ if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE) { if (rd_xheader(arcn, hd->typeflag == GHDRTYPE, (off_t)asc_ul(hd->size, sizeof(hd->size), OCT)) < 0) return (-1); /* Update and check the ustar header. */ if (rd_wrbuf(buf, BLKMULT) != BLKMULT) return (-1); if (ustar_id(buf, BLKMULT) < 0) return(-1); /* if the next block is another extension, reset the values */ if (hd->typeflag == XHDRTYPE || hd->typeflag == GHDRTYPE) goto reset; } #endif if (!arcn->nlen) { /* * See if the filename is split into two parts. if, so join * the parts. We copy the prefix first and add a / between * the prefix and name. */ dest = arcn->name; if (*(hd->prefix) != '\0') { cnt = fieldcpy(dest, sizeof(arcn->name) - 1, hd->prefix, sizeof(hd->prefix)); dest += cnt; *dest++ = '/'; cnt++; } else cnt = 0; if (hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { arcn->nlen = cnt + expandname(dest, sizeof(arcn->name) - cnt, &gnu_name_string, hd->name, sizeof(hd->name)); } } if (!arcn->ln_nlen && hd->typeflag != LONGLINKTYPE && hd->typeflag != LONGNAMETYPE) { arcn->ln_nlen = expandname(arcn->ln_name, sizeof(arcn->ln_name), &gnu_link_string, hd->linkname, sizeof(hd->linkname)); } /* * follow the spec to the letter. we should only have mode bits, strip * off all other crud we may be passed. */ arcn->sb.st_mode = (mode_t)(asc_ul(hd->mode, sizeof(hd->mode), OCT) & 0xfff); arcn->sb.st_size = (off_t)asc_ull(hd->size, sizeof(hd->size), OCT); val = asc_ull(hd->mtime, sizeof(hd->mtime), OCT); if (val > MAX_TIME_T) arcn->sb.st_mtime = INT_MAX; /* XXX 2038 */ else arcn->sb.st_mtime = val; arcn->sb.st_ctime = arcn->sb.st_atime = arcn->sb.st_mtime; /* * If we can find the ascii names for gname and uname in the password * and group files we will use the uid's and gid they bind. Otherwise * we use the uid and gid values stored in the header. (This is what * the posix spec wants). */ hd->gname[sizeof(hd->gname) - 1] = '\0'; if ((anonarch & ANON_NUMID) || gid_name(hd->gname, &(arcn->sb.st_gid)) < 0) arcn->sb.st_gid = (gid_t)asc_ul(hd->gid, sizeof(hd->gid), OCT); hd->uname[sizeof(hd->uname) - 1] = '\0'; if ((anonarch & ANON_NUMID) || uid_name(hd->uname, &(arcn->sb.st_uid)) < 0) arcn->sb.st_uid = (uid_t)asc_ul(hd->uid, sizeof(hd->uid), OCT); /* * set the defaults, these may be changed depending on the file type */ arcn->pad = 0; arcn->skip = 0; arcn->sb.st_rdev = (dev_t)0; /* * set the mode and PAX type according to the typeflag in the header */ switch (hd->typeflag) { case FIFOTYPE: arcn->type = PAX_FIF; arcn->sb.st_mode |= S_IFIFO; break; case DIRTYPE: arcn->type = PAX_DIR; arcn->sb.st_mode |= S_IFDIR; arcn->sb.st_nlink = 2; /* * Some programs that create ustar archives append a '/' * to the pathname for directories. This clearly violates * ustar specs, but we will silently strip it off anyway. */ if (arcn->name[arcn->nlen - 1] == '/') arcn->name[--arcn->nlen] = '\0'; break; case BLKTYPE: case CHRTYPE: /* * this type requires the rdev field to be set. */ if (hd->typeflag == BLKTYPE) { arcn->type = PAX_BLK; arcn->sb.st_mode |= S_IFBLK; } else { arcn->type = PAX_CHR; arcn->sb.st_mode |= S_IFCHR; } devmajor = (dev_t)asc_ul(hd->devmajor,sizeof(hd->devmajor),OCT); devminor = (dev_t)asc_ul(hd->devminor,sizeof(hd->devminor),OCT); arcn->sb.st_rdev = TODEV(devmajor, devminor); break; case SYMTYPE: case LNKTYPE: if (hd->typeflag == SYMTYPE) { arcn->type = PAX_SLK; arcn->sb.st_mode |= S_IFLNK; } else { arcn->type = PAX_HLK; /* * so printing looks better */ arcn->sb.st_mode |= S_IFREG; arcn->sb.st_nlink = 2; } break; case LONGLINKTYPE: case LONGNAMETYPE: /* * GNU long link/file; we tag these here and let the * pax internals deal with it -- too ugly otherwise. */ arcn->type = hd->typeflag == LONGLINKTYPE ? PAX_GLL : PAX_GLF; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; break; case CONTTYPE: case AREGTYPE: case REGTYPE: default: /* * these types have file data that follows. Set the skip and * pad fields. */ arcn->type = PAX_REG; arcn->pad = TAR_PAD(arcn->sb.st_size); arcn->skip = arcn->sb.st_size; arcn->sb.st_mode |= S_IFREG; break; } return(0); } /* * ustar_wr() * write a ustar header for the file specified in the ARCHD to the archive * Have to check for file types that cannot be stored and file names that * are too long. Be careful of the term (last arg) to ul_oct, we only use * '\0' for the termination character (this is different than picky tar) * ASSUMED: space after header in header block is zero filled * Return: * 0 if file has data to be written after the header, 1 if file has NO * data to write after the header, -1 if archive write failed */ int ustar_wr(ARCHD *arcn) { HD_USTAR *hd; const char *name; char *pt, hdblk[sizeof(HD_USTAR)]; u_long t_uid, t_gid; time_t t_mtime; anonarch_init(); /* * check for those filesystem types ustar cannot store */ if (arcn->type == PAX_SCK) { paxwarn(1, "%s cannot archive a %s %s", "ustar", "socket", arcn->org_name); return (1); } #ifndef SMALL /* * user asked that dirs not be written to the archive */ if (arcn->type == PAX_DIR && tar_nodir) return (1); #endif /* * check the length of the linkname */ if (PAX_IS_LINK(arcn->type) && ((size_t)arcn->ln_nlen > sizeof(hd->linkname))) { paxwarn(1, "%s name too long for %s %s", "Link", "ustar", arcn->ln_name); return (1); } /* * if -M gslash: append a slash if directory */ if ((anonarch & ANON_DIRSLASH) && arcn->type == PAX_DIR && (size_t)arcn->nlen < (sizeof(arcn->name) - 1)) { arcn->name[arcn->nlen++] = '/'; arcn->name[arcn->nlen] = '\0'; } /* * split the path name into prefix and name fields (if needed). if * pt != arcn->name, the name has to be split */ if ((pt = name_split(arcn->name, arcn->nlen)) == NULL) { paxwarn(1, "%s name too long for %s %s", "File", "ustar", arcn->name); return (1); } /* * zero out the header so we don't have to worry about zero fill below */ memset(hdblk, 0, sizeof(hdblk)); hd = (HD_USTAR *)hdblk; arcn->pad = 0; /* * split the name, or zero out the prefix */ if (pt != arcn->name) { /* * name was split, pt points at the / where the split is to * occur, we remove the / and copy the first part to the prefix */ *pt = '\0'; fieldcpy(hd->prefix, sizeof(hd->prefix), arcn->name, sizeof(arcn->name)); *pt++ = '/'; } /* * copy the name part. this may be the whole path or the part after * the prefix */ fieldcpy(hd->name, sizeof(hd->name), pt, sizeof(arcn->name) - (pt - arcn->name)); t_uid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_uid; t_gid = (anonarch & ANON_UIDGID) ? 0UL : (u_long)arcn->sb.st_gid; t_mtime = (anonarch & ANON_MTIME) ? 0 : arcn->sb.st_mtime; /* * set the fields in the header that are type dependent */ switch (arcn->type) { case PAX_DIR: hd->typeflag = DIRTYPE; if (ul_oct(0, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_CHR: case PAX_BLK: if (arcn->type == PAX_CHR) hd->typeflag = CHRTYPE; else hd->typeflag = BLKTYPE; if (ul_oct(MAJOR(arcn->sb.st_rdev), hd->devmajor, sizeof(hd->devmajor), 3) || ul_oct(MINOR(arcn->sb.st_rdev), hd->devminor, sizeof(hd->devminor), 3) || ul_oct(0, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_FIF: hd->typeflag = FIFOTYPE; if (ul_oct(0, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_SLK: case PAX_HLK: case PAX_HRG: if (arcn->type == PAX_SLK) hd->typeflag = SYMTYPE; else hd->typeflag = LNKTYPE; fieldcpy(hd->linkname, sizeof(hd->linkname), arcn->ln_name, sizeof(arcn->ln_name)); if (ul_oct(0, hd->size, sizeof(hd->size), 3)) goto out; break; case PAX_REG: case PAX_CTG: default: /* * file data with this type, set the padding */ if (arcn->type == PAX_CTG) hd->typeflag = CONTTYPE; else hd->typeflag = REGTYPE; arcn->pad = TAR_PAD(arcn->sb.st_size); if (ull_oct(arcn->sb.st_size, hd->size, sizeof(hd->size), 3)) { paxwarn(1, "File is too large for %s format %s", "ustar", arcn->org_name); return (1); } break; } memcpy(hd->magic, TMAGIC, TMAGLEN); memcpy(hd->version, TVERSION, TVERSLEN); /* * set the remaining fields. Some versions want all 16 bits of mode * we better humor them (they really do not meet spec though).... */ if (ul_oct(t_uid, hd->uid, sizeof(hd->uid), 3)) { if (uid_nobody == 0) { if (uid_name("nobody", &uid_nobody) == -1) goto out; } if (uid_warn != t_uid) { uid_warn = t_uid; paxwarn(1, "ustar header field is too small for %cid %lu, " "using nobody", 'u', t_uid); } if (ul_oct(uid_nobody, hd->uid, sizeof(hd->uid), 3)) goto out; } if (ul_oct(t_gid, hd->gid, sizeof(hd->gid), 3)) { if (gid_nobody == 0) { if (gid_name("nobody", &gid_nobody) == -1) goto out; } if (gid_warn != t_gid) { gid_warn = t_gid; paxwarn(1, "ustar header field is too small for %cid %lu, " "using nobody", 'g', t_gid); } if (ul_oct(gid_nobody, hd->gid, sizeof(hd->gid), 3)) goto out; } if (ull_oct(t_mtime < 0 ? 0 : t_mtime, hd->mtime, sizeof(hd->mtime), 3) || ul_oct(arcn->sb.st_mode, hd->mode, sizeof(hd->mode), 3)) goto out; if (!(anonarch & ANON_NUMID)) { if ((name = name_uid(t_uid, 0)) != NULL) strncpy(hd->uname, name, sizeof(hd->uname)); if ((name = name_gid(t_gid, 0)) != NULL) strncpy(hd->gname, name, sizeof(hd->gname)); } /* * calculate and store the checksum write the header to the archive * return 0 tells the caller to now write the file data, 1 says no data * needs to be written */ if (ul_oct(tar_chksm(hdblk, sizeof(HD_USTAR)), hd->chksum, sizeof(hd->chksum), 3)) goto out; #ifndef SMALL if (anonarch & ANON_DEBUG) { tar_dbgfld(NULL, NULL, 0); tar_dbgfld("writing name '", hd->name, TNMSZ); tar_dbgfld("' mode ", hd->mode, 8); tar_dbgfld(" uid ", hd->uid, 8); tar_dbgfld(" (", hd->uname, 32); tar_dbgfld(") gid ", hd->gid, 8); tar_dbgfld(" (", hd->gname, 32); tar_dbgfld(") size ", hd->size, 12); tar_dbgfld(" mtime ", hd->mtime, 12); tar_dbgfld(" type ", &(hd->typeflag), 1); tar_dbgfld(" linked to '", hd->linkname, TNMSZ); tar_dbgfld("' magic '", hd->magic, TMAGLEN); tar_dbgfld("' v", hd->version, TVERSLEN); tar_dbgfld(" device '", hd->devmajor, 8); tar_dbgfld(":", hd->devminor, 8); tar_dbgfld("' prefix '", hd->prefix, TPFSZ); tar_dbgfld("' checksum ", hd->chksum, CHK_LEN); tar_dbgfld(NULL, NULL, 1); } #endif if (wr_rdbuf(hdblk, sizeof(HD_USTAR)) < 0) return(-1); if (wr_skip(BLKMULT - sizeof(HD_USTAR)) < 0) return(-1); if (PAX_IS_REG(arcn->type)) return(0); return(1); out: /* * header field is out of range */ paxwarn(1, "%s header field is too small for file %s", "ustar", arcn->org_name); return (1); } /* * name_split() * see if the name has to be split for storage in a ustar header. We try * to fit the entire name in the name field without splitting if we can. * The split point is always at a / * Return * character pointer to split point (always the / that is to be removed * if the split is not needed, the points is set to the start of the file * name (it would violate the spec to split there). A NULL is returned if * the file name is too long */ static char * name_split(char *name, int len) { char *start; /* * check to see if the file name is small enough to fit in the name * field. if so just return a pointer to the name. * The strings can fill the complete name and prefix fields * without a NUL terminator. */ if (len <= TNMSZ) return(name); if (len > (TPFSZ + TNMSZ + 1)) return(NULL); /* * we start looking at the biggest sized piece that fits in the name * field. We walk forward looking for a slash to split at. The idea is * to find the biggest piece to fit in the name field (or the smallest * prefix we can find) (the -1 is correct the biggest piece would * include the slash between the two parts that gets thrown away) */ start = name + len - TNMSZ - 1; /* * the prefix may not be empty, so skip the first character when * trying to split a path of exactly TNMSZ+1 characters. * NOTE: This means the ustar format can't store /str if * str contains no slashes and the length of str == TNMSZ */ if (start == name) ++start; while ((*start != '\0') && (*start != '/')) ++start; /* * if we hit the end of the string, this name cannot be split, so we * cannot store this file. */ if (*start == '\0') return(NULL); /* * the split point isn't valid if it results in a prefix * longer than TPFSZ */ if ((start - name) > TPFSZ) return(NULL); /* * ok have a split point, return it to the caller */ return(start); } static size_t expandname(char *buf, size_t len, char **gnu_name, const char *name, size_t limit) { size_t nlen; if (*gnu_name) { /* *gnu_name is NUL terminated */ if ((nlen = strlcpy(buf, *gnu_name, len)) >= len) nlen = len - 1; free(*gnu_name); *gnu_name = NULL; } else nlen = fieldcpy(buf, len, name, limit); return(nlen); } #ifndef SMALL static void tar_dbgfld(const char *pfx, const char *sp, size_t len) { static char fbuf[256]; char tbuf[256], *s; if ((pfx == NULL) || (sp == NULL)) { if ((pfx == NULL) && (sp == NULL)) { if (len == 0) { *fbuf = 0; } else { paxwarn(0, "%s", fbuf); } } else paxwarn(0, "tar_dbgfld: wrong call"); return; } strlcat(fbuf, pfx, sizeof (fbuf)); if (len == 0) return; if (len > (sizeof (tbuf) - 1)) len = sizeof (tbuf) - 1; memmove(s = tbuf, sp, len); tbuf[len] = 0; while (*s == ' ') ++s; while (s[strlen(s) - 1] == ' ') s[strlen(s) - 1] = 0; strlcat(fbuf, tbuf, sizeof (fbuf)); } /* shortest possible extended record: "5 a=\n" */ #define MINXHDRSZ 5 /* longest record we'll accept */ #define MAXXHDRSZ BLKMULT static int rd_xheader(ARCHD *arcn, int global, off_t size) { char buf[MAXXHDRSZ]; long len; char *delim, *keyword; char *nextp, *p, *end; int pad, ret = 0; /* before we alter size, make note of how much we have to skip */ pad = TAR_PAD((unsigned)size); p = end = buf; while (size > 0 || p < end) { if (size > 0) { int rdlen; /* shift stuff down */ if (p > buf) { memmove(buf, p, end - p); end -= p - buf; p = buf; } /* fill starting at end */ rdlen = MINIMUM(size, (buf + sizeof buf) - end); if (rd_wrbuf(end, rdlen) != rdlen) { ret = -1; break; } size -= rdlen; end += rdlen; } /* [p, end) is good */ if (memchr(p, ' ', end - p) == NULL || !isdigit((unsigned char)*p)) { paxwarn(1, "Invalid extended header record"); ret = -1; break; } errno = 0; len = strtol(p, &delim, 10); if (*delim != ' ' || (errno == ERANGE && len == LONG_MAX) || len < MINXHDRSZ) { paxwarn(1, "%s length", "Invalid extended header record"); ret = -1; break; } if (len > end - p) { paxwarn(1, "Extended header record length %lu is " "out of range", len); /* if we can just toss this record, do so */ len -= end - p; if (len <= size && rd_skip(len) == 0) { size -= len; p = end = buf; continue; } ret = -1; break; } nextp = p + len; keyword = p = delim + 1; p = memchr(p, '=', len); if (!p || nextp[-1] != '\n') { paxwarn(1, "Malformed extended header record"); ret = -1; break; } *p++ = nextp[-1] = '\0'; if (!global) { if (!strcmp(keyword, "path")) { arcn->nlen = strlcpy(arcn->name, p, sizeof(arcn->name)); } else if (!strcmp(keyword, "linkpath")) { arcn->ln_nlen = strlcpy(arcn->ln_name, p, sizeof(arcn->ln_name)); } } p = nextp; } if (rd_skip(size + pad) < 0) return (-1); return (ret); } #endif pax/tar.h010064400000000000000000000130101340424731300075300ustar00/* $OpenBSD: tar.h,v 1.9 2014/01/08 06:43:34 deraadt Exp $ */ /* $NetBSD: tar.h,v 1.3 1995/03/21 09:07:51 cgd Exp $ */ /*- * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)tar.h 8.2 (Berkeley) 4/18/94 */ #ifdef EXTERN __IDSTRING(rcsid_tar_h, "$MirOS: src/bin/pax/tar.h,v 1.4 2018/12/12 18:08:48 tg Exp $"); #endif /* * defines and data structures common to all tar formats */ #define CHK_LEN 8 /* length of checksum field */ #define TNMSZ 100 /* size of name field */ #ifdef _PAX_ #define NULLCNT 2 /* number of null blocks in trailer */ #define CHK_OFFSET 148 /* start of chksum field */ #define BLNKSUM 256L /* sum of checksum field using ' ' */ #endif /* _PAX_ */ /* * Values used in typeflag field in all tar formats * (only REGTYPE, LNKTYPE and SYMTYPE are used in old bsd tar headers) */ #define REGTYPE '0' /* Regular File */ #define AREGTYPE '\0' /* Regular File */ #define LNKTYPE '1' /* Link */ #define SYMTYPE '2' /* Symlink */ #define CHRTYPE '3' /* Character Special File */ #define BLKTYPE '4' /* Block Special File */ #define DIRTYPE '5' /* Directory */ #define FIFOTYPE '6' /* FIFO */ #define CONTTYPE '7' /* high perf file */ /* * Extended header - POSIX.1-2001 */ #define XHDRTYPE 'x' /* Extended header */ #define GHDRTYPE 'g' /* Global header*/ /* * GNU tar compatibility; */ #define LONGLINKTYPE 'K' /* Long Symlink */ #define LONGNAMETYPE 'L' /* Long File */ /* * Mode field encoding of the different file types - values in octal */ #define TSUID 04000 /* Set UID on execution */ #define TSGID 02000 /* Set GID on execution */ #define TSVTX 01000 /* Reserved */ #define TUREAD 00400 /* Read by owner */ #define TUWRITE 00200 /* Write by owner */ #define TUEXEC 00100 /* Execute/Search by owner */ #define TGREAD 00040 /* Read by group */ #define TGWRITE 00020 /* Write by group */ #define TGEXEC 00010 /* Execute/Search by group */ #define TOREAD 00004 /* Read by other */ #define TOWRITE 00002 /* Write by other */ #define TOEXEC 00001 /* Execute/Search by other */ #ifdef _PAX_ /* * Pad with a bit mask, much faster than doing a mod but only works on powers * of 2. Macro below is for block of 512 bytes. */ #define TAR_PAD(x) ((512 - ((x) & 511)) & 511) #endif /* _PAX_ */ /* * structure of an old tar header as it appeared in BSD releases */ typedef struct { char name[TNMSZ]; /* name of entry */ char mode[8]; /* mode */ char uid[8]; /* uid */ char gid[8]; /* gid */ char size[12]; /* size */ char mtime[12]; /* modification time */ char chksum[CHK_LEN]; /* checksum */ char linkflag; /* norm, hard, or sym. */ char linkname[TNMSZ]; /* linked to name */ } HD_TAR; #ifdef _PAX_ /* * -o options for BSD tar to not write directories to the archive */ #define TAR_NODIR "nodir" #define TAR_OPTION "write_opt" /* * default device names */ #define DEV_0 "/dev/rst0" #define DEV_1 "/dev/rst1" #define DEV_4 "/dev/rst4" #define DEV_5 "/dev/rst5" #define DEV_7 "/dev/rst7" #define DEV_8 "/dev/rst8" #endif /* _PAX_ */ /* * Data Interchange Format - Extended tar header format - POSIX 1003.1-1990 */ #define TPFSZ 155 #define TMAGIC "ustar" /* ustar and a null */ #define TMAGLEN 6 #define TVERSION "00" /* 00 and no null */ #define TVERSLEN 2 typedef struct { char name[TNMSZ]; /* name of entry */ char mode[8]; /* mode */ char uid[8]; /* uid */ char gid[8]; /* gid */ char size[12]; /* size */ char mtime[12]; /* modification time */ char chksum[CHK_LEN]; /* checksum */ char typeflag; /* type of file. */ char linkname[TNMSZ]; /* linked to name */ char magic[TMAGLEN]; /* magic cookie */ char version[TVERSLEN]; /* version */ char uname[32]; /* ascii owner name */ char gname[32]; /* ascii group name */ char devmajor[8]; /* major device number */ char devminor[8]; /* minor device number */ char prefix[TPFSZ]; /* linked to name */ } HD_USTAR; pax/tty_subs.c010064400000000000000000000131051340424731300106160ustar00/* $OpenBSD: tty_subs.c,v 1.17 2016/08/26 04:22:13 guenther Exp $ */ /* $NetBSD: tty_subs.c,v 1.5 1995/03/21 09:07:52 cgd Exp $ */ /*- * Copyright (c) 2012, 2014, 2016 * mirabilos * Copyright (c) 1992 Keith Muller. * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Keith Muller of the University of California, San Diego. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #if HAVE_STRINGS_H #include #endif #include #include "pax.h" #include "extern.h" __RCSID("$MirOS: src/bin/pax/tty_subs.c,v 1.13 2018/12/12 18:08:48 tg Exp $"); /* * routines that deal with I/O to and from the user */ /* device for interactive I/O */ static const char devtty[] = "/dev/tty"; /* file descriptor for accessing it */ static int ttyfd; /* * tty_init() * try to open the controlling terminal (if any) for this process. if the * open fails, future ops that require user input will get an EOF */ int tty_init(void) { if ((ttyfd = binopen2(BO_CLEXEC, devtty, O_RDWR)) == -1 && iflag) { syswarn(1, errno, "Fatal error, cannot open %s", devtty); return (-1); } return (0); } /* * tty_prnt() * print a message using the specified format to the controlling tty * if there is no controlling terminal, just return. */ void tty_prnt(const char *fmt, ...) { va_list ap; char *cp; int len; va_start(ap, fmt); if (ttyfd != -1) { len = vasprintf(&cp, fmt, ap); if (len != -1) { dwrite(ttyfd, cp, len); free(cp); } } va_end(ap); } /* * tty_rd() * read a string from the controlling terminal if it is open * Return: * pointer caller must free if data was read, NULL otherwise */ char * tty_rd(void) { return (ttyfd == -1 ? NULL : fdgetline(ttyfd)); } /* * paxwarn() * write a warning message to stderr. if "set" the exit value of pax * will be non-zero. */ void paxwarn(int set, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (set) exit_val = 1; /* * when vflag we better ship out an extra \n to get this message on a * line by itself */ if (vfpart) { (void)fflush(listf); (void)fputc('\n', stderr); vfpart = 0; } (void)fprintf(stderr, "%s: ", argv0); (void)vfprintf(stderr, fmt, ap); va_end(ap); (void)fputc('\n', stderr); } /* * syswarn() * write a warning message to stderr. if "set" the exit value of pax * will be non-zero. */ void syswarn(int set, int errnum, const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (set) exit_val = 1; /* * when vflag we better ship out an extra \n to get this message on a * line by itself */ if (vfpart) { (void)fflush(listf); (void)fputc('\n', stderr); vfpart = 0; } (void)fprintf(stderr, "%s: ", argv0); (void)vfprintf(stderr, fmt, ap); va_end(ap); /* * format and print the errno */ if (errnum > 0) (void)fprintf(stderr, ": %s", strerror(errnum)); (void)fputc('\n', stderr); } /* * fdgetline() * read a line from a file descriptor, similar to fgetln(3). * caller must free(3) the result string. */ char fdgetline_err; char * fdgetline(int fd) { size_t n = 0; char *rv = NULL; size_t z = 32; ssize_t rdr; char *np; int term; term = zeroflag ? '\0' : '\n'; /* path termination character */ goto fdgetline_alloc; do { if (n == z) { z <<= 1; if (z < n) { /* overflow */ break; } fdgetline_alloc: if ((np = realloc(rv, z)) == NULL) { /* allocation error */ break; } rv = np; } rdr = read(fd, rv + n, 1); if (rdr == 0 && n == 0) { /* EOF reached, but nothing ever read */ free(rv); rv = NULL; goto fdgetline_eod; } if (rdr == 0 || (rdr == 1 && rv[n] == term)) { /* EOF or EOL */ rv[n++] = 0; if ((np = realloc(rv, n)) != NULL) rv = np; fdgetline_eod: fdgetline_err = 0; return (rv); } ++n; } while (rdr == 1); /* fall through do-while if rdr > 1 (read too much) or < 0 (error) */ /* get here via break on memory allocation errors */ free(rv); fdgetline_err = 1; return (NULL); }