rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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 }