rpm
5.2.1
|
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 }