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