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