rpm 5.3.7

tools/rpmmtree.c

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 1989, 1990, 1993
00003  *      The Regents of the University of California.  All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions
00007  * are met:
00008  * 1. Redistributions of source code must retain the above copyright
00009  *    notice, this list of conditions and the following disclaimer.
00010  * 2. Redistributions in binary form must reproduce the above copyright
00011  *    notice, this list of conditions and the following disclaimer in the
00012  *    documentation and/or other materials provided with the distribution.
00013  * 3. All advertising materials mentioning features or use of this software
00014  *    must display the following acknowledgement:
00015  *      This product includes software developed by the University of
00016  *      California, Berkeley and its contributors.
00017  * 4. Neither the name of the University nor the names of its contributors
00018  *    may be used to endorse or promote products derived from this software
00019  *    without specific prior written permission.
00020  *
00021  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00022  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00023  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00024  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00025  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00026  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00027  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00028  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00029  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00030  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00031  * SUCH DAMAGE.
00032  */
00033 
00034 #ifndef lint
00035 static const char copyright[] =
00036 "@(#) Copyright (c) 1989, 1990, 1993\n\
00037         The Regents of the University of California.  All rights reserved.\n";
00038 #endif /* not lint */
00039 
00040 #include "system.h"
00041 
00042 #include <fnmatch.h>
00043 #include <signal.h>
00044 #include <stdarg.h>
00045 
00046 #if defined(__QNXNTO__)
00047 #define st_mtimespec    st_mtime
00048 #endif
00049 
00050 #include <rpmio_internal.h>     /* XXX fdInitDigest() et al */
00051 #include <fts.h>
00052 #include <ugid.h>
00053 #include <poptIO.h>
00054 
00055 #undef  _RPMFI_INTERNAL         /* XXX don't enable *.rpm/ containers yet. */
00056 #if defined(_RPMFI_INTERNAL)
00057 #define _RPMAV_INTERNAL
00058 #include <rpmdav.h>
00059 #include <rpmtag.h>
00060 #include <rpmfi.h>
00061 
00062 #include <rpmlib.h>             /* XXX for rpmts typedef */
00063 #include <rpmts.h>
00064 #endif
00065 
00066 #define RPM_LIST_HEAD(name, type) \
00067     struct name { struct type *lh_first; }
00068 #define RPM_LIST_ENTRY(type) \
00069     struct { struct type *le_next;struct type **le_prev; }
00070 #define RPM_LIST_EMPTY(head) \
00071     ((head)->lh_first == NULL)
00072 #define RPM_LIST_FIRST(head) \
00073     ((head)->lh_first)
00074 #define RPM_LIST_NEXT(elm, field) \
00075     ((elm)->field.le_next)
00076 #define RPM_LIST_INIT(head) \
00077     do { RPM_LIST_FIRST((head)) = NULL; } while (0)
00078 #define RPM_LIST_INSERT_HEAD(head, elm, field) \
00079     do { if ((RPM_LIST_NEXT((elm), field) = RPM_LIST_FIRST((head))) != NULL) \
00080         RPM_LIST_FIRST((head))->field.le_prev = &RPM_LIST_NEXT((elm), field);\
00081     RPM_LIST_FIRST((head)) = (elm); \
00082     (elm)->field.le_prev = &RPM_LIST_FIRST((head)); } while (0)
00083 #define RPM_LIST_FOREACH(var, head, field) \
00084     for ((var) = RPM_LIST_FIRST((head)); (var); (var) = RPM_LIST_NEXT((var), field))
00085 
00086 #define _MTREE_INTERNAL
00087 /*==============================================================*/
00088 
00089 #define _KFB(n) (1U << (n))
00090 #define _MFB(n) (_KFB(n) | 0x40000000)
00091 
00095 enum mtreeFlags_e {
00096     MTREE_FLAGS_NONE            = 0,
00097     MTREE_FLAGS_QUIET           = _MFB( 0), 
00098     MTREE_FLAGS_WARN            = _MFB( 1), 
00099     MTREE_FLAGS_CREATE          = _MFB( 2), 
00100     MTREE_FLAGS_DIRSONLY        = _MFB( 3), 
00101     MTREE_FLAGS_IGNORE          = _MFB( 4), 
00102     MTREE_FLAGS_INDENT          = _MFB( 5), 
00103     MTREE_FLAGS_LOOSE           = _MFB( 6), 
00104     MTREE_FLAGS_NOCOMMENT       = _MFB( 7), 
00105     MTREE_FLAGS_REMOVE          = _MFB( 8), 
00106     MTREE_FLAGS_SEEDED          = _MFB( 9), 
00107     MTREE_FLAGS_TOUCH           = _MFB(10), 
00108     MTREE_FLAGS_UPDATE          = _MFB(11), 
00109     MTREE_FLAGS_MISMATCHOK      = _MFB(12), 
00110         /* 13-31 unused */
00111 };
00112 
00115 typedef struct rpmfts_s * rpmfts;
00116 
00117 #if defined(_MTREE_INTERNAL)
00118 
00121 enum mtreeKeys_e {
00122     MTREE_KEYS_NONE             = 0,
00123     MTREE_KEYS_CKSUM            = _KFB( 0),     
00124     MTREE_KEYS_DONE             = _KFB( 1),     
00125     MTREE_KEYS_GID              = _KFB( 2),     
00126     MTREE_KEYS_GNAME            = _KFB( 3),     
00127     MTREE_KEYS_IGN              = _KFB( 4),     
00128     MTREE_KEYS_MAGIC            = _KFB( 5),     
00129     MTREE_KEYS_MODE             = _KFB( 6),     
00130     MTREE_KEYS_NLINK            = _KFB( 7),     
00131     MTREE_KEYS_SIZE             = _KFB( 8),     
00132     MTREE_KEYS_SLINK            = _KFB( 9),     
00133     MTREE_KEYS_TIME             = _KFB(10),     
00134     MTREE_KEYS_TYPE             = _KFB(11),     
00135     MTREE_KEYS_UID              = _KFB(12),     
00136     MTREE_KEYS_UNAME            = _KFB(13),     
00137     MTREE_KEYS_VISIT            = _KFB(14),     
00138     MTREE_KEYS_FLAGS            = _KFB(15),     
00139     MTREE_KEYS_NOCHANGE         = _KFB(16),     
00140     MTREE_KEYS_OPT              = _KFB(17),     
00141     MTREE_KEYS_DIGEST           = _KFB(18),     
00142         /* 19-31 unused */
00143 };
00144 
00145 typedef struct _node {
00146     struct _node        *parent, *child;        
00147     struct _node        *prev, *next;           
00148     struct stat         sb;                     
00149     char                *slink;                 
00150     ARGI_t              algos;                  
00151     ARGV_t              digests;                
00153     uint32_t            cksum;                  
00155     enum mtreeKeys_e flags;                     
00157     uint8_t             type;                   
00158 #define F_BLOCK 0x001                           
00159 #define F_CHAR  0x002                           
00160 #define F_DIR   0x004                           
00161 #define F_FIFO  0x008                           
00162 #define F_FILE  0x010                           
00163 #define F_LINK  0x020                           
00164 #define F_SOCK  0x040                           
00166     char                name[1];                /* file name (must be last) */
00167 } NODE;
00168 
00169 struct rpmfts_s {
00170     FTS * t;
00171     FTSENT * p;
00172     struct stat sb;                     
00173     int sb_is_valid;                    
00174     uint32_t crc_total;
00175     unsigned lineno;
00176 /*@null@*/
00177     NODE * root;
00178 /*@null@*/
00179     ARGV_t paths;
00180     enum mtreeKeys_e keys;
00181 /*@null@*/
00182     ARGI_t algos;
00183 
00184 /*@dependent@*/ /*@null@*/
00185     FILE * spec1;
00186 /*@dependent@*/ /*@null@*/
00187     FILE * spec2;
00188 
00189 /*@null@*/
00190     const char * fullpath;
00191 /*@null@*/
00192     char * path;
00193     int ftsoptions;
00194 
00195 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
00196     size_t maxf;
00197 /*@null@*/
00198     unsigned long * f;
00199 #endif
00200     size_t maxg;
00201 /*@null@*/
00202     gid_t * g;
00203     size_t maxm;
00204 /*@null@*/
00205     mode_t * m;
00206     size_t maxu;
00207 /*@null@*/
00208     uid_t * u;
00209 
00210 #if defined(_RPMFI_INTERNAL)
00211 /*@null@*/
00212     rpmts ts;
00213 /*@null@*/
00214     rpmfi fi;
00215 #endif
00216 };
00217 #endif  /* _MTREE_INTERNAL */
00218 
00219 #undef  _KFB
00220 #undef  _MFB
00221 
00222 #ifdef __cplusplus
00223 extern "C" {
00224 #endif
00225 
00226 /*@null@*/
00227 static NODE * mtreeSpec(rpmfts fts, /*@null@*/ FILE * fp)
00228         /*@globals fileSystem, internalState @*/
00229         /*@modifies fts, fp, fileSystem, internalState @*/;
00230 
00231 static int mtreeVSpec(rpmfts fts)
00232         /*@globals fileSystem, internalState @*/
00233         /*@modifies fts, fileSystem, internalState @*/;
00234 
00235 static int mtreeCWalk(rpmfts fts)
00236         /*@globals h_errno, fileSystem, internalState @*/
00237         /*@modifies fts, fileSystem, internalState @*/;
00238 
00239 static int mtreeVWalk(rpmfts fts)
00240         /*@globals h_errno, fileSystem, internalState @*/
00241         /*@modifies fts, fileSystem, internalState @*/;
00242 
00243 #ifdef __cplusplus
00244 }
00245 #endif
00246 
00247 /*==============================================================*/
00248 
00249 static void mtreeMiss(rpmfts fts, /*@null@*/ NODE * p, char * tail)
00250         /*@globals h_errno, errno, fileSystem, internalState @*/
00251         /*@modifies p, tail, errno, fileSystem, internalState @*/;
00252 
00253 #include "debug.h"
00254 
00255 /*@access DIR @*/
00256 /*@access FD_t @*/
00257 /*@access rpmfi @*/
00258 /*@access rpmts @*/
00259 
00260 #define MF_ISSET(_FLAG) ((mtreeFlags & ((MTREE_FLAGS_##_FLAG) & ~0x40000000)) != MTREE_FLAGS_NONE)
00261 
00262 #define KEYDEFAULT \
00263     (MTREE_KEYS_GID | MTREE_KEYS_MODE | MTREE_KEYS_NLINK | MTREE_KEYS_SIZE | \
00264      MTREE_KEYS_SLINK | MTREE_KEYS_TIME | MTREE_KEYS_UID)
00265 
00266 #define MISMATCHEXIT    2
00267 
00268 #define MBITS   (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
00269 
00270 /*@unchecked@*/
00271 static struct rpmfts_s __rpmfts;
00272 /*@unchecked@*/
00273 static rpmfts _rpmfts = &__rpmfts;
00274 
00275 /*@unchecked@*/
00276 static enum mtreeFlags_e mtreeFlags = MTREE_FLAGS_NONE;
00277 
00278 /* XXX merge into _rpmfts, use mmiRE instead */
00279 struct exclude {
00280     RPM_LIST_ENTRY(exclude) link;
00281     const char *glob;
00282     int pathname;
00283 };
00284 
00285 /*@unchecked@*/
00286 static RPM_LIST_HEAD(, exclude) excludes;
00287 
00288 /*@unchecked@*/
00289 static struct rpmop_s dc_totalops;
00290 
00291 /*@unchecked@*/
00292 static struct rpmop_s dc_readops;
00293 
00294 /*@unchecked@*/
00295 static struct rpmop_s dc_digestops;
00296 
00297 /*==============================================================*/
00298 
00299 /*@exits@*/
00300 static void
00301 mtree_error(const char *fmt, ...)
00302 #ifdef __GNUC__
00303 __attribute__ ((format (printf, 1, 2)))
00304 #endif
00305         /*@globals fileSystem @*/
00306         /*@modifies fileSystem @*/;
00307 
00308 void
00309 mtree_error(const char *fmt, ...)
00310 {
00311     va_list ap;
00312 
00313     va_start(ap, fmt);
00314     (void) fflush(NULL);
00315     (void) fprintf(stderr, "\n%s: ", __progname);
00316     (void) vfprintf(stderr, fmt, ap);
00317     va_end (ap);
00318     (void) fprintf(stderr, "\n");
00319     if (_rpmfts->lineno)
00320         (void)fprintf(stderr, _("%s: failed at line %d of the specification\n"),
00321                 __progname, _rpmfts->lineno);
00322     exit(EXIT_FAILURE);
00323     /*@notreached@*/
00324 }
00325 
00326 typedef struct _key {
00327 /*@observer@*/
00328         const char *name;               /* key name */
00329         unsigned val;                   /* value */
00330 #define NEEDVALUE       0xffffffff
00331         uint32_t flags;
00332 } KEY;
00333 
00334 /* NB: the following table must be sorted lexically. */
00335 /*@unchecked@*/ /*@observer@*/
00336 static KEY keylist[] = {
00337     { "adler32",        MTREE_KEYS_DIGEST,      PGPHASHALGO_ADLER32 },
00338     { "cksum",          MTREE_KEYS_CKSUM,       NEEDVALUE },
00339     { "crc32",          MTREE_KEYS_DIGEST,      PGPHASHALGO_CRC32 },
00340     { "crc64",          MTREE_KEYS_DIGEST,      PGPHASHALGO_CRC64 },
00341     { "flags",          MTREE_KEYS_FLAGS,       NEEDVALUE },
00342     { "gid",            MTREE_KEYS_GID,         NEEDVALUE },
00343     { "gname",          MTREE_KEYS_GNAME,       NEEDVALUE },
00344     { "haval160digest", MTREE_KEYS_DIGEST,      PGPHASHALGO_HAVAL_5_160 },
00345     { "ignore",         MTREE_KEYS_IGN,         0 },
00346     { "jlu32",          MTREE_KEYS_DIGEST,      PGPHASHALGO_JLU32 },
00347     { "link",           MTREE_KEYS_SLINK,       NEEDVALUE },
00348     { "md2digest",      MTREE_KEYS_DIGEST,      PGPHASHALGO_MD2 },
00349     { "md4digest",      MTREE_KEYS_DIGEST,      PGPHASHALGO_MD4 },
00350     { "md5digest",      MTREE_KEYS_DIGEST,      PGPHASHALGO_MD5 },
00351     { "mode",           MTREE_KEYS_MODE,        NEEDVALUE },
00352     { "nlink",          MTREE_KEYS_NLINK,       NEEDVALUE },
00353     { "nochange",       MTREE_KEYS_NOCHANGE,    0 },
00354     { "optional",       MTREE_KEYS_OPT,         0 },
00355     { "rmd128digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_RIPEMD128 },
00356     { "rmd160digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_RIPEMD160 },
00357     { "rmd256digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_RIPEMD256 },
00358     { "rmd320digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_RIPEMD320 },
00359     { "salsa10",        MTREE_KEYS_DIGEST,      PGPHASHALGO_SALSA10 },
00360     { "salsa20",        MTREE_KEYS_DIGEST,      PGPHASHALGO_SALSA20 },
00361     { "sha1digest",     MTREE_KEYS_DIGEST,      PGPHASHALGO_SHA1 },
00362     { "sha224digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_SHA224 },
00363     { "sha256digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_SHA256 },
00364     { "sha384digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_SHA384 },
00365     { "sha512digest",   MTREE_KEYS_DIGEST,      PGPHASHALGO_SHA512 },
00366     { "size",           MTREE_KEYS_SIZE,        NEEDVALUE },
00367     { "tiger192digest", MTREE_KEYS_DIGEST,      PGPHASHALGO_TIGER192 },
00368     { "time",           MTREE_KEYS_TIME,        NEEDVALUE },
00369     { "type",           MTREE_KEYS_TYPE,        NEEDVALUE },
00370     { "uid",            MTREE_KEYS_UID,         NEEDVALUE },
00371     { "uname",          MTREE_KEYS_UNAME,       NEEDVALUE },
00372 };
00373 
00374 static int
00375 keycompare(const void * a, const void * b)
00376         /*@*/
00377 {
00378     return strcmp(((KEY *)a)->name, ((KEY *)b)->name);
00379 }
00380 
00381 static unsigned
00382 parsekey(char *name, /*@out@*/ uint32_t *needvaluep)
00383         /*@globals fileSystem @*/
00384         /*@modifies *needvaluep, fileSystem @*/
00385 
00386 {
00387     KEY *k, tmp;
00388 
00389     if (needvaluep != NULL)
00390         *needvaluep = 0;
00391     if (*name == '\0')
00392         return 0;
00393     tmp.name = name;
00394     k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(keylist[0]),
00395             sizeof(keylist[0]), keycompare);
00396     if (k == NULL)
00397         mtree_error("unknown keyword %s", name);
00398 
00399     if (needvaluep != NULL)
00400         *needvaluep = k->flags;
00401     return k->val;
00402 }
00403 
00404 static /*@observer@*/ /*@null@*/ const char *
00405 algo2tagname(uint32_t algo)
00406         /*@*/
00407 {
00408     const char * tagname = NULL;
00409 
00410     switch (algo) {
00411     case PGPHASHALGO_MD5:       tagname = "md5digest";          break;
00412     case PGPHASHALGO_SHA1:      tagname = "sha1digest";         break;
00413     case PGPHASHALGO_RIPEMD160: tagname = "rmd160digest";       break;
00414     case PGPHASHALGO_MD2:       tagname = "md2digest";          break;
00415     case PGPHASHALGO_TIGER192:  tagname = "tiger192digest";     break;
00416     case PGPHASHALGO_HAVAL_5_160: tagname = "haval160digest";   break;
00417     case PGPHASHALGO_SHA256:    tagname = "sha256digest";       break;
00418     case PGPHASHALGO_SHA384:    tagname = "sha384digest";       break;
00419     case PGPHASHALGO_SHA512:    tagname = "sha512digest";       break;
00420     case PGPHASHALGO_MD4:       tagname = "md4digest";          break;
00421     case PGPHASHALGO_RIPEMD128: tagname = "rmd128digest";       break;
00422     case PGPHASHALGO_CRC32:     tagname = "crc32";              break;
00423     case PGPHASHALGO_ADLER32:   tagname = "adler32";            break;
00424     case PGPHASHALGO_CRC64:     tagname = "crc64";              break;
00425     case PGPHASHALGO_JLU32:     tagname = "jlu32";              break;
00426     case PGPHASHALGO_SHA224:    tagname = "sha224digest";       break;
00427     case PGPHASHALGO_RIPEMD256: tagname = "rmd256digest";       break;
00428     case PGPHASHALGO_RIPEMD320: tagname = "rmd320digest";       break;
00429     case PGPHASHALGO_SALSA10:   tagname = "salsa10";            break;
00430     case PGPHASHALGO_SALSA20:   tagname = "salsa20";            break;
00431     default:                    tagname = NULL;                 break;
00432     }
00433     return tagname;
00434 }
00435 
00436 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
00437 static const char *
00438 flags_to_string(u_long fflags)
00439         /*@*/
00440 {
00441     char * string = fflagstostr(fflags);
00442     if (string != NULL && *string == '\0') {
00443         free(string);
00444         string = xstrdup("none");
00445     }
00446     return string;
00447 }
00448 #endif
00449 
00450 /*==============================================================*/
00451 
00452 /*@unchecked@*/ /*@observer@*/
00453 static const uint32_t crctab[] = {
00454     0x0,
00455     0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
00456     0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
00457     0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
00458     0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
00459     0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
00460     0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
00461     0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
00462     0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
00463     0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
00464     0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
00465     0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
00466     0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
00467     0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
00468     0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
00469     0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
00470     0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
00471     0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
00472     0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
00473     0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
00474     0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
00475     0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
00476     0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
00477     0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
00478     0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
00479     0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
00480     0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
00481     0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
00482     0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
00483     0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
00484     0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
00485     0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
00486     0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
00487     0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
00488     0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
00489     0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
00490     0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
00491     0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
00492     0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
00493     0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
00494     0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
00495     0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
00496     0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
00497     0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
00498     0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
00499     0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
00500     0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
00501     0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
00502     0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
00503     0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
00504     0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
00505     0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
00506 };
00507 
00508 /*
00509  * Compute a POSIX 1003.2 checksum.  This routine has been broken out so that
00510  * other programs can use it.  It takes a file descriptor to read from and
00511  * locations to store the crc and the number of bytes read.  It returns 0 on
00512  * success and 1 on failure.  Errno is set on failure.
00513  */
00514 static int
00515 crc(FD_t fd, /*@out@*/ uint32_t * cval, /*@out@*/ uint32_t * clen)
00516         /*@globals _rpmfts, fileSystem @*/
00517         /*@modifies fd, *clen, *cval, _rpmfts, fileSystem @*/
00518 {
00519     uint32_t crc = 0;
00520     uint32_t len = 0;
00521 
00522 #define COMPUTE(var, ch)        (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
00523 
00524     _rpmfts->crc_total ^= 0xffffffff;
00525 
00526     {   uint8_t buf[16 * 1024];
00527         size_t nr;
00528         while ((nr = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) != 0) {
00529             uint8_t *p;
00530             for (len += nr, p = buf; nr--; ++p) {
00531                 COMPUTE(crc, *p);
00532                 COMPUTE(_rpmfts->crc_total, *p);
00533             }
00534         }
00535         if (Ferror(fd))
00536             return 1;
00537     }
00538 
00539     *clen = len;
00540 
00541     /* Include the length of the file. */
00542     for (; len != 0; len >>= 8) {
00543         COMPUTE(crc, len & 0xff);
00544         COMPUTE(_rpmfts->crc_total, len & 0xff);
00545     }
00546 
00547     *cval = (crc ^ 0xffffffff);
00548     _rpmfts->crc_total ^= 0xffffffff;
00549     return 0;
00550 }
00551 
00552 /*==============================================================*/
00553 
00554 /*
00555  * to select alternate encoding format
00556  */
00557 #define VIS_OCTAL       0x01    /* use octal \ddd format */
00558 #define VIS_CSTYLE      0x02    /* use \[nrft0..] where appropriate */
00559 
00560 /*
00561  * to alter set of characters encoded (default is to encode all
00562  * non-graphic except space, tab, and newline).
00563  */
00564 #define VIS_SP          0x04    /* also encode space */
00565 #define VIS_TAB         0x08    /* also encode tab */
00566 #define VIS_NL          0x10    /* also encode newline */
00567 #define VIS_WHITE       (VIS_SP | VIS_TAB | VIS_NL)
00568 #define VIS_SAFE        0x20    /* only encode "unsafe" characters */
00569 
00570 /*
00571  * other
00572  */
00573 #define VIS_NOSLASH     0x40    /* inhibit printing '\' */
00574 
00575 /*
00576  * unvis return codes
00577  */
00578 #define UNVIS_VALID      1      /* character valid */
00579 #define UNVIS_VALIDPUSH  2      /* character valid, push back passed char */
00580 #define UNVIS_NOCHAR     3      /* valid sequence, no character produced */
00581 #define UNVIS_SYNBAD    -1      /* unrecognized escape sequence */
00582 #define UNVIS_ERROR     -2      /* decoder in unknown state (unrecoverable) */
00583 
00584 /*
00585  * unvis flags
00586  */
00587 #define UNVIS_END       1       /* no more characters */
00588 
00589 static char *vis(/*@returned@*/ /*@out@*/ char *dst, int c, int flag, int nextc)
00590         /*@modifies dst @*/;
00591 static int strvis(/*@out@*/ char *dst, const char *src, int flag)
00592         /*@modifies dst @*/;
00593 #ifdef  NOTUSED
00594 static int strnvis(/*@out@*/ char *dst, const char *src, size_t siz, int flag)
00595         /*@modifies dst @*/;
00596 static int strvisx(/*@out@*/ char *dst, const char *src, size_t len, int flag)
00597         /*@modifies dst @*/;
00598 #endif
00599 static int strunvis(/*@out@*/ char *dst, const char *src)
00600         /*@modifies dst @*/;
00601 static int unvis(/*@out@*/ char *cp, char c, int *astate, int flag)
00602         /*@modifies cp, astate @*/;
00603 
00604 #define isoctal(c) (((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
00605 #define isvisible(c)    \
00606   (((unsigned)(c) <= (unsigned)UCHAR_MAX && isascii((unsigned char)(c)) && \
00607         isgraph((unsigned char)(c)))                    \
00608    ||   ((flag & VIS_SP) == 0 && (c) == (int)' ')       \
00609    ||   ((flag & VIS_TAB) == 0 && (c) == (int)'\t')     \
00610    ||   ((flag & VIS_NL) == 0 && (c) == (int)'\n')      \
00611    ||   ((flag & VIS_SAFE) \
00612          && ((c) == (int)'\b' || (c) == (int)'\007' || (c) == (int)'\r')))
00613 
00614 /*
00615  * vis - visually encode characters
00616  */
00617 char *
00618 vis(char * dst, int c, int flag, int nextc)
00619 {
00620     if (isvisible(c)) {
00621         *dst++ = (char)c;
00622         if (c == (int)'\\' && (flag & VIS_NOSLASH) == 0)
00623             *dst++ = '\\';
00624         *dst = '\0';
00625         return dst;
00626     }
00627 
00628     if (flag & VIS_CSTYLE) {
00629         switch(c) {
00630         case '\n':
00631             *dst++ = '\\';
00632             *dst++ = 'n';
00633             goto done;
00634         case '\r':
00635             *dst++ = '\\';
00636             *dst++ = 'r';
00637             goto done;
00638         case '\b':
00639             *dst++ = '\\';
00640             *dst++ = 'b';
00641             goto done;
00642         case '\a':
00643             *dst++ = '\\';
00644             *dst++ = 'a';
00645             goto done;
00646         case '\v':
00647             *dst++ = '\\';
00648             *dst++ = 'v';
00649             goto done;
00650         case '\t':
00651             *dst++ = '\\';
00652             *dst++ = 't';
00653             goto done;
00654         case '\f':
00655             *dst++ = '\\';
00656             *dst++ = 'f';
00657             goto done;
00658         case ' ':
00659             *dst++ = '\\';
00660             *dst++ = 's';
00661             goto done;
00662         case '\0':
00663             *dst++ = '\\';
00664             *dst++ = '0';
00665             if (isoctal(nextc)) {
00666                 *dst++ = '0';
00667                 *dst++ = '0';
00668             }
00669             goto done;
00670         }
00671     }
00672     if (((c & 0177) == (int)' ') || (flag & VIS_OCTAL)) {       
00673         *dst++ = '\\';
00674         *dst++ = ((unsigned char)c >> 6 & 07) + '0';
00675         *dst++ = ((unsigned char)c >> 3 & 07) + '0';
00676         *dst++ = ((unsigned char)c & 07) + '0';
00677         goto done;
00678     }
00679     if ((flag & VIS_NOSLASH) == 0)
00680         *dst++ = '\\';
00681     if (c & 0200) {
00682         c &= 0177;
00683         *dst++ = 'M';
00684     }
00685     if (iscntrl(c)) {
00686         *dst++ = '^';
00687         if (c == 0177)
00688             *dst++ = '?';
00689         else
00690             *dst++ = (char)(c + (int)'@');
00691     } else {
00692         *dst++ = '-';
00693         *dst++ = (char)c;
00694     }
00695 
00696 done:
00697     *dst = '\0';
00698     return dst;
00699 }
00700 
00701 /*
00702  * strvis, strnvis, strvisx - visually encode characters from src into dst
00703  *      
00704  *      Dst must be 4 times the size of src to account for possible
00705  *      expansion.  The length of dst, not including the trailing NULL,
00706  *      is returned.
00707  *
00708  *      Strnvis will write no more than siz-1 bytes (and will NULL terminate).
00709  *      The number of bytes needed to fully encode the string is returned.
00710  *
00711  *      Strvisx encodes exactly len bytes from src into dst.
00712  *      This is useful for encoding a block of data.
00713  */
00714 int
00715 strvis(char * dst, const char * src, int flag)
00716 {
00717     char c;
00718     char *start;
00719 
00720     for (start = dst; (c = *src) != '\0';)
00721         dst = vis(dst, (int)c, flag, (int)*++src);
00722     *dst = '\0';
00723     return (dst - start);
00724 }
00725 
00726 #ifdef  NOTUSED
00727 int
00728 strnvis(char * dst, const char * src, size_t siz, int flag)
00729 {
00730     char c;
00731     char *start, *end;
00732 
00733     for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
00734         if (isvisible((int)c)) {
00735             *dst++ = c;
00736             if (c == '\\' && (flag & VIS_NOSLASH) == 0) {
00737                 /* need space for the extra '\\' */
00738                 if (dst < end)
00739                     *dst++ = '\\';
00740                 else {
00741                     dst--;
00742                     break;
00743                 }
00744             }
00745             src++;
00746         } else {
00747             /* vis(3) requires up to 4 chars */
00748             if (dst + 3 < end)
00749                 dst = vis(dst, (int)c, flag, (int)*++src);
00750             else
00751                 break;
00752         }
00753     }
00754     *dst = '\0';
00755     if (dst >= end) {
00756         char tbuf[5];
00757 
00758         /* adjust return value for truncation */
00759         while ((c = *src) != '\0')
00760             dst += vis(tbuf, (int)c, flag, (int)*++src) - tbuf;
00761     }
00762     return (dst - start);
00763 }
00764 #endif  /* NOTUSED */
00765 
00766 #ifdef  NOTUSED
00767 int
00768 strvisx(char * dst, const char * src, size_t len, int flag)
00769 {
00770     char c;
00771     char *start;
00772 
00773     for (start = dst; len > 1; len--) {
00774         c = *src;
00775         dst = vis(dst, (int)c, flag, (int)*++src);
00776     }
00777     if (len)
00778         dst = vis(dst, (int)*src, flag, (int)'\0');
00779     *dst = '\0';
00780     return (dst - start);
00781 }
00782 #endif  /* NOTUSED */
00783 
00784 /*
00785  * decode driven by state machine
00786  */
00787 #define S_GROUND        0       /* haven't seen escape char */
00788 #define S_START         1       /* start decoding special sequence */
00789 #define S_META          2       /* metachar started (M) */
00790 #define S_META1         3       /* metachar more, regular char (-) */
00791 #define S_CTRL          4       /* control char started (^) */
00792 #define S_OCTAL2        5       /* octal digit 2 */
00793 #define S_OCTAL3        6       /* octal digit 3 */
00794 
00795 #if !defined(isoctal)
00796 #define isoctal(c) (((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
00797 #endif
00798 
00799 /*
00800  * unvis - decode characters previously encoded by vis
00801  */
00802 int
00803 unvis(char *cp, char c, int *astate, int flag)
00804 {
00805 
00806     if (flag & UNVIS_END) {
00807         if (*astate == S_OCTAL2 || *astate == S_OCTAL3) {
00808             *astate = S_GROUND;
00809             return (UNVIS_VALID);
00810         }
00811         return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
00812     }
00813 
00814     switch (*astate) {
00815 
00816     case S_GROUND:
00817         *cp = '\0';
00818         if (c == '\\') {
00819             *astate = S_START;
00820             return (0);
00821         }
00822         *cp = c;
00823         return (UNVIS_VALID);
00824 
00825     case S_START:
00826         switch(c) {
00827         case '\\':
00828             *cp = c;
00829             *astate = S_GROUND;
00830             return (UNVIS_VALID);
00831         case '0': case '1': case '2': case '3':
00832         case '4': case '5': case '6': case '7':
00833             *cp = (c - '0');
00834             *astate = S_OCTAL2;
00835             return (0);
00836         case 'M':
00837             *cp = (char) 0200;
00838             *astate = S_META;
00839             return (0);
00840         case '^':
00841             *astate = S_CTRL;
00842             return (0);
00843         case 'n':
00844             *cp = '\n';
00845             *astate = S_GROUND;
00846             return (UNVIS_VALID);
00847         case 'r':
00848             *cp = '\r';
00849             *astate = S_GROUND;
00850             return (UNVIS_VALID);
00851         case 'b':
00852             *cp = '\b';
00853             *astate = S_GROUND;
00854             return (UNVIS_VALID);
00855         case 'a':
00856             *cp = '\007';
00857             *astate = S_GROUND;
00858             return (UNVIS_VALID);
00859         case 'v':
00860             *cp = '\v';
00861             *astate = S_GROUND;
00862             return (UNVIS_VALID);
00863         case 't':
00864             *cp = '\t';
00865             *astate = S_GROUND;
00866             return (UNVIS_VALID);
00867         case 'f':
00868             *cp = '\f';
00869             *astate = S_GROUND;
00870             return (UNVIS_VALID);
00871         case 's':
00872             *cp = ' ';
00873             *astate = S_GROUND;
00874             return (UNVIS_VALID);
00875         case 'E':
00876             *cp = '\033';
00877             *astate = S_GROUND;
00878             return (UNVIS_VALID);
00879         case '\n':      /* hidden newline */
00880             *astate = S_GROUND;
00881             return (UNVIS_NOCHAR);
00882         case '$':       /* hidden marker */
00883             *astate = S_GROUND;
00884             return (UNVIS_NOCHAR);
00885         }
00886         *astate = S_GROUND;
00887         return (UNVIS_SYNBAD);
00888 
00889     case S_META:
00890         if (c == '-')
00891             *astate = S_META1;
00892         else if (c == '^')
00893             *astate = S_CTRL;
00894         else {
00895             *astate = S_GROUND;
00896             return (UNVIS_SYNBAD);
00897         }
00898         return (0);
00899 
00900     case S_META1:
00901         *astate = S_GROUND;
00902         *cp |= c;
00903         return (UNVIS_VALID);
00904 
00905     case S_CTRL:
00906         if (c == '?')
00907             *cp |= 0177;
00908         else
00909             *cp |= c & 037;
00910         *astate = S_GROUND;
00911         return (UNVIS_VALID);
00912 
00913     case S_OCTAL2:      /* second possible octal digit */
00914         if (isoctal(c)) {
00915             /*
00916              * yes - and maybe a third
00917              */
00918             *cp = (*cp << 3) + (c - '0');
00919             *astate = S_OCTAL3; 
00920             return (0);
00921         }
00922         /*
00923          * no - done with current sequence, push back passed char
00924          */
00925         *astate = S_GROUND;
00926         return (UNVIS_VALIDPUSH);
00927 
00928     case S_OCTAL3:      /* third possible octal digit */
00929         *astate = S_GROUND;
00930         if (isoctal(c)) {
00931             *cp = (*cp << 3) + (c - '0');
00932             return (UNVIS_VALID);
00933         }
00934         /*
00935          * we were done, push back passed char
00936          */
00937         return (UNVIS_VALIDPUSH);
00938                         
00939     default:    
00940         /*
00941          * decoder in unknown state - (probably uninitialized)
00942          */
00943         *astate = S_GROUND;
00944         return (UNVIS_SYNBAD);
00945     }
00946 }
00947 
00948 /*
00949  * strunvis - decode src into dst
00950  *
00951  *      Number of chars decoded into dst is returned, -1 on error.
00952  *      Dst is null terminated.
00953  */
00954 
00955 int
00956 strunvis(char * dst, const char * src)
00957 {
00958     char c;
00959     char *start = dst;
00960     int state = 0;
00961 
00962     while ((c = *src++) != '\0') {
00963     again:
00964         switch (unvis(dst, c, &state, 0)) {
00965         case UNVIS_VALID:
00966             dst++;
00967             /*@switchbreak@*/ break;
00968         case UNVIS_VALIDPUSH:
00969             dst++;
00970             goto again;
00971         case 0:
00972         case UNVIS_NOCHAR:
00973             /*@switchbreak@*/ break;
00974         default:
00975             return (-1);
00976         }
00977     }
00978     if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
00979         dst++;
00980     *dst = '\0';
00981     return (dst - start);
00982 }
00983 
00984 /*==============================================================*/
00985 
00986 /* XXX *BSD systems already have getmode(3) and setmode(3) */
00987 #if defined(__linux__) || defined(__sun__) || defined(__LCLINT__) || defined(__QNXNTO__)
00988 #if !defined(HAVE_GETMODE) || !defined(HAVE_SETMODE)
00989 
00990 #define SET_LEN 6               /* initial # of bitcmd struct to malloc */
00991 #define SET_LEN_INCR 4          /* # of bitcmd structs to add as needed */
00992 
00993 typedef struct bitcmd {
00994     char        cmd;
00995     char        cmd2;
00996     mode_t      bits;
00997 } BITCMD;
00998 
00999 #define CMD2_CLR        0x01
01000 #define CMD2_SET        0x02
01001 #define CMD2_GBITS      0x04
01002 #define CMD2_OBITS      0x08
01003 #define CMD2_UBITS      0x10
01004 
01005 #if !defined(HAVE_GETMODE)
01006 /*
01007  * Given the old mode and an array of bitcmd structures, apply the operations
01008  * described in the bitcmd structures to the old mode, and return the new mode.
01009  * Note that there is no '=' command; a strict assignment is just a '-' (clear
01010  * bits) followed by a '+' (set bits).
01011  */
01012 static mode_t
01013 getmode(const void * bbox, mode_t omode)
01014         /*@*/
01015 {
01016     const BITCMD *set;
01017     mode_t clrval, newmode, value;
01018 
01019     set = (const BITCMD *)bbox;
01020     newmode = omode;
01021     for (value = 0;; set++)
01022         switch(set->cmd) {
01023         /*
01024          * When copying the user, group or other bits around, we "know"
01025          * where the bits are in the mode so that we can do shifts to
01026          * copy them around.  If we don't use shifts, it gets real
01027          * grundgy with lots of single bit checks and bit sets.
01028          */
01029         case 'u':
01030             value = (newmode & S_IRWXU) >> 6;
01031             goto common;
01032 
01033         case 'g':
01034             value = (newmode & S_IRWXG) >> 3;
01035             goto common;
01036 
01037         case 'o':
01038             value = newmode & S_IRWXO;
01039 common:     if ((set->cmd2 & CMD2_CLR) != '\0') {
01040                 clrval = (set->cmd2 & CMD2_SET) != '\0' ?  S_IRWXO : value;
01041                 if ((set->cmd2 & CMD2_UBITS) != '\0')
01042                     newmode &= ~((clrval<<6) & set->bits);
01043                 if ((set->cmd2 & CMD2_GBITS) != '\0')
01044                     newmode &= ~((clrval<<3) & set->bits);
01045                 if ((set->cmd2 & CMD2_OBITS) != '\0')
01046                     newmode &= ~(clrval & set->bits);
01047             }
01048             if ((set->cmd2 & CMD2_SET) != '\0') {
01049                 if ((set->cmd2 & CMD2_UBITS) != '\0')
01050                     newmode |= (value<<6) & set->bits;
01051                 if ((set->cmd2 & CMD2_GBITS) != '\0')
01052                     newmode |= (value<<3) & set->bits;
01053                 if ((set->cmd2 & CMD2_OBITS) != '\0')
01054                     newmode |= value & set->bits;
01055             }
01056             /*@switchbreak@*/ break;
01057 
01058         case '+':
01059             newmode |= set->bits;
01060             /*@switchbreak@*/ break;
01061 
01062         case '-':
01063             newmode &= ~set->bits;
01064             /*@switchbreak@*/ break;
01065 
01066         case 'X':
01067             if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
01068                         newmode |= set->bits;
01069             /*@switchbreak@*/ break;
01070 
01071         case '\0':
01072         default:
01073 #ifdef SETMODE_DEBUG
01074             (void) printf("getmode:%04o -> %04o\n", omode, newmode);
01075 #endif
01076             return newmode;
01077         }
01078 }
01079 #endif  /* !defined(HAVE_GETMODE) */
01080 
01081 #if !defined(HAVE_SETMODE)
01082 #ifdef SETMODE_DEBUG
01083 static void
01084 dumpmode(BITCMD *set)
01085 {
01086     for (; set->cmd; ++set)
01087         (void) printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
01088             set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
01089             set->cmd2 & CMD2_CLR ? " CLR" : "",
01090             set->cmd2 & CMD2_SET ? " SET" : "",
01091             set->cmd2 & CMD2_UBITS ? " UBITS" : "",
01092             set->cmd2 & CMD2_GBITS ? " GBITS" : "",
01093             set->cmd2 & CMD2_OBITS ? " OBITS" : "");
01094 }
01095 #endif
01096 
01097 #define ADDCMD(a, b, c, d)                                      \
01098     if (set >= endset) {                                        \
01099         BITCMD *newset;                                         \
01100         setlen += SET_LEN_INCR;                                 \
01101         newset = realloc(saveset, sizeof(*newset) * setlen);    \
01102         if (newset == NULL) {                                   \
01103                 if (saveset != NULL)                            \
01104                         free(saveset);                          \
01105                 saveset = NULL;                                 \
01106                 return (NULL);                                  \
01107         }                                                       \
01108         set = newset + (set - saveset);                         \
01109         saveset = newset;                                       \
01110         endset = newset + (setlen - 2);                         \
01111     }                                                           \
01112     set = addcmd(set, (a), (b), (c), (d))
01113 
01114 #define STANDARD_BITS   (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
01115 
01116 static BITCMD *
01117 addcmd(/*@returned@*/ BITCMD *set, int op, int who, int oparg, unsigned mask)
01118         /*@modifies set @*/
01119 {
01120     switch (op) {
01121     case '=':
01122         set->cmd = '-';
01123         set->bits = who ? who : (int) STANDARD_BITS;
01124         set++;
01125 
01126         op = (int)'+';
01127         /*@fallthrough@*/
01128     case '+':
01129     case '-':
01130     case 'X':
01131         set->cmd = (char)op;
01132         set->bits = (who ? (unsigned)who : mask) & oparg;
01133         break;
01134 
01135     case 'u':
01136     case 'g':
01137     case 'o':
01138         set->cmd = (char)op;
01139         if (who) {
01140             set->cmd2 = (char)( ((who & S_IRUSR) ? CMD2_UBITS : 0) |
01141                                 ((who & S_IRGRP) ? CMD2_GBITS : 0) |
01142                                 ((who & S_IROTH) ? CMD2_OBITS : 0));
01143             set->bits = (mode_t)~0;
01144         } else {
01145             set->cmd2 =(char)(CMD2_UBITS | CMD2_GBITS | CMD2_OBITS);
01146             set->bits = mask;
01147         }
01148         
01149         if (oparg == (int)'+')
01150             set->cmd2 |= CMD2_SET;
01151         else if (oparg == (int)'-')
01152             set->cmd2 |= CMD2_CLR;
01153         else if (oparg == (int)'=')
01154             set->cmd2 |= CMD2_SET|CMD2_CLR;
01155         break;
01156     }
01157     return set + 1;
01158 }
01159 
01160 /*
01161  * Given an array of bitcmd structures, compress by compacting consecutive
01162  * '+', '-' and 'X' commands into at most 3 commands, one of each.  The 'u',
01163  * 'g' and 'o' commands continue to be separate.  They could probably be
01164  * compacted, but it's not worth the effort.
01165  */
01166 static void
01167 compress_mode(/*@out@*/ BITCMD *set)
01168         /*@modifies set @*/
01169 {
01170     BITCMD *nset;
01171     int setbits, clrbits, Xbits, op;
01172 
01173     for (nset = set;;) {
01174         /* Copy over any 'u', 'g' and 'o' commands. */
01175         while ((op = (int)nset->cmd) != (int)'+' && op != (int)'-' && op != (int)'X') {
01176             *set++ = *nset++;
01177             if (!op)
01178                 return;
01179         }
01180 
01181         for (setbits = clrbits = Xbits = 0;; nset++) {
01182             if ((op = (int)nset->cmd) == (int)'-') {
01183                 clrbits |= nset->bits;
01184                 setbits &= ~nset->bits;
01185                 Xbits &= ~nset->bits;
01186             } else if (op == (int)'+') {
01187                 setbits |= nset->bits;
01188                 clrbits &= ~nset->bits;
01189                 Xbits &= ~nset->bits;
01190             } else if (op == (int)'X')
01191                 Xbits |= nset->bits & ~setbits;
01192             else
01193                 /*@innerbreak@*/ break;
01194         }
01195         if (clrbits) {
01196             set->cmd = '-';
01197             set->cmd2 = '\0';
01198             set->bits = clrbits;
01199             set++;
01200         }
01201         if (setbits) {
01202             set->cmd = '+';
01203             set->cmd2 = '\0';
01204             set->bits = setbits;
01205             set++;
01206         }
01207         if (Xbits) {
01208             set->cmd = 'X';
01209             set->cmd2 = '\0';
01210             set->bits = Xbits;
01211             set++;
01212         }
01213     }
01214 }
01215 
01216 /*@-usereleased@*/
01217 /*@null@*/
01218 static void *
01219 setmode(const char * p)
01220         /*@globals fileSystem @*/
01221         /*@modifies fileSystem @*/
01222 {
01223     int perm, who;
01224     char op;
01225     BITCMD *set, *saveset, *endset;
01226     sigset_t sigset, sigoset;
01227     mode_t mask;
01228     int equalopdone = 0;
01229     int permXbits, setlen;
01230     long perml;
01231 
01232     if (!*p)
01233         return (NULL);
01234 
01235     /*
01236      * Get a copy of the mask for the permissions that are mask relative.
01237      * Flip the bits, we want what's not set.  Since it's possible that
01238      * the caller is opening files inside a signal handler, protect them
01239      * as best we can.
01240      */
01241     (void) sigfillset(&sigset);
01242     (void) sigprocmask(SIG_BLOCK, &sigset, &sigoset);
01243     (void) umask(mask = umask(0));
01244     mask = ~mask;
01245     (void) sigprocmask(SIG_SETMASK, &sigoset, NULL);
01246 
01247     setlen = SET_LEN + 2;
01248         
01249     if ((set = malloc((unsigned)(sizeof(*set) * setlen))) == NULL)
01250         return (NULL);
01251     saveset = set;
01252     endset = set + (setlen - 2);
01253 
01254     /*
01255      * If an absolute number, get it and return; disallow non-octal digits
01256      * or illegal bits.
01257      */
01258     if (isdigit(*p)) {
01259         perml = strtol(p, NULL, 8);
01260 /*@-unrecog@*/
01261         if (perml < 0 || (perml & ~(STANDARD_BITS|S_ISTXT)))
01262 /*@=unrecog@*/
01263         {
01264             free(saveset);
01265             return (NULL);
01266         }
01267         perm = (int)(mode_t)perml;
01268         while (*++p != '\0')
01269             if (*p < '0' || *p > '7') {
01270                 free(saveset);
01271                 return (NULL);
01272             }
01273         ADDCMD((int)'=', (int)(STANDARD_BITS|S_ISTXT), perm, (unsigned)mask);
01274         return (saveset);
01275     }
01276 
01277     /*
01278      * Build list of structures to set/clear/copy bits as described by
01279      * each clause of the symbolic mode.
01280      */
01281     for (;;) {
01282         /* First, find out which bits might be modified. */
01283         for (who = 0;; ++p) {
01284             switch (*p) {
01285             case 'a':
01286                 who |= STANDARD_BITS;
01287                 /*@switchbreak@*/ break;
01288             case 'u':
01289                 who |= S_ISUID|S_IRWXU;
01290                 /*@switchbreak@*/ break;
01291             case 'g':
01292                 who |= S_ISGID|S_IRWXG;
01293                 /*@switchbreak@*/ break;
01294             case 'o':
01295                 who |= S_IRWXO;
01296                 /*@switchbreak@*/ break;
01297             default:
01298                 goto getop;
01299             }
01300         }
01301 
01302 getop:  if ((op = *p++) != '+' && op != '-' && op != '=') {
01303             free(saveset);
01304             return (NULL);
01305         }
01306         if (op == '=')
01307             equalopdone = 0;
01308 
01309         who &= ~S_ISTXT;
01310         for (perm = 0, permXbits = 0;; ++p) {
01311             switch (*p) {
01312             case 'r':
01313                 perm |= S_IRUSR|S_IRGRP|S_IROTH;
01314                 /*@switchbreak@*/ break;
01315             case 's':
01316                 /*
01317                  * If specific bits where requested and
01318                  * only "other" bits ignore set-id.
01319                  */
01320                 if (who == 0 || (who & ~S_IRWXO))
01321                     perm |= S_ISUID|S_ISGID;
01322                 /*@switchbreak@*/ break;
01323             case 't':
01324                 /*
01325                  * If specific bits where requested and
01326                  * only "other" bits ignore sticky.
01327                  */
01328                 if (who == 0 || (who & ~S_IRWXO)) {
01329                     who |= S_ISTXT;
01330                     perm |= S_ISTXT;
01331                 }
01332                 /*@switchbreak@*/ break;
01333             case 'w':
01334                 perm |= S_IWUSR|S_IWGRP|S_IWOTH;
01335                 /*@switchbreak@*/ break;
01336             case 'X':
01337                 permXbits = (int)(S_IXUSR|S_IXGRP|S_IXOTH);
01338                 /*@switchbreak@*/ break;
01339             case 'x':
01340                 perm |= S_IXUSR|S_IXGRP|S_IXOTH;
01341                 /*@switchbreak@*/ break;
01342             case 'u':
01343             case 'g':
01344             case 'o':
01345                 /*
01346                  * When ever we hit 'u', 'g', or 'o', we have
01347                  * to flush out any partial mode that we have,
01348                  * and then do the copying of the mode bits.
01349                  */
01350                 if (perm) {
01351                     ADDCMD((int)op, who, perm, (unsigned)mask);
01352                     perm = 0;
01353                 }
01354                 if (op == '=')
01355                     equalopdone = 1;
01356                 if (op == '+' && permXbits) {
01357                     ADDCMD((int)'X', who, permXbits, (unsigned)mask);
01358                     permXbits = 0;
01359                 }
01360                 ADDCMD((int)*p, who, (int)op, (unsigned)mask);
01361                 /*@switchbreak@*/ break;
01362 
01363             default:
01364                 /*
01365                  * Add any permissions that we haven't already
01366                  * done.
01367                  */
01368                 if (perm || (op == '=' && !equalopdone)) {
01369                     if (op == '=')
01370                         equalopdone = 1;
01371                     ADDCMD((int)op, who, perm, (unsigned)mask);
01372                     perm = 0;
01373                 }
01374                 if (permXbits) {
01375                     ADDCMD((int)'X', who, permXbits, (unsigned)mask);
01376                     permXbits = 0;
01377                 }
01378                 goto apply;
01379             }
01380         }
01381 
01382 apply:  if (!*p)
01383             break;
01384         if (*p != ',')
01385             goto getop;
01386         ++p;
01387     }
01388     set->cmd = '\0';
01389 #ifdef SETMODE_DEBUG
01390     (void) printf("Before compress_mode()\n");
01391     dumpmode(saveset);
01392 #endif
01393     compress_mode(saveset);
01394 #ifdef SETMODE_DEBUG
01395     (void) printf("After compress_mode()\n");
01396     dumpmode(saveset);
01397 #endif
01398     return saveset;
01399 }
01400 /*@=usereleased@*/
01401 #endif  /* !defined(HAVE_SETMODE) */
01402 #endif  /* !defined(HAVE_GETMODE) || !defined(HAVE_SETMODE) */
01403 #endif  /* __linux__ */
01404 
01405 /*==============================================================*/
01406 
01407 static void
01408 set(char * t, NODE * ip)
01409         /*@globals fileSystem, internalState @*/
01410         /*@modifies t, ip, fileSystem, internalState @*/
01411 {
01412     char *kw;
01413 
01414     for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) {
01415         uint32_t needvalue;
01416         enum mtreeKeys_e type = parsekey(kw, &needvalue);
01417         char *val = NULL;
01418         char *ep;
01419 
01420         if (needvalue && (val = strtok(NULL, " \t\n")) == NULL)
01421                 mtree_error("missing value");
01422         ip->flags |= type;
01423         switch(type) {
01424         case MTREE_KEYS_CKSUM:
01425             ip->cksum = strtoul(val, &ep, 10);
01426             if (*ep != '\0')
01427                 mtree_error("invalid checksum %s", val);
01428             /*@switchbreak@*/ break;
01429         case MTREE_KEYS_FLAGS:
01430 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
01431             if (!strcmp(val, "none")) {
01432                 ip->sb.st_flags = 0;
01433                 /*@switchbreak@*/ break;
01434             }
01435             {   unsigned long fset, fclr;
01436                 if (strtofflags(&val, &fset, &fclr))
01437                     mtree_error("%s", strerror(errno));
01438                 ip->sb.st_flags = fset;
01439             }
01440 #endif
01441             /*@switchbreak@*/ break;
01442         case MTREE_KEYS_GID:
01443             ip->sb.st_gid = strtoul(val, &ep, 10);
01444             if (*ep != '\0')
01445                 mtree_error("invalid gid %s", val);
01446             /*@switchbreak@*/ break;
01447         case MTREE_KEYS_GNAME:
01448             if (gnameToGid(val, &ip->sb.st_gid) == -1)
01449                 mtree_error("unknown group %s", val);
01450             /*@switchbreak@*/ break;
01451         case MTREE_KEYS_IGN:
01452             /* just set flag bit */
01453             /*@switchbreak@*/ break;
01454         case MTREE_KEYS_MODE:
01455         {   mode_t *m;
01456             if ((m = setmode(val)) == NULL)
01457                 mtree_error("invalid file mode %s", val);
01458             ip->sb.st_mode = getmode(m, 0);
01459             free(m);
01460         }   /*@switchbreak@*/ break;
01461         case MTREE_KEYS_NLINK:
01462             ip->sb.st_nlink = strtoul(val, &ep, 10);
01463             if (*ep != '\0')
01464                 mtree_error("invalid link count %s", val);
01465             /*@switchbreak@*/ break;
01466         case MTREE_KEYS_DIGEST:
01467             (void) argiAdd(&ip->algos, -1, (int)needvalue);
01468             (void) argvAdd(&ip->digests, val);
01469             /*@switchbreak@*/ break;
01470         case MTREE_KEYS_SIZE:
01471 /*@-unrecog@*/
01472             ip->sb.st_size = strtoul(val, &ep, 10);
01473 /*@=unrecog@*/
01474             if (*ep != '\0')
01475                 mtree_error("invalid size %s", val);
01476             /*@switchbreak@*/ break;
01477         case MTREE_KEYS_SLINK:
01478             ip->slink = xmalloc(strlen(val) + 1);
01479             if (strunvis(ip->slink, val) == -1) {
01480                 fprintf(stderr, _("%s: filename (%s) encoded incorrectly\n"),
01481                         __progname, val);
01482                 /* XXX Mac OS X exits here. */
01483                 strcpy(ip->slink, val);
01484             }
01485             /*@switchbreak@*/ break;
01486         case MTREE_KEYS_TIME:
01487 #if defined(TIMEVAL_TO_TIMESPEC)
01488             ip->sb.st_mtimespec.tv_sec = strtoul(val, &ep, 10);
01489             if (*ep != '.')
01490                 mtree_error("invalid time %s", val);
01491             val = ep + 1;
01492             ip->sb.st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
01493             if (*ep != '\0')
01494                 mtree_error("invalid time %s", val);
01495 #else
01496             ip->sb.st_mtime = strtoul(val, &ep, 10);
01497             if (*ep != '.')
01498                 mtree_error("invalid time %s", val);
01499             val = ep + 1;
01500             (void) strtoul(val, &ep, 10);
01501             if (*ep != '\0')
01502                 mtree_error("invalid time %s", val);
01503 #endif
01504             /*@switchbreak@*/ break;
01505         case MTREE_KEYS_TYPE:
01506             switch(*val) {
01507             case 'b':
01508                 if (!strcmp(val, "block"))
01509                     ip->type = F_BLOCK;
01510                 /*@innerbreak@*/ break;
01511             case 'c':
01512                 if (!strcmp(val, "char"))
01513                     ip->type = F_CHAR;
01514                 /*@innerbreak@*/ break;
01515             case 'd':
01516                 if (!strcmp(val, "dir"))
01517                     ip->type = F_DIR;
01518                 /*@innerbreak@*/ break;
01519             case 'f':
01520                 if (!strcmp(val, "file"))
01521                     ip->type = F_FILE;
01522                 if (!strcmp(val, "fifo"))
01523                     ip->type = F_FIFO;
01524                 /*@innerbreak@*/ break;
01525             case 'l':
01526                 if (!strcmp(val, "link"))
01527                     ip->type = F_LINK;
01528                 /*@innerbreak@*/ break;
01529             case 's':
01530                 if (!strcmp(val, "socket"))
01531                     ip->type = F_SOCK;
01532                 /*@innerbreak@*/ break;
01533             default:
01534                 mtree_error("unknown file type %s", val);
01535             }
01536             /*@switchbreak@*/ break;
01537         case MTREE_KEYS_UID:
01538             ip->sb.st_uid = strtoul(val, &ep, 10);
01539             if (*ep != '\0')
01540                 mtree_error("invalid uid %s", val);
01541             /*@switchbreak@*/ break;
01542         case MTREE_KEYS_UNAME:
01543             if (unameToUid(val, &ip->sb.st_uid) == -1)
01544                 mtree_error("unknown user %s", val);
01545             /*@switchbreak@*/ break;
01546         case MTREE_KEYS_NONE:
01547         case MTREE_KEYS_DONE:
01548         case MTREE_KEYS_MAGIC:
01549         case MTREE_KEYS_VISIT:
01550         case MTREE_KEYS_NOCHANGE:
01551         case MTREE_KEYS_OPT:
01552             ip->flags &= ~type;         /* XXX clean up "can't happen" cruft? */
01553             /*@notreached@*/ /*@switchbreak@*/ break;
01554         }
01555     }
01556 }
01557 
01558 static void
01559 unset(char * t, NODE * ip)
01560         /*@globals fileSystem, internalState @*/
01561         /*@modifies t, ip, fileSystem, internalState @*/
01562 {
01563     char *p;
01564 
01565     while ((p = strtok(t, "\n\t ")) != NULL)
01566         ip->flags &= ~parsekey(p, NULL);
01567 }
01568 
01569 #define KF_ISSET(_keys, _KEY) ((_keys) & (MTREE_KEYS_##_KEY))
01570 
01571 /* XXX todo: only fts->lineo used. lightweight struct {fn,fp,lineno} instead. */
01572 NODE *
01573 mtreeSpec(rpmfts fts, FILE * fp)
01574 {
01575     NODE *centry = NULL;
01576     NODE *last = NULL;
01577     char *p;
01578     NODE ginfo;
01579     NODE *root = NULL;
01580     NODE *forest = NULL;
01581     int c_cur = 0;
01582     int c_next = 0;
01583     char buf[2048];
01584 
01585     if (fp == NULL)
01586         fp = stdin;
01587 
01588     memset(&ginfo, 0, sizeof(ginfo));
01589     for (fts->lineno = 1; fgets(buf, (int)sizeof(buf), fp) != NULL;
01590             ++fts->lineno, c_cur = c_next, c_next = 0)
01591     {
01592         /* Skip empty lines. */
01593         if (buf[0] == '\n')
01594             continue;
01595 
01596         /* Find end of line. */
01597         if ((p = strchr(buf, '\n')) == NULL)
01598             mtree_error("line %d too long", fts->lineno);
01599 
01600         /* See if next line is continuation line. */
01601         if (p[-1] == '\\') {
01602             --p;
01603             c_next = 1;
01604         }
01605 
01606         /* Null-terminate the line. */
01607         *p = '\0';
01608 
01609         /* Skip leading whitespace. */
01610         for (p = buf; *p && isspace(*p); ++p);
01611 
01612         /* If nothing but whitespace or comment char, continue. */
01613         if (*p == '\0' || *p == '#')
01614             continue;
01615 
01616 #ifdef DEBUG
01617         (void)fprintf(stderr, "line %3d: {%s}\n", fts->lineno, p);
01618 #endif
01619         if (c_cur) {
01620             set(p, centry);
01621             continue;
01622         }
01623                         
01624         /* Grab file name, "$", "set", or "unset". */
01625         if ((p = strtok(p, "\n\t ")) == NULL)
01626             mtree_error("missing field");
01627 
01628         if (p[0] == '/')
01629             switch(p[1]) {
01630             case 's':
01631                 if (strcmp(p + 1, "set"))
01632                     /*@switchbreak@*/ break;
01633                 set(NULL, &ginfo);
01634                 continue;
01635             case 'u':
01636                 if (strcmp(p + 1, "unset"))
01637                     /*@switchbreak@*/ break;
01638                 unset(NULL, &ginfo);
01639                 continue;
01640             }
01641 
01642 #if !defined(_RPMFI_INTERNAL)   /* XXX *.rpm/ specs include '/' in names. */
01643         if (strchr(p, '/') != NULL)
01644             mtree_error("slash character in file name");
01645 #endif
01646 
01647         if (!strcmp(p, "..")) {
01648             /* Don't go up, if haven't gone down. */
01649             if (root == NULL)
01650                 goto noparent;
01651             if (last->type != F_DIR || KF_ISSET(last->flags, DONE)) {
01652                 if (last == root)
01653                     goto noparent;
01654                 last = last->parent;
01655             }
01656             last->flags |= MTREE_KEYS_DONE;
01657             continue;
01658 
01659 noparent:    mtree_error("no parent node");
01660         }
01661 
01662         /* XXX sizeof(*centry) includes room for final '\0' */
01663         centry = xcalloc(1, sizeof(*centry) + strlen(p));
01664         *centry = ginfo;        /* structure assignment */
01665 #define MAGIC   "?*["
01666         if (strpbrk(p, MAGIC) != NULL)
01667             centry->flags |= MTREE_KEYS_MAGIC;
01668         if (strunvis(centry->name, p) == -1) {
01669             fprintf(stderr, _("%s: filename (%s) encoded incorrectly\n"),
01670                         __progname, p);
01671             strcpy(centry->name, p);
01672         }
01673         set(NULL, centry);
01674 
01675         if (root == NULL) {
01676             last = root = centry;
01677             root->parent = root;
01678             if (forest == NULL)
01679                 forest = root;
01680         } else if (centry->name[0] == '.' && centry->name[1] == '\0') {
01681             centry->prev = root;
01682             last = root = root->next = centry;
01683             root->parent = root;
01684         } else if (last->type == F_DIR && !KF_ISSET(last->flags, DONE)) {
01685             centry->parent = last;
01686             last = last->child = centry;
01687         } else {
01688             centry->parent = last->parent;
01689             centry->prev = last;
01690             last = last->next = centry;
01691         }
01692     }
01693     return forest;
01694 }
01695 
01696 /*==============================================================*/
01697 
01698 /*@observer@*/
01699 static const char *
01700 ftype(unsigned type)
01701         /*@*/
01702 {
01703     switch(type) {
01704     case F_BLOCK: return "block";
01705     case F_CHAR:  return "char";
01706     case F_DIR:   return "dir";
01707     case F_FIFO:  return "fifo";
01708     case F_FILE:  return "file";
01709     case F_LINK:  return "link";
01710     case F_SOCK:  return "socket";
01711     default:      return "unknown";
01712     }
01713     /*@notreached@*/
01714 }
01715 
01716 /*@observer@*/
01717 static const char *
01718 inotype(mode_t mode)
01719         /*@*/
01720 {
01721     switch(mode & S_IFMT) {
01722     case S_IFBLK:  return "block";
01723     case S_IFCHR:  return "char";
01724     case S_IFDIR:  return "dir";
01725     case S_IFIFO:  return "fifo";
01726     case S_IFREG:  return "file";
01727     case S_IFLNK:  return "link";
01728 /*@-unrecog@*/
01729     case S_IFSOCK: return "socket";
01730 /*@=unrecog@*/
01731     default:       return "unknown";
01732     }
01733     /*@notreached@*/
01734 }
01735 
01736 /*-
01737  * Copyright (c) 2003 Poul-Henning Kamp
01738  * All rights reserved.
01739  *
01740  * Redistribution and use in source and binary forms, with or without
01741  * modification, are permitted provided that the following conditions
01742  * are met:
01743  * 1. Redistributions of source code must retain the above copyright
01744  *    notice, this list of conditions and the following disclaimer.
01745  * 2. Redistributions in binary form must reproduce the above copyright
01746  *    notice, this list of conditions and the following disclaimer in the
01747  *    documentation and/or other materials provided with the distribution.
01748  *
01749  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
01750  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
01751  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
01752  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
01753  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
01754  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
01755  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
01756  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
01757  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
01758  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
01759  * SUCH DAMAGE.
01760  */
01761 
01762 #define FF(a, b, c, d) \
01763         (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
01764 #define FS(a, b, c, d) \
01765         (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
01766 #define FM(a, b, c, d) \
01767         (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
01768 
01769 static void
01770 shownode(NODE *n, enum mtreeKeys_e keys, const char *path)
01771         /*@globals fileSystem @*/
01772         /*@modifies fileSystem @*/
01773 {
01774     printf("%s%s %s", path, n->name, ftype((unsigned)n->type));
01775     if (KF_ISSET(keys, CKSUM))
01776         printf(" cksum=%lu", (unsigned long) n->cksum);
01777     if (KF_ISSET(keys, GID))
01778         printf(" gid=%lu", (unsigned long) n->sb.st_gid);
01779     if (KF_ISSET(keys, GNAME)) {
01780         const char * gname = gidToGname(n->sb.st_gid);
01781         if (gname != NULL)
01782             printf(" gname=%s", gname);
01783         else
01784             printf(" gid=%lu", (unsigned long) n->sb.st_gid);
01785     }
01786     if (KF_ISSET(keys, MODE))
01787         printf(" mode=%o", (unsigned) n->sb.st_mode);
01788     if (KF_ISSET(keys, NLINK))
01789         printf(" nlink=%lu", (unsigned long) n->sb.st_nlink);
01790 /*@-duplicatequals@*/
01791     if (KF_ISSET(keys, SIZE))
01792         printf(" size=%llu", (unsigned long long)n->sb.st_size);
01793 /*@=duplicatequals@*/
01794     if (KF_ISSET(keys, UID))
01795         printf(" uid=%lu", (unsigned long) n->sb.st_uid);
01796     if (KF_ISSET(keys, UNAME)) {
01797         const char * uname = uidToUname(n->sb.st_uid);
01798         if (uname != NULL)
01799             printf(" uname=%s", uname);
01800         else
01801             printf(" uid=%lu", (unsigned long) n->sb.st_uid);
01802     }
01803 
01804     /* Output all the digests. */
01805     if (KF_ISSET(keys, DIGEST)) {
01806         int i;
01807 
01808         if (n->algos != NULL)
01809         for (i = 0; i < (int) n->algos->nvals; i++) {
01810             uint32_t algo = n->algos->vals[i];
01811             const char * tagname = algo2tagname(algo);
01812             if (tagname != NULL)
01813                 printf(" %s=%s", tagname, n->digests[i]);
01814         }
01815     }
01816 
01817 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
01818     if (KF_ISSET(keys, FLAGS))
01819         printf(" flags=%s", flags_to_string(n->sb.st_flags));
01820 #endif
01821     printf("\n");
01822 }
01823 
01824 static int
01825 mismatch(NODE *n1, NODE *n2, enum mtreeKeys_e  differ, const char *path)
01826         /*@globals fileSystem @*/
01827         /*@modifies fileSystem @*/
01828 {
01829     enum mtreeKeys_e keys = _rpmfts->keys;
01830 
01831     if (n2 == NULL) {
01832         shownode(n1, differ, path);
01833         return 1;
01834     }
01835     if (n1 == NULL) {
01836         printf("\t");
01837         shownode(n2, differ, path);
01838         return 1;
01839     }
01840     if (!(differ & keys))
01841         return 0;
01842     printf("\t\t");
01843     shownode(n1, differ, path);
01844     printf("\t\t");
01845     shownode(n2, differ, path);
01846     return 1;
01847 }
01848 
01849 static int
01850 compare_nodes(NODE *n1, NODE *n2, const char *path)
01851         /*@globals fileSystem @*/
01852         /*@modifies n1, n2, fileSystem @*/
01853 {
01854     enum mtreeKeys_e differs = MTREE_KEYS_NONE;
01855     int xx;
01856         
01857     if (n1 != NULL && n1->type == F_LINK)
01858         n1->flags &= ~MTREE_KEYS_MODE;
01859     if (n2 != NULL && n2->type == F_LINK)
01860         n2->flags &= ~MTREE_KEYS_MODE;
01861     if (n1 == NULL && n2 != NULL) {
01862         differs = n2->flags;
01863         xx = mismatch(n1, n2, differs, path);
01864         return 1;
01865     }
01866     if (n1 != NULL && n2 == NULL) {
01867         differs = n1->flags;
01868         xx = mismatch(n1, n2, differs, path);
01869         return 1;
01870     }
01871     if (n1->type != n2->type) {
01872         differs = MTREE_KEYS_NONE;      /* XXX unneeded */
01873         xx = mismatch(n1, n2, differs, path);
01874         return 1;
01875     }
01876     if (FF(n1, n2, MTREE_KEYS_CKSUM, cksum))
01877         differs |= MTREE_KEYS_CKSUM;
01878     if (FF(n1, n2, MTREE_KEYS_GID, sb.st_gid))
01879         differs |= MTREE_KEYS_GID;
01880     if (FF(n1, n2, MTREE_KEYS_GNAME, sb.st_gid))
01881         differs |= MTREE_KEYS_GNAME;
01882     if (FF(n1, n2, MTREE_KEYS_MODE, sb.st_mode))
01883         differs |= MTREE_KEYS_MODE;
01884     if (FF(n1, n2, MTREE_KEYS_NLINK, sb.st_nlink))
01885         differs |= MTREE_KEYS_NLINK;
01886     if (FF(n1, n2, MTREE_KEYS_SIZE, sb.st_size))
01887         differs |= MTREE_KEYS_SIZE;
01888 
01889     if (FS(n1, n2, MTREE_KEYS_SLINK, slink))
01890         differs |= MTREE_KEYS_SLINK;
01891 
01892 /*@-type@*/
01893     if (FM(n1, n2, MTREE_KEYS_TIME, sb.st_mtimespec))
01894         differs |= MTREE_KEYS_TIME;
01895 /*@=type@*/
01896     if (FF(n1, n2, MTREE_KEYS_UID, sb.st_uid))
01897         differs |= MTREE_KEYS_UID;
01898     if (FF(n1, n2, MTREE_KEYS_UNAME, sb.st_uid))
01899         differs |= MTREE_KEYS_UNAME;
01900 
01901     /* Compare all the digests. */
01902     if (KF_ISSET(n1->flags, DIGEST) || KF_ISSET(n2->flags, DIGEST)) {
01903         if ((KF_ISSET(n1->flags, DIGEST) != KF_ISSET(n2->flags, DIGEST))
01904          || (n1->algos == NULL || n2->algos == NULL)
01905          || (n1->algos->nvals != n2->algos->nvals))
01906             differs |= MTREE_KEYS_DIGEST;
01907         else {
01908             int i;
01909 
01910             for (i = 0; i < (int) n1->algos->nvals; i++) {
01911                 if ((n1->algos->vals[i] == n2->algos->vals[i])
01912                  && !strcmp(n1->digests[i], n2->digests[i]))
01913                     continue;
01914                 differs |= MTREE_KEYS_DIGEST;
01915                 break;
01916             }
01917         }
01918     }
01919 
01920 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
01921     if (FF(n1, n2, MTREE_KEYS_FLAGS, sb.st_flags))
01922         differs |= MTREE_KEYS_FLAGS;
01923 #endif
01924 
01925     if (differs) {
01926         xx = mismatch(n1, n2, differs, path);
01927         return 1;
01928     }
01929     return 0;
01930 }
01931 
01932 static int
01933 mtreeSWalk(NODE *t1, NODE *t2, const char *path)
01934         /*@globals fileSystem @*/
01935         /*@modifies t1, t2, fileSystem @*/
01936 {
01937     NODE *c1 = (t1 != NULL ? t1->child : NULL);
01938     NODE *c2 = (t2 != NULL ? t2->child : NULL);
01939     int r = 0;
01940 
01941     while (c1 != NULL || c2 != NULL) {
01942         NODE *n1, *n2;
01943         char *np;
01944         int i;
01945 
01946         n1 = (c1 != NULL ? c1->next : NULL);
01947         n2 = (c2 != NULL ? c2->next : NULL);
01948         if (c1 != NULL && c2 != NULL) {
01949             if (c1->type != F_DIR && c2->type == F_DIR) {
01950                 n2 = c2;
01951                 c2 = NULL;
01952             } else if (c1->type == F_DIR && c2->type != F_DIR) {
01953                 n1 = c1;
01954                 c1 = NULL;
01955             } else {
01956                 i = strcmp(c1->name, c2->name);
01957                 if (i > 0) {
01958                     n1 = c1;
01959                     c1 = NULL;
01960                 } else if (i < 0) {
01961                     n2 = c2;
01962                     c2 = NULL;
01963                 }
01964             }
01965         }
01966 /*@-noeffectuncon -unrecog@*/
01967         if (c1 == NULL && c2->type == F_DIR) {
01968             if (asprintf(&np, "%s%s/", path, c2->name)) {
01969                 perror("asprintf");
01970             }
01971             i = mtreeSWalk(c1, c2, np);
01972             free(np);
01973             i += compare_nodes(c1, c2, path);
01974         } else if (c2 == NULL && c1->type == F_DIR) {
01975             if (asprintf(&np, "%s%s/", path, c1->name)) {
01976                 perror("asprintf");
01977             }
01978             i = mtreeSWalk(c1, c2, np);
01979             free(np);
01980             i += compare_nodes(c1, c2, path);
01981         } else if (c1 == NULL || c2 == NULL) {
01982             i = compare_nodes(c1, c2, path);
01983         } else if (c1->type == F_DIR && c2->type == F_DIR) {
01984             if (asprintf(&np, "%s%s/", path, c1->name)) {
01985                 perror("asprintf");
01986             }
01987             i = mtreeSWalk(c1, c2, np);
01988             free(np);
01989             i += compare_nodes(c1, c2, path);
01990         } else {
01991             i = compare_nodes(c1, c2, path);
01992         }
01993 /*@=noeffectuncon =unrecog@*/
01994         r += i;
01995         c1 = n1;
01996         c2 = n2;
01997     }
01998     return r;   
01999 }
02000 
02001 int
02002 mtreeVSpec(rpmfts fts)
02003 {
02004     NODE * root1 = mtreeSpec(fts, fts->spec1);
02005     NODE * root2 = mtreeSpec(fts, fts->spec2);
02006     int rval = 0;
02007 
02008     rval = mtreeSWalk(root1, root2, "");
02009     rval += compare_nodes(root1, root2, "");
02010     return (rval > 0 ? MISMATCHEXIT : 0);
02011 }
02012 
02013 /*==============================================================*/
02014 
02015 /*@observer@*/
02016 static const char *
02017 rlink(const char * name)
02018         /*@globals h_errno, fileSystem, internalState @*/
02019         /*@modifies fileSystem, internalState @*/
02020 
02021 {
02022     static char lbuf[MAXPATHLEN];
02023     int len;
02024 
02025     if ((len = Readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
02026         mtree_error("%s: %s", name, strerror(errno));
02027     lbuf[len] = '\0';
02028     return lbuf;
02029 }
02030 
02031 #define SKIPDOTSLASH(_f) ((_f)[0] == '.' && (_f)[1] == '/' ? (_f) + 2 : (_f))
02032 
02033 #define COMPAREINDENTNAMELEN    8
02034 #define LABEL                   \
02035     if (!label++) {             \
02036         (void) printf(_("%s changed\n"), SKIPDOTSLASH(p->fts_path)); \
02037         tab = "\t";             \
02038     }
02039 
02040 /*@observer@*/
02041 static const char * algo2name(uint32_t algo)
02042         /*@*/
02043 {
02044     switch (algo) {
02045     case PGPHASHALGO_MD5:          return "MD5";
02046     case PGPHASHALGO_SHA1:         return "SHA1";
02047     case PGPHASHALGO_RIPEMD160:    return "RIPEMD160";
02048     case PGPHASHALGO_MD2:          return "MD2";
02049     case PGPHASHALGO_TIGER192:     return "TIGER192";
02050     case PGPHASHALGO_HAVAL_5_160:  return "HAVAL-5-160";
02051     case PGPHASHALGO_SHA256:       return "SHA256";
02052     case PGPHASHALGO_SHA384:       return "SHA384";
02053     case PGPHASHALGO_SHA512:       return "SHA512";
02054 
02055     case PGPHASHALGO_MD4:          return "MD4";
02056     case PGPHASHALGO_RIPEMD128:    return "RIPEMD128";
02057     case PGPHASHALGO_CRC32:        return "CRC32";
02058     case PGPHASHALGO_ADLER32:      return "ADLER32";
02059     case PGPHASHALGO_CRC64:        return "CRC64";
02060     case PGPHASHALGO_JLU32:        return "JLU32";
02061     case PGPHASHALGO_SHA224:       return "SHA224";
02062     case PGPHASHALGO_RIPEMD256:    return "RIPEMD256";
02063     case PGPHASHALGO_RIPEMD320:    return "RIPEMD320";
02064     case PGPHASHALGO_SALSA10:      return "SALSA10";
02065     case PGPHASHALGO_SALSA20:      return "SALSA20";
02066 
02067     default:                       return "Unknown";
02068     }
02069     /*@notreached@*/
02070 }
02071 
02072 static int
02073 compare(rpmfts fts, NODE *const s)
02074         /*@globals errno, h_errno, fileSystem, internalState @*/
02075         /*@modifies errno, fileSystem, internalState @*/
02076 
02077 {
02078     const char * name = s->name;
02079     FTSENT *const p = fts->p;
02080     const char * fts_accpath = p->fts_accpath;
02081     struct stat *const st = p->fts_statp;
02082     enum mtreeKeys_e keys = s->flags;
02083     int label = 0;
02084     const char *cp;
02085     const char *tab = "";
02086 
02087     switch(s->type) {
02088     case F_BLOCK:
02089         if (!S_ISBLK(st->st_mode))
02090             goto typeerr;
02091         break;
02092     case F_CHAR:
02093         if (!S_ISCHR(st->st_mode))
02094             goto typeerr;
02095         break;
02096     case F_DIR:
02097         if (!S_ISDIR(st->st_mode))
02098             goto typeerr;
02099         break;
02100     case F_FIFO:
02101         if (!S_ISFIFO(st->st_mode))
02102             goto typeerr;
02103         break;
02104     case F_FILE:
02105         if (!S_ISREG(st->st_mode))
02106             goto typeerr;
02107         break;
02108     case F_LINK:
02109         if (!S_ISLNK(st->st_mode))
02110             goto typeerr;
02111         break;
02112     case F_SOCK:
02113 /*@-unrecog@*/
02114         if (!S_ISSOCK(st->st_mode)) {
02115 typeerr:    LABEL;
02116             (void) printf(_("\ttype expected %s found %s)\n"),
02117                     ftype((unsigned)s->type), inotype(st->st_mode));
02118         }
02119 /*@=unrecog@*/
02120         break;
02121     }
02122 
02123     /* Set the uid/gid first, then set the mode. */
02124     if ((KF_ISSET(keys, UID) || KF_ISSET(keys, UNAME)) && s->sb.st_uid != st->st_uid) {
02125         LABEL;
02126         (void) printf(_("%s%s expected %lu found %lu"), tab, "user",
02127                 (unsigned long)s->sb.st_uid, (unsigned long)st->st_uid);
02128         if (MF_ISSET(UPDATE)) {
02129             if (Chown(fts_accpath, s->sb.st_uid, -1))
02130                 (void) printf(_(" not modified: %s)\n"), strerror(errno));
02131             else
02132                 (void) printf(_(" modified)\n"));
02133         } else
02134             (void) printf("\n");
02135         tab = "\t";
02136     }
02137     if ((KF_ISSET(keys, GID) || KF_ISSET(keys, GNAME)) && s->sb.st_gid != st->st_gid) {
02138         LABEL;
02139         (void) printf(_("%s%s expected %lu found %lu"), tab, "gid",
02140                 (unsigned long)s->sb.st_gid, (unsigned long)st->st_gid);
02141         if (MF_ISSET(UPDATE)) {
02142             if (Chown(fts_accpath, -1, s->sb.st_gid))
02143                 (void) printf(_(" not modified: %s)\n"), strerror(errno));
02144             else
02145                 (void) printf(_(" modified)\n"));
02146         } else
02147             (void) printf("\n");
02148         tab = "\t";
02149     }
02150     if (KF_ISSET(keys, MODE) && s->sb.st_mode != (st->st_mode & MBITS)) {
02151         if (MF_ISSET(LOOSE)) {
02152             mode_t tmode = s->sb.st_mode;
02153             mode_t mode = (st->st_mode & MBITS);
02154 
02155             /*
02156             * if none of the suid/sgid/etc bits are set,
02157             * then if the mode is a subset of the target,
02158             * skip.
02159             */
02160             if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO))
02161              ||    (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
02162                 if ((mode | tmode) == tmode)
02163                     goto skip;
02164         }
02165         LABEL;
02166         (void) printf(_("%s%s expected %#o found %#o"), tab, "permissions",
02167                 (unsigned)s->sb.st_mode, (unsigned)(st->st_mode & MBITS));
02168         if (MF_ISSET(UPDATE)) {
02169             if (Chmod(fts_accpath, s->sb.st_mode))
02170                 (void) printf(_(" not modified: %s)\n"), strerror(errno));
02171             else
02172                 (void) printf(_(" modified)\n"));
02173         } else
02174             (void) printf("\n");
02175         tab = "\t";
02176         skip:
02177             ;
02178     }
02179     if (KF_ISSET(keys, NLINK) && s->type != F_DIR &&
02180         s->sb.st_nlink != st->st_nlink)
02181     {
02182         LABEL;
02183         (void) printf(_("%s%s expected %lu found %lu)\n"), tab, "link_count",
02184                 (unsigned long)s->sb.st_nlink, (unsigned long)st->st_nlink);
02185         tab = "\t";
02186     }
02187     if (KF_ISSET(keys, SIZE) && s->sb.st_size != st->st_size) {
02188         LABEL;
02189 /*@-duplicatequals@*/
02190         (void) printf(_("%s%s expected %llu found %llu\n"), tab, "size",
02191                 (unsigned long long)s->sb.st_size,
02192                 (unsigned long long)st->st_size);
02193 /*@=duplicatequals@*/
02194         tab = "\t";
02195     }
02196     /*
02197      * XXX
02198      * Since utimes(2) only takes a timeval, there's no point in
02199      * comparing the low bits of the timespec nanosecond field.  This
02200      * will only result in mismatches that we can never fix.
02201      *
02202      * Doesn't display microsecond differences.
02203      */
02204     if (KF_ISSET(keys, TIME)) {
02205         struct timeval tv[2];
02206 
02207 /*@-noeffectuncon -unrecog @*/
02208 #if defined(TIMESPEC_TO_TIMEVAL)
02209         TIMESPEC_TO_TIMEVAL(&tv[0], &s->sb.st_mtimespec);
02210         TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
02211 #else
02212         tv[0].tv_sec = (long)s->sb.st_mtime;
02213         tv[0].tv_usec = 0L;
02214         tv[1].tv_sec = (long)st->st_mtime;
02215         tv[1].tv_usec = 0L;
02216 #endif
02217 /*@=noeffectuncon =unrecog @*/
02218         if (tv[0].tv_sec != tv[1].tv_sec
02219 #ifdef  NOTYET  /* XXX avoid timespec jitter issues for now. */
02220          || tv[0].tv_usec != tv[1].tv_usec
02221 #endif
02222         ) {
02223             time_t t1 = (time_t)tv[0].tv_sec;
02224             time_t t2 = (time_t)tv[1].tv_sec;
02225             LABEL;
02226             (void) printf(_("%s%s expected %.24s "), tab, "modification time", ctime(&t1));
02227             (void) printf(_("found %.24s"), ctime(&t2));
02228             if (MF_ISSET(TOUCH)) {
02229                 tv[1] = tv[0];
02230                 if (Utimes(fts_accpath, tv))
02231                     (void) printf(_(" not modified: %s)\n"), strerror(errno));
02232                 else
02233                     (void) printf(_(" modified\n"));
02234             } else
02235                 (void) printf("\n");
02236             tab = "\t";
02237         }
02238     }
02239 
02240     /* Any digests to calculate? */
02241     if (KF_ISSET(keys, CKSUM) || s->algos != NULL) {
02242         FD_t fd = Fopen(fts_accpath, "r.ufdio");
02243         uint32_t vlen, val;
02244         int i;
02245 
02246         if (fd == NULL || Ferror(fd)) {
02247             LABEL;
02248             (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
02249             goto cleanup;
02250         }
02251 
02252         /* Setup all digest calculations.  Reversed order is effete ... */
02253         if (s->algos != NULL)
02254         for (i = s->algos->nvals; i-- > 0;)
02255             fdInitDigest(fd, s->algos->vals[i], 0);
02256 
02257         /* Compute the cksum and digests. */
02258         if (KF_ISSET(keys, CKSUM))
02259             i = crc(fd, &val, &vlen);
02260         else {
02261             char buffer[16 * 1024];
02262             while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
02263                 {};
02264             i = (Ferror(fd) ? 1 : 0);
02265         }
02266         if (i) {
02267             LABEL;
02268             (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
02269             goto cleanup;
02270         }
02271 
02272         /* Verify cksum. */
02273         if (KF_ISSET(keys, CKSUM)) {
02274             if (s->cksum != val) {
02275                 LABEL;
02276                 (void) printf(_("%s%s expected %lu found %lu\n"), tab, "cksum",
02277                                 (unsigned long) s->cksum, (unsigned long) val);
02278                 tab = "\t";
02279             }
02280         }
02281 
02282         /* Verify all the digests. */
02283         if (s->algos != NULL)
02284         for (i = 0; i < (int) s->algos->nvals; i++) {
02285             static int asAscii = 1;
02286             uint32_t algo = s->algos->vals[i];
02287             const char * digest = NULL;
02288             size_t digestlen = 0;
02289 
02290             fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
02291 assert(digest != NULL);
02292             if (strcmp(digest, s->digests[i])) {
02293                 LABEL;
02294                 printf(_("%s%s expected %s found %s\n"), tab, algo2name(algo),
02295                         s->digests[i], digest);
02296                 tab = "\t";
02297             }
02298             digest = _free(digest);
02299             digestlen = 0;
02300         }
02301 
02302         /* Accumulate statistics and clean up. */
02303 cleanup:
02304         if (fd != NULL) {
02305             (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
02306             (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
02307             (void) Fclose(fd);
02308             fd = NULL;
02309         }
02310     }
02311 
02312     if (KF_ISSET(keys, SLINK) && strcmp(cp = rlink(name), s->slink)) {
02313         LABEL;
02314         (void) printf(_("%s%s expected %s found %s\n"), tab, "link_ref",
02315                 cp, s->slink);
02316     }
02317 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02318     if (KF_ISSET(keys, FLAGS) && s->sb.st_flags != st->st_flags) {
02319         char *fflags;
02320 
02321         LABEL;
02322         fflags = fflagstostr(s->sb.st_flags);
02323         (void) printf(_("%s%s expected \"%s\""), tab, "flags", fflags);
02324         fflags = _free(fflags);
02325 
02326         fflags = fflagstostr(st->st_flags);
02327         (void) printf(_(" found \"%s\""), fflags);
02328         fflags = _free(fflags);
02329 
02330         if (MF_ISSET(UPDATE)) {
02331             if (chflags(fts_accpath, s->sb.st_flags))
02332                 (void) printf(" not modified: %s)\n", strerror(errno));
02333             else        
02334                 (void) printf(" modified)\n");
02335             }
02336         } else {
02337             (void) printf("\n");
02338         tab = "\t";
02339     }
02340 #endif
02341     return label;
02342 }
02343 
02344 /*==============================================================*/
02345 
02346 #define _FTSCALLOC(_p, _n)      \
02347     if ((_n) > 0) { \
02348         (_p) = _free(_p); (_p) = xcalloc((_n), sizeof(*(_p))); \
02349     }
02350 
02351 static int
02352 mtreeVisitD(rpmfts fts)
02353         /*@globals fileSystem, internalState @*/
02354         /*@modifies fts, fileSystem, internalState @*/
02355 {
02356     enum mtreeKeys_e keys = fts->keys;
02357     const FTSENT *const parent = fts->p;
02358     const FTSENT * p;
02359     struct stat sb;
02360     gid_t maxgid = 0;
02361     uid_t maxuid = 0;
02362     mode_t maxmode = 0;
02363 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02364     unsigned long maxflags = 0;
02365 #endif
02366 
02367     /* Retrieve all directory members. */
02368     if ((p = Fts_children(fts->t, 0)) == NULL) {
02369         if (errno)
02370             mtree_error("%s: %s", SKIPDOTSLASH(parent->fts_path),
02371                         strerror(errno));
02372         return 1;
02373     }
02374 
02375     sb = fts->sb;               /* structure assignment */
02376     _FTSCALLOC(fts->g, fts->maxg);
02377     _FTSCALLOC(fts->m, fts->maxm);
02378     _FTSCALLOC(fts->u, fts->maxu);
02379 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02380     _FTSCALLOC(fts->f, fts->maxf);
02381 #endif
02382 
02383     /* Find the most common stat(2) settings for the next directory. */
02384     for (; p != NULL; p = p->fts_link) {
02385         struct stat *const st = p->fts_statp;
02386 
02387         if (MF_ISSET(DIRSONLY) || !S_ISDIR(st->st_mode))
02388             continue;
02389 
02390         if (fts->m != NULL)
02391         {   mode_t st_mode = st->st_mode & MBITS;
02392             if (st_mode < fts->maxm && ++fts->m[st_mode] > maxmode) {
02393                 sb.st_mode = st_mode;
02394                 maxmode = fts->m[st_mode];
02395             }
02396         }
02397         if (fts->g != NULL)
02398         if (st->st_gid < fts->maxg && ++fts->g[st->st_gid] > maxgid) {
02399             sb.st_gid = st->st_gid;
02400             maxgid = fts->g[st->st_gid];
02401         }
02402         if (fts->u != NULL)
02403         if (st->st_uid < fts->maxu && ++fts->u[st->st_uid] > maxuid) {
02404             sb.st_uid = st->st_uid;
02405             maxuid = fts->u[st->st_uid];
02406         }
02407 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02408         /*
02409          * XXX
02410          * note that the below will break when file flags
02411          * are extended beyond the first 4 bytes of each
02412          * half word of the flags
02413          */
02414 #define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
02415         if (fts->f != NULL)
02416         {   unsigned long st_flags = FLAGS2IDX(st->st_flags);
02417             if (st_flags < fts->maxf && ++fts->f[st_flags] > maxflags) {
02418                 /* XXX note st->st_flags saved, not FLAGS2IDX(st->st_flags) */
02419                 sb.st_flags = st->st_flags;
02420                 maxflags = fts->f[st_flags];
02421             }
02422         }
02423 #endif
02424     }
02425 
02426     /*
02427      * If the /set record is the same as the last one we do not need to output
02428      * a new one.  So first we check to see if anything changed.  Note that we
02429      * always output a /set record for the first directory.
02430      */
02431     if (((KF_ISSET(keys, UNAME) || KF_ISSET(keys, UID)) && (fts->sb.st_uid != sb.st_uid))
02432      || ((KF_ISSET(keys, GNAME) || KF_ISSET(keys, GID)) && (fts->sb.st_gid != sb.st_gid))
02433      ||  (KF_ISSET(keys, MODE) && (fts->sb.st_mode != sb.st_mode))
02434 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02435      ||  (KF_ISSET(keys, FLAGS) && (fts->sb.st_flags != sb.st_flags))
02436 #endif
02437      || fts->sb_is_valid == 0)
02438     {
02439         fts->sb_is_valid = 1;
02440         if (MF_ISSET(DIRSONLY))
02441             (void) printf("/set type=dir");
02442         else
02443             (void) printf("/set type=file");
02444         if (KF_ISSET(keys, UNAME)) {
02445             const char * uname = uidToUname(sb.st_uid);
02446             if (uname != NULL)
02447                 (void) printf(" uname=%s", uname);
02448             else if (MF_ISSET(WARN))
02449                 fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
02450                         __progname, (unsigned long) sb.st_uid);
02451             else
02452                 mtree_error("could not get uname for uid=%lu",
02453                         (unsigned long)sb.st_uid);
02454         }
02455         if (KF_ISSET(keys, UID))
02456             (void) printf(" uid=%lu", (unsigned long)sb.st_uid);
02457         if (KF_ISSET(keys, GNAME)) {
02458             const char * gname = gidToGname(sb.st_gid);
02459             if (gname != NULL)
02460                 (void) printf(" gname=%s", gname);
02461             else if (MF_ISSET(WARN))
02462                 fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
02463                         __progname, (unsigned long) sb.st_gid);
02464             else
02465                 mtree_error("could not get gname for gid=%lu",
02466                         (unsigned long) sb.st_gid);
02467         }
02468         if (KF_ISSET(keys, GID))
02469             (void) printf(" gid=%lu", (unsigned long)sb.st_gid);
02470         if (KF_ISSET(keys, MODE))
02471             (void) printf(" mode=%#o", (unsigned)sb.st_mode);
02472         if (KF_ISSET(keys, NLINK))
02473             (void) printf(" nlink=1");
02474 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02475         if (KF_ISSET(keys, FLAGS)) {
02476             const char * fflags = flags_to_string(sb.st_flags);
02477             (void) printf(" flags=%s", fflags);
02478             fflags = _free(fflags);
02479         }
02480 #endif
02481         (void) printf("\n");
02482         fts->sb = sb;           /* structure assignment */
02483     }
02484     return (0);
02485 }
02486 
02487 #define CWALKINDENTNAMELEN      15
02488 #define MAXLINELEN      80
02489 
02490 
02491 static void
02492 output(int indent, int * offset, const char * fmt, ...)
02493         /*@globals fileSystem @*/
02494         /*@modifies *offset, fileSystem @*/
02495 {
02496     char buf[1024];
02497     va_list ap;
02498 
02499     va_start(ap, fmt);
02500     (void) vsnprintf(buf, sizeof(buf), fmt, ap);
02501     va_end(ap);
02502 
02503     if (*offset + strlen(buf) > MAXLINELEN - 3) {
02504         (void)printf(" \\\n%*s", CWALKINDENTNAMELEN + indent, "");
02505         *offset = CWALKINDENTNAMELEN + indent;
02506     }
02507     *offset += printf(" %s", buf) + 1;
02508 }
02509 
02510 static void
02511 mtreeVisitF(rpmfts fts)
02512         /*@globals errno, h_errno, fileSystem, internalState @*/
02513         /*@modifies errno, fileSystem, internalState @*/
02514 {
02515     enum mtreeKeys_e keys = fts->keys;
02516     const char * fts_accpath = fts->p->fts_accpath;
02517     unsigned short fts_info = fts->p->fts_info;
02518     struct stat *const st = fts->p->fts_statp;
02519     int indent = (MF_ISSET(INDENT) ? fts->p->fts_level * 4 : 0);
02520     int offset;
02521 
02522     {   const char * fts_name = fts->p->fts_name;
02523         size_t fts_namelen = fts->p->fts_namelen;
02524         char * escname;
02525 
02526         /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
02527         if (fts->p->fts_level == 0 && fts_namelen == 0) {
02528             fts_name = ".";
02529             fts_namelen = sizeof(".") - 1;
02530         }
02531 
02532         escname = xmalloc(fts_namelen * 4  +  1);
02533         /* XXX TODO: Mac OS X uses VIS_GLOB as well */
02534         (void) strvis(escname, fts_name, VIS_WHITE | VIS_OCTAL);
02535 
02536         if (MF_ISSET(INDENT) || S_ISDIR(st->st_mode))
02537             offset = printf("%*s%s", indent, "", escname);
02538         else
02539             offset = printf("%*s    %s", indent, "", escname);
02540         escname = _free(escname);
02541     }
02542 
02543     if (offset > (CWALKINDENTNAMELEN + indent))
02544         offset = MAXLINELEN;
02545     else
02546         offset += printf("%*s", (CWALKINDENTNAMELEN + indent) - offset, "");
02547 
02548     if (!S_ISREG(st->st_mode) && !MF_ISSET(DIRSONLY))
02549         output(indent, &offset, "type=%s", inotype(st->st_mode));
02550     if (st->st_uid != fts->sb.st_uid) {
02551         if (KF_ISSET(keys, UNAME)) {
02552             const char * uname = uidToUname(st->st_uid);
02553             if (uname != NULL)
02554                 output(indent, &offset, "uname=%s", uname);
02555             else if (MF_ISSET(WARN))
02556                 fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
02557                         __progname, (unsigned long) st->st_uid);
02558             else
02559                 mtree_error("could not get uname for uid=%lu",
02560                         (unsigned long)st->st_uid);
02561         }
02562         if (KF_ISSET(keys, UID))
02563             output(indent, &offset, "uid=%u", st->st_uid);
02564     }
02565     if (st->st_gid != fts->sb.st_gid) {
02566         if (KF_ISSET(keys, GNAME)) {
02567             const char * gname = gidToGname(st->st_gid);
02568             if (gname != NULL)
02569                 output(indent, &offset, "gname=%s", gname);
02570             else if (MF_ISSET(WARN))
02571                 fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
02572                         __progname, (unsigned long) st->st_gid);
02573             else
02574                 mtree_error("Could not get gname for gid=%lu",
02575                         (unsigned long) st->st_gid);
02576         }
02577         if (KF_ISSET(keys, GID))
02578            output(indent, &offset, "gid=%lu", (unsigned long)st->st_gid);
02579     }
02580     if (KF_ISSET(keys, MODE) && (st->st_mode & MBITS) != fts->sb.st_mode)
02581         output(indent, &offset, "mode=%#o", (st->st_mode & MBITS));
02582     if (KF_ISSET(keys, NLINK) && st->st_nlink != 1)
02583         output(indent, &offset, "nlink=%lu", (unsigned long)st->st_nlink);
02584     if (KF_ISSET(keys, SIZE) && S_ISREG(st->st_mode))
02585         output(indent, &offset, "size=%llu", (unsigned long long)st->st_size);
02586     if (KF_ISSET(keys, TIME)) {
02587         struct timeval tv;
02588 #if defined(TIMESPEC_TO_TIMEVAL)
02589         TIMESPEC_TO_TIMEVAL(&tv, &st->st_mtimespec);
02590 #else
02591         tv.tv_sec = (long)st->st_mtime;
02592         tv.tv_usec = 0L;
02593 #endif
02594         output(indent, &offset, "time=%lu.%lu",
02595                 (unsigned long) tv.tv_sec,
02596                 (unsigned long) tv.tv_usec);
02597     }
02598 
02599     /* Only files can have digests. */
02600     if (S_ISREG(st->st_mode)) {
02601 
02602         /* Any digests to calculate? */
02603         if (KF_ISSET(keys, CKSUM) || fts->algos != NULL) {
02604             FD_t fd = Fopen(fts_accpath, "r.ufdio");
02605             uint32_t len, val;
02606             int i;
02607 
02608             if (fd == NULL || Ferror(fd)) {
02609 #ifdef  NOTYET  /* XXX can't exit in a library API. */
02610                 (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
02611                         __progname, fts_accpath, Fstrerror(fd));
02612                 goto cleanup;
02613 #else
02614                 mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
02615                 /*@notreached@*/
02616 #endif
02617             }
02618 
02619             /* Setup all digest calculations.  Reversed order is effete ... */
02620             if (fts->algos != NULL)
02621             for (i = fts->algos->nvals; i-- > 0;)
02622                 fdInitDigest(fd, fts->algos->vals[i], 0);
02623 
02624             /* Compute the cksum and digests. */
02625             if (KF_ISSET(keys, CKSUM))
02626                 i = crc(fd, &val, &len);
02627             else {
02628                 char buffer[16 * 1024];
02629                 while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
02630                     {};
02631                 i = (Ferror(fd) ? 1 : 0);
02632             }
02633             if (i) {
02634 #ifdef  NOTYET  /* XXX can't exit in a library API. */
02635                 (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
02636                         __progname, fts_accpath, Fstrerror(fd));
02637 #else
02638                 mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
02639                 /*@notreached@*/
02640 #endif
02641                 goto cleanup;
02642             }
02643 
02644             /* Output cksum. */
02645             if (KF_ISSET(keys, CKSUM)) {
02646                 output(indent, &offset, "cksum=%lu", (unsigned long)val);
02647             }
02648 
02649             /* Output all the digests. */
02650             if (fts->algos != NULL)
02651             for (i = 0; i < (int) fts->algos->nvals; i++) {
02652                 static int asAscii = 1;
02653                 const char * digest = NULL;
02654                 size_t digestlen = 0;
02655                 uint32_t algo;
02656 
02657                 algo = fts->algos->vals[i];
02658                 fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
02659 #ifdef  NOTYET  /* XXX can't exit in a library API. */
02660 assert(digest != NULL);
02661 #else
02662                 if (digest == NULL)
02663                     mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
02664 #endif
02665                 {   const char * tagname = algo2tagname(algo);
02666                     if (tagname != NULL)
02667                         output(indent, &offset, "%s=%s", tagname, digest);
02668                 }
02669                 digest = _free(digest);
02670                 digestlen = 0;
02671             }
02672 
02673 cleanup:    /* Accumulate statistics and clean up. */
02674             if (fd != NULL) {
02675                 (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
02676                 (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
02677                 (void) Fclose(fd);
02678                 fd = NULL;
02679             }
02680         }
02681     }
02682 
02683     if (KF_ISSET(keys, SLINK) && (fts_info == FTS_SL || fts_info == FTS_SLNONE))
02684     {
02685         const char * name = rlink(fts_accpath);
02686         char * escname = xmalloc(strlen(name) * 4  +  1);
02687         (void) strvis(escname, name, VIS_WHITE | VIS_OCTAL);
02688         output(indent, &offset, "link=%s", escname);
02689         escname = _free(escname);
02690     }
02691 
02692     if (KF_ISSET(keys, FLAGS) && !S_ISLNK(st->st_mode)) {
02693 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
02694         char * fflags = fflagstostr(st->st_flags);
02695 
02696         if (fflags != NULL && fflags[0] != '\0')
02697             output(indent, &offset, "flags=%s", fflags);
02698         else
02699             output(indent, &offset, "flags=none");
02700         free(fflags);
02701 #else
02702         output(indent, &offset, "flags=none");
02703 #endif
02704     }
02705     (void) putchar('\n');
02706 }
02707 
02708 /*==============================================================*/
02709 
02710 /*
02711  * Copyright 2000 Massachusetts Institute of Technology
02712  *
02713  * Permission to use, copy, modify, and distribute this software and
02714  * its documentation for any purpose and without fee is hereby
02715  * granted, provided that both the above copyright notice and this
02716  * permission notice appear in all copies, that both the above
02717  * copyright notice and this permission notice appear in all
02718  * supporting documentation, and that the name of M.I.T. not be used
02719  * in advertising or publicity pertaining to distribution of the
02720  * software without specific, written prior permission.  M.I.T. makes
02721  * no representations about the suitability of this software for any
02722  * purpose.  It is provided "as is" without express or implied
02723  * warranty.
02724  *
02725  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''.  M.I.T. DISCLAIMS
02726  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
02727  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
02728  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
02729  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
02730  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
02731  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
02732  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
02733  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
02734  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
02735  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
02736  * SUCH DAMAGE.
02737  */
02738 
02739 /*
02740  * We're assuming that there won't be a whole lot of excludes,
02741  * so it's OK to use a stupid algorithm.
02742  */
02743 
02744 /*@mayexit@*/
02745 static void
02746 mtreeReadExcludes(const char * fn)
02747         /*@globals excludes, h_errno, fileSystem, internalState @*/
02748         /*@modifies excludes, fileSystem, internalState @*/
02749 {
02750     FD_t fd = Fopen(fn, "r.fpio");
02751     FILE *fp;
02752     char buffer[16 * 1024];
02753 
02754     if (fd == NULL || Ferror(fd) || (fp = fdGetFILE(fd)) == NULL) {
02755         fprintf(stderr, _("%s: open of %s failed: %s\n"), __progname,
02756                 fn, Fstrerror(fd));
02757         if (fd != NULL) (void) Fclose(fd);
02758         exit(EXIT_FAILURE);
02759     }
02760 
02761     while (fgets(buffer, (int)sizeof(buffer), fp) != NULL) {
02762         struct exclude *e;
02763         char * line;
02764         size_t len;
02765         
02766         buffer[sizeof(buffer)-1] = '\0';
02767         for (line = buffer; *line != '\0'; line++)
02768             if (strchr(" \t\n\r", line[1]) == NULL) /*@innerbreak@*/ break;
02769         if (*line == '\0' || *line == '#')
02770             continue;
02771         for (len = strlen(line); len > 0; len--)
02772             if (strchr(" \t\n\r", line[len-1]) == NULL) /*@innerbreak@*/ break;
02773         if (len == 0)
02774             continue;
02775 
02776         e = xmalloc(sizeof(*e));
02777         e->glob = xstrdup(line);
02778         e->pathname = (strchr(line, '/') != NULL ? 1 : 0);
02779 /*@-immediatetrans@*/
02780         RPM_LIST_INSERT_HEAD(&excludes, e, link);
02781 /*@=immediatetrans@*/
02782     }
02783     if (fd != NULL)
02784         (void) Fclose(fd);
02785 /*@-compmempass -nullstate @*/
02786     return;
02787 /*@=compmempass =nullstate @*/
02788 }
02789 
02790 static int
02791 mtreeCheckExcludes(const char *fname, const char *path)
02792         /*@*/
02793 {
02794     struct exclude *e;
02795 
02796     /* fnmatch(3) has a funny return value convention... */
02797 #define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
02798 
02799 /*@-predboolptr@*/
02800     RPM_LIST_FOREACH(e, &excludes, link) {
02801         if ((e->pathname && MATCH(e->glob, path)) || MATCH(e->glob, fname))
02802             return 1;
02803     }
02804 /*@=predboolptr@*/
02805     return 0;
02806 }
02807 
02808 /*==============================================================*/
02809 
02810 static int
02811 dsort(const FTSENT ** a, const FTSENT ** b)
02812         /*@*/
02813 {
02814     if (S_ISDIR((*a)->fts_statp->st_mode)) {
02815         if (!S_ISDIR((*b)->fts_statp->st_mode))
02816             return 1;
02817     } else if (S_ISDIR((*b)->fts_statp->st_mode))
02818         return -1;
02819     return strcmp((*a)->fts_name, (*b)->fts_name);
02820 }
02821 
02822 #if defined(_RPMFI_INTERNAL)
02823 
02829 static int chkSuffix(const char * fn, const char * suffix)
02830         /*@*/
02831 {
02832     size_t flen = strlen(fn);
02833     size_t slen = strlen(suffix);
02834     return (flen > slen && !strcmp(fn + flen - slen, suffix));
02835 }
02836 
02837 static int _rpmfiStat(const char * path, struct stat * st)
02838         /*@globals fileSystem @*/
02839         /*@modifies fileSystem @*/
02840 {
02841     rpmfts fts = _rpmfts;
02842     rpmfi fi = _rpmfts->fi;
02843     size_t len = strlen(fts->paths[0]);
02844     int rc;
02845 
02846     rc = rpmfiStat(fi, path+len, st);
02847 
02848 if (_fts_debug)
02849 fprintf(stderr, "*** _rpmfiStat(%s, %p) fi %p rc %d\n", path+len, st, fi, rc);
02850 
02851     return rc;
02852 }
02853 
02854 static int _rpmfiClosedir(/*@only@*/ DIR * dir)
02855         /*@globals fileSystem @*/
02856         /*@modifies dir, fileSystem @*/
02857 {
02858     rpmfi fi = _rpmfts->fi;
02859 
02860 if (_fts_debug)
02861 fprintf(stderr, "*** _rpmfiClosedir(%p) fi %p\n", dir, fi);
02862 
02863     return avClosedir(dir);
02864 }
02865 
02866 static /*@null@*/ struct dirent * _rpmfiReaddir(DIR * dir)
02867         /*@globals fileSystem @*/
02868         /*@modifies fileSystem @*/
02869 {
02870     rpmfi fi = _rpmfts->fi;
02871     struct dirent * dp = (struct dirent *) avReaddir(dir);
02872 
02873 if (_fts_debug)
02874 fprintf(stderr, "*** _rpmfiReaddir(%p) fi %p %p \"%s\"\n", dir, fi, dp, (dp != NULL ? dp->d_name : ""));
02875 
02876 /*@-dependenttrans@*/
02877     return dp;
02878 /*@=dependenttrans@*/
02879 }
02880 
02881 static /*@null@*/
02882 uint8_t * rpmfiParentDirNotWithin(rpmfi fi)
02883         /*@*/
02884 {
02885     size_t * dnlens = xmalloc(fi->dc * sizeof(*dnlens));
02886     uint8_t * noparent = memset(xmalloc(fi->dc), 1, fi->dc);
02887     int i, j;
02888 
02889     for (i = 0; i < (int)fi->dc; i++)
02890         dnlens[i] = strlen(fi->dnl[i]);
02891 
02892     /* Mark parent directories that are not contained within same package. */
02893     for (i = 0; i < (int)fi->fc; i++) {
02894         size_t dnlen, bnlen;
02895 
02896         if (!S_ISDIR(fi->fmodes[i]))
02897             continue;
02898 
02899         dnlen = dnlens[fi->dil[i]];
02900         bnlen = strlen(fi->bnl[i]);
02901 
02902         for (j = 0; j < (int)fi->dc; j++) {
02903 
02904             if (!noparent[j] || j == (int)fi->dil[i])
02905                 /*@innercontinue@*/ continue;
02906             if (dnlens[j] != (dnlen+bnlen+1))
02907                 /*@innercontinue@*/ continue;
02908             if (strncmp(fi->dnl[j], fi->dnl[fi->dil[i]], dnlen))
02909                 /*@innercontinue@*/ continue;
02910             if (strncmp(fi->dnl[j]+dnlen, fi->bnl[i], bnlen))
02911                 /*@innercontinue@*/ continue;
02912             if (fi->dnl[j][dnlen+bnlen] != '/' || fi->dnl[j][dnlen+bnlen+1] != '\0')
02913                 /*@innercontinue@*/ continue;
02914 
02915             /* This parent directory is contained within the package. */
02916             noparent[j] = (uint8_t)0;
02917             /*@innerbreak@*/ break;
02918         }
02919     }
02920     dnlens = _free(dnlens);
02921     return noparent;
02922 }
02923 
02924 static Header rpmftsReadHeader(rpmfts fts, const char * path)
02925         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
02926         /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
02927 {
02928     FD_t fd = Fopen(path, "r.ufdio");
02929     Header h = NULL;
02930 
02931     if (fd != NULL) {
02932         /* XXX what if path needs expansion? */
02933         rpmRC rpmrc = rpmReadPackageFile(fts->ts, fd, path, &h);
02934 
02935         (void) Fclose(fd);
02936 
02937         switch (rpmrc) {
02938         case RPMRC_NOTFOUND:
02939             /* XXX Read a package manifest. Restart ftswalk on success. */
02940         case RPMRC_FAIL:
02941         default:
02942             (void)headerFree(h);
02943             h = NULL;
02944             break;
02945         case RPMRC_NOTTRUSTED:
02946         case RPMRC_NOKEY:
02947         case RPMRC_OK:
02948             break;
02949         }
02950     }
02951     return h;
02952 }
02953 
02954 static /*@null@*/ rpmfi rpmftsLoadFileInfo(rpmfts fts, const char * path)
02955         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
02956         /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
02957 {
02958     char * fn = xstrdup(path);
02959     size_t nb = strlen(fn);
02960     Header h = NULL;
02961 
02962     fn[nb-1] = '\0';            /* XXX trim pesky trailing '/' */
02963     h = rpmftsReadHeader(fts, fn);
02964     fn = _free(fn);
02965 
02966     if (h != NULL) {
02967         fts->fi = rpmfiNew(fts->ts, h, RPMTAG_BASENAMES, 0);
02968         (void)headerFree(h);
02969         h = NULL;
02970     }
02971     return fts->fi;
02972 }
02973 
02974 static /*@null@*/ DIR * _rpmfiOpendir(const char * path)
02975         /*@globals _rpmfts, rpmGlobalMacroContext, h_errno,
02976                 fileSystem, internalState @*/
02977         /*@modifies _rpmfts, rpmGlobalMacroContext,
02978                 fileSystem, internalState @*/
02979 {
02980     rpmfts fts = _rpmfts;
02981     DIR * dir = NULL;
02982 
02983     if (fts->ts == NULL)
02984         fts->ts = rpmtsCreate();
02985 
02986     if (fts->fi == NULL) {
02987         rpmfi fi = rpmftsLoadFileInfo(fts, path);
02988         uint8_t * noparent = rpmfiParentDirNotWithin(fi);
02989         uint16_t * fmodes = xcalloc(rpmfiFC(fi)+1, sizeof(*fmodes));
02990         const char ** fnames = NULL;
02991         int ac = 0;
02992         int i;
02993 
02994         /* Collect top level files/dirs from the package. */
02995         fi = rpmfiInit(fi, 0);
02996         while ((i = rpmfiNext(fi)) >= 0) {
02997             int xx;
02998             if (!S_ISDIR(fi->fmodes[i]) && !noparent[fi->dil[i]])
02999                 continue;
03000             xx = argvAdd(&fnames, rpmfiFN(fi));
03001             fmodes[ac++] = fi->fmodes[i];
03002         }
03003 
03004         dir = (DIR *) avOpendir(path, fnames, fmodes);
03005 
03006         fnames = argvFree(fnames);
03007         fmodes = _free(fmodes);
03008         noparent = _free(noparent);
03009 
03010     } else {
03011         const char * dn = path + strlen(fts->paths[0]);
03012 
03013         dir = rpmfiOpendir(fts->fi, dn);
03014     }
03015 
03016 if (_fts_debug)
03017 fprintf(stderr, "*** _rpmfiOpendir(%s) dir %p\n", path, dir);
03018 
03019     return dir;
03020 }
03021 
03022 #define ALIGNBYTES      (__alignof__ (long double) - 1)
03023 #define ALIGN(p)        (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
03024 
03025 static FTSENT *
03026 fts_alloc(FTS * sp, const char * name, int namelen)
03027         /*@*/
03028 {
03029         register FTSENT *p;
03030         size_t len;
03031 
03032         /*
03033          * The file name is a variable length array and no stat structure is
03034          * necessary if the user has set the nostat bit.  Allocate the FTSENT
03035          * structure, the file name and the stat structure in one chunk, but
03036          * be careful that the stat structure is reasonably aligned.  Since the
03037          * fts_name field is declared to be of size 1, the fts_name pointer is
03038          * namelen + 2 before the first possible address of the stat structure.
03039          */
03040         len = sizeof(*p) + namelen;
03041 /*@-sizeoftype@*/
03042         if (!(sp->fts_options & FTS_NOSTAT))
03043                 len += sizeof(*p->fts_statp) + ALIGNBYTES;
03044 /*@=sizeoftype@*/
03045         p = xmalloc(len);
03046 
03047         /* Copy the name and guarantee NUL termination. */
03048         memmove(p->fts_name, name, namelen);
03049         p->fts_name[namelen] = '\0';
03050 
03051 /*@-sizeoftype@*/
03052         if (!(sp->fts_options & FTS_NOSTAT))
03053                 p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
03054 /*@=sizeoftype@*/
03055         p->fts_namelen = namelen;
03056         p->fts_path = sp->fts_path;
03057         p->fts_errno = 0;
03058         p->fts_flags = 0;
03059         p->fts_instr = FTS_NOINSTR;
03060         p->fts_number = 0;
03061         p->fts_pointer = NULL;
03062         return (p);
03063 }
03064 
03065 static void _rpmfiSetFts(rpmfts fts)
03066         /*@globals h_errno, fileSystem, internalState @*/
03067         /*@modifies fts, fileSystem, internalState @*/
03068 {
03069     char *const * argv = (char *const *) fts->paths;
03070     FTS * sp = fts->t;
03071 #ifdef  NOTYET
03072     int (*compar) (const FTSENT **, const FTSENT **) = sp->compar;
03073 #endif
03074         register FTSENT *p, *root;
03075         register int nitems;
03076         FTSENT *parent = NULL;
03077         FTSENT *tmp = NULL;
03078         size_t len;
03079 
03080 if (_fts_debug)
03081 fprintf(stderr, "*** _rpmfiSetFts(%p)\n", fts);
03082 
03083 /*@-type@*/
03084     sp->fts_opendir = _rpmfiOpendir;
03085     sp->fts_readdir = _rpmfiReaddir;
03086     sp->fts_closedir = _rpmfiClosedir;
03087     sp->fts_stat = _rpmfiStat;
03088     sp->fts_lstat = _rpmfiStat;
03089 /*@=type@*/
03090 
03091         /* Allocate/initialize root's parent. */
03092         if (*argv != NULL) {
03093                 parent = fts_alloc(sp, "", 0);
03094                 parent->fts_level = FTS_ROOTPARENTLEVEL;
03095         }
03096 
03097         /* Allocate/initialize root(s). */
03098         for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
03099                 len = strlen(*argv);
03100 
03101                 p = fts_alloc(sp, *argv, (int)len);
03102                 p->fts_level = FTS_ROOTLEVEL;
03103                 p->fts_parent = parent;
03104                 p->fts_accpath = p->fts_name;
03105 #ifdef  NOTYET
03106                 p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
03107 
03108                 /* Command-line "." and ".." are real directories. */
03109                 if (p->fts_info == FTS_DOT)
03110                         p->fts_info = FTS_D;
03111 
03112 #else
03113                 p->fts_name[len-1] = '\0';
03114                 {   struct stat * st = p->fts_statp;
03115                     int xx = Stat(p->fts_accpath, st);
03116                     xx = xx;
03117                     st->st_mode &= ~S_IFMT;
03118                     st->st_mode |= S_IFDIR;
03119                     p->fts_dev = st->st_dev;
03120                     p->fts_ino = st->st_ino;
03121                     p->fts_nlink = st->st_nlink;
03122                 }
03123                 p->fts_name[len-1] = '/';
03124                 p->fts_info = FTS_D;
03125 #endif
03126 
03127 #ifdef  NOTYET
03128                 /*
03129                  * If comparison routine supplied, traverse in sorted
03130                  * order; otherwise traverse in the order specified.
03131                  */
03132                 if (compar) {
03133                         p->fts_link = root;
03134                         root = p;
03135                 } else
03136 #endif
03137                 {
03138                         p->fts_link = NULL;
03139                         if (root == NULL)
03140                                 tmp = root = p;
03141                         else {
03142                                 if (tmp != NULL)        /* XXX can't happen */
03143                                         tmp->fts_link = p;
03144                                 tmp = p;
03145                         }
03146                 }
03147         }
03148 #ifdef  NOTYET
03149         if (compar && nitems > 1)
03150                 root = fts_sort(sp, root, nitems);
03151 #endif
03152 
03153         /*
03154          * Allocate a dummy pointer and make fts_read think that we've just
03155          * finished the node before the root(s); set p->fts_info to FTS_INIT
03156          * so that everything about the "current" node is ignored.
03157          */
03158     sp->fts_cur = _free(sp->fts_cur);
03159         sp->fts_cur = fts_alloc(sp, "", 0);
03160         sp->fts_cur->fts_link = root;
03161         sp->fts_cur->fts_info = FTS_INIT;
03162 
03163     return;
03164 }
03165 #endif
03166 
03167 int
03168 mtreeCWalk(rpmfts fts)
03169 {
03170 #if defined(_RPMFI_INTERNAL)
03171     int isrpm = chkSuffix(fts->paths[0], ".rpm/");
03172 #else
03173     int isrpm = 0;
03174 #endif
03175     const char * empty = NULL;
03176     char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
03177     int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
03178     int rval = 0;
03179 
03180     fts->t = Fts_open(paths, ftsoptions, dsort);
03181     if (fts->t == NULL)
03182         mtree_error("Fts_open: %s", strerror(errno));
03183 
03184 #if defined(_RPMFI_INTERNAL)
03185     if (isrpm) {
03186         fts->keys &= ~MTREE_KEYS_SLINK;         /* XXX no rpmfiReadlink yet. */
03187         _rpmfiSetFts(fts);
03188     }
03189 #endif
03190 
03191     while ((fts->p = Fts_read(fts->t)) != NULL) {
03192         int indent = 0;
03193         if (MF_ISSET(INDENT))
03194             indent = fts->p->fts_level * 4;
03195         if (mtreeCheckExcludes(fts->p->fts_name, fts->p->fts_path)) {
03196             (void) Fts_set(fts->t, fts->p, FTS_SKIP);
03197             continue;
03198         }
03199         switch(fts->p->fts_info) {
03200         case FTS_D:
03201             if (!MF_ISSET(DIRSONLY))
03202                 (void) printf("\n");
03203             if (!MF_ISSET(NOCOMMENT))
03204                 (void) printf("# %s\n", fts->p->fts_path);
03205             (void) mtreeVisitD(fts);
03206             mtreeVisitF(fts);
03207             /*@switchbreak@*/ break;
03208         case FTS_DP:
03209             if (!MF_ISSET(NOCOMMENT) && (fts->p->fts_level > 0))
03210                 (void) printf("%*s# %s\n", indent, "", fts->p->fts_path);
03211             (void) printf("%*s..\n", indent, "");
03212             if (!MF_ISSET(DIRSONLY))
03213                 (void) printf("\n");
03214             /*@switchbreak@*/ break;
03215         case FTS_DNR:
03216         case FTS_ERR:
03217         case FTS_NS:
03218             (void) fprintf(stderr, "%s: %s: %s\n", __progname,
03219                         fts->p->fts_path, strerror(fts->p->fts_errno));
03220             /*@switchbreak@*/ break;
03221         default:
03222             if (!MF_ISSET(DIRSONLY))
03223                 mtreeVisitF(fts);
03224             /*@switchbreak@*/ break;
03225         }
03226     }
03227     (void) Fts_close(fts->t);
03228     fts->p = NULL;
03229     fts->t = NULL;
03230     return rval;
03231 }
03232 
03233 /*==============================================================*/
03234 
03235 void
03236 mtreeMiss(rpmfts fts, /*@null@*/ NODE * p, char * tail)
03237 {
03238     int create;
03239     char *tp;
03240     const char *type;
03241 
03242     for (; p != NULL; p = p->next) {
03243         /* XXX Mac OS X doesn't do this. */
03244         if (KF_ISSET(p->flags, OPT) && !KF_ISSET(p->flags, VISIT))
03245             continue;
03246         if (p->type != F_DIR && (MF_ISSET(DIRSONLY) || KF_ISSET(p->flags, VISIT)))
03247             continue;
03248         (void) strcpy(tail, p->name);
03249         if (!KF_ISSET(p->flags, VISIT)) {
03250             /* Don't print missing message if file exists as a
03251                symbolic link and the -q flag is set. */
03252             struct stat sb;
03253 
03254             if (MF_ISSET(QUIET) && Stat(fts->path, &sb) == 0)
03255                 p->flags |= MTREE_KEYS_VISIT;
03256             else
03257                 (void) printf(_("missing: %s"), fts->path);
03258         }
03259         if (p->type != F_DIR && p->type != F_LINK) {
03260             (void) putchar('\n');
03261             continue;
03262         }
03263 
03264         create = 0;
03265         type = (p->type == F_LINK ? "symlink" : "directory");
03266         if (!KF_ISSET(p->flags, VISIT) && MF_ISSET(UPDATE)) {
03267             if (!(KF_ISSET(p->flags, UID) && KF_ISSET(p->flags, UNAME)))
03268                 (void) printf(_(" (%s not created: user not specified)"), type);
03269             else if (!(KF_ISSET(p->flags, GID) || KF_ISSET(p->flags, GNAME)))
03270                 (void) printf(_(" (%s not created: group not specified))"), type);
03271             else if (p->type == F_LINK) {
03272                 if (Symlink(p->slink, fts->path))
03273                     (void) printf(_(" (%s not created: %s)\n"), type,
03274                                 strerror(errno));
03275                 else
03276                     (void) printf(_(" (%s created)\n"), type);
03277                 if (lchown(fts->path, p->sb.st_uid, p->sb.st_gid) == -1) {
03278                     const char * what;
03279                     int serr = errno;
03280 
03281                     if (p->sb.st_uid == (uid_t)-1)
03282                         what = "group";
03283                     else if (lchown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
03284                         what = "user & group";
03285                     else {
03286                         what = "user";
03287                         errno = serr;
03288                     }
03289                     (void) printf(_("%s: %s not modified: %s\n"),
03290                                 fts->path, what, strerror(errno));
03291                 }
03292                 continue;
03293             } else if (!KF_ISSET(p->flags, MODE))
03294                 (void) printf(_(" (%s not created: mode not specified)"), type);
03295             else if (Mkdir(fts->path, S_IRWXU))
03296                 (void) printf(_(" (%s not created: %s)"),type, strerror(errno));
03297             else {
03298                 create = 1;
03299                 (void) printf(_(" (%s created)"), type);
03300             }
03301         }
03302 
03303         if (!KF_ISSET(p->flags, VISIT))
03304             (void) putchar('\n');
03305 
03306         for (tp = tail; *tp != '\0'; ++tp);
03307         *tp = '/';
03308         mtreeMiss(fts, p->child, tp + 1);
03309         *tp = '\0';
03310 
03311         if (!create)
03312             continue;
03313         if (Chown(fts->path, p->sb.st_uid, p->sb.st_gid)) {
03314             const char * what;
03315             int serr = errno;
03316 
03317             if (p->sb.st_uid == (uid_t)-1)
03318                 what = "group";
03319             else if (Chown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
03320                 what = "user & group";
03321             else {
03322                 what = "user";
03323                 errno = serr;
03324             }
03325             (void) printf(_("%s: %s not modified: %s\n"),
03326                     fts->path, what, strerror(errno));
03327             continue;
03328         }
03329         if (Chmod(fts->path, p->sb.st_mode))
03330             (void) printf(_("%s: permissions not set: %s\n"),
03331                     fts->path, strerror(errno));
03332 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
03333         if (KF_ISSET(p->flags, FLAGS) && p->sb.st_flags != 0 &&
03334                     chflags(fts->path, p->sb.st_flags))
03335                         (void) printf(_("%s: file flags not set: %s\n"),
03336                                 fts->path, strerror(errno));
03337 #endif
03338     }
03339 }
03340 
03341 int
03342 mtreeVWalk(rpmfts fts)
03343 {
03344 #if defined(_RPMFI_INTERNAL)
03345     int isrpm = chkSuffix(fts->paths[0], ".rpm/");
03346 #else
03347     int isrpm = 0;
03348 #endif
03349     const char * empty = NULL;
03350     char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
03351     int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
03352     NODE * level = NULL;
03353     NODE * root = NULL;
03354     int specdepth = 0;
03355     int rval = 0;
03356 
03357     fts->t = Fts_open((char *const *)paths, ftsoptions, NULL);
03358     if (fts->t == NULL)
03359         mtree_error("Fts_open: %s", strerror(errno));
03360 
03361 #if defined(_RPMFI_INTERNAL)
03362     if (isrpm) {
03363         fts->keys &= ~MTREE_KEYS_SLINK;         /* XXX no rpmfiReadlink yet. */
03364         _rpmfiSetFts(fts);
03365     }
03366 #endif
03367 
03368     while ((fts->p = Fts_read(fts->t)) != NULL) {
03369         const char * fts_name = fts->p->fts_name;
03370         size_t fts_namelen = fts->p->fts_namelen;
03371 
03372 #if 0
03373 fprintf(stderr, "==> level %d info 0x%x name %p[%d] \"%s\" accpath \"%s\" path \"%s\"\n", fts->p->fts_level, fts->p->fts_info, fts_name, fts_namelen, fts_name, fts->p->fts_accpath, fts->p->fts_path);
03374 #endif
03375         /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
03376         if (fts->p->fts_level == 0 && fts_namelen == 0) {
03377             fts_name = ".";
03378             fts_namelen = sizeof(".") - 1;
03379         }
03380 
03381         if (mtreeCheckExcludes(fts_name, fts->p->fts_path)) {
03382             (void) Fts_set(fts->t, fts->p, FTS_SKIP);
03383             continue;
03384         }
03385         switch(fts->p->fts_info) {
03386         case FTS_D:
03387         case FTS_SL:
03388             if (fts->p->fts_level == 0) {
03389 assert(specdepth == 0);
03390                 if (root == NULL)
03391                     level = root = fts->root;
03392                 else if (root->next != fts->root)
03393                     level = root = root->next;
03394 assert(level == level->parent);
03395             }
03396            /*@switchbreak@*/ break;
03397         case FTS_DP:
03398             if (specdepth > fts->p->fts_level) {
03399                 for (level = level->parent; level->prev != NULL; level = level->prev);
03400                 --specdepth;
03401             }
03402             continue;
03403         case FTS_DNR:
03404         case FTS_ERR:
03405         case FTS_NS:
03406             (void) fprintf(stderr, "%s: %s: %s\n", __progname,
03407                         SKIPDOTSLASH(fts->p->fts_path),
03408                         strerror(fts->p->fts_errno));
03409             continue;
03410         default:
03411             if (MF_ISSET(DIRSONLY))
03412                 continue;
03413         }
03414 
03415         if (specdepth == fts->p->fts_level) {
03416             NODE *ep;
03417             for (ep = level; ep != NULL; ep = ep->next)
03418                 if ((KF_ISSET(ep->flags, MAGIC) &&
03419 /*@-moduncon@*/
03420                     !fnmatch(ep->name, fts_name, FNM_PATHNAME)) ||
03421 /*@=moduncon@*/
03422                     !strcmp(ep->name, fts_name))
03423                 {
03424                     ep->flags |= MTREE_KEYS_VISIT;
03425                     if (!KF_ISSET(ep->flags, NOCHANGE) &&
03426                         compare(fts, ep))
03427                             rval = MISMATCHEXIT;
03428                     if (KF_ISSET(ep->flags, IGN))
03429                             (void) Fts_set(fts->t, fts->p, FTS_SKIP);
03430                     else
03431                     if (ep->child && ep->type == F_DIR && fts->p->fts_info == FTS_D)
03432                     {
03433                             level = ep->child;
03434                             ++specdepth;
03435                 }
03436                 /*@innerbreak@*/ break;
03437             }
03438             if (ep != NULL)
03439                 continue;
03440         }
03441 
03442         if (!MF_ISSET(IGNORE)) {
03443             (void) printf("%s extra", SKIPDOTSLASH(fts->p->fts_path));
03444             if (MF_ISSET(REMOVE)) {
03445                 if ((S_ISDIR(fts->p->fts_statp->st_mode)
03446                     ? Rmdir : Unlink)(fts->p->fts_accpath)) {
03447                         (void) printf(_(", not removed: %s"), strerror(errno));
03448                 } else
03449                         (void) printf(_(", removed"));
03450             }
03451             (void) putchar('\n');
03452         }
03453         (void) Fts_set(fts->t, fts->p, FTS_SKIP);
03454     }
03455     (void) Fts_close(fts->t);
03456     fts->p = NULL;
03457     fts->t = NULL;
03458     return rval;
03459 }
03460 
03461 /*==============================================================*/
03462 
03465 static void mtreeArgCallback(poptContext con,
03466                 /*@unused@*/ enum poptCallbackReason reason,
03467                 const struct poptOption * opt, const char * arg,
03468                 /*@unused@*/ void * data)
03469         /*@globals _rpmfts, rpmioFtsOpts, h_errno, fileSystem, internalState @*/
03470         /*@modifies _rpmfts, rpmioFtsOpts, fileSystem, internalState @*/
03471 {
03472     char * p;
03473 
03474     /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
03475     if (opt->arg == NULL)
03476     switch (opt->val) {
03477 
03478     case 'f':
03479         if (_rpmfts->spec1 == NULL) {
03480             if ((_rpmfts->spec1 = fopen(arg, "r")) != NULL)
03481                 mtree_error("%s: %s", arg, strerror(errno));
03482         } else if (_rpmfts->spec2 == NULL) {
03483             if ((_rpmfts->spec2 = fopen(arg, "r")) != NULL)
03484                 mtree_error("%s: %s", arg, strerror(errno));
03485         } else {
03486             /* XXX error message, too many -f options. */
03487             poptPrintUsage(con, stderr, 0);
03488             exit(EXIT_FAILURE);
03489             /*@notreached@*/
03490         }
03491         break;
03492     case 'k':
03493         /* XXX fts->keys = KEYDEFAULT in main(), clear default now. */
03494         _rpmfts->keys = MTREE_KEYS_TYPE;
03495         /*@fallthrough@*/
03496     case 'K':
03497 /*@-unrecog@*/
03498         while ((p = strsep((char **)&arg, " \t,")) != NULL) {
03499             uint32_t needvalue;
03500             enum mtreeKeys_e type = parsekey(p, &needvalue);
03501             if (type == 0) {
03502                 /* XXX unknown key error. */
03503                 continue;
03504             }
03505             _rpmfts->keys |= type;
03506             /* XXX dupes can occur */
03507             if (KF_ISSET(_rpmfts->keys, DIGEST) && needvalue)
03508                 (void) argiAdd(&_rpmfts->algos, -1, (int)needvalue);
03509         }
03510 /*@=unrecog@*/
03511         break;
03512 
03513     /* XXX redundant with --logical. */
03514     case 'L':
03515         rpmioFtsOpts &= ~FTS_PHYSICAL;
03516         rpmioFtsOpts |= FTS_LOGICAL;
03517         break;
03518     /* XXX redundant with --physical. */
03519     case 'P':
03520         rpmioFtsOpts &= ~FTS_LOGICAL;
03521         rpmioFtsOpts |= FTS_PHYSICAL;
03522         break;
03523     case 'X':
03524         mtreeReadExcludes(arg);
03525         break;
03526 
03527     case '?':
03528     default:
03529         fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
03530         poptPrintUsage(con, stderr, 0);
03531         exit(EXIT_FAILURE);
03532         /*@notreached@*/ break;
03533     }
03534 }
03535 
03536 /*@unchecked@*/ /*@observer@*/
03537 static struct poptOption optionsTable[] = {
03538 /*@-type@*/ /* FIX: cast? */
03539  { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
03540         mtreeArgCallback, 0, NULL, NULL },
03541 /*@=type@*/
03542 
03543     /* XXX redundant with --logical. */
03544   { NULL,'L', POPT_ARG_NONE,    NULL, (int)'L',
03545         N_("Follow symlinks"), NULL },
03546     /* XXX redundant with --physical. */
03547   { NULL,'P', POPT_ARG_NONE,    NULL, (int)'P',
03548         N_("Don't follow symlinks"), NULL },
03549   { NULL,'X', POPT_ARG_NONE,    NULL, (int)'X',
03550         N_("Read fnmatch(3) exclude patterns from <file>"), N_("<file>") },
03551 
03552   { "create",'c', POPT_BIT_SET,         &mtreeFlags, MTREE_FLAGS_CREATE,
03553         N_("Print file tree specification to stdout"), NULL },
03554   { "dirs",'d', POPT_BIT_SET,           &mtreeFlags, MTREE_FLAGS_DIRSONLY,
03555         N_("Directories only"), NULL },
03556   { "ignore",'e', POPT_BIT_SET,         &mtreeFlags, MTREE_FLAGS_IGNORE,
03557         N_("Don't complain about files not in the specification"), NULL },
03558   { "file",'f', POPT_ARG_STRING,        NULL, (int)'f',
03559         N_("Read file tree <spec>"), N_("<spec>") },
03560   { "indent",'i', POPT_BIT_SET,         &mtreeFlags, MTREE_FLAGS_INDENT,
03561         N_("Indent sub-directories"), NULL },
03562   { "add",'K', POPT_ARG_STRING, NULL, (int)'K',
03563         N_("Add <key> to specification"), N_("<key>") },
03564   { "key",'k', POPT_ARG_STRING, NULL, (int)'k',
03565         N_("Use \"type\" keywords instead"), N_("<key>") },
03566   { "loose",'l', POPT_BIT_SET,          &mtreeFlags, MTREE_FLAGS_LOOSE,
03567         N_("Loose permissions check"), NULL },
03568   { "nocomment",'n', POPT_BIT_SET,      &mtreeFlags, MTREE_FLAGS_NOCOMMENT,
03569         N_("Don't include sub-directory comments"), NULL },
03570   { "path",'p', POPT_ARG_ARGV,  &__rpmfts.paths, 0,
03571         N_("Use <path> rather than current directory"), N_("<path>") },
03572   /* XXX --quiet collides w poptIO */
03573   { "quiet",'q', POPT_BIT_SET,          &mtreeFlags, MTREE_FLAGS_QUIET,
03574         N_("Quiet mode"), NULL },
03575   { "remove",'r', POPT_BIT_SET,         &mtreeFlags, MTREE_FLAGS_REMOVE,
03576         N_("Remove files not in specification"), NULL },
03577   { "seed",'s', POPT_ARG_INT,           &__rpmfts.crc_total, 0,
03578         N_("Display crc for file(s) with <seed>"), N_("<seed>") },
03579   { "touch",'t', POPT_BIT_SET,          &mtreeFlags, MTREE_FLAGS_TOUCH,
03580         N_("Touch files iff timestamp differs"), NULL },
03581   { "update",'u', POPT_BIT_SET,         &mtreeFlags, MTREE_FLAGS_UPDATE,
03582         N_("Update owner/group/permissions to match specification"), NULL },
03583   { "mismatch",'U', POPT_BIT_SET, &mtreeFlags, (MTREE_FLAGS_UPDATE|MTREE_FLAGS_MISMATCHOK),
03584         N_("Same as -u, but ignore match status on exit"), NULL },
03585   { "warn",'w', POPT_BIT_SET,           &mtreeFlags, MTREE_FLAGS_WARN,
03586         N_("Treat missing uid/gid as warning"), NULL },
03587     /* XXX duplicated with --xdev. */
03588   { "xdev",'x', POPT_BIT_SET,  &rpmioFtsOpts, FTS_XDEV,
03589         N_("Don't cross mount points"), NULL },
03590 
03591  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
03592         N_("Fts(3) traversal options:"), NULL },
03593 
03594  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
03595         N_("Available digests:"), NULL },
03596 
03597  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
03598         N_("Common options for all rpmio executables:"),
03599         NULL },
03600 
03601   POPT_AUTOALIAS
03602   POPT_AUTOHELP
03603 
03604   { NULL, (char)-1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
03605         "\
03606 Usage: mtree [-cdeilnqrtUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n\
03607 ", NULL },
03608 
03609   POPT_TABLEEND
03610 };
03611 
03612 #if defined(__linux__)
03613 /*@null@*/ /*@observer@*/
03614 static const char *my_getlogin(void)
03615         /*@globals fileSystem @*/
03616         /*@modifies fileSystem @*/
03617 {
03618     const char *s = getlogin();
03619 
03620     if (s && *s) {
03621         return (char *) s;
03622     } else {
03623         struct passwd *pw = getpwuid(geteuid());
03624         char *ss = NULL;
03625 /*@-unrecog@*/
03626         if (pw != NULL && pw->pw_name != NULL && pw->pw_name[0] != '\0') {
03627             if (asprintf(&ss, _("(no controlling terminal) %s"), pw->pw_name) < 0) {
03628                 perror("asprintf");
03629                 return NULL;
03630             }
03631         } else {
03632             if (asprintf(&ss, _("(no controlling terminal) #%d"), geteuid()) < 0) {
03633                 perror("asprintf");
03634                 return NULL;
03635             }
03636         }
03637 /*@=unrecog@*/
03638         return ss;
03639     }
03640 }
03641 #define __getlogin      my_getlogin
03642 #else
03643 #define __getlogin      getlogin
03644 #endif
03645 
03646 int
03647 main(int argc, char *argv[])
03648         /*@globals _rpmfts, mtreeFlags, excludes, __assert_program_name,
03649                 rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
03650         /*@modifies _rpmfts, mtreeFlags, excludes, __assert_program_name,
03651                 rpmGlobalMacroContext, fileSystem, internalState @*/
03652 {
03653     rpmfts fts = _rpmfts;
03654     poptContext optCon;
03655     int rc = 1;         /* assume failure */
03656     int i;
03657 
03658     __progname = "mtree";
03659 
03660     RPM_LIST_INIT(&excludes);
03661     fts->keys = KEYDEFAULT;
03662     fts->maxg = 5000;
03663     fts->maxu = 5000;
03664     fts->maxm = (MBITS + 1);
03665 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
03666     fts->maxf = 256;
03667     fts->sb.st_flags = 0xffffffff;
03668 #endif
03669 
03670     /* Process options. */
03671     optCon = rpmioInit(argc, argv, optionsTable);
03672 
03673     /* XXX ./rpmmtree w no args waits for stdin. poptPrintUsage more better. */
03674     argv = (char **) poptGetArgs(optCon);
03675     if (!(argv == NULL || argv[0] == NULL)) {
03676         poptPrintUsage(optCon, stderr, 0);
03677         goto exit;
03678     }
03679 
03680     if (MF_ISSET(LOOSE) && MF_ISSET(UPDATE))
03681         mtree_error("-l and -u flags are mutually exclusive");
03682 
03683     /*
03684      * Either FTS_PHYSICAL or FTS_LOGICAL must be set. Don't follow symlinks
03685      * unless explicitly overridden with FTS_LOGICAL.
03686      */
03687     fts->ftsoptions = rpmioFtsOpts;
03688     switch (fts->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) {
03689     case (FTS_LOGICAL|FTS_PHYSICAL):
03690         mtree_error("-L and -P flags are mutually exclusive");
03691         /*@notreached@*/ break;
03692     case 0:
03693         fts->ftsoptions |= FTS_PHYSICAL;
03694         break;
03695     }
03696 
03697     if (fts->paths == NULL || fts->paths[0] == NULL) {
03698         fts->paths = xcalloc(2, sizeof(fts->paths[0]));
03699         fts->paths[0] = xstrdup(".");
03700     }
03701 
03702     /* Use absolute paths since Chdir(2) is problematic with remote URI's */
03703     for (i = 0; fts->paths[i] != NULL; i++) {
03704         char fullpath[MAXPATHLEN];
03705         struct stat sb;
03706         const char * rpath;
03707         const char * lpath = NULL;
03708         int ut = urlPath(fts->paths[i], &lpath);
03709         size_t nb = (size_t)(lpath - fts->paths[i]);
03710         int isdir = (lpath[strlen(lpath)-1] == '/');
03711         
03712         /* Convert to absolute/clean/malloc'd path. */
03713         if (lpath[0] != '/') {
03714             /* XXX GLIBC: realpath(path, NULL) return malloc'd */
03715             rpath = Realpath(lpath, NULL);
03716             if (rpath == NULL)
03717                 rpath = Realpath(lpath, fullpath);
03718             if (rpath == NULL)
03719                 mtree_error("Realpath(%s): %s", lpath, strerror(errno));
03720             lpath = rpmGetPath(rpath, NULL);
03721             if (rpath != fullpath)      /* XXX GLIBC extension malloc's */
03722                 rpath = _free(rpath);
03723         } else
03724             lpath = rpmGetPath(lpath, NULL);
03725 
03726         /* Reattach the URI to the absolute/clean path. */
03727         /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */
03728         switch (ut) {
03729         case URL_IS_DASH:
03730         case URL_IS_UNKNOWN:
03731             rpath = lpath;
03732             lpath = NULL;
03733             /*@switchbreak@*/ break;
03734         default:
03735             strncpy(fullpath, fts->paths[i], nb);
03736             fullpath[nb] = '\0';
03737             rpath = rpmGenPath(fullpath, lpath, NULL);
03738             lpath = _free(lpath);
03739             /*@switchbreak@*/ break;
03740         }
03741 
03742         /* Add a trailing '/' on directories. */
03743         lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode))
03744                 ? "/" : NULL);
03745         fts->paths[i] = _free(fts->paths[i]);
03746         fts->paths[i] = rpmExpand(rpath, lpath, NULL);
03747         fts->fullpath = xstrdup(fts->paths[i]);
03748         rpath = _free(rpath);
03749     }
03750 
03751     /* XXX prohibits -s 0 invocation */
03752     if (fts->crc_total)
03753         mtreeFlags |= MTREE_FLAGS_SEEDED;
03754     fts->crc_total ^= 0xffffffff;
03755 
03756     (void) rpmswEnter(&dc_totalops, -1);
03757 
03758     if (MF_ISSET(CREATE)) {
03759             if (!MF_ISSET(NOCOMMENT)) {
03760                 time_t clock;
03761                 char host[MAXHOSTNAMELEN];
03762 
03763                 (void) time(&clock);
03764                 (void) gethostname(host, sizeof(host));
03765                 (void) printf("#\t   user: %s\n", __getlogin());
03766                 (void) printf("#\tmachine: %s\n", host);
03767                 for (i = 0; fts->paths[i] != NULL; i++)
03768                     (void) printf("#\t   tree: %s\n", fts->paths[i]);
03769                 (void) printf("#\t   date: %s", ctime(&clock));
03770             }
03771             rc = mtreeCWalk(fts);
03772             if (MF_ISSET(SEEDED) && KF_ISSET(fts->keys, CKSUM))
03773                 (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
03774                         fts->fullpath, (unsigned)fts->crc_total);
03775 
03776     } else {
03777         if (fts->spec2 != NULL) {
03778             rc = mtreeVSpec(fts);
03779         } else {
03780 /*@-evalorder@*/
03781             fts->root = mtreeSpec(fts, fts->spec1);
03782 /*@=evalorder@*/
03783             fts->path = xmalloc(MAXPATHLEN);
03784             rc = mtreeVWalk(fts);
03785             mtreeMiss(fts, fts->root, fts->path);
03786             fts->path = _free(fts->path);
03787             if (MF_ISSET(SEEDED))
03788                 (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
03789                         fts->fullpath, (unsigned) fts->crc_total);
03790         }
03791     }
03792     if (MF_ISSET(MISMATCHOK) && (rc == MISMATCHEXIT))
03793         rc = 0;
03794 
03795     (void) rpmswExit(&dc_totalops, 0);
03796     if (_rpmsw_stats) {
03797         rpmswPrint(" total:", &dc_totalops, NULL);
03798         rpmswPrint("  read:", &dc_readops, NULL);
03799         rpmswPrint("digest:", &dc_digestops, NULL);
03800     }
03801 
03802 exit:
03803 #if defined(_RPMFI_INTERNAL)
03804     (void)rpmtsFree(fts->ts); 
03805     fts->ts = NULL;
03806     fts->fi = rpmfiFree(fts->fi);
03807     tagClean(NULL);     /* Free header tag indices. */
03808 #endif
03809     if (fts->spec1 != NULL && fileno(fts->spec1) > 2) {
03810         (void) fclose(fts->spec1);
03811         fts->spec1 = NULL;
03812     }
03813     if (fts->spec2 != NULL && fileno(fts->spec2) > 2) {
03814         (void) fclose(fts->spec2);
03815         fts->spec2 = NULL;
03816     }
03817     fts->paths = argvFree(fts->paths);
03818 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
03819     fts->f = _free(fts->f);
03820 #endif
03821     fts->g = _free(fts->g);
03822     fts->m = _free(fts->m);
03823     fts->u = _free(fts->u);
03824     fts->fullpath = _free(fts->fullpath);
03825     /* XXX TODO: clean excludes */
03826 
03827     optCon = rpmioFini(optCon);
03828 
03829     return rc;
03830 }