rpm  5.2.1
rpmmtree.c
Go to the documentation of this file.
1 /*-
2  * Copyright (c) 1989, 1990, 1993
3  * The Regents of the University of California. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  * notice, this list of conditions and the following disclaimer in the
12  * documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  * must display the following acknowledgement:
15  * This product includes software developed by the University of
16  * California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  * may be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #ifndef lint
35 static const char copyright[] =
36 "@(#) Copyright (c) 1989, 1990, 1993\n\
37  The Regents of the University of California. All rights reserved.\n";
38 #endif /* not lint */
39 
40 #include "system.h"
41 
42 #include <fnmatch.h>
43 #include <signal.h>
44 #include <stdarg.h>
45 
46 #if !defined(HAVE_ASPRINTF)
47 #include "asprintf.h"
48 #endif
49 
50 #if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) || defined(__APPLE__)
51 #define HAVE_ST_FLAGS 1 /* XXX TODO: should be AutoFu test */
52 #else
53 #undef HAVE_ST_FLAGS /* XXX TODO: should be AutoFu test */
54 #endif
55 
56 #if defined(__linux__)
57 #define st_mtimespec st_mtim
58 #endif
59 
60 #if defined(__QNXNTO__)
61 #define st_mtimespec st_mtime
62 #endif
63 
64 #include <rpmio_internal.h> /* XXX fdInitDigest() et al */
65 #include <fts.h>
66 #include <ugid.h>
67 #include <poptIO.h>
68 
69 #undef _RPMFI_INTERNAL /* XXX don't enable *.rpm/ containers yet. */
70 #if defined(_RPMFI_INTERNAL)
71 #define _RPMAV_INTERNAL
72 #include <rpmdav.h>
73 #include <rpmtag.h>
74 #include <rpmfi.h>
75 
76 #include <rpmlib.h> /* XXX for rpmts typedef */
77 #include <rpmts.h>
78 #endif
79 
80 #define RPM_LIST_HEAD(name, type) \
81  struct name { struct type *lh_first; }
82 #define RPM_LIST_ENTRY(type) \
83  struct { struct type *le_next;struct type **le_prev; }
84 #define RPM_LIST_EMPTY(head) \
85  ((head)->lh_first == NULL)
86 #define RPM_LIST_FIRST(head) \
87  ((head)->lh_first)
88 #define RPM_LIST_NEXT(elm, field) \
89  ((elm)->field.le_next)
90 #define RPM_LIST_INIT(head) \
91  do { RPM_LIST_FIRST((head)) = NULL; } while (0)
92 #define RPM_LIST_INSERT_HEAD(head, elm, field) \
93  do { if ((RPM_LIST_NEXT((elm), field) = RPM_LIST_FIRST((head))) != NULL) \
94  RPM_LIST_FIRST((head))->field.le_prev = &RPM_LIST_NEXT((elm), field);\
95  RPM_LIST_FIRST((head)) = (elm); \
96  (elm)->field.le_prev = &RPM_LIST_FIRST((head)); } while (0)
97 #define RPM_LIST_FOREACH(var, head, field) \
98  for ((var) = RPM_LIST_FIRST((head)); (var); (var) = RPM_LIST_NEXT((var), field))
99 
100 #define _MTREE_INTERNAL
101 /*==============================================================*/
102 
103 #define _KFB(n) (1U << (n))
104 #define _MFB(n) (_KFB(n) | 0x40000000)
105 
124  /* 13-31 unused */
125 };
126 
129 typedef struct rpmfts_s * rpmfts;
130 
131 #if defined(_MTREE_INTERNAL)
132 
156  /* 19-31 unused */
157 };
158 
159 typedef struct _node {
160  struct _node *parent, *child;
161  struct _node *prev, *next;
162  struct stat sb;
163  char *slink;
167  uint32_t cksum;
171  uint8_t type;
172 #define F_BLOCK 0x001
173 #define F_CHAR 0x002
174 #define F_DIR 0x004
175 #define F_FIFO 0x008
176 #define F_FILE 0x010
177 #define F_LINK 0x020
178 #define F_SOCK 0x040
180  char name[1]; /* file name (must be last) */
181 } NODE;
182 
183 struct rpmfts_s {
184  FTS * t;
186  struct stat sb;
188  uint32_t crc_total;
189  unsigned lineno;
190 /*@null@*/
192 /*@null@*/
195 /*@null@*/
197 
198 /*@dependent@*/ /*@null@*/
199  FILE * spec1;
200 /*@dependent@*/ /*@null@*/
201  FILE * spec2;
202 
203 /*@null@*/
204  const char * fullpath;
205 /*@null@*/
206  char * path;
208 
209 #if defined(HAVE_ST_FLAGS)
210  size_t maxf;
211 /*@null@*/
212  unsigned long * f;
213 #endif
214  size_t maxg;
215 /*@null@*/
216  gid_t * g;
217  size_t maxm;
218 /*@null@*/
219  mode_t * m;
220  size_t maxu;
221 /*@null@*/
222  uid_t * u;
223 
224 #if defined(_RPMFI_INTERNAL)
225 /*@null@*/
226  rpmts ts;
227 /*@null@*/
228  rpmfi fi;
229 #endif
230 };
231 #endif /* _MTREE_INTERNAL */
232 
233 #undef _KFB
234 #undef _MFB
235 
236 #ifdef __cplusplus
237 extern "C" {
238 #endif
239 
240 /*@null@*/
241 static NODE * mtreeSpec(rpmfts fts, /*@null@*/ FILE * fp)
242  /*@globals fileSystem, internalState @*/
243  /*@modifies fts, fp, fileSystem, internalState @*/;
244 
245 static int mtreeVSpec(rpmfts fts)
246  /*@globals fileSystem, internalState @*/
247  /*@modifies fts, fileSystem, internalState @*/;
248 
249 static int mtreeCWalk(rpmfts fts)
250  /*@globals h_errno, fileSystem, internalState @*/
251  /*@modifies fts, fileSystem, internalState @*/;
252 
253 static int mtreeVWalk(rpmfts fts)
254  /*@globals h_errno, fileSystem, internalState @*/
255  /*@modifies fts, fileSystem, internalState @*/;
256 
257 #ifdef __cplusplus
258 }
259 #endif
260 
261 /*==============================================================*/
262 
263 static void mtreeMiss(rpmfts fts, /*@null@*/ NODE * p, char * tail)
264  /*@globals h_errno, errno, fileSystem, internalState @*/
265  /*@modifies p, tail, errno, fileSystem, internalState @*/;
266 
267 #include "debug.h"
268 
269 /*@access DIR @*/
270 /*@access FD_t @*/
271 /*@access rpmfi @*/
272 /*@access rpmts @*/
273 
274 #define MF_ISSET(_FLAG) ((mtreeFlags & ((MTREE_FLAGS_##_FLAG) & ~0x40000000)) != MTREE_FLAGS_NONE)
275 
276 #define KEYDEFAULT \
277  (MTREE_KEYS_GID | MTREE_KEYS_MODE | MTREE_KEYS_NLINK | MTREE_KEYS_SIZE | \
278  MTREE_KEYS_SLINK | MTREE_KEYS_TIME | MTREE_KEYS_UID)
279 
280 #define MISMATCHEXIT 2
281 
282 #if !defined(S_ISTXT) && defined(S_ISVTX) /* XXX linux != BSD */
283 #define S_ISTXT S_ISVTX
284 #endif
285 #define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
286 
287 /*@unchecked@*/
288 static struct rpmfts_s __rpmfts;
289 /*@unchecked@*/
290 static rpmfts _rpmfts = &__rpmfts;
291 
292 /*@unchecked@*/
294 
295 /* XXX merge into _rpmfts, use mmiRE instead */
296 struct exclude {
298  const char *glob;
299  int pathname;
300 };
301 
302 /*@unchecked@*/
304 
305 /*@unchecked@*/
306 static struct rpmop_s dc_totalops;
307 
308 /*@unchecked@*/
309 static struct rpmop_s dc_readops;
310 
311 /*@unchecked@*/
312 static struct rpmop_s dc_digestops;
313 
314 /*==============================================================*/
315 
316 /*@exits@*/
317 static void
318 mtree_error(const char *fmt, ...)
319 #ifdef __GNUC__
320 __attribute__ ((format (printf, 1, 2)))
321 #endif
322  /*@globals fileSystem @*/
323  /*@modifies fileSystem @*/;
324 
325 void
326 mtree_error(const char *fmt, ...)
327 {
328  va_list ap;
329 
330  va_start(ap, fmt);
331  (void) fflush(NULL);
332  (void) fprintf(stderr, "\n%s: ", __progname);
333  (void) vfprintf(stderr, fmt, ap);
334  va_end (ap);
335  (void) fprintf(stderr, "\n");
336  if (_rpmfts->lineno)
337  (void)fprintf(stderr, _("%s: failed at line %d of the specification\n"),
338  __progname, _rpmfts->lineno);
339  exit(EXIT_FAILURE);
340  /*@notreached@*/
341 }
342 
343 typedef struct _key {
344 /*@observer@*/
345  const char *name; /* key name */
346  unsigned val; /* value */
347 #define NEEDVALUE 0xffffffff
348  uint32_t flags;
349 } KEY;
350 
351 /* NB: the following table must be sorted lexically. */
352 /*@unchecked@*/ /*@observer@*/
353 static KEY keylist[] = {
354  { "adler32", MTREE_KEYS_DIGEST, PGPHASHALGO_ADLER32 },
355  { "cksum", MTREE_KEYS_CKSUM, NEEDVALUE },
356  { "crc32", MTREE_KEYS_DIGEST, PGPHASHALGO_CRC32 },
357  { "crc64", MTREE_KEYS_DIGEST, PGPHASHALGO_CRC64 },
358  { "flags", MTREE_KEYS_FLAGS, NEEDVALUE },
359  { "gid", MTREE_KEYS_GID, NEEDVALUE },
360  { "gname", MTREE_KEYS_GNAME, NEEDVALUE },
361  { "haval160digest", MTREE_KEYS_DIGEST, PGPHASHALGO_HAVAL_5_160 },
362  { "ignore", MTREE_KEYS_IGN, 0 },
363  { "jlu32", MTREE_KEYS_DIGEST, PGPHASHALGO_JLU32 },
364  { "link", MTREE_KEYS_SLINK, NEEDVALUE },
365  { "md2digest", MTREE_KEYS_DIGEST, PGPHASHALGO_MD2 },
366  { "md4digest", MTREE_KEYS_DIGEST, PGPHASHALGO_MD4 },
367  { "md5digest", MTREE_KEYS_DIGEST, PGPHASHALGO_MD5 },
368  { "mode", MTREE_KEYS_MODE, NEEDVALUE },
369  { "nlink", MTREE_KEYS_NLINK, NEEDVALUE },
370  { "nochange", MTREE_KEYS_NOCHANGE, 0 },
371  { "optional", MTREE_KEYS_OPT, 0 },
372  { "rmd128digest", MTREE_KEYS_DIGEST, PGPHASHALGO_RIPEMD128 },
373  { "rmd160digest", MTREE_KEYS_DIGEST, PGPHASHALGO_RIPEMD160 },
374  { "rmd256digest", MTREE_KEYS_DIGEST, PGPHASHALGO_RIPEMD256 },
375  { "rmd320digest", MTREE_KEYS_DIGEST, PGPHASHALGO_RIPEMD320 },
376  { "salsa10", MTREE_KEYS_DIGEST, PGPHASHALGO_SALSA10 },
377  { "salsa20", MTREE_KEYS_DIGEST, PGPHASHALGO_SALSA20 },
378  { "sha1digest", MTREE_KEYS_DIGEST, PGPHASHALGO_SHA1 },
379  { "sha224digest", MTREE_KEYS_DIGEST, PGPHASHALGO_SHA224 },
380  { "sha256digest", MTREE_KEYS_DIGEST, PGPHASHALGO_SHA256 },
381  { "sha384digest", MTREE_KEYS_DIGEST, PGPHASHALGO_SHA384 },
382  { "sha512digest", MTREE_KEYS_DIGEST, PGPHASHALGO_SHA512 },
383  { "size", MTREE_KEYS_SIZE, NEEDVALUE },
384  { "tiger192digest", MTREE_KEYS_DIGEST, PGPHASHALGO_TIGER192 },
385  { "time", MTREE_KEYS_TIME, NEEDVALUE },
386  { "type", MTREE_KEYS_TYPE, NEEDVALUE },
387  { "uid", MTREE_KEYS_UID, NEEDVALUE },
388  { "uname", MTREE_KEYS_UNAME, NEEDVALUE },
389 };
390 
391 static int
392 keycompare(const void * a, const void * b)
393  /*@*/
394 {
395  return strcmp(((KEY *)a)->name, ((KEY *)b)->name);
396 }
397 
398 static unsigned
399 parsekey(char *name, /*@out@*/ uint32_t *needvaluep)
400  /*@globals fileSystem @*/
401  /*@modifies *needvaluep, fileSystem @*/
402 
403 {
404  KEY *k, tmp;
405 
406  if (needvaluep != NULL)
407  *needvaluep = 0;
408  if (*name == '\0')
409  return 0;
410  tmp.name = name;
411  k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(keylist[0]),
412  sizeof(keylist[0]), keycompare);
413  if (k == NULL)
414  mtree_error("unknown keyword %s", name);
415 
416  if (needvaluep != NULL)
417  *needvaluep = k->flags;
418  return k->val;
419 }
420 
421 static /*@observer@*/ /*@null@*/ const char *
422 algo2tagname(uint32_t algo)
423  /*@*/
424 {
425  const char * tagname = NULL;
426 
427  switch (algo) {
428  case PGPHASHALGO_MD5: tagname = "md5digest"; break;
429  case PGPHASHALGO_SHA1: tagname = "sha1digest"; break;
430  case PGPHASHALGO_RIPEMD160: tagname = "rmd160digest"; break;
431  case PGPHASHALGO_MD2: tagname = "md2digest"; break;
432  case PGPHASHALGO_TIGER192: tagname = "tiger192digest"; break;
433  case PGPHASHALGO_HAVAL_5_160: tagname = "haval160digest"; break;
434  case PGPHASHALGO_SHA256: tagname = "sha256digest"; break;
435  case PGPHASHALGO_SHA384: tagname = "sha384digest"; break;
436  case PGPHASHALGO_SHA512: tagname = "sha512digest"; break;
437  case PGPHASHALGO_MD4: tagname = "md4digest"; break;
438  case PGPHASHALGO_RIPEMD128: tagname = "rmd128digest"; break;
439  case PGPHASHALGO_CRC32: tagname = "crc32"; break;
440  case PGPHASHALGO_ADLER32: tagname = "adler32"; break;
441  case PGPHASHALGO_CRC64: tagname = "crc64"; break;
442  case PGPHASHALGO_JLU32: tagname = "jlu32"; break;
443  case PGPHASHALGO_SHA224: tagname = "sha224digest"; break;
444  case PGPHASHALGO_RIPEMD256: tagname = "rmd256digest"; break;
445  case PGPHASHALGO_RIPEMD320: tagname = "rmd320digest"; break;
446  case PGPHASHALGO_SALSA10: tagname = "salsa10"; break;
447  case PGPHASHALGO_SALSA20: tagname = "salsa20"; break;
448  default: tagname = NULL; break;
449  }
450  return tagname;
451 }
452 
453 #if defined(HAVE_ST_FLAGS)
454 static const char *
455 flags_to_string(u_long fflags)
456  /*@*/
457 {
458  char * string = fflagstostr(fflags);
459  if (string != NULL && *string == '\0') {
460  free(string);
461  string = xstrdup("none");
462  }
463  return string;
464 }
465 #endif
466 
467 /*==============================================================*/
468 
469 /*@unchecked@*/ /*@observer@*/
470 static const uint32_t crctab[] = {
471  0x0,
472  0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
473  0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
474  0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
475  0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
476  0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
477  0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
478  0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
479  0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
480  0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
481  0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
482  0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
483  0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
484  0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
485  0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
486  0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
487  0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
488  0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
489  0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
490  0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
491  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
492  0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
493  0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
494  0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
495  0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
496  0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
497  0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
498  0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
499  0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
500  0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
501  0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
502  0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
503  0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
504  0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
505  0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
506  0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
507  0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
508  0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
509  0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
510  0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
511  0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
512  0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
513  0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
514  0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
515  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
516  0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
517  0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
518  0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
519  0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
520  0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
521  0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
522  0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
523 };
524 
525 /*
526  * Compute a POSIX 1003.2 checksum. This routine has been broken out so that
527  * other programs can use it. It takes a file descriptor to read from and
528  * locations to store the crc and the number of bytes read. It returns 0 on
529  * success and 1 on failure. Errno is set on failure.
530  */
531 static int
532 crc(FD_t fd, /*@out@*/ uint32_t * cval, /*@out@*/ uint32_t * clen)
533  /*@globals _rpmfts, fileSystem @*/
534  /*@modifies fd, *clen, *cval, _rpmfts, fileSystem @*/
535 {
536  uint32_t crc = 0;
537  uint32_t len = 0;
538 
539 #define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)]
540 
541  _rpmfts->crc_total ^= 0xffffffff;
542 
543  { uint8_t buf[16 * 1024];
544  size_t nr;
545  while ((nr = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) != 0) {
546  uint8_t *p;
547  for (len += nr, p = buf; nr--; ++p) {
548  COMPUTE(crc, *p);
549  COMPUTE(_rpmfts->crc_total, *p);
550  }
551  }
552  if (Ferror(fd))
553  return 1;
554  }
555 
556  *clen = len;
557 
558  /* Include the length of the file. */
559  for (; len != 0; len >>= 8) {
560  COMPUTE(crc, len & 0xff);
561  COMPUTE(_rpmfts->crc_total, len & 0xff);
562  }
563 
564  *cval = (crc ^ 0xffffffff);
565  _rpmfts->crc_total ^= 0xffffffff;
566  return 0;
567 }
568 
569 /*==============================================================*/
570 
571 /*
572  * to select alternate encoding format
573  */
574 #define VIS_OCTAL 0x01 /* use octal \ddd format */
575 #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */
576 
577 /*
578  * to alter set of characters encoded (default is to encode all
579  * non-graphic except space, tab, and newline).
580  */
581 #define VIS_SP 0x04 /* also encode space */
582 #define VIS_TAB 0x08 /* also encode tab */
583 #define VIS_NL 0x10 /* also encode newline */
584 #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL)
585 #define VIS_SAFE 0x20 /* only encode "unsafe" characters */
586 
587 /*
588  * other
589  */
590 #define VIS_NOSLASH 0x40 /* inhibit printing '\' */
591 
592 /*
593  * unvis return codes
594  */
595 #define UNVIS_VALID 1 /* character valid */
596 #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */
597 #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */
598 #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */
599 #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */
600 
601 /*
602  * unvis flags
603  */
604 #define UNVIS_END 1 /* no more characters */
605 
606 static char *vis(/*@returned@*/ /*@out@*/ char *dst, int c, int flag, int nextc)
607  /*@modifies dst @*/;
608 static int strvis(/*@out@*/ char *dst, const char *src, int flag)
609  /*@modifies dst @*/;
610 #ifdef NOTUSED
611 static int strnvis(/*@out@*/ char *dst, const char *src, size_t siz, int flag)
612  /*@modifies dst @*/;
613 static int strvisx(/*@out@*/ char *dst, const char *src, size_t len, int flag)
614  /*@modifies dst @*/;
615 #endif
616 static int strunvis(/*@out@*/ char *dst, const char *src)
617  /*@modifies dst @*/;
618 static int unvis(/*@out@*/ char *cp, char c, int *astate, int flag)
619  /*@modifies cp, astate @*/;
620 
621 #define isoctal(c) (((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
622 #define isvisible(c) \
623  (((unsigned)(c) <= (unsigned)UCHAR_MAX && isascii((unsigned char)(c)) && \
624  isgraph((unsigned char)(c))) \
625  || ((flag & VIS_SP) == 0 && (c) == (int)' ') \
626  || ((flag & VIS_TAB) == 0 && (c) == (int)'\t') \
627  || ((flag & VIS_NL) == 0 && (c) == (int)'\n') \
628  || ((flag & VIS_SAFE) \
629  && ((c) == (int)'\b' || (c) == (int)'\007' || (c) == (int)'\r')))
630 
631 /*
632  * vis - visually encode characters
633  */
634 char *
635 vis(char * dst, int c, int flag, int nextc)
636 {
637  if (isvisible(c)) {
638  *dst++ = (char)c;
639  if (c == (int)'\\' && (flag & VIS_NOSLASH) == 0)
640  *dst++ = '\\';
641  *dst = '\0';
642  return dst;
643  }
644 
645  if (flag & VIS_CSTYLE) {
646  switch(c) {
647  case '\n':
648  *dst++ = '\\';
649  *dst++ = 'n';
650  goto done;
651  case '\r':
652  *dst++ = '\\';
653  *dst++ = 'r';
654  goto done;
655  case '\b':
656  *dst++ = '\\';
657  *dst++ = 'b';
658  goto done;
659  case '\a':
660  *dst++ = '\\';
661  *dst++ = 'a';
662  goto done;
663  case '\v':
664  *dst++ = '\\';
665  *dst++ = 'v';
666  goto done;
667  case '\t':
668  *dst++ = '\\';
669  *dst++ = 't';
670  goto done;
671  case '\f':
672  *dst++ = '\\';
673  *dst++ = 'f';
674  goto done;
675  case ' ':
676  *dst++ = '\\';
677  *dst++ = 's';
678  goto done;
679  case '\0':
680  *dst++ = '\\';
681  *dst++ = '0';
682  if (isoctal(nextc)) {
683  *dst++ = '0';
684  *dst++ = '0';
685  }
686  goto done;
687  }
688  }
689  if (((c & 0177) == (int)' ') || (flag & VIS_OCTAL)) {
690  *dst++ = '\\';
691  *dst++ = ((unsigned char)c >> 6 & 07) + '0';
692  *dst++ = ((unsigned char)c >> 3 & 07) + '0';
693  *dst++ = ((unsigned char)c & 07) + '0';
694  goto done;
695  }
696  if ((flag & VIS_NOSLASH) == 0)
697  *dst++ = '\\';
698  if (c & 0200) {
699  c &= 0177;
700  *dst++ = 'M';
701  }
702  if (iscntrl(c)) {
703  *dst++ = '^';
704  if (c == 0177)
705  *dst++ = '?';
706  else
707  *dst++ = (char)(c + (int)'@');
708  } else {
709  *dst++ = '-';
710  *dst++ = (char)c;
711  }
712 
713 done:
714  *dst = '\0';
715  return dst;
716 }
717 
718 /*
719  * strvis, strnvis, strvisx - visually encode characters from src into dst
720  *
721  * Dst must be 4 times the size of src to account for possible
722  * expansion. The length of dst, not including the trailing NULL,
723  * is returned.
724  *
725  * Strnvis will write no more than siz-1 bytes (and will NULL terminate).
726  * The number of bytes needed to fully encode the string is returned.
727  *
728  * Strvisx encodes exactly len bytes from src into dst.
729  * This is useful for encoding a block of data.
730  */
731 int
732 strvis(char * dst, const char * src, int flag)
733 {
734  char c;
735  char *start;
736 
737  for (start = dst; (c = *src) != '\0';)
738  dst = vis(dst, (int)c, flag, (int)*++src);
739  *dst = '\0';
740  return (dst - start);
741 }
742 
743 #ifdef NOTUSED
744 int
745 strnvis(char * dst, const char * src, size_t siz, int flag)
746 {
747  char c;
748  char *start, *end;
749 
750  for (start = dst, end = start + siz - 1; (c = *src) && dst < end; ) {
751  if (isvisible((int)c)) {
752  *dst++ = c;
753  if (c == '\\' && (flag & VIS_NOSLASH) == 0) {
754  /* need space for the extra '\\' */
755  if (dst < end)
756  *dst++ = '\\';
757  else {
758  dst--;
759  break;
760  }
761  }
762  src++;
763  } else {
764  /* vis(3) requires up to 4 chars */
765  if (dst + 3 < end)
766  dst = vis(dst, (int)c, flag, (int)*++src);
767  else
768  break;
769  }
770  }
771  *dst = '\0';
772  if (dst >= end) {
773  char tbuf[5];
774 
775  /* adjust return value for truncation */
776  while ((c = *src) != '\0')
777  dst += vis(tbuf, (int)c, flag, (int)*++src) - tbuf;
778  }
779  return (dst - start);
780 }
781 #endif /* NOTUSED */
782 
783 #ifdef NOTUSED
784 int
785 strvisx(char * dst, const char * src, size_t len, int flag)
786 {
787  char c;
788  char *start;
789 
790  for (start = dst; len > 1; len--) {
791  c = *src;
792  dst = vis(dst, (int)c, flag, (int)*++src);
793  }
794  if (len)
795  dst = vis(dst, (int)*src, flag, (int)'\0');
796  *dst = '\0';
797  return (dst - start);
798 }
799 #endif /* NOTUSED */
800 
801 /*
802  * decode driven by state machine
803  */
804 #define S_GROUND 0 /* haven't seen escape char */
805 #define S_START 1 /* start decoding special sequence */
806 #define S_META 2 /* metachar started (M) */
807 #define S_META1 3 /* metachar more, regular char (-) */
808 #define S_CTRL 4 /* control char started (^) */
809 #define S_OCTAL2 5 /* octal digit 2 */
810 #define S_OCTAL3 6 /* octal digit 3 */
811 
812 #if !defined(isoctal)
813 #define isoctal(c) (((unsigned char)(c)) >= '0' && ((unsigned char)(c)) <= '7')
814 #endif
815 
816 /*
817  * unvis - decode characters previously encoded by vis
818  */
819 int
820 unvis(char *cp, char c, int *astate, int flag)
821 {
822 
823  if (flag & UNVIS_END) {
824  if (*astate == S_OCTAL2 || *astate == S_OCTAL3) {
825  *astate = S_GROUND;
826  return (UNVIS_VALID);
827  }
828  return (*astate == S_GROUND ? UNVIS_NOCHAR : UNVIS_SYNBAD);
829  }
830 
831  switch (*astate) {
832 
833  case S_GROUND:
834  *cp = '\0';
835  if (c == '\\') {
836  *astate = S_START;
837  return (0);
838  }
839  *cp = c;
840  return (UNVIS_VALID);
841 
842  case S_START:
843  switch(c) {
844  case '\\':
845  *cp = c;
846  *astate = S_GROUND;
847  return (UNVIS_VALID);
848  case '0': case '1': case '2': case '3':
849  case '4': case '5': case '6': case '7':
850  *cp = (c - '0');
851  *astate = S_OCTAL2;
852  return (0);
853  case 'M':
854  *cp = (char) 0200;
855  *astate = S_META;
856  return (0);
857  case '^':
858  *astate = S_CTRL;
859  return (0);
860  case 'n':
861  *cp = '\n';
862  *astate = S_GROUND;
863  return (UNVIS_VALID);
864  case 'r':
865  *cp = '\r';
866  *astate = S_GROUND;
867  return (UNVIS_VALID);
868  case 'b':
869  *cp = '\b';
870  *astate = S_GROUND;
871  return (UNVIS_VALID);
872  case 'a':
873  *cp = '\007';
874  *astate = S_GROUND;
875  return (UNVIS_VALID);
876  case 'v':
877  *cp = '\v';
878  *astate = S_GROUND;
879  return (UNVIS_VALID);
880  case 't':
881  *cp = '\t';
882  *astate = S_GROUND;
883  return (UNVIS_VALID);
884  case 'f':
885  *cp = '\f';
886  *astate = S_GROUND;
887  return (UNVIS_VALID);
888  case 's':
889  *cp = ' ';
890  *astate = S_GROUND;
891  return (UNVIS_VALID);
892  case 'E':
893  *cp = '\033';
894  *astate = S_GROUND;
895  return (UNVIS_VALID);
896  case '\n': /* hidden newline */
897  *astate = S_GROUND;
898  return (UNVIS_NOCHAR);
899  case '$': /* hidden marker */
900  *astate = S_GROUND;
901  return (UNVIS_NOCHAR);
902  }
903  *astate = S_GROUND;
904  return (UNVIS_SYNBAD);
905 
906  case S_META:
907  if (c == '-')
908  *astate = S_META1;
909  else if (c == '^')
910  *astate = S_CTRL;
911  else {
912  *astate = S_GROUND;
913  return (UNVIS_SYNBAD);
914  }
915  return (0);
916 
917  case S_META1:
918  *astate = S_GROUND;
919  *cp |= c;
920  return (UNVIS_VALID);
921 
922  case S_CTRL:
923  if (c == '?')
924  *cp |= 0177;
925  else
926  *cp |= c & 037;
927  *astate = S_GROUND;
928  return (UNVIS_VALID);
929 
930  case S_OCTAL2: /* second possible octal digit */
931  if (isoctal(c)) {
932  /*
933  * yes - and maybe a third
934  */
935  *cp = (*cp << 3) + (c - '0');
936  *astate = S_OCTAL3;
937  return (0);
938  }
939  /*
940  * no - done with current sequence, push back passed char
941  */
942  *astate = S_GROUND;
943  return (UNVIS_VALIDPUSH);
944 
945  case S_OCTAL3: /* third possible octal digit */
946  *astate = S_GROUND;
947  if (isoctal(c)) {
948  *cp = (*cp << 3) + (c - '0');
949  return (UNVIS_VALID);
950  }
951  /*
952  * we were done, push back passed char
953  */
954  return (UNVIS_VALIDPUSH);
955 
956  default:
957  /*
958  * decoder in unknown state - (probably uninitialized)
959  */
960  *astate = S_GROUND;
961  return (UNVIS_SYNBAD);
962  }
963 }
964 
965 /*
966  * strunvis - decode src into dst
967  *
968  * Number of chars decoded into dst is returned, -1 on error.
969  * Dst is null terminated.
970  */
971 
972 int
973 strunvis(char * dst, const char * src)
974 {
975  char c;
976  char *start = dst;
977  int state = 0;
978 
979  while ((c = *src++) != '\0') {
980  again:
981  switch (unvis(dst, c, &state, 0)) {
982  case UNVIS_VALID:
983  dst++;
984  /*@switchbreak@*/ break;
985  case UNVIS_VALIDPUSH:
986  dst++;
987  goto again;
988  case 0:
989  case UNVIS_NOCHAR:
990  /*@switchbreak@*/ break;
991  default:
992  return (-1);
993  }
994  }
995  if (unvis(dst, c, &state, UNVIS_END) == UNVIS_VALID)
996  dst++;
997  *dst = '\0';
998  return (dst - start);
999 }
1000 
1001 /*==============================================================*/
1002 
1003 /* XXX *BSD systems already have getmode(3) and setmode(3) */
1004 #if defined(__linux__) || defined(__LCLINT__) || defined(__QNXNTO__)
1005 #if !defined(HAVE_GETMODE) || !defined(HAVE_SETMODE)
1006 
1007 #define SET_LEN 6 /* initial # of bitcmd struct to malloc */
1008 #define SET_LEN_INCR 4 /* # of bitcmd structs to add as needed */
1009 
1010 typedef struct bitcmd {
1011  char cmd;
1012  char cmd2;
1013  mode_t bits;
1014 } BITCMD;
1015 
1016 #define CMD2_CLR 0x01
1017 #define CMD2_SET 0x02
1018 #define CMD2_GBITS 0x04
1019 #define CMD2_OBITS 0x08
1020 #define CMD2_UBITS 0x10
1021 
1022 #if !defined(HAVE_GETMODE)
1023 /*
1024  * Given the old mode and an array of bitcmd structures, apply the operations
1025  * described in the bitcmd structures to the old mode, and return the new mode.
1026  * Note that there is no '=' command; a strict assignment is just a '-' (clear
1027  * bits) followed by a '+' (set bits).
1028  */
1029 static mode_t
1030 getmode(const void * bbox, mode_t omode)
1031  /*@*/
1032 {
1033  const BITCMD *set;
1034  mode_t clrval, newmode, value;
1035 
1036  set = (const BITCMD *)bbox;
1037  newmode = omode;
1038  for (value = 0;; set++)
1039  switch(set->cmd) {
1040  /*
1041  * When copying the user, group or other bits around, we "know"
1042  * where the bits are in the mode so that we can do shifts to
1043  * copy them around. If we don't use shifts, it gets real
1044  * grundgy with lots of single bit checks and bit sets.
1045  */
1046  case 'u':
1047  value = (newmode & S_IRWXU) >> 6;
1048  goto common;
1049 
1050  case 'g':
1051  value = (newmode & S_IRWXG) >> 3;
1052  goto common;
1053 
1054  case 'o':
1055  value = newmode & S_IRWXO;
1056 common: if ((set->cmd2 & CMD2_CLR) != '\0') {
1057  clrval = (set->cmd2 & CMD2_SET) != '\0' ? S_IRWXO : value;
1058  if ((set->cmd2 & CMD2_UBITS) != '\0')
1059  newmode &= ~((clrval<<6) & set->bits);
1060  if ((set->cmd2 & CMD2_GBITS) != '\0')
1061  newmode &= ~((clrval<<3) & set->bits);
1062  if ((set->cmd2 & CMD2_OBITS) != '\0')
1063  newmode &= ~(clrval & set->bits);
1064  }
1065  if ((set->cmd2 & CMD2_SET) != '\0') {
1066  if ((set->cmd2 & CMD2_UBITS) != '\0')
1067  newmode |= (value<<6) & set->bits;
1068  if ((set->cmd2 & CMD2_GBITS) != '\0')
1069  newmode |= (value<<3) & set->bits;
1070  if ((set->cmd2 & CMD2_OBITS) != '\0')
1071  newmode |= value & set->bits;
1072  }
1073  /*@switchbreak@*/ break;
1074 
1075  case '+':
1076  newmode |= set->bits;
1077  /*@switchbreak@*/ break;
1078 
1079  case '-':
1080  newmode &= ~set->bits;
1081  /*@switchbreak@*/ break;
1082 
1083  case 'X':
1084  if (omode & (S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH))
1085  newmode |= set->bits;
1086  /*@switchbreak@*/ break;
1087 
1088  case '\0':
1089  default:
1090 #ifdef SETMODE_DEBUG
1091  (void) printf("getmode:%04o -> %04o\n", omode, newmode);
1092 #endif
1093  return newmode;
1094  }
1095 }
1096 #endif /* !defined(HAVE_GETMODE) */
1097 
1098 #if !defined(HAVE_SETMODE)
1099 #ifdef SETMODE_DEBUG
1100 static void
1101 dumpmode(BITCMD *set)
1102 {
1103  for (; set->cmd; ++set)
1104  (void) printf("cmd: '%c' bits %04o%s%s%s%s%s%s\n",
1105  set->cmd, set->bits, set->cmd2 ? " cmd2:" : "",
1106  set->cmd2 & CMD2_CLR ? " CLR" : "",
1107  set->cmd2 & CMD2_SET ? " SET" : "",
1108  set->cmd2 & CMD2_UBITS ? " UBITS" : "",
1109  set->cmd2 & CMD2_GBITS ? " GBITS" : "",
1110  set->cmd2 & CMD2_OBITS ? " OBITS" : "");
1111 }
1112 #endif
1113 
1114 #define ADDCMD(a, b, c, d) \
1115  if (set >= endset) { \
1116  BITCMD *newset; \
1117  setlen += SET_LEN_INCR; \
1118  newset = realloc(saveset, sizeof(*newset) * setlen); \
1119  if (newset == NULL) { \
1120  if (saveset != NULL) \
1121  free(saveset); \
1122  saveset = NULL; \
1123  return (NULL); \
1124  } \
1125  set = newset + (set - saveset); \
1126  saveset = newset; \
1127  endset = newset + (setlen - 2); \
1128  } \
1129  set = addcmd(set, (a), (b), (c), (d))
1130 
1131 #define STANDARD_BITS (S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO)
1132 
1133 static BITCMD *
1134 addcmd(/*@returned@*/ BITCMD *set, int op, int who, int oparg, unsigned mask)
1135  /*@modifies set @*/
1136 {
1137  switch (op) {
1138  case '=':
1139  set->cmd = '-';
1140  set->bits = who ? who : (int) STANDARD_BITS;
1141  set++;
1142 
1143  op = (int)'+';
1144  /*@fallthrough@*/
1145  case '+':
1146  case '-':
1147  case 'X':
1148  set->cmd = (char)op;
1149  set->bits = (who ? (unsigned)who : mask) & oparg;
1150  break;
1151 
1152  case 'u':
1153  case 'g':
1154  case 'o':
1155  set->cmd = (char)op;
1156  if (who) {
1157  set->cmd2 = (char)( ((who & S_IRUSR) ? CMD2_UBITS : 0) |
1158  ((who & S_IRGRP) ? CMD2_GBITS : 0) |
1159  ((who & S_IROTH) ? CMD2_OBITS : 0));
1160  set->bits = (mode_t)~0;
1161  } else {
1162  set->cmd2 =(char)(CMD2_UBITS | CMD2_GBITS | CMD2_OBITS);
1163  set->bits = mask;
1164  }
1165 
1166  if (oparg == (int)'+')
1167  set->cmd2 |= CMD2_SET;
1168  else if (oparg == (int)'-')
1169  set->cmd2 |= CMD2_CLR;
1170  else if (oparg == (int)'=')
1171  set->cmd2 |= CMD2_SET|CMD2_CLR;
1172  break;
1173  }
1174  return set + 1;
1175 }
1176 
1177 /*
1178  * Given an array of bitcmd structures, compress by compacting consecutive
1179  * '+', '-' and 'X' commands into at most 3 commands, one of each. The 'u',
1180  * 'g' and 'o' commands continue to be separate. They could probably be
1181  * compacted, but it's not worth the effort.
1182  */
1183 static void
1184 compress_mode(/*@out@*/ BITCMD *set)
1185  /*@modifies set @*/
1186 {
1187  BITCMD *nset;
1188  int setbits, clrbits, Xbits, op;
1189 
1190  for (nset = set;;) {
1191  /* Copy over any 'u', 'g' and 'o' commands. */
1192  while ((op = (int)nset->cmd) != (int)'+' && op != (int)'-' && op != (int)'X') {
1193  *set++ = *nset++;
1194  if (!op)
1195  return;
1196  }
1197 
1198  for (setbits = clrbits = Xbits = 0;; nset++) {
1199  if ((op = (int)nset->cmd) == (int)'-') {
1200  clrbits |= nset->bits;
1201  setbits &= ~nset->bits;
1202  Xbits &= ~nset->bits;
1203  } else if (op == (int)'+') {
1204  setbits |= nset->bits;
1205  clrbits &= ~nset->bits;
1206  Xbits &= ~nset->bits;
1207  } else if (op == (int)'X')
1208  Xbits |= nset->bits & ~setbits;
1209  else
1210  /*@innerbreak@*/ break;
1211  }
1212  if (clrbits) {
1213  set->cmd = '-';
1214  set->cmd2 = '\0';
1215  set->bits = clrbits;
1216  set++;
1217  }
1218  if (setbits) {
1219  set->cmd = '+';
1220  set->cmd2 = '\0';
1221  set->bits = setbits;
1222  set++;
1223  }
1224  if (Xbits) {
1225  set->cmd = 'X';
1226  set->cmd2 = '\0';
1227  set->bits = Xbits;
1228  set++;
1229  }
1230  }
1231 }
1232 
1233 /*@-usereleased@*/
1234 /*@null@*/
1235 static void *
1236 setmode(const char * p)
1237  /*@globals fileSystem @*/
1238  /*@modifies fileSystem @*/
1239 {
1240  int perm, who;
1241  char op;
1242  BITCMD *set, *saveset, *endset;
1243  sigset_t sigset, sigoset;
1244  mode_t mask;
1245  int equalopdone = 0;
1246  int permXbits, setlen;
1247  long perml;
1248 
1249  if (!*p)
1250  return (NULL);
1251 
1252  /*
1253  * Get a copy of the mask for the permissions that are mask relative.
1254  * Flip the bits, we want what's not set. Since it's possible that
1255  * the caller is opening files inside a signal handler, protect them
1256  * as best we can.
1257  */
1258  (void) sigfillset(&sigset);
1259  (void) sigprocmask(SIG_BLOCK, &sigset, &sigoset);
1260  (void) umask(mask = umask(0));
1261  mask = ~mask;
1262  (void) sigprocmask(SIG_SETMASK, &sigoset, NULL);
1263 
1264  setlen = SET_LEN + 2;
1265 
1266  if ((set = malloc((unsigned)(sizeof(*set) * setlen))) == NULL)
1267  return (NULL);
1268  saveset = set;
1269  endset = set + (setlen - 2);
1270 
1271  /*
1272  * If an absolute number, get it and return; disallow non-octal digits
1273  * or illegal bits.
1274  */
1275  if (isdigit(*p)) {
1276  perml = strtol(p, NULL, 8);
1277 /*@-unrecog@*/
1278  if (perml < 0 || (perml & ~(STANDARD_BITS|S_ISTXT)))
1279 /*@=unrecog@*/
1280  {
1281  free(saveset);
1282  return (NULL);
1283  }
1284  perm = (int)(mode_t)perml;
1285  while (*++p != '\0')
1286  if (*p < '0' || *p > '7') {
1287  free(saveset);
1288  return (NULL);
1289  }
1290  ADDCMD((int)'=', (int)(STANDARD_BITS|S_ISTXT), perm, (unsigned)mask);
1291  return (saveset);
1292  }
1293 
1294  /*
1295  * Build list of structures to set/clear/copy bits as described by
1296  * each clause of the symbolic mode.
1297  */
1298  for (;;) {
1299  /* First, find out which bits might be modified. */
1300  for (who = 0;; ++p) {
1301  switch (*p) {
1302  case 'a':
1303  who |= STANDARD_BITS;
1304  /*@switchbreak@*/ break;
1305  case 'u':
1306  who |= S_ISUID|S_IRWXU;
1307  /*@switchbreak@*/ break;
1308  case 'g':
1309  who |= S_ISGID|S_IRWXG;
1310  /*@switchbreak@*/ break;
1311  case 'o':
1312  who |= S_IRWXO;
1313  /*@switchbreak@*/ break;
1314  default:
1315  goto getop;
1316  }
1317  }
1318 
1319 getop: if ((op = *p++) != '+' && op != '-' && op != '=') {
1320  free(saveset);
1321  return (NULL);
1322  }
1323  if (op == '=')
1324  equalopdone = 0;
1325 
1326  who &= ~S_ISTXT;
1327  for (perm = 0, permXbits = 0;; ++p) {
1328  switch (*p) {
1329  case 'r':
1330  perm |= S_IRUSR|S_IRGRP|S_IROTH;
1331  /*@switchbreak@*/ break;
1332  case 's':
1333  /*
1334  * If specific bits where requested and
1335  * only "other" bits ignore set-id.
1336  */
1337  if (who == 0 || (who & ~S_IRWXO))
1338  perm |= S_ISUID|S_ISGID;
1339  /*@switchbreak@*/ break;
1340  case 't':
1341  /*
1342  * If specific bits where requested and
1343  * only "other" bits ignore sticky.
1344  */
1345  if (who == 0 || (who & ~S_IRWXO)) {
1346  who |= S_ISTXT;
1347  perm |= S_ISTXT;
1348  }
1349  /*@switchbreak@*/ break;
1350  case 'w':
1351  perm |= S_IWUSR|S_IWGRP|S_IWOTH;
1352  /*@switchbreak@*/ break;
1353  case 'X':
1354  permXbits = (int)(S_IXUSR|S_IXGRP|S_IXOTH);
1355  /*@switchbreak@*/ break;
1356  case 'x':
1357  perm |= S_IXUSR|S_IXGRP|S_IXOTH;
1358  /*@switchbreak@*/ break;
1359  case 'u':
1360  case 'g':
1361  case 'o':
1362  /*
1363  * When ever we hit 'u', 'g', or 'o', we have
1364  * to flush out any partial mode that we have,
1365  * and then do the copying of the mode bits.
1366  */
1367  if (perm) {
1368  ADDCMD((int)op, who, perm, (unsigned)mask);
1369  perm = 0;
1370  }
1371  if (op == '=')
1372  equalopdone = 1;
1373  if (op == '+' && permXbits) {
1374  ADDCMD((int)'X', who, permXbits, (unsigned)mask);
1375  permXbits = 0;
1376  }
1377  ADDCMD((int)*p, who, (int)op, (unsigned)mask);
1378  /*@switchbreak@*/ break;
1379 
1380  default:
1381  /*
1382  * Add any permissions that we haven't already
1383  * done.
1384  */
1385  if (perm || (op == '=' && !equalopdone)) {
1386  if (op == '=')
1387  equalopdone = 1;
1388  ADDCMD((int)op, who, perm, (unsigned)mask);
1389  perm = 0;
1390  }
1391  if (permXbits) {
1392  ADDCMD((int)'X', who, permXbits, (unsigned)mask);
1393  permXbits = 0;
1394  }
1395  goto apply;
1396  }
1397  }
1398 
1399 apply: if (!*p)
1400  break;
1401  if (*p != ',')
1402  goto getop;
1403  ++p;
1404  }
1405  set->cmd = '\0';
1406 #ifdef SETMODE_DEBUG
1407  (void) printf("Before compress_mode()\n");
1408  dumpmode(saveset);
1409 #endif
1410  compress_mode(saveset);
1411 #ifdef SETMODE_DEBUG
1412  (void) printf("After compress_mode()\n");
1413  dumpmode(saveset);
1414 #endif
1415  return saveset;
1416 }
1417 /*@=usereleased@*/
1418 #endif /* !defined(HAVE_SETMODE) */
1419 #endif /* !defined(HAVE_GETMODE) || !defined(HAVE_SETMODE) */
1420 #endif /* __linux__ */
1421 
1422 /*==============================================================*/
1423 
1424 static void
1425 set(char * t, NODE * ip)
1426  /*@globals fileSystem, internalState @*/
1427  /*@modifies t, ip, fileSystem, internalState @*/
1428 {
1429  char *kw;
1430 
1431  for (; (kw = strtok(t, "= \t\n")) != NULL; t = NULL) {
1432  uint32_t needvalue;
1433  enum mtreeKeys_e type = parsekey(kw, &needvalue);
1434  char *val = NULL;
1435  char *ep;
1436 
1437  if (needvalue && (val = strtok(NULL, " \t\n")) == NULL)
1438  mtree_error("missing value");
1439  ip->flags |= type;
1440  switch(type) {
1441  case MTREE_KEYS_CKSUM:
1442  ip->cksum = strtoul(val, &ep, 10);
1443  if (*ep != '\0')
1444  mtree_error("invalid checksum %s", val);
1445  /*@switchbreak@*/ break;
1446  case MTREE_KEYS_FLAGS:
1447 #if defined(HAVE_ST_FLAGS)
1448  if (!strcmp(val, "none")) {
1449  ip->sb.st_flags = 0;
1450  /*@switchbreak@*/ break;
1451  }
1452  { unsigned long fset, fclr;
1453  if (strtofflags(&val, &fset, &fclr))
1454  mtree_error("%s", strerror(errno));
1455  ip->sb.st_flags = fset;
1456  }
1457 #endif
1458  /*@switchbreak@*/ break;
1459  case MTREE_KEYS_GID:
1460  ip->sb.st_gid = strtoul(val, &ep, 10);
1461  if (*ep != '\0')
1462  mtree_error("invalid gid %s", val);
1463  /*@switchbreak@*/ break;
1464  case MTREE_KEYS_GNAME:
1465  if (gnameToGid(val, &ip->sb.st_gid) == -1)
1466  mtree_error("unknown group %s", val);
1467  /*@switchbreak@*/ break;
1468  case MTREE_KEYS_IGN:
1469  /* just set flag bit */
1470  /*@switchbreak@*/ break;
1471  case MTREE_KEYS_MODE:
1472  { mode_t *m;
1473  if ((m = setmode(val)) == NULL)
1474  mtree_error("invalid file mode %s", val);
1475  ip->sb.st_mode = getmode(m, 0);
1476  free(m);
1477  } /*@switchbreak@*/ break;
1478  case MTREE_KEYS_NLINK:
1479  ip->sb.st_nlink = strtoul(val, &ep, 10);
1480  if (*ep != '\0')
1481  mtree_error("invalid link count %s", val);
1482  /*@switchbreak@*/ break;
1483  case MTREE_KEYS_DIGEST:
1484  (void) argiAdd(&ip->algos, -1, (int)needvalue);
1485  (void) argvAdd(&ip->digests, val);
1486  /*@switchbreak@*/ break;
1487  case MTREE_KEYS_SIZE:
1488 /*@-unrecog@*/
1489  ip->sb.st_size = strtoul(val, &ep, 10);
1490 /*@=unrecog@*/
1491  if (*ep != '\0')
1492  mtree_error("invalid size %s", val);
1493  /*@switchbreak@*/ break;
1494  case MTREE_KEYS_SLINK:
1495  ip->slink = xmalloc(strlen(val) + 1);
1496  if (strunvis(ip->slink, val) == -1) {
1497  fprintf(stderr, _("%s: filename (%s) encoded incorrectly\n"),
1498  __progname, val);
1499  /* XXX Mac OS X exits here. */
1500  strcpy(ip->slink, val);
1501  }
1502  /*@switchbreak@*/ break;
1503  case MTREE_KEYS_TIME:
1504 #if defined(TIMEVAL_TO_TIMESPEC)
1505  ip->sb.st_mtimespec.tv_sec = strtoul(val, &ep, 10);
1506  if (*ep != '.')
1507  mtree_error("invalid time %s", val);
1508  val = ep + 1;
1509  ip->sb.st_mtimespec.tv_nsec = strtoul(val, &ep, 10);
1510  if (*ep != '\0')
1511  mtree_error("invalid time %s", val);
1512 #else
1513  ip->sb.st_mtime = strtoul(val, &ep, 10);
1514  if (*ep != '.')
1515  mtree_error("invalid time %s", val);
1516  val = ep + 1;
1517  (void) strtoul(val, &ep, 10);
1518  if (*ep != '\0')
1519  mtree_error("invalid time %s", val);
1520 #endif
1521  /*@switchbreak@*/ break;
1522  case MTREE_KEYS_TYPE:
1523  switch(*val) {
1524  case 'b':
1525  if (!strcmp(val, "block"))
1526  ip->type = F_BLOCK;
1527  /*@innerbreak@*/ break;
1528  case 'c':
1529  if (!strcmp(val, "char"))
1530  ip->type = F_CHAR;
1531  /*@innerbreak@*/ break;
1532  case 'd':
1533  if (!strcmp(val, "dir"))
1534  ip->type = F_DIR;
1535  /*@innerbreak@*/ break;
1536  case 'f':
1537  if (!strcmp(val, "file"))
1538  ip->type = F_FILE;
1539  if (!strcmp(val, "fifo"))
1540  ip->type = F_FIFO;
1541  /*@innerbreak@*/ break;
1542  case 'l':
1543  if (!strcmp(val, "link"))
1544  ip->type = F_LINK;
1545  /*@innerbreak@*/ break;
1546  case 's':
1547  if (!strcmp(val, "socket"))
1548  ip->type = F_SOCK;
1549  /*@innerbreak@*/ break;
1550  default:
1551  mtree_error("unknown file type %s", val);
1552  }
1553  /*@switchbreak@*/ break;
1554  case MTREE_KEYS_UID:
1555  ip->sb.st_uid = strtoul(val, &ep, 10);
1556  if (*ep != '\0')
1557  mtree_error("invalid uid %s", val);
1558  /*@switchbreak@*/ break;
1559  case MTREE_KEYS_UNAME:
1560  if (unameToUid(val, &ip->sb.st_uid) == -1)
1561  mtree_error("unknown user %s", val);
1562  /*@switchbreak@*/ break;
1563  case MTREE_KEYS_NONE:
1564  case MTREE_KEYS_DONE:
1565  case MTREE_KEYS_MAGIC:
1566  case MTREE_KEYS_VISIT:
1567  case MTREE_KEYS_NOCHANGE:
1568  case MTREE_KEYS_OPT:
1569  ip->flags &= ~type; /* XXX clean up "can't happen" cruft? */
1570  /*@notreached@*/ /*@switchbreak@*/ break;
1571  }
1572  }
1573 }
1574 
1575 static void
1576 unset(char * t, NODE * ip)
1577  /*@globals fileSystem, internalState @*/
1578  /*@modifies t, ip, fileSystem, internalState @*/
1579 {
1580  char *p;
1581 
1582  while ((p = strtok(t, "\n\t ")) != NULL)
1583  ip->flags &= ~parsekey(p, NULL);
1584 }
1585 
1586 #define KF_ISSET(_keys, _KEY) ((_keys) & (MTREE_KEYS_##_KEY))
1587 
1588 /* XXX todo: only fts->lineo used. lightweight struct {fn,fp,lineno} instead. */
1589 NODE *
1590 mtreeSpec(rpmfts fts, FILE * fp)
1591 {
1592  NODE *centry = NULL;
1593  NODE *last = NULL;
1594  char *p;
1595  NODE ginfo;
1596  NODE *root = NULL;
1597  NODE *forest = NULL;
1598  int c_cur = 0;
1599  int c_next = 0;
1600  char buf[2048];
1601 
1602  if (fp == NULL)
1603  fp = stdin;
1604 
1605  memset(&ginfo, 0, sizeof(ginfo));
1606  for (fts->lineno = 1; fgets(buf, (int)sizeof(buf), fp) != NULL;
1607  ++fts->lineno, c_cur = c_next, c_next = 0)
1608  {
1609  /* Skip empty lines. */
1610  if (buf[0] == '\n')
1611  continue;
1612 
1613  /* Find end of line. */
1614  if ((p = strchr(buf, '\n')) == NULL)
1615  mtree_error("line %d too long", fts->lineno);
1616 
1617  /* See if next line is continuation line. */
1618  if (p[-1] == '\\') {
1619  --p;
1620  c_next = 1;
1621  }
1622 
1623  /* Null-terminate the line. */
1624  *p = '\0';
1625 
1626  /* Skip leading whitespace. */
1627  for (p = buf; *p && isspace(*p); ++p);
1628 
1629  /* If nothing but whitespace or comment char, continue. */
1630  if (*p == '\0' || *p == '#')
1631  continue;
1632 
1633 #ifdef DEBUG
1634  (void)fprintf(stderr, "line %3d: {%s}\n", fts->lineno, p);
1635 #endif
1636  if (c_cur) {
1637  set(p, centry);
1638  continue;
1639  }
1640 
1641  /* Grab file name, "$", "set", or "unset". */
1642  if ((p = strtok(p, "\n\t ")) == NULL)
1643  mtree_error("missing field");
1644 
1645  if (p[0] == '/')
1646  switch(p[1]) {
1647  case 's':
1648  if (strcmp(p + 1, "set"))
1649  /*@switchbreak@*/ break;
1650  set(NULL, &ginfo);
1651  continue;
1652  case 'u':
1653  if (strcmp(p + 1, "unset"))
1654  /*@switchbreak@*/ break;
1655  unset(NULL, &ginfo);
1656  continue;
1657  }
1658 
1659 #if !defined(_RPMFI_INTERNAL) /* XXX *.rpm/ specs include '/' in names. */
1660  if (strchr(p, '/') != NULL)
1661  mtree_error("slash character in file name");
1662 #endif
1663 
1664  if (!strcmp(p, "..")) {
1665  /* Don't go up, if haven't gone down. */
1666  if (root == NULL)
1667  goto noparent;
1668  if (last->type != F_DIR || KF_ISSET(last->flags, DONE)) {
1669  if (last == root)
1670  goto noparent;
1671  last = last->parent;
1672  }
1673  last->flags |= MTREE_KEYS_DONE;
1674  continue;
1675 
1676 noparent: mtree_error("no parent node");
1677  }
1678 
1679  /* XXX sizeof(*centry) includes room for final '\0' */
1680  centry = xcalloc(1, sizeof(*centry) + strlen(p));
1681  *centry = ginfo; /* structure assignment */
1682 #define MAGIC "?*["
1683  if (strpbrk(p, MAGIC) != NULL)
1684  centry->flags |= MTREE_KEYS_MAGIC;
1685  if (strunvis(centry->name, p) == -1) {
1686  fprintf(stderr, _("%s: filename (%s) encoded incorrectly\n"),
1687  __progname, p);
1688  strcpy(centry->name, p);
1689  }
1690  set(NULL, centry);
1691 
1692  if (root == NULL) {
1693  last = root = centry;
1694  root->parent = root;
1695  if (forest == NULL)
1696  forest = root;
1697  } else if (centry->name[0] == '.' && centry->name[1] == '\0') {
1698  centry->prev = root;
1699  last = root = root->next = centry;
1700  root->parent = root;
1701  } else if (last->type == F_DIR && !KF_ISSET(last->flags, DONE)) {
1702  centry->parent = last;
1703  last = last->child = centry;
1704  } else {
1705  centry->parent = last->parent;
1706  centry->prev = last;
1707  last = last->next = centry;
1708  }
1709  }
1710  return forest;
1711 }
1712 
1713 /*==============================================================*/
1714 
1715 /*@observer@*/
1716 static const char *
1717 ftype(unsigned type)
1718  /*@*/
1719 {
1720  switch(type) {
1721  case F_BLOCK: return "block";
1722  case F_CHAR: return "char";
1723  case F_DIR: return "dir";
1724  case F_FIFO: return "fifo";
1725  case F_FILE: return "file";
1726  case F_LINK: return "link";
1727  case F_SOCK: return "socket";
1728  default: return "unknown";
1729  }
1730  /*@notreached@*/
1731 }
1732 
1733 /*@observer@*/
1734 static const char *
1735 inotype(mode_t mode)
1736  /*@*/
1737 {
1738  switch(mode & S_IFMT) {
1739  case S_IFBLK: return "block";
1740  case S_IFCHR: return "char";
1741  case S_IFDIR: return "dir";
1742  case S_IFIFO: return "fifo";
1743  case S_IFREG: return "file";
1744  case S_IFLNK: return "link";
1745 /*@-unrecog@*/
1746  case S_IFSOCK: return "socket";
1747 /*@=unrecog@*/
1748  default: return "unknown";
1749  }
1750  /*@notreached@*/
1751 }
1752 
1753 /*-
1754  * Copyright (c) 2003 Poul-Henning Kamp
1755  * All rights reserved.
1756  *
1757  * Redistribution and use in source and binary forms, with or without
1758  * modification, are permitted provided that the following conditions
1759  * are met:
1760  * 1. Redistributions of source code must retain the above copyright
1761  * notice, this list of conditions and the following disclaimer.
1762  * 2. Redistributions in binary form must reproduce the above copyright
1763  * notice, this list of conditions and the following disclaimer in the
1764  * documentation and/or other materials provided with the distribution.
1765  *
1766  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1767  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1768  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1769  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1770  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1771  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1772  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1773  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1774  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1775  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1776  * SUCH DAMAGE.
1777  */
1778 
1779 #define FF(a, b, c, d) \
1780  (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
1781 #define FS(a, b, c, d) \
1782  (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
1783 #define FM(a, b, c, d) \
1784  (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
1785 
1786 static void
1787 shownode(NODE *n, enum mtreeKeys_e keys, const char *path)
1788  /*@globals fileSystem @*/
1789  /*@modifies fileSystem @*/
1790 {
1791  printf("%s%s %s", path, n->name, ftype((unsigned)n->type));
1792  if (KF_ISSET(keys, CKSUM))
1793  printf(" cksum=%lu", (unsigned long) n->cksum);
1794  if (KF_ISSET(keys, GID))
1795  printf(" gid=%lu", (unsigned long) n->sb.st_gid);
1796  if (KF_ISSET(keys, GNAME)) {
1797  const char * gname = gidToGname(n->sb.st_gid);
1798  if (gname != NULL)
1799  printf(" gname=%s", gname);
1800  else
1801  printf(" gid=%lu", (unsigned long) n->sb.st_gid);
1802  }
1803  if (KF_ISSET(keys, MODE))
1804  printf(" mode=%o", (unsigned) n->sb.st_mode);
1805  if (KF_ISSET(keys, NLINK))
1806  printf(" nlink=%lu", (unsigned long) n->sb.st_nlink);
1807 /*@-duplicatequals@*/
1808  if (KF_ISSET(keys, SIZE))
1809  printf(" size=%llu", (unsigned long long)n->sb.st_size);
1810 /*@=duplicatequals@*/
1811  if (KF_ISSET(keys, UID))
1812  printf(" uid=%lu", (unsigned long) n->sb.st_uid);
1813  if (KF_ISSET(keys, UNAME)) {
1814  const char * uname = uidToUname(n->sb.st_uid);
1815  if (uname != NULL)
1816  printf(" uname=%s", uname);
1817  else
1818  printf(" uid=%lu", (unsigned long) n->sb.st_uid);
1819  }
1820 
1821  /* Output all the digests. */
1822  if (KF_ISSET(keys, DIGEST)) {
1823  int i;
1824 
1825  if (n->algos != NULL)
1826  for (i = 0; i < (int) n->algos->nvals; i++) {
1827  uint32_t algo = n->algos->vals[i];
1828  const char * tagname = algo2tagname(algo);
1829  if (tagname != NULL)
1830  printf(" %s=%s", tagname, n->digests[i]);
1831  }
1832  }
1833 
1834 #if defined(HAVE_ST_FLAGS)
1835  if (KF_ISSET(keys, FLAGS))
1836  printf(" flags=%s", flags_to_string(n->sb.st_flags));
1837 #endif
1838  printf("\n");
1839 }
1840 
1841 static int
1842 mismatch(NODE *n1, NODE *n2, enum mtreeKeys_e differ, const char *path)
1843  /*@globals fileSystem @*/
1844  /*@modifies fileSystem @*/
1845 {
1846  enum mtreeKeys_e keys = _rpmfts->keys;
1847 
1848  if (n2 == NULL) {
1849  shownode(n1, differ, path);
1850  return 1;
1851  }
1852  if (n1 == NULL) {
1853  printf("\t");
1854  shownode(n2, differ, path);
1855  return 1;
1856  }
1857  if (!(differ & keys))
1858  return 0;
1859  printf("\t\t");
1860  shownode(n1, differ, path);
1861  printf("\t\t");
1862  shownode(n2, differ, path);
1863  return 1;
1864 }
1865 
1866 static int
1867 compare_nodes(NODE *n1, NODE *n2, const char *path)
1868  /*@globals fileSystem @*/
1869  /*@modifies n1, n2, fileSystem @*/
1870 {
1871  enum mtreeKeys_e differs = MTREE_KEYS_NONE;
1872  int xx;
1873 
1874  if (n1 != NULL && n1->type == F_LINK)
1875  n1->flags &= ~MTREE_KEYS_MODE;
1876  if (n2 != NULL && n2->type == F_LINK)
1877  n2->flags &= ~MTREE_KEYS_MODE;
1878  if (n1 == NULL && n2 != NULL) {
1879  differs = n2->flags;
1880  xx = mismatch(n1, n2, differs, path);
1881  return 1;
1882  }
1883  if (n1 != NULL && n2 == NULL) {
1884  differs = n1->flags;
1885  xx = mismatch(n1, n2, differs, path);
1886  return 1;
1887  }
1888  if (n1->type != n2->type) {
1889  differs = MTREE_KEYS_NONE; /* XXX unneeded */
1890  xx = mismatch(n1, n2, differs, path);
1891  return 1;
1892  }
1893  if (FF(n1, n2, MTREE_KEYS_CKSUM, cksum))
1894  differs |= MTREE_KEYS_CKSUM;
1895  if (FF(n1, n2, MTREE_KEYS_GID, sb.st_gid))
1896  differs |= MTREE_KEYS_GID;
1897  if (FF(n1, n2, MTREE_KEYS_GNAME, sb.st_gid))
1898  differs |= MTREE_KEYS_GNAME;
1899  if (FF(n1, n2, MTREE_KEYS_MODE, sb.st_mode))
1900  differs |= MTREE_KEYS_MODE;
1901  if (FF(n1, n2, MTREE_KEYS_NLINK, sb.st_nlink))
1902  differs |= MTREE_KEYS_NLINK;
1903  if (FF(n1, n2, MTREE_KEYS_SIZE, sb.st_size))
1904  differs |= MTREE_KEYS_SIZE;
1905 
1906  if (FS(n1, n2, MTREE_KEYS_SLINK, slink))
1907  differs |= MTREE_KEYS_SLINK;
1908 
1909 /*@-type@*/
1910  if (FM(n1, n2, MTREE_KEYS_TIME, sb.st_mtimespec))
1911  differs |= MTREE_KEYS_TIME;
1912 /*@=type@*/
1913  if (FF(n1, n2, MTREE_KEYS_UID, sb.st_uid))
1914  differs |= MTREE_KEYS_UID;
1915  if (FF(n1, n2, MTREE_KEYS_UNAME, sb.st_uid))
1916  differs |= MTREE_KEYS_UNAME;
1917 
1918  /* Compare all the digests. */
1919  if (KF_ISSET(n1->flags, DIGEST) || KF_ISSET(n2->flags, DIGEST)) {
1920  if ((KF_ISSET(n1->flags, DIGEST) != KF_ISSET(n2->flags, DIGEST))
1921  || (n1->algos == NULL || n2->algos == NULL)
1922  || (n1->algos->nvals != n2->algos->nvals))
1923  differs |= MTREE_KEYS_DIGEST;
1924  else {
1925  int i;
1926 
1927  for (i = 0; i < (int) n1->algos->nvals; i++) {
1928  if ((n1->algos->vals[i] == n2->algos->vals[i])
1929  && !strcmp(n1->digests[i], n2->digests[i]))
1930  continue;
1931  differs |= MTREE_KEYS_DIGEST;
1932  break;
1933  }
1934  }
1935  }
1936 
1937 #if defined(HAVE_ST_FLAGS)
1938  if (FF(n1, n2, MTREE_KEYS_FLAGS, sb.st_flags))
1939  differs |= MTREE_KEYS_FLAGS;
1940 #endif
1941 
1942  if (differs) {
1943  xx = mismatch(n1, n2, differs, path);
1944  return 1;
1945  }
1946  return 0;
1947 }
1948 
1949 static int
1950 mtreeSWalk(NODE *t1, NODE *t2, const char *path)
1951  /*@globals fileSystem @*/
1952  /*@modifies t1, t2, fileSystem @*/
1953 {
1954  NODE *c1 = (t1 != NULL ? t1->child : NULL);
1955  NODE *c2 = (t2 != NULL ? t2->child : NULL);
1956  int r = 0;
1957 
1958  while (c1 != NULL || c2 != NULL) {
1959  NODE *n1, *n2;
1960  char *np;
1961  int i;
1962 
1963  n1 = (c1 != NULL ? c1->next : NULL);
1964  n2 = (c2 != NULL ? c2->next : NULL);
1965  if (c1 != NULL && c2 != NULL) {
1966  if (c1->type != F_DIR && c2->type == F_DIR) {
1967  n2 = c2;
1968  c2 = NULL;
1969  } else if (c1->type == F_DIR && c2->type != F_DIR) {
1970  n1 = c1;
1971  c1 = NULL;
1972  } else {
1973  i = strcmp(c1->name, c2->name);
1974  if (i > 0) {
1975  n1 = c1;
1976  c1 = NULL;
1977  } else if (i < 0) {
1978  n2 = c2;
1979  c2 = NULL;
1980  }
1981  }
1982  }
1983 /*@-noeffectuncon -unrecog@*/
1984  if (c1 == NULL && c2->type == F_DIR) {
1985  if (asprintf(&np, "%s%s/", path, c2->name)) {
1986  perror("asprintf");
1987  }
1988  i = mtreeSWalk(c1, c2, np);
1989  free(np);
1990  i += compare_nodes(c1, c2, path);
1991  } else if (c2 == NULL && c1->type == F_DIR) {
1992  if (asprintf(&np, "%s%s/", path, c1->name)) {
1993  perror("asprintf");
1994  }
1995  i = mtreeSWalk(c1, c2, np);
1996  free(np);
1997  i += compare_nodes(c1, c2, path);
1998  } else if (c1 == NULL || c2 == NULL) {
1999  i = compare_nodes(c1, c2, path);
2000  } else if (c1->type == F_DIR && c2->type == F_DIR) {
2001  if (asprintf(&np, "%s%s/", path, c1->name)) {
2002  perror("asprintf");
2003  }
2004  i = mtreeSWalk(c1, c2, np);
2005  free(np);
2006  i += compare_nodes(c1, c2, path);
2007  } else {
2008  i = compare_nodes(c1, c2, path);
2009  }
2010 /*@=noeffectuncon =unrecog@*/
2011  r += i;
2012  c1 = n1;
2013  c2 = n2;
2014  }
2015  return r;
2016 }
2017 
2018 int
2019 mtreeVSpec(rpmfts fts)
2020 {
2021  NODE * root1 = mtreeSpec(fts, fts->spec1);
2022  NODE * root2 = mtreeSpec(fts, fts->spec2);
2023  int rval = 0;
2024 
2025  rval = mtreeSWalk(root1, root2, "");
2026  rval += compare_nodes(root1, root2, "");
2027  return (rval > 0 ? MISMATCHEXIT : 0);
2028 }
2029 
2030 /*==============================================================*/
2031 
2032 /*@observer@*/
2033 static const char *
2034 rlink(const char * name)
2035  /*@globals h_errno, fileSystem, internalState @*/
2036  /*@modifies fileSystem, internalState @*/
2037 
2038 {
2039  static char lbuf[MAXPATHLEN];
2040  int len;
2041 
2042  if ((len = Readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
2043  mtree_error("%s: %s", name, strerror(errno));
2044  lbuf[len] = '\0';
2045  return lbuf;
2046 }
2047 
2048 #define SKIPDOTSLASH(_f) ((_f)[0] == '.' && (_f)[1] == '/' ? (_f) + 2 : (_f))
2049 
2050 #define COMPAREINDENTNAMELEN 8
2051 #define LABEL \
2052  if (!label++) { \
2053  (void) printf(_("%s changed\n"), SKIPDOTSLASH(p->fts_path)); \
2054  tab = "\t"; \
2055  }
2056 
2057 /*@observer@*/
2058 static const char * algo2name(uint32_t algo)
2059  /*@*/
2060 {
2061  switch (algo) {
2062  case PGPHASHALGO_MD5: return "MD5";
2063  case PGPHASHALGO_SHA1: return "SHA1";
2064  case PGPHASHALGO_RIPEMD160: return "RIPEMD160";
2065  case PGPHASHALGO_MD2: return "MD2";
2066  case PGPHASHALGO_TIGER192: return "TIGER192";
2067  case PGPHASHALGO_HAVAL_5_160: return "HAVAL-5-160";
2068  case PGPHASHALGO_SHA256: return "SHA256";
2069  case PGPHASHALGO_SHA384: return "SHA384";
2070  case PGPHASHALGO_SHA512: return "SHA512";
2071 
2072  case PGPHASHALGO_MD4: return "MD4";
2073  case PGPHASHALGO_RIPEMD128: return "RIPEMD128";
2074  case PGPHASHALGO_CRC32: return "CRC32";
2075  case PGPHASHALGO_ADLER32: return "ADLER32";
2076  case PGPHASHALGO_CRC64: return "CRC64";
2077  case PGPHASHALGO_JLU32: return "JLU32";
2078  case PGPHASHALGO_SHA224: return "SHA224";
2079  case PGPHASHALGO_RIPEMD256: return "RIPEMD256";
2080  case PGPHASHALGO_RIPEMD320: return "RIPEMD320";
2081  case PGPHASHALGO_SALSA10: return "SALSA10";
2082  case PGPHASHALGO_SALSA20: return "SALSA20";
2083 
2084  default: return "Unknown";
2085  }
2086  /*@notreached@*/
2087 }
2088 
2089 static int
2090 compare(rpmfts fts, NODE *const s)
2091  /*@globals errno, h_errno, fileSystem, internalState @*/
2092  /*@modifies errno, fileSystem, internalState @*/
2093 
2094 {
2095  const char * name = s->name;
2096  FTSENT *const p = fts->p;
2097  const char * fts_accpath = p->fts_accpath;
2098  struct stat *const st = p->fts_statp;
2099  enum mtreeKeys_e keys = s->flags;
2100  int label = 0;
2101  const char *cp;
2102  const char *tab = "";
2103 
2104  switch(s->type) {
2105  case F_BLOCK:
2106  if (!S_ISBLK(st->st_mode))
2107  goto typeerr;
2108  break;
2109  case F_CHAR:
2110  if (!S_ISCHR(st->st_mode))
2111  goto typeerr;
2112  break;
2113  case F_DIR:
2114  if (!S_ISDIR(st->st_mode))
2115  goto typeerr;
2116  break;
2117  case F_FIFO:
2118  if (!S_ISFIFO(st->st_mode))
2119  goto typeerr;
2120  break;
2121  case F_FILE:
2122  if (!S_ISREG(st->st_mode))
2123  goto typeerr;
2124  break;
2125  case F_LINK:
2126  if (!S_ISLNK(st->st_mode))
2127  goto typeerr;
2128  break;
2129  case F_SOCK:
2130 /*@-unrecog@*/
2131  if (!S_ISSOCK(st->st_mode)) {
2132 typeerr: LABEL;
2133  (void) printf(_("\ttype expected %s found %s)\n"),
2134  ftype((unsigned)s->type), inotype(st->st_mode));
2135  }
2136 /*@=unrecog@*/
2137  break;
2138  }
2139 
2140  /* Set the uid/gid first, then set the mode. */
2141  if ((KF_ISSET(keys, UID) || KF_ISSET(keys, UNAME)) && s->sb.st_uid != st->st_uid) {
2142  LABEL;
2143  (void) printf(_("%s%s expected %lu found %lu"), tab, "user",
2144  (unsigned long)s->sb.st_uid, (unsigned long)st->st_uid);
2145  if (MF_ISSET(UPDATE)) {
2146  if (Chown(fts_accpath, s->sb.st_uid, -1))
2147  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2148  else
2149  (void) printf(_(" modified)\n"));
2150  } else
2151  (void) printf("\n");
2152  tab = "\t";
2153  }
2154  if ((KF_ISSET(keys, GID) || KF_ISSET(keys, GNAME)) && s->sb.st_gid != st->st_gid) {
2155  LABEL;
2156  (void) printf(_("%s%s expected %lu found %lu"), tab, "gid",
2157  (unsigned long)s->sb.st_gid, (unsigned long)st->st_gid);
2158  if (MF_ISSET(UPDATE)) {
2159  if (Chown(fts_accpath, -1, s->sb.st_gid))
2160  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2161  else
2162  (void) printf(_(" modified)\n"));
2163  } else
2164  (void) printf("\n");
2165  tab = "\t";
2166  }
2167  if (KF_ISSET(keys, MODE) && s->sb.st_mode != (st->st_mode & MBITS)) {
2168  if (MF_ISSET(LOOSE)) {
2169  mode_t tmode = s->sb.st_mode;
2170  mode_t mode = (st->st_mode & MBITS);
2171 
2172  /*
2173  * if none of the suid/sgid/etc bits are set,
2174  * then if the mode is a subset of the target,
2175  * skip.
2176  */
2177  if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO))
2178  || (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
2179  if ((mode | tmode) == tmode)
2180  goto skip;
2181  }
2182  LABEL;
2183  (void) printf(_("%s%s expected %#o found %#o"), tab, "permissions",
2184  (unsigned)s->sb.st_mode, (unsigned)(st->st_mode & MBITS));
2185  if (MF_ISSET(UPDATE)) {
2186  if (Chmod(fts_accpath, s->sb.st_mode))
2187  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2188  else
2189  (void) printf(_(" modified)\n"));
2190  } else
2191  (void) printf("\n");
2192  tab = "\t";
2193  skip:
2194  ;
2195  }
2196  if (KF_ISSET(keys, NLINK) && s->type != F_DIR &&
2197  s->sb.st_nlink != st->st_nlink)
2198  {
2199  LABEL;
2200  (void) printf(_("%s%s expected %lu found %lu)\n"), tab, "link_count",
2201  (unsigned long)s->sb.st_nlink, (unsigned long)st->st_nlink);
2202  tab = "\t";
2203  }
2204  if (KF_ISSET(keys, SIZE) && s->sb.st_size != st->st_size) {
2205  LABEL;
2206 /*@-duplicatequals@*/
2207  (void) printf(_("%s%s expected %llu found %llu\n"), tab, "size",
2208  (unsigned long long)s->sb.st_size,
2209  (unsigned long long)st->st_size);
2210 /*@=duplicatequals@*/
2211  tab = "\t";
2212  }
2213  /*
2214  * XXX
2215  * Since utimes(2) only takes a timeval, there's no point in
2216  * comparing the low bits of the timespec nanosecond field. This
2217  * will only result in mismatches that we can never fix.
2218  *
2219  * Doesn't display microsecond differences.
2220  */
2221  if (KF_ISSET(keys, TIME)) {
2222  struct timeval tv[2];
2223 
2224 /*@-noeffectuncon -unrecog @*/
2225 #if defined(TIMESPEC_TO_TIMEVAL)
2226  TIMESPEC_TO_TIMEVAL(&tv[0], &s->sb.st_mtimespec);
2227  TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
2228 #else
2229  tv[0].tv_sec = (long)s->sb.st_mtime;
2230  tv[0].tv_usec = 0L;
2231  tv[1].tv_sec = (long)st->st_mtime;
2232  tv[1].tv_usec = 0L;
2233 #endif
2234 /*@=noeffectuncon =unrecog @*/
2235  if (tv[0].tv_sec != tv[1].tv_sec || tv[0].tv_usec != tv[1].tv_usec) {
2236  time_t t1 = (time_t)tv[0].tv_sec;
2237  time_t t2 = (time_t)tv[1].tv_sec;
2238  LABEL;
2239  (void) printf(_("%s%s expected %.24s "), tab, "modification time", ctime(&t1));
2240  (void) printf(_("found %.24s"), ctime(&t2));
2241  if (MF_ISSET(TOUCH)) {
2242  tv[1] = tv[0];
2243  if (Utimes(fts_accpath, tv))
2244  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2245  else
2246  (void) printf(_(" modified\n"));
2247  } else
2248  (void) printf("\n");
2249  tab = "\t";
2250  }
2251  }
2252 
2253  /* Any digests to calculate? */
2254  if (KF_ISSET(keys, CKSUM) || s->algos != NULL) {
2255  FD_t fd = Fopen(fts_accpath, "r.ufdio");
2256  uint32_t vlen, val;
2257  int i;
2258 
2259  if (fd == NULL || Ferror(fd)) {
2260  LABEL;
2261  (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
2262  goto cleanup;
2263  }
2264 
2265  /* Setup all digest calculations. Reversed order is effete ... */
2266  if (s->algos != NULL)
2267  for (i = s->algos->nvals; i-- > 0;)
2268  fdInitDigest(fd, s->algos->vals[i], 0);
2269 
2270  /* Compute the cksum and digests. */
2271  if (KF_ISSET(keys, CKSUM))
2272  i = crc(fd, &val, &vlen);
2273  else {
2274  char buffer[16 * 1024];
2275  while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
2276  {};
2277  i = (Ferror(fd) ? 1 : 0);
2278  }
2279  if (i) {
2280  LABEL;
2281  (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
2282  goto cleanup;
2283  }
2284 
2285  /* Verify cksum. */
2286  if (KF_ISSET(keys, CKSUM)) {
2287  if (s->cksum != val) {
2288  LABEL;
2289  (void) printf(_("%s%s expected %lu found %lu\n"), tab, "cksum",
2290  (unsigned long) s->cksum, (unsigned long) val);
2291  tab = "\t";
2292  }
2293  }
2294 
2295  /* Verify all the digests. */
2296  if (s->algos != NULL)
2297  for (i = 0; i < (int) s->algos->nvals; i++) {
2298  static int asAscii = 1;
2299  uint32_t algo = s->algos->vals[i];
2300  const char * digest = NULL;
2301  size_t digestlen = 0;
2302 
2303  fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
2304 assert(digest != NULL);
2305  if (strcmp(digest, s->digests[i])) {
2306  LABEL;
2307  printf(_("%s%s expected %s found %s\n"), tab, algo2name(algo),
2308  s->digests[i], digest);
2309  tab = "\t";
2310  }
2311  digest = _free(digest);
2312  digestlen = 0;
2313  }
2314 
2315  /* Accumulate statistics and clean up. */
2316 cleanup:
2317  if (fd != NULL) {
2318  (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
2319  (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
2320  (void) Fclose(fd);
2321  fd = NULL;
2322  }
2323  }
2324 
2325  if (KF_ISSET(keys, SLINK) && strcmp(cp = rlink(name), s->slink)) {
2326  LABEL;
2327  (void) printf(_("%s%s expected %s found %s\n"), tab, "link_ref",
2328  cp, s->slink);
2329  }
2330 #if defined(HAVE_ST_FLAGS)
2331  if (KF_ISSET(keys, FLAGS) && s->sb.st_flags != st->st_flags) {
2332  char *fflags;
2333 
2334  LABEL;
2335  fflags = fflagstostr(s->sb.st_flags);
2336  (void) printf(_("%s%s expected \"%s\""), tab, "flags", fflags);
2337  fflags = _free(fflags);
2338 
2339  fflags = fflagstostr(st->st_flags);
2340  (void) printf(_(" found \"%s\""), fflags);
2341  fflags = _free(fflags);
2342 
2343  if (MF_ISSET(UPDATE)) {
2344  if (chflags(fts_accpath, s->sb.st_flags))
2345  (void) printf(" not modified: %s)\n", strerror(errno));
2346  else
2347  (void) printf(" modified)\n");
2348  }
2349  } else {
2350  (void) printf("\n");
2351  tab = "\t";
2352  }
2353 #endif
2354  return label;
2355 }
2356 
2357 /*==============================================================*/
2358 
2359 #define _FTSCALLOC(_p, _n) \
2360  if ((_n) > 0) { \
2361  (_p) = _free(_p); (_p) = xcalloc((_n), sizeof(*(_p))); \
2362  }
2363 
2364 static int
2365 mtreeVisitD(rpmfts fts)
2366  /*@globals fileSystem, internalState @*/
2367  /*@modifies fts, fileSystem, internalState @*/
2368 {
2369  enum mtreeKeys_e keys = fts->keys;
2370  const FTSENT *const parent = fts->p;
2371  const FTSENT * p;
2372  struct stat sb;
2373  gid_t maxgid = 0;
2374  uid_t maxuid = 0;
2375  mode_t maxmode = 0;
2376 #if defined(HAVE_ST_FLAGS)
2377  unsigned long maxflags = 0;
2378 #endif
2379 
2380  /* Retrieve all directory members. */
2381  if ((p = Fts_children(fts->t, 0)) == NULL) {
2382  if (errno)
2383  mtree_error("%s: %s", SKIPDOTSLASH(parent->fts_path),
2384  strerror(errno));
2385  return 1;
2386  }
2387 
2388  sb = fts->sb; /* structure assignment */
2389  _FTSCALLOC(fts->g, fts->maxg);
2390  _FTSCALLOC(fts->m, fts->maxm);
2391  _FTSCALLOC(fts->u, fts->maxu);
2392 #if defined(HAVE_ST_FLAGS)
2393  _FTSCALLOC(fts->f, fts->maxf);
2394 #endif
2395 
2396  /* Find the most common stat(2) settings for the next directory. */
2397  for (; p != NULL; p = p->fts_link) {
2398  struct stat *const st = p->fts_statp;
2399 
2400  if (MF_ISSET(DIRSONLY) || !S_ISDIR(st->st_mode))
2401  continue;
2402 
2403  if (fts->m != NULL)
2404  { mode_t st_mode = st->st_mode & MBITS;
2405  if (st_mode < fts->maxm && ++fts->m[st_mode] > maxmode) {
2406  sb.st_mode = st_mode;
2407  maxmode = fts->m[st_mode];
2408  }
2409  }
2410  if (fts->g != NULL)
2411  if (st->st_gid < fts->maxg && ++fts->g[st->st_gid] > maxgid) {
2412  sb.st_gid = st->st_gid;
2413  maxgid = fts->g[st->st_gid];
2414  }
2415  if (fts->u != NULL)
2416  if (st->st_uid < fts->maxu && ++fts->u[st->st_uid] > maxuid) {
2417  sb.st_uid = st->st_uid;
2418  maxuid = fts->u[st->st_uid];
2419  }
2420 #if defined(HAVE_ST_FLAGS)
2421  /*
2422  * XXX
2423  * note that the below will break when file flags
2424  * are extended beyond the first 4 bytes of each
2425  * half word of the flags
2426  */
2427 #define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
2428  if (fts->f != NULL)
2429  { unsigned long st_flags = FLAGS2IDX(st->st_flags);
2430  if (st_flags < fts->maxf && ++fts->f[st_flags] > maxflags) {
2431  /* XXX note st->st_flags saved, not FLAGS2IDX(st->st_flags) */
2432  sb.st_flags = st->st_flags;
2433  maxflags = fts->f[st_flags];
2434  }
2435  }
2436 #endif
2437  }
2438 
2439  /*
2440  * If the /set record is the same as the last one we do not need to output
2441  * a new one. So first we check to see if anything changed. Note that we
2442  * always output a /set record for the first directory.
2443  */
2444  if (((KF_ISSET(keys, UNAME) || KF_ISSET(keys, UID)) && (fts->sb.st_uid != sb.st_uid))
2445  || ((KF_ISSET(keys, GNAME) || KF_ISSET(keys, GID)) && (fts->sb.st_gid != sb.st_gid))
2446  || (KF_ISSET(keys, MODE) && (fts->sb.st_mode != sb.st_mode))
2447 #if defined(HAVE_ST_FLAGS)
2448  || (KF_ISSET(keys, FLAGS) && (fts->sb.st_flags != sb.st_flags))
2449 #endif
2450  || fts->sb_is_valid == 0)
2451  {
2452  fts->sb_is_valid = 1;
2453  if (MF_ISSET(DIRSONLY))
2454  (void) printf("/set type=dir");
2455  else
2456  (void) printf("/set type=file");
2457  if (KF_ISSET(keys, UNAME)) {
2458  const char * uname = uidToUname(sb.st_uid);
2459  if (uname != NULL)
2460  (void) printf(" uname=%s", uname);
2461  else if (MF_ISSET(WARN))
2462  fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
2463  __progname, (unsigned long) sb.st_uid);
2464  else
2465  mtree_error("could not get uname for uid=%lu",
2466  (unsigned long)sb.st_uid);
2467  }
2468  if (KF_ISSET(keys, UID))
2469  (void) printf(" uid=%lu", (unsigned long)sb.st_uid);
2470  if (KF_ISSET(keys, GNAME)) {
2471  const char * gname = gidToGname(sb.st_gid);
2472  if (gname != NULL)
2473  (void) printf(" gname=%s", gname);
2474  else if (MF_ISSET(WARN))
2475  fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
2476  __progname, (unsigned long) sb.st_gid);
2477  else
2478  mtree_error("could not get gname for gid=%lu",
2479  (unsigned long) sb.st_gid);
2480  }
2481  if (KF_ISSET(keys, GID))
2482  (void) printf(" gid=%lu", (unsigned long)sb.st_gid);
2483  if (KF_ISSET(keys, MODE))
2484  (void) printf(" mode=%#o", (unsigned)sb.st_mode);
2485  if (KF_ISSET(keys, NLINK))
2486  (void) printf(" nlink=1");
2487 #if defined(HAVE_ST_FLAGS)
2488  if (KF_ISSET(keys, FLAGS)) {
2489  const char * fflags = flags_to_string(sb.st_flags);
2490  (void) printf(" flags=%s", fflags);
2491  fflags = _free(fflags);
2492  }
2493 #endif
2494  (void) printf("\n");
2495  fts->sb = sb; /* structure assignment */
2496  }
2497  return (0);
2498 }
2499 
2500 #define CWALKINDENTNAMELEN 15
2501 #define MAXLINELEN 80
2502 
2503 
2504 static void
2505 output(int indent, int * offset, const char * fmt, ...)
2506  /*@globals fileSystem @*/
2507  /*@modifies *offset, fileSystem @*/
2508 {
2509  char buf[1024];
2510  va_list ap;
2511 
2512  va_start(ap, fmt);
2513  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
2514  va_end(ap);
2515 
2516  if (*offset + strlen(buf) > MAXLINELEN - 3) {
2517  (void)printf(" \\\n%*s", CWALKINDENTNAMELEN + indent, "");
2518  *offset = CWALKINDENTNAMELEN + indent;
2519  }
2520  *offset += printf(" %s", buf) + 1;
2521 }
2522 
2523 static void
2524 mtreeVisitF(rpmfts fts)
2525  /*@globals errno, h_errno, fileSystem, internalState @*/
2526  /*@modifies errno, fileSystem, internalState @*/
2527 {
2528  enum mtreeKeys_e keys = fts->keys;
2529  const char * fts_accpath = fts->p->fts_accpath;
2530  unsigned short fts_info = fts->p->fts_info;
2531  struct stat *const st = fts->p->fts_statp;
2532  int indent = (MF_ISSET(INDENT) ? fts->p->fts_level * 4 : 0);
2533  int offset;
2534 
2535  { const char * fts_name = fts->p->fts_name;
2536  size_t fts_namelen = fts->p->fts_namelen;
2537  char * escname;
2538 
2539  /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
2540  if (fts->p->fts_level == 0 && fts_namelen == 0) {
2541  fts_name = ".";
2542  fts_namelen = sizeof(".") - 1;
2543  }
2544 
2545  escname = xmalloc(fts_namelen * 4 + 1);
2546  /* XXX TODO: Mac OS X uses VIS_GLOB as well */
2547  (void) strvis(escname, fts_name, VIS_WHITE | VIS_OCTAL);
2548 
2549  if (MF_ISSET(INDENT) || S_ISDIR(st->st_mode))
2550  offset = printf("%*s%s", indent, "", escname);
2551  else
2552  offset = printf("%*s %s", indent, "", escname);
2553  escname = _free(escname);
2554  }
2555 
2556  if (offset > (CWALKINDENTNAMELEN + indent))
2557  offset = MAXLINELEN;
2558  else
2559  offset += printf("%*s", (CWALKINDENTNAMELEN + indent) - offset, "");
2560 
2561  if (!S_ISREG(st->st_mode) && !MF_ISSET(DIRSONLY))
2562  output(indent, &offset, "type=%s", inotype(st->st_mode));
2563  if (st->st_uid != fts->sb.st_uid) {
2564  if (KF_ISSET(keys, UNAME)) {
2565  const char * uname = uidToUname(st->st_uid);
2566  if (uname != NULL)
2567  output(indent, &offset, "uname=%s", uname);
2568  else if (MF_ISSET(WARN))
2569  fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
2570  __progname, (unsigned long) st->st_uid);
2571  else
2572  mtree_error("could not get uname for uid=%lu",
2573  (unsigned long)st->st_uid);
2574  }
2575  if (KF_ISSET(keys, UID))
2576  output(indent, &offset, "uid=%u", st->st_uid);
2577  }
2578  if (st->st_gid != fts->sb.st_gid) {
2579  if (KF_ISSET(keys, GNAME)) {
2580  const char * gname = gidToGname(st->st_gid);
2581  if (gname != NULL)
2582  output(indent, &offset, "gname=%s", gname);
2583  else if (MF_ISSET(WARN))
2584  fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
2585  __progname, (unsigned long) st->st_gid);
2586  else
2587  mtree_error("Could not get gname for gid=%lu",
2588  (unsigned long) st->st_gid);
2589  }
2590  if (KF_ISSET(keys, GID))
2591  output(indent, &offset, "gid=%lu", (unsigned long)st->st_gid);
2592  }
2593  if (KF_ISSET(keys, MODE) && (st->st_mode & MBITS) != fts->sb.st_mode)
2594  output(indent, &offset, "mode=%#o", (st->st_mode & MBITS));
2595  if (KF_ISSET(keys, NLINK) && st->st_nlink != 1)
2596  output(indent, &offset, "nlink=%lu", (unsigned long)st->st_nlink);
2597  if (KF_ISSET(keys, SIZE) && S_ISREG(st->st_mode))
2598  output(indent, &offset, "size=%llu", (unsigned long long)st->st_size);
2599  if (KF_ISSET(keys, TIME)) {
2600  struct timeval tv;
2601 #if defined(TIMESPEC_TO_TIMEVAL)
2602  TIMESPEC_TO_TIMEVAL(&tv, &st->st_mtimespec);
2603 #else
2604  tv.tv_sec = (long)st->st_mtime;
2605  tv.tv_usec = 0L;
2606 #endif
2607  output(indent, &offset, "time=%lu.%lu",
2608  (unsigned long) tv.tv_sec,
2609  (unsigned long) tv.tv_usec);
2610  }
2611 
2612  /* Only files can have digests. */
2613  if (S_ISREG(st->st_mode)) {
2614 
2615  /* Any digests to calculate? */
2616  if (KF_ISSET(keys, CKSUM) || fts->algos != NULL) {
2617  FD_t fd = Fopen(fts_accpath, "r.ufdio");
2618  uint32_t len, val;
2619  int i;
2620 
2621  if (fd == NULL || Ferror(fd)) {
2622 #ifdef NOTYET /* XXX can't exit in a library API. */
2623  (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
2624  __progname, fts_accpath, Fstrerror(fd));
2625  goto cleanup;
2626 #else
2627  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2628  /*@notreached@*/
2629 #endif
2630  }
2631 
2632  /* Setup all digest calculations. Reversed order is effete ... */
2633  if (fts->algos != NULL)
2634  for (i = fts->algos->nvals; i-- > 0;)
2635  fdInitDigest(fd, fts->algos->vals[i], 0);
2636 
2637  /* Compute the cksum and digests. */
2638  if (KF_ISSET(keys, CKSUM))
2639  i = crc(fd, &val, &len);
2640  else {
2641  char buffer[16 * 1024];
2642  while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
2643  {};
2644  i = (Ferror(fd) ? 1 : 0);
2645  }
2646  if (i) {
2647 #ifdef NOTYET /* XXX can't exit in a library API. */
2648  (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
2649  __progname, fts_accpath, Fstrerror(fd));
2650 #else
2651  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2652  /*@notreached@*/
2653 #endif
2654  goto cleanup;
2655  }
2656 
2657  /* Output cksum. */
2658  if (KF_ISSET(keys, CKSUM)) {
2659  output(indent, &offset, "cksum=%lu", (unsigned long)val);
2660  }
2661 
2662  /* Output all the digests. */
2663  if (fts->algos != NULL)
2664  for (i = 0; i < (int) fts->algos->nvals; i++) {
2665  static int asAscii = 1;
2666  const char * digest = NULL;
2667  size_t digestlen = 0;
2668  uint32_t algo;
2669 
2670  algo = fts->algos->vals[i];
2671  fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
2672 #ifdef NOTYET /* XXX can't exit in a library API. */
2673 assert(digest != NULL);
2674 #else
2675  if (digest == NULL)
2676  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2677 #endif
2678  { const char * tagname = algo2tagname(algo);
2679  if (tagname != NULL)
2680  output(indent, &offset, "%s=%s", tagname, digest);
2681  }
2682  digest = _free(digest);
2683  digestlen = 0;
2684  }
2685 
2686 cleanup: /* Accumulate statistics and clean up. */
2687  if (fd != NULL) {
2688  (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
2689  (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
2690  (void) Fclose(fd);
2691  fd = NULL;
2692  }
2693  }
2694  }
2695 
2696  if (KF_ISSET(keys, SLINK) && (fts_info == FTS_SL || fts_info == FTS_SLNONE))
2697  {
2698  const char * name = rlink(fts_accpath);
2699  char * escname = xmalloc(strlen(name) * 4 + 1);
2700  (void) strvis(escname, name, VIS_WHITE | VIS_OCTAL);
2701  output(indent, &offset, "link=%s", escname);
2702  escname = _free(escname);
2703  }
2704 
2705  if (KF_ISSET(keys, FLAGS) && !S_ISLNK(st->st_mode)) {
2706 #if defined(HAVE_ST_FLAGS)
2707  char * fflags = fflagstostr(st->st_flags);
2708 
2709  if (fflags != NULL && fflags[0] != '\0')
2710  output(indent, &offset, "flags=%s", fflags);
2711  else
2712  output(indent, &offset, "flags=none");
2713  free(fflags);
2714 #else
2715  output(indent, &offset, "flags=none");
2716 #endif
2717  }
2718  (void) putchar('\n');
2719 }
2720 
2721 /*==============================================================*/
2722 
2723 /*
2724  * Copyright 2000 Massachusetts Institute of Technology
2725  *
2726  * Permission to use, copy, modify, and distribute this software and
2727  * its documentation for any purpose and without fee is hereby
2728  * granted, provided that both the above copyright notice and this
2729  * permission notice appear in all copies, that both the above
2730  * copyright notice and this permission notice appear in all
2731  * supporting documentation, and that the name of M.I.T. not be used
2732  * in advertising or publicity pertaining to distribution of the
2733  * software without specific, written prior permission. M.I.T. makes
2734  * no representations about the suitability of this software for any
2735  * purpose. It is provided "as is" without express or implied
2736  * warranty.
2737  *
2738  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
2739  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
2740  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2741  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2742  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2743  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2744  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2745  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2746  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2747  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2748  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2749  * SUCH DAMAGE.
2750  */
2751 
2752 /*
2753  * We're assuming that there won't be a whole lot of excludes,
2754  * so it's OK to use a stupid algorithm.
2755  */
2756 
2757 /*@mayexit@*/
2758 static void
2759 mtreeReadExcludes(const char * fn)
2760  /*@globals excludes, h_errno, fileSystem, internalState @*/
2761  /*@modifies excludes, fileSystem, internalState @*/
2762 {
2763  FD_t fd = Fopen(fn, "r.fpio");
2764  FILE *fp;
2765  char buffer[16 * 1024];
2766 
2767  if (fd == NULL || Ferror(fd) || (fp = fdGetFILE(fd)) == NULL) {
2768  fprintf(stderr, _("%s: open of %s failed: %s\n"), __progname,
2769  fn, Fstrerror(fd));
2770  if (fd != NULL) (void) Fclose(fd);
2771  exit(EXIT_FAILURE);
2772  }
2773 
2774  while (fgets(buffer, (int)sizeof(buffer), fp) != NULL) {
2775  struct exclude *e;
2776  char * line;
2777  size_t len;
2778 
2779  buffer[sizeof(buffer)-1] = '\0';
2780  for (line = buffer; *line != '\0'; line++)
2781  if (strchr(" \t\n\r", line[1]) == NULL) /*@innerbreak@*/ break;
2782  if (*line == '\0' || *line == '#')
2783  continue;
2784  for (len = strlen(line); len > 0; len--)
2785  if (strchr(" \t\n\r", line[len-1]) == NULL) /*@innerbreak@*/ break;
2786  if (len == 0)
2787  continue;
2788 
2789  e = xmalloc(sizeof(*e));
2790  e->glob = xstrdup(line);
2791  e->pathname = (strchr(line, '/') != NULL ? 1 : 0);
2792 /*@-immediatetrans@*/
2793  RPM_LIST_INSERT_HEAD(&excludes, e, link);
2794 /*@=immediatetrans@*/
2795  }
2796  if (fd != NULL)
2797  (void) Fclose(fd);
2798 /*@-compmempass -nullstate @*/
2799  return;
2800 /*@=compmempass =nullstate @*/
2801 }
2802 
2803 static int
2804 mtreeCheckExcludes(const char *fname, const char *path)
2805  /*@*/
2806 {
2807  struct exclude *e;
2808 
2809  /* fnmatch(3) has a funny return value convention... */
2810 #define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
2811 
2812 /*@-predboolptr@*/
2813  RPM_LIST_FOREACH(e, &excludes, link) {
2814  if ((e->pathname && MATCH(e->glob, path)) || MATCH(e->glob, fname))
2815  return 1;
2816  }
2817 /*@=predboolptr@*/
2818  return 0;
2819 }
2820 
2821 /*==============================================================*/
2822 
2823 static int
2824 dsort(const FTSENT ** a, const FTSENT ** b)
2825  /*@*/
2826 {
2827  if (S_ISDIR((*a)->fts_statp->st_mode)) {
2828  if (!S_ISDIR((*b)->fts_statp->st_mode))
2829  return 1;
2830  } else if (S_ISDIR((*b)->fts_statp->st_mode))
2831  return -1;
2832  return strcmp((*a)->fts_name, (*b)->fts_name);
2833 }
2834 
2835 #if defined(_RPMFI_INTERNAL)
2836 
2842 static int chkSuffix(const char * fn, const char * suffix)
2843  /*@*/
2844 {
2845  size_t flen = strlen(fn);
2846  size_t slen = strlen(suffix);
2847  return (flen > slen && !strcmp(fn + flen - slen, suffix));
2848 }
2849 
2850 static int _rpmfiStat(const char * path, struct stat * st)
2851  /*@globals fileSystem @*/
2852  /*@modifies fileSystem @*/
2853 {
2854  rpmfts fts = _rpmfts;
2855  rpmfi fi = _rpmfts->fi;
2856  size_t len = strlen(fts->paths[0]);
2857  int rc;
2858 
2859  rc = rpmfiStat(fi, path+len, st);
2860 
2861 if (_fts_debug)
2862 fprintf(stderr, "*** _rpmfiStat(%s, %p) fi %p rc %d\n", path+len, st, fi, rc);
2863 
2864  return rc;
2865 }
2866 
2867 static int _rpmfiClosedir(/*@only@*/ DIR * dir)
2868  /*@globals fileSystem @*/
2869  /*@modifies dir, fileSystem @*/
2870 {
2871  rpmfi fi = _rpmfts->fi;
2872 
2873 if (_fts_debug)
2874 fprintf(stderr, "*** _rpmfiClosedir(%p) fi %p\n", dir, fi);
2875 
2876  return avClosedir(dir);
2877 }
2878 
2879 static /*@null@*/ struct dirent * _rpmfiReaddir(DIR * dir)
2880  /*@globals fileSystem @*/
2881  /*@modifies fileSystem @*/
2882 {
2883  rpmfi fi = _rpmfts->fi;
2884  struct dirent * dp = (struct dirent *) avReaddir(dir);
2885 
2886 if (_fts_debug)
2887 fprintf(stderr, "*** _rpmfiReaddir(%p) fi %p %p \"%s\"\n", dir, fi, dp, (dp != NULL ? dp->d_name : ""));
2888 
2889 /*@-dependenttrans@*/
2890  return dp;
2891 /*@=dependenttrans@*/
2892 }
2893 
2894 static /*@null@*/
2895 uint8_t * rpmfiParentDirNotWithin(rpmfi fi)
2896  /*@*/
2897 {
2898  size_t * dnlens = xmalloc(fi->dc * sizeof(*dnlens));
2899  uint8_t * noparent = memset(xmalloc(fi->dc), 1, fi->dc);
2900  int i, j;
2901 
2902  for (i = 0; i < (int)fi->dc; i++)
2903  dnlens[i] = strlen(fi->dnl[i]);
2904 
2905  /* Mark parent directories that are not contained within same package. */
2906  for (i = 0; i < (int)fi->fc; i++) {
2907  size_t dnlen, bnlen;
2908 
2909  if (!S_ISDIR(fi->fmodes[i]))
2910  continue;
2911 
2912  dnlen = dnlens[fi->dil[i]];
2913  bnlen = strlen(fi->bnl[i]);
2914 
2915  for (j = 0; j < (int)fi->dc; j++) {
2916 
2917  if (!noparent[j] || j == (int)fi->dil[i])
2918  /*@innercontinue@*/ continue;
2919  if (dnlens[j] != (dnlen+bnlen+1))
2920  /*@innercontinue@*/ continue;
2921  if (strncmp(fi->dnl[j], fi->dnl[fi->dil[i]], dnlen))
2922  /*@innercontinue@*/ continue;
2923  if (strncmp(fi->dnl[j]+dnlen, fi->bnl[i], bnlen))
2924  /*@innercontinue@*/ continue;
2925  if (fi->dnl[j][dnlen+bnlen] != '/' || fi->dnl[j][dnlen+bnlen+1] != '\0')
2926  /*@innercontinue@*/ continue;
2927 
2928  /* This parent directory is contained within the package. */
2929  noparent[j] = (uint8_t)0;
2930  /*@innerbreak@*/ break;
2931  }
2932  }
2933  dnlens = _free(dnlens);
2934  return noparent;
2935 }
2936 
2937 static Header rpmftsReadHeader(rpmfts fts, const char * path)
2938  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
2939  /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
2940 {
2941  FD_t fd = Fopen(path, "r.ufdio");
2942  Header h = NULL;
2943 
2944  if (fd != NULL) {
2945  /* XXX what if path needs expansion? */
2946  rpmRC rpmrc = rpmReadPackageFile(fts->ts, fd, path, &h);
2947 
2948  (void) Fclose(fd);
2949 
2950  switch (rpmrc) {
2951  case RPMRC_NOTFOUND:
2952  /* XXX Read a package manifest. Restart ftswalk on success. */
2953  case RPMRC_FAIL:
2954  default:
2955  (void)headerFree(h);
2956  h = NULL;
2957  break;
2958  case RPMRC_NOTTRUSTED:
2959  case RPMRC_NOKEY:
2960  case RPMRC_OK:
2961  break;
2962  }
2963  }
2964  return h;
2965 }
2966 
2967 static /*@null@*/ rpmfi rpmftsLoadFileInfo(rpmfts fts, const char * path)
2968  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
2969  /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
2970 {
2971  char * fn = xstrdup(path);
2972  size_t nb = strlen(fn);
2973  Header h = NULL;
2974 
2975  fn[nb-1] = '\0'; /* XXX trim pesky trailing '/' */
2976  h = rpmftsReadHeader(fts, fn);
2977  fn = _free(fn);
2978 
2979  if (h != NULL) {
2980  fts->fi = rpmfiNew(fts->ts, h, RPMTAG_BASENAMES, 0);
2981  (void)headerFree(h);
2982  h = NULL;
2983  }
2984  return fts->fi;
2985 }
2986 
2987 static /*@null@*/ DIR * _rpmfiOpendir(const char * path)
2988  /*@globals _rpmfts, rpmGlobalMacroContext, h_errno,
2989  fileSystem, internalState @*/
2990  /*@modifies _rpmfts, rpmGlobalMacroContext,
2991  fileSystem, internalState @*/
2992 {
2993  rpmfts fts = _rpmfts;
2994  DIR * dir = NULL;
2995 
2996  if (fts->ts == NULL)
2997  fts->ts = rpmtsCreate();
2998 
2999  if (fts->fi == NULL) {
3000  rpmfi fi = rpmftsLoadFileInfo(fts, path);
3001  uint8_t * noparent = rpmfiParentDirNotWithin(fi);
3002  uint16_t * fmodes = xcalloc(rpmfiFC(fi)+1, sizeof(*fmodes));
3003  const char ** fnames = NULL;
3004  int ac = 0;
3005  int i;
3006 
3007  /* Collect top level files/dirs from the package. */
3008  fi = rpmfiInit(fi, 0);
3009  while ((i = rpmfiNext(fi)) >= 0) {
3010  int xx;
3011  if (!S_ISDIR(fi->fmodes[i]) && !noparent[fi->dil[i]])
3012  continue;
3013  xx = argvAdd(&fnames, rpmfiFN(fi));
3014  fmodes[ac++] = fi->fmodes[i];
3015  }
3016 
3017  dir = (DIR *) avOpendir(path, fnames, fmodes);
3018 
3019  fnames = argvFree(fnames);
3020  fmodes = _free(fmodes);
3021  noparent = _free(noparent);
3022 
3023  } else {
3024  const char * dn = path + strlen(fts->paths[0]);
3025 
3026  dir = rpmfiOpendir(fts->fi, dn);
3027  }
3028 
3029 if (_fts_debug)
3030 fprintf(stderr, "*** _rpmfiOpendir(%s) dir %p\n", path, dir);
3031 
3032  return dir;
3033 }
3034 
3035 #define ALIGNBYTES (__alignof__ (long double) - 1)
3036 #define ALIGN(p) (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
3037 
3038 static FTSENT *
3039 fts_alloc(FTS * sp, const char * name, int namelen)
3040  /*@*/
3041 {
3042  register FTSENT *p;
3043  size_t len;
3044 
3045  /*
3046  * The file name is a variable length array and no stat structure is
3047  * necessary if the user has set the nostat bit. Allocate the FTSENT
3048  * structure, the file name and the stat structure in one chunk, but
3049  * be careful that the stat structure is reasonably aligned. Since the
3050  * fts_name field is declared to be of size 1, the fts_name pointer is
3051  * namelen + 2 before the first possible address of the stat structure.
3052  */
3053  len = sizeof(*p) + namelen;
3054 /*@-sizeoftype@*/
3055  if (!(sp->fts_options & FTS_NOSTAT))
3056  len += sizeof(*p->fts_statp) + ALIGNBYTES;
3057 /*@=sizeoftype@*/
3058  p = xmalloc(len);
3059 
3060  /* Copy the name and guarantee NUL termination. */
3061  memmove(p->fts_name, name, namelen);
3062  p->fts_name[namelen] = '\0';
3063 
3064 /*@-sizeoftype@*/
3065  if (!(sp->fts_options & FTS_NOSTAT))
3066  p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
3067 /*@=sizeoftype@*/
3068  p->fts_namelen = namelen;
3069  p->fts_path = sp->fts_path;
3070  p->fts_errno = 0;
3071  p->fts_flags = 0;
3072  p->fts_instr = FTS_NOINSTR;
3073  p->fts_number = 0;
3074  p->fts_pointer = NULL;
3075  return (p);
3076 }
3077 
3078 static void _rpmfiSetFts(rpmfts fts)
3079  /*@globals h_errno, fileSystem, internalState @*/
3080  /*@modifies fts, fileSystem, internalState @*/
3081 {
3082  char *const * argv = (char *const *) fts->paths;
3083  FTS * sp = fts->t;
3084 #ifdef NOTYET
3085  int (*compar) (const FTSENT **, const FTSENT **) = sp->compar;
3086 #endif
3087  register FTSENT *p, *root;
3088  register int nitems;
3089  FTSENT *parent = NULL;
3090  FTSENT *tmp = NULL;
3091  size_t len;
3092 
3093 if (_fts_debug)
3094 fprintf(stderr, "*** _rpmfiSetFts(%p)\n", fts);
3095 
3096 /*@-type@*/
3097  sp->fts_opendir = _rpmfiOpendir;
3098  sp->fts_readdir = _rpmfiReaddir;
3099  sp->fts_closedir = _rpmfiClosedir;
3100  sp->fts_stat = _rpmfiStat;
3101  sp->fts_lstat = _rpmfiStat;
3102 /*@=type@*/
3103 
3104  /* Allocate/initialize root's parent. */
3105  if (*argv != NULL) {
3106  parent = fts_alloc(sp, "", 0);
3107  parent->fts_level = FTS_ROOTPARENTLEVEL;
3108  }
3109 
3110  /* Allocate/initialize root(s). */
3111  for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
3112  len = strlen(*argv);
3113 
3114  p = fts_alloc(sp, *argv, (int)len);
3115  p->fts_level = FTS_ROOTLEVEL;
3116  p->fts_parent = parent;
3117  p->fts_accpath = p->fts_name;
3118 #ifdef NOTYET
3119  p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
3120 
3121  /* Command-line "." and ".." are real directories. */
3122  if (p->fts_info == FTS_DOT)
3123  p->fts_info = FTS_D;
3124 
3125 #else
3126  p->fts_name[len-1] = '\0';
3127  { struct stat * st = p->fts_statp;
3128  int xx = Stat(p->fts_accpath, st);
3129  xx = xx;
3130  st->st_mode &= ~S_IFMT;
3131  st->st_mode |= S_IFDIR;
3132  p->fts_dev = st->st_dev;
3133  p->fts_ino = st->st_ino;
3134  p->fts_nlink = st->st_nlink;
3135  }
3136  p->fts_name[len-1] = '/';
3137  p->fts_info = FTS_D;
3138 #endif
3139 
3140 #ifdef NOTYET
3141  /*
3142  * If comparison routine supplied, traverse in sorted
3143  * order; otherwise traverse in the order specified.
3144  */
3145  if (compar) {
3146  p->fts_link = root;
3147  root = p;
3148  } else
3149 #endif
3150  {
3151  p->fts_link = NULL;
3152  if (root == NULL)
3153  tmp = root = p;
3154  else {
3155  if (tmp != NULL) /* XXX can't happen */
3156  tmp->fts_link = p;
3157  tmp = p;
3158  }
3159  }
3160  }
3161 #ifdef NOTYET
3162  if (compar && nitems > 1)
3163  root = fts_sort(sp, root, nitems);
3164 #endif
3165 
3166  /*
3167  * Allocate a dummy pointer and make fts_read think that we've just
3168  * finished the node before the root(s); set p->fts_info to FTS_INIT
3169  * so that everything about the "current" node is ignored.
3170  */
3171  sp->fts_cur = _free(sp->fts_cur);
3172  sp->fts_cur = fts_alloc(sp, "", 0);
3173  sp->fts_cur->fts_link = root;
3174  sp->fts_cur->fts_info = FTS_INIT;
3175 
3176  return;
3177 }
3178 #endif
3179 
3180 int
3181 mtreeCWalk(rpmfts fts)
3182 {
3183 #if defined(_RPMFI_INTERNAL)
3184  int isrpm = chkSuffix(fts->paths[0], ".rpm/");
3185 #else
3186  int isrpm = 0;
3187 #endif
3188  const char * empty = NULL;
3189  char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
3190  int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
3191  int rval = 0;
3192 
3193  fts->t = Fts_open(paths, ftsoptions, dsort);
3194  if (fts->t == NULL)
3195  mtree_error("Fts_open: %s", strerror(errno));
3196 
3197 #if defined(_RPMFI_INTERNAL)
3198  if (isrpm) {
3199  fts->keys &= ~MTREE_KEYS_SLINK; /* XXX no rpmfiReadlink yet. */
3200  _rpmfiSetFts(fts);
3201  }
3202 #endif
3203 
3204  while ((fts->p = Fts_read(fts->t)) != NULL) {
3205  int indent = 0;
3206  if (MF_ISSET(INDENT))
3207  indent = fts->p->fts_level * 4;
3208  if (mtreeCheckExcludes(fts->p->fts_name, fts->p->fts_path)) {
3209  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3210  continue;
3211  }
3212  switch(fts->p->fts_info) {
3213  case FTS_D:
3214  if (!MF_ISSET(DIRSONLY))
3215  (void) printf("\n");
3216  if (!MF_ISSET(NOCOMMENT))
3217  (void) printf("# %s\n", fts->p->fts_path);
3218  (void) mtreeVisitD(fts);
3219  mtreeVisitF(fts);
3220  /*@switchbreak@*/ break;
3221  case FTS_DP:
3222  if (!MF_ISSET(NOCOMMENT) && (fts->p->fts_level > 0))
3223  (void) printf("%*s# %s\n", indent, "", fts->p->fts_path);
3224  (void) printf("%*s..\n", indent, "");
3225  if (!MF_ISSET(DIRSONLY))
3226  (void) printf("\n");
3227  /*@switchbreak@*/ break;
3228  case FTS_DNR:
3229  case FTS_ERR:
3230  case FTS_NS:
3231  (void) fprintf(stderr, "%s: %s: %s\n", __progname,
3232  fts->p->fts_path, strerror(fts->p->fts_errno));
3233  /*@switchbreak@*/ break;
3234  default:
3235  if (!MF_ISSET(DIRSONLY))
3236  mtreeVisitF(fts);
3237  /*@switchbreak@*/ break;
3238  }
3239  }
3240  (void) Fts_close(fts->t);
3241  fts->p = NULL;
3242  fts->t = NULL;
3243  return rval;
3244 }
3245 
3246 /*==============================================================*/
3247 
3248 void
3249 mtreeMiss(rpmfts fts, /*@null@*/ NODE * p, char * tail)
3250 {
3251  int create;
3252  char *tp;
3253  const char *type;
3254 
3255  for (; p != NULL; p = p->next) {
3256  /* XXX Mac OS X doesn't do this. */
3257  if (KF_ISSET(p->flags, OPT) && !KF_ISSET(p->flags, VISIT))
3258  continue;
3259  if (p->type != F_DIR && (MF_ISSET(DIRSONLY) || KF_ISSET(p->flags, VISIT)))
3260  continue;
3261  (void) strcpy(tail, p->name);
3262  if (!KF_ISSET(p->flags, VISIT)) {
3263  /* Don't print missing message if file exists as a
3264  symbolic link and the -q flag is set. */
3265  struct stat sb;
3266 
3267  if (MF_ISSET(QUIET) && Stat(fts->path, &sb) == 0)
3268  p->flags |= MTREE_KEYS_VISIT;
3269  else
3270  (void) printf(_("missing: %s"), fts->path);
3271  }
3272  if (p->type != F_DIR && p->type != F_LINK) {
3273  (void) putchar('\n');
3274  continue;
3275  }
3276 
3277  create = 0;
3278  type = (p->type == F_LINK ? "symlink" : "directory");
3279  if (!KF_ISSET(p->flags, VISIT) && MF_ISSET(UPDATE)) {
3280  if (!(KF_ISSET(p->flags, UID) && KF_ISSET(p->flags, UNAME)))
3281  (void) printf(_(" (%s not created: user not specified)"), type);
3282  else if (!(KF_ISSET(p->flags, GID) || KF_ISSET(p->flags, GNAME)))
3283  (void) printf(_(" (%s not created: group not specified))"), type);
3284  else if (p->type == F_LINK) {
3285  if (Symlink(p->slink, fts->path))
3286  (void) printf(_(" (%s not created: %s)\n"), type,
3287  strerror(errno));
3288  else
3289  (void) printf(_(" (%s created)\n"), type);
3290  if (lchown(fts->path, p->sb.st_uid, p->sb.st_gid) == -1) {
3291  const char * what;
3292  int serr = errno;
3293 
3294  if (p->sb.st_uid == (uid_t)-1)
3295  what = "group";
3296  else if (lchown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
3297  what = "user & group";
3298  else {
3299  what = "user";
3300  errno = serr;
3301  }
3302  (void) printf(_("%s: %s not modified: %s\n"),
3303  fts->path, what, strerror(errno));
3304  }
3305  continue;
3306  } else if (!KF_ISSET(p->flags, MODE))
3307  (void) printf(_(" (%s not created: mode not specified)"), type);
3308  else if (Mkdir(fts->path, S_IRWXU))
3309  (void) printf(_(" (%s not created: %s)"),type, strerror(errno));
3310  else {
3311  create = 1;
3312  (void) printf(_(" (%s created)"), type);
3313  }
3314  }
3315 
3316  if (!KF_ISSET(p->flags, VISIT))
3317  (void) putchar('\n');
3318 
3319  for (tp = tail; *tp != '\0'; ++tp);
3320  *tp = '/';
3321  mtreeMiss(fts, p->child, tp + 1);
3322  *tp = '\0';
3323 
3324  if (!create)
3325  continue;
3326  if (Chown(fts->path, p->sb.st_uid, p->sb.st_gid)) {
3327  const char * what;
3328  int serr = errno;
3329 
3330  if (p->sb.st_uid == (uid_t)-1)
3331  what = "group";
3332  else if (Chown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
3333  what = "user & group";
3334  else {
3335  what = "user";
3336  errno = serr;
3337  }
3338  (void) printf(_("%s: %s not modified: %s\n"),
3339  fts->path, what, strerror(errno));
3340  continue;
3341  }
3342  if (Chmod(fts->path, p->sb.st_mode))
3343  (void) printf(_("%s: permissions not set: %s\n"),
3344  fts->path, strerror(errno));
3345 #if defined(HAVE_ST_FLAGS)
3346  if (KF_ISSET(p->flags, FLAGS) && p->sb.st_flags != 0 &&
3347  chflags(fts->path, p->sb.st_flags))
3348  (void) printf(_("%s: file flags not set: %s\n"),
3349  fts->path, strerror(errno));
3350 #endif
3351  }
3352 }
3353 
3354 int
3355 mtreeVWalk(rpmfts fts)
3356 {
3357 #if defined(_RPMFI_INTERNAL)
3358  int isrpm = chkSuffix(fts->paths[0], ".rpm/");
3359 #else
3360  int isrpm = 0;
3361 #endif
3362  const char * empty = NULL;
3363  char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
3364  int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
3365  NODE * level = NULL;
3366  NODE * root = NULL;
3367  int specdepth = 0;
3368  int rval = 0;
3369 
3370  fts->t = Fts_open((char *const *)paths, ftsoptions, NULL);
3371  if (fts->t == NULL)
3372  mtree_error("Fts_open: %s", strerror(errno));
3373 
3374 #if defined(_RPMFI_INTERNAL)
3375  if (isrpm) {
3376  fts->keys &= ~MTREE_KEYS_SLINK; /* XXX no rpmfiReadlink yet. */
3377  _rpmfiSetFts(fts);
3378  }
3379 #endif
3380 
3381  while ((fts->p = Fts_read(fts->t)) != NULL) {
3382  const char * fts_name = fts->p->fts_name;
3383  size_t fts_namelen = fts->p->fts_namelen;
3384 
3385 #if 0
3386 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);
3387 #endif
3388  /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
3389  if (fts->p->fts_level == 0 && fts_namelen == 0) {
3390  fts_name = ".";
3391  fts_namelen = sizeof(".") - 1;
3392  }
3393 
3394  if (mtreeCheckExcludes(fts_name, fts->p->fts_path)) {
3395  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3396  continue;
3397  }
3398  switch(fts->p->fts_info) {
3399  case FTS_D:
3400  case FTS_SL:
3401  if (fts->p->fts_level == 0) {
3402 assert(specdepth == 0);
3403  if (root == NULL)
3404  level = root = fts->root;
3405  else if (root->next != fts->root)
3406  level = root = root->next;
3407 assert(level == level->parent);
3408  }
3409  /*@switchbreak@*/ break;
3410  case FTS_DP:
3411  if (specdepth > fts->p->fts_level) {
3412  for (level = level->parent; level->prev != NULL; level = level->prev);
3413  --specdepth;
3414  }
3415  continue;
3416  case FTS_DNR:
3417  case FTS_ERR:
3418  case FTS_NS:
3419  (void) fprintf(stderr, "%s: %s: %s\n", __progname,
3420  SKIPDOTSLASH(fts->p->fts_path),
3421  strerror(fts->p->fts_errno));
3422  continue;
3423  default:
3424  if (MF_ISSET(DIRSONLY))
3425  continue;
3426  }
3427 
3428  if (specdepth == fts->p->fts_level) {
3429  NODE *ep;
3430  for (ep = level; ep != NULL; ep = ep->next)
3431  if ((KF_ISSET(ep->flags, MAGIC) &&
3432 /*@-moduncon@*/
3433  !fnmatch(ep->name, fts_name, FNM_PATHNAME)) ||
3434 /*@=moduncon@*/
3435  !strcmp(ep->name, fts_name))
3436  {
3437  ep->flags |= MTREE_KEYS_VISIT;
3438  if (!KF_ISSET(ep->flags, NOCHANGE) &&
3439  compare(fts, ep))
3440  rval = MISMATCHEXIT;
3441  if (KF_ISSET(ep->flags, IGN))
3442  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3443  else
3444  if (ep->child && ep->type == F_DIR && fts->p->fts_info == FTS_D)
3445  {
3446  level = ep->child;
3447  ++specdepth;
3448  }
3449  /*@innerbreak@*/ break;
3450  }
3451  if (ep != NULL)
3452  continue;
3453  }
3454 
3455  if (!MF_ISSET(IGNORE)) {
3456  (void) printf("%s extra", SKIPDOTSLASH(fts->p->fts_path));
3457  if (MF_ISSET(REMOVE)) {
3458  if ((S_ISDIR(fts->p->fts_statp->st_mode)
3459  ? Rmdir : Unlink)(fts->p->fts_accpath)) {
3460  (void) printf(_(", not removed: %s"), strerror(errno));
3461  } else
3462  (void) printf(_(", removed"));
3463  }
3464  (void) putchar('\n');
3465  }
3466  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3467  }
3468  (void) Fts_close(fts->t);
3469  fts->p = NULL;
3470  fts->t = NULL;
3471  return rval;
3472 }
3473 
3474 /*==============================================================*/
3475 
3478 static void mtreeArgCallback(poptContext con,
3479  /*@unused@*/ enum poptCallbackReason reason,
3480  const struct poptOption * opt, const char * arg,
3481  /*@unused@*/ void * data)
3482  /*@globals _rpmfts, rpmioFtsOpts, h_errno, fileSystem, internalState @*/
3483  /*@modifies _rpmfts, rpmioFtsOpts, fileSystem, internalState @*/
3484 {
3485  char * p;
3486 
3487  /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
3488  if (opt->arg == NULL)
3489  switch (opt->val) {
3490 
3491  case 'f':
3492  if (_rpmfts->spec1 == NULL) {
3493  if ((_rpmfts->spec1 = fopen(arg, "r")) != NULL)
3494  mtree_error("%s: %s", arg, strerror(errno));
3495  } else if (_rpmfts->spec2 == NULL) {
3496  if ((_rpmfts->spec2 = fopen(arg, "r")) != NULL)
3497  mtree_error("%s: %s", arg, strerror(errno));
3498  } else {
3499  /* XXX error message, too many -f options. */
3500  poptPrintUsage(con, stderr, 0);
3501  exit(EXIT_FAILURE);
3502  /*@notreached@*/
3503  }
3504  break;
3505  case 'k':
3506  /* XXX fts->keys = KEYDEFAULT in main(), clear default now. */
3507  _rpmfts->keys = MTREE_KEYS_TYPE;
3508  /*@fallthrough@*/
3509  case 'K':
3510 /*@-unrecog@*/
3511  while ((p = strsep((char **)&arg, " \t,")) != NULL) {
3512  uint32_t needvalue;
3513  enum mtreeKeys_e type = parsekey(p, &needvalue);
3514  if (type == 0) {
3515  /* XXX unknown key error. */
3516  continue;
3517  }
3518  _rpmfts->keys |= type;
3519  /* XXX dupes can occur */
3520  if (KF_ISSET(_rpmfts->keys, DIGEST) && needvalue)
3521  (void) argiAdd(&_rpmfts->algos, -1, (int)needvalue);
3522  }
3523 /*@=unrecog@*/
3524  break;
3525 
3526  /* XXX redundant with --logical. */
3527  case 'L':
3530  break;
3531  /* XXX redundant with --physical. */
3532  case 'P':
3535  break;
3536  case 'X':
3537  mtreeReadExcludes(arg);
3538  break;
3539 
3540  case '?':
3541  default:
3542  fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
3543  poptPrintUsage(con, stderr, 0);
3544  exit(EXIT_FAILURE);
3545  /*@notreached@*/ break;
3546  }
3547 }
3548 
3549 /*@unchecked@*/ /*@observer@*/
3550 static struct poptOption optionsTable[] = {
3551 /*@-type@*/ /* FIX: cast? */
3552  { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
3553  mtreeArgCallback, 0, NULL, NULL },
3554 /*@=type@*/
3555 
3556  /* XXX redundant with --logical. */
3557  { NULL,'L', POPT_ARG_NONE, NULL, (int)'L',
3558  N_("Follow symlinks"), NULL },
3559  /* XXX redundant with --physical. */
3560  { NULL,'P', POPT_ARG_NONE, NULL, (int)'P',
3561  N_("Don't follow symlinks"), NULL },
3562  { NULL,'X', POPT_ARG_NONE, NULL, (int)'X',
3563  N_("Read fnmatch(3) exclude patterns from <file>"), N_("<file>") },
3564 
3565  { "create",'c', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_CREATE,
3566  N_("Print file tree specification to stdout"), NULL },
3567  { "dirs",'d', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_DIRSONLY,
3568  N_("Directories only"), NULL },
3569  { "ignore",'e', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_IGNORE,
3570  N_("Don't complain about files not in the specification"), NULL },
3571  { "file",'f', POPT_ARG_STRING, NULL, (int)'f',
3572  N_("Read file tree <spec>"), N_("<spec>") },
3573  { "indent",'i', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_INDENT,
3574  N_("Indent sub-directories"), NULL },
3575  { "add",'K', POPT_ARG_STRING, NULL, (int)'K',
3576  N_("Add <key> to specification"), N_("<key>") },
3577  { "key",'k', POPT_ARG_STRING, NULL, (int)'k',
3578  N_("Use \"type\" keywords instead"), N_("<key>") },
3579  { "loose",'l', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_LOOSE,
3580  N_("Loose permissions check"), NULL },
3581  { "nocomment",'n', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_NOCOMMENT,
3582  N_("Don't include sub-directory comments"), NULL },
3583  { "path",'p', POPT_ARG_ARGV, &__rpmfts.paths, 0,
3584  N_("Use <path> rather than current directory"), N_("<path>") },
3585  /* XXX --quiet collides w poptIO */
3586  { "quiet",'q', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_QUIET,
3587  N_("Quiet mode"), NULL },
3588  { "remove",'r', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_REMOVE,
3589  N_("Remove files not in specification"), NULL },
3590  { "seed",'s', POPT_ARG_INT, &__rpmfts.crc_total, 0,
3591  N_("Display crc for file(s) with <seed>"), N_("<seed>") },
3592  { "touch",'t', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_TOUCH,
3593  N_("Touch files iff timestamp differs"), NULL },
3594  { "update",'u', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_UPDATE,
3595  N_("Update owner/group/permissions to match specification"), NULL },
3596  { "mismatch",'U', POPT_BIT_SET, &mtreeFlags, (MTREE_FLAGS_UPDATE|MTREE_FLAGS_MISMATCHOK),
3597  N_("Same as -u, but ignore match status on exit"), NULL },
3598  { "warn",'w', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_WARN,
3599  N_("Treat missing uid/gid as warning"), NULL },
3600  /* XXX duplicated with --xdev. */
3601  { "xdev",'x', POPT_BIT_SET, &rpmioFtsOpts, FTS_XDEV,
3602  N_("Don't cross mount points"), NULL },
3603 
3604  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
3605  N_("Fts(3) traversal options:"), NULL },
3606 
3607  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
3608  N_("Available digests:"), NULL },
3609 
3610  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
3611  N_("Common options for all rpmio executables:"),
3612  NULL },
3613 
3614  POPT_AUTOALIAS
3615  POPT_AUTOHELP
3616 
3617  { NULL, (char)-1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
3618  "\
3619 Usage: mtree [-cdeilnqrtUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n\
3620 ", NULL },
3621 
3622  POPT_TABLEEND
3623 };
3624 
3625 #if defined(__linux__)
3626 /*@null@*/ /*@observer@*/
3627 static const char *my_getlogin(void)
3628  /*@globals fileSystem @*/
3629  /*@modifies fileSystem @*/
3630 {
3631  const char *s = getlogin();
3632 
3633  if (s && *s) {
3634  return (char *) s;
3635  } else {
3636  struct passwd *pw = getpwuid(geteuid());
3637  char *ss = NULL;
3638 /*@-unrecog@*/
3639  if (pw != NULL && pw->pw_name != NULL && pw->pw_name[0] != '\0') {
3640  if (asprintf(&ss, _("(no controlling terminal) %s"), pw->pw_name) < 0) {
3641  perror("asprintf");
3642  return NULL;
3643  }
3644  } else {
3645  if (asprintf(&ss, _("(no controlling terminal) #%d"), geteuid()) < 0) {
3646  perror("asprintf");
3647  return NULL;
3648  }
3649  }
3650 /*@=unrecog@*/
3651  return ss;
3652  }
3653 }
3654 #define __getlogin my_getlogin
3655 #else
3656 #define __getlogin getlogin
3657 #endif
3658 
3659 int
3660 main(int argc, char *argv[])
3661  /*@globals _rpmfts, mtreeFlags, excludes, __assert_program_name,
3662  rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
3663  /*@modifies _rpmfts, mtreeFlags, excludes, __assert_program_name,
3664  rpmGlobalMacroContext, fileSystem, internalState @*/
3665 {
3666  rpmfts fts = _rpmfts;
3667  poptContext optCon;
3668  int rc = 1; /* assume failure */
3669  int i;
3670 
3671  __progname = "rpmmtree";
3672 
3673  RPM_LIST_INIT(&excludes);
3674  fts->keys = KEYDEFAULT;
3675  fts->maxg = 5000;
3676  fts->maxu = 5000;
3677  fts->maxm = (MBITS + 1);
3678 #if defined(HAVE_ST_FLAGS)
3679  fts->maxf = 256;
3680  fts->sb.st_flags = 0xffffffff;
3681 #endif
3682 
3683  /* Process options. */
3684  optCon = rpmioInit(argc, argv, optionsTable);
3685 
3686  /* XXX ./rpmmtree w no args waits for stdin. poptPrintUsage more better. */
3687  argv = (char **) poptGetArgs(optCon);
3688  if (!(argv == NULL || argv[0] == NULL)) {
3689  poptPrintUsage(optCon, stderr, 0);
3690  goto exit;
3691  }
3692 
3693  if (MF_ISSET(LOOSE) && MF_ISSET(UPDATE))
3694  mtree_error("-l and -u flags are mutually exclusive");
3695 
3696  /*
3697  * Either FTS_PHYSICAL or FTS_LOGICAL must be set. Don't follow symlinks
3698  * unless explicitly overridden with FTS_LOGICAL.
3699  */
3700  fts->ftsoptions = rpmioFtsOpts;
3701  switch (fts->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) {
3702  case (FTS_LOGICAL|FTS_PHYSICAL):
3703  mtree_error("-L and -P flags are mutually exclusive");
3704  /*@notreached@*/ break;
3705  case 0:
3706  fts->ftsoptions |= FTS_PHYSICAL;
3707  break;
3708  }
3709 
3710  if (fts->paths == NULL || fts->paths[0] == NULL) {
3711  fts->paths = xcalloc(2, sizeof(fts->paths[0]));
3712  fts->paths[0] = xstrdup(".");
3713  }
3714 
3715  /* Use absolute paths since Chdir(2) is problematic with remote URI's */
3716  for (i = 0; fts->paths[i] != NULL; i++) {
3717  char fullpath[MAXPATHLEN];
3718  struct stat sb;
3719  const char * rpath;
3720  const char * lpath = NULL;
3721  int ut = urlPath(fts->paths[i], &lpath);
3722  size_t nb = (size_t)(lpath - fts->paths[i]);
3723  int isdir = (lpath[strlen(lpath)-1] == '/');
3724 
3725  /* Convert to absolute/clean/malloc'd path. */
3726  if (lpath[0] != '/') {
3727  /* XXX GLIBC: realpath(path, NULL) return malloc'd */
3728  rpath = Realpath(lpath, NULL);
3729  if (rpath == NULL)
3730  rpath = Realpath(lpath, fullpath);
3731  if (rpath == NULL)
3732  mtree_error("Realpath(%s): %s", lpath, strerror(errno));
3733  lpath = rpmGetPath(rpath, NULL);
3734  if (rpath != fullpath) /* XXX GLIBC extension malloc's */
3735  rpath = _free(rpath);
3736  } else
3737  lpath = rpmGetPath(lpath, NULL);
3738 
3739  /* Reattach the URI to the absolute/clean path. */
3740  /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */
3741  switch (ut) {
3742  case URL_IS_DASH:
3743  case URL_IS_UNKNOWN:
3744  rpath = lpath;
3745  lpath = NULL;
3746  /*@switchbreak@*/ break;
3747  default:
3748  strncpy(fullpath, fts->paths[i], nb);
3749  fullpath[nb] = '\0';
3750  rpath = rpmGenPath(fullpath, lpath, NULL);
3751  lpath = _free(lpath);
3752  /*@switchbreak@*/ break;
3753  }
3754 
3755  /* Add a trailing '/' on directories. */
3756  lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode))
3757  ? "/" : NULL);
3758  fts->paths[i] = _free(fts->paths[i]);
3759  fts->paths[i] = rpmExpand(rpath, lpath, NULL);
3760  fts->fullpath = xstrdup(fts->paths[i]);
3761  rpath = _free(rpath);
3762  }
3763 
3764  /* XXX prohibits -s 0 invocation */
3765  if (fts->crc_total)
3767  fts->crc_total ^= 0xffffffff;
3768 
3769  (void) rpmswEnter(&dc_totalops, -1);
3770 
3771  if (MF_ISSET(CREATE)) {
3772  if (!MF_ISSET(NOCOMMENT)) {
3773  time_t clock;
3774  char host[MAXHOSTNAMELEN];
3775 
3776  (void) time(&clock);
3777  (void) gethostname(host, sizeof(host));
3778  (void) printf("#\t user: %s\n", __getlogin());
3779  (void) printf("#\tmachine: %s\n", host);
3780  for (i = 0; fts->paths[i] != NULL; i++)
3781  (void) printf("#\t tree: %s\n", fts->paths[i]);
3782  (void) printf("#\t date: %s", ctime(&clock));
3783  }
3784  rc = mtreeCWalk(fts);
3785  if (MF_ISSET(SEEDED) && KF_ISSET(fts->keys, CKSUM))
3786  (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
3787  fts->fullpath, (unsigned)fts->crc_total);
3788 
3789  } else {
3790  if (fts->spec2 != NULL) {
3791  rc = mtreeVSpec(fts);
3792  } else {
3793 /*@-evalorder@*/
3794  fts->root = mtreeSpec(fts, fts->spec1);
3795 /*@=evalorder@*/
3796  fts->path = xmalloc(MAXPATHLEN);
3797  rc = mtreeVWalk(fts);
3798  mtreeMiss(fts, fts->root, fts->path);
3799  fts->path = _free(fts->path);
3800  if (MF_ISSET(SEEDED))
3801  (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
3802  fts->fullpath, (unsigned) fts->crc_total);
3803  }
3804  }
3805  if (MF_ISSET(MISMATCHOK) && (rc == MISMATCHEXIT))
3806  rc = 0;
3807 
3808  (void) rpmswExit(&dc_totalops, 0);
3809  if (_rpmsw_stats) {
3810  rpmswPrint(" total:", &dc_totalops);
3811  rpmswPrint(" read:", &dc_readops);
3812  rpmswPrint("digest:", &dc_digestops);
3813  }
3814 
3815 exit:
3816 #if defined(_RPMFI_INTERNAL)
3817  (void)rpmtsFree(fts->ts);
3818  fts->ts = NULL;
3819  fts->fi = rpmfiFree(fts->fi);
3820  tagClean(NULL); /* Free header tag indices. */
3821 #endif
3822  if (fts->spec1 != NULL && fileno(fts->spec1) > 2) {
3823  (void) fclose(fts->spec1);
3824  fts->spec1 = NULL;
3825  }
3826  if (fts->spec2 != NULL && fileno(fts->spec2) > 2) {
3827  (void) fclose(fts->spec2);
3828  fts->spec2 = NULL;
3829  }
3830  fts->paths = argvFree(fts->paths);
3831 #if defined(HAVE_ST_FLAGS)
3832  fts->f = _free(fts->f);
3833 #endif
3834  fts->g = _free(fts->g);
3835  fts->m = _free(fts->m);
3836  fts->u = _free(fts->u);
3837  fts->fullpath = _free(fts->fullpath);
3838  /* XXX TODO: clean excludes */
3839 
3840  optCon = rpmioFini(optCon);
3841 
3842  return rc;
3843 }