rpm 5.2.1

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