rpm  5.4.14
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);
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 assert(centry); /* XXX coverity #1035809 */
1621  set(p, centry);
1622  continue;
1623  }
1624 
1625  /* Grab file name, "$", "set", or "unset". */
1626  if ((p = strtok(p, "\n\t ")) == NULL)
1627  mtree_error("missing field");
1628 
1629  if (p[0] == '/')
1630  switch(p[1]) {
1631  case 's':
1632  if (strcmp(p + 1, "set"))
1633  /*@switchbreak@*/ break;
1634  set(NULL, &ginfo);
1635  continue;
1636  case 'u':
1637  if (strcmp(p + 1, "unset"))
1638  /*@switchbreak@*/ break;
1639  unset(NULL, &ginfo);
1640  continue;
1641  }
1642 
1643 #if !defined(_RPMFI_INTERNAL) /* XXX *.rpm/ specs include '/' in names. */
1644  if (strchr(p, '/') != NULL)
1645  mtree_error("slash character in file name");
1646 #endif
1647 
1648  if (!strcmp(p, "..")) {
1649  /* Don't go up, if haven't gone down. */
1650  if (root == NULL)
1651  goto noparent;
1652  if (last->type != F_DIR || KF_ISSET(last->flags, DONE)) {
1653  if (last == root)
1654  goto noparent;
1655  last = last->parent;
1656  }
1657  last->flags |= MTREE_KEYS_DONE;
1658  continue;
1659 
1660 noparent: mtree_error("no parent node");
1661  }
1662 
1663  /* XXX sizeof(*centry) includes room for final '\0' */
1664  centry = xcalloc(1, sizeof(*centry) + strlen(p));
1665  *centry = ginfo; /* structure assignment */
1666 #define MAGIC "?*["
1667  if (strpbrk(p, MAGIC) != NULL)
1668  centry->flags |= MTREE_KEYS_MAGIC;
1669  if (strunvis(centry->name, p) == -1) {
1670  fprintf(stderr, _("%s: filename (%s) encoded incorrectly\n"),
1671  __progname, p);
1672  strcpy(centry->name, p);
1673  }
1674  set(NULL, centry);
1675 
1676  if (root == NULL) {
1677  last = root = centry;
1678  root->parent = root;
1679  if (forest == NULL)
1680  forest = root;
1681  } else if (centry->name[0] == '.' && centry->name[1] == '\0') {
1682  centry->prev = root;
1683  last = root = root->next = centry;
1684  root->parent = root;
1685  } else if (last->type == F_DIR && !KF_ISSET(last->flags, DONE)) {
1686  centry->parent = last;
1687  last = last->child = centry;
1688  } else {
1689  centry->parent = last->parent;
1690  centry->prev = last;
1691  last = last->next = centry;
1692  }
1693  }
1694  return forest;
1695 }
1696 
1697 /*==============================================================*/
1698 
1699 /*@observer@*/
1700 static const char *
1701 ftype(unsigned type)
1702  /*@*/
1703 {
1704  switch(type) {
1705  case F_BLOCK: return "block";
1706  case F_CHAR: return "char";
1707  case F_DIR: return "dir";
1708  case F_FIFO: return "fifo";
1709  case F_FILE: return "file";
1710  case F_LINK: return "link";
1711  case F_SOCK: return "socket";
1712  default: return "unknown";
1713  }
1714  /*@notreached@*/
1715 }
1716 
1717 /*@observer@*/
1718 static const char *
1719 inotype(mode_t mode)
1720  /*@*/
1721 {
1722  switch(mode & S_IFMT) {
1723  case S_IFBLK: return "block";
1724  case S_IFCHR: return "char";
1725  case S_IFDIR: return "dir";
1726  case S_IFIFO: return "fifo";
1727  case S_IFREG: return "file";
1728  case S_IFLNK: return "link";
1729 /*@-unrecog@*/
1730  case S_IFSOCK: return "socket";
1731 /*@=unrecog@*/
1732  default: return "unknown";
1733  }
1734  /*@notreached@*/
1735 }
1736 
1737 /*-
1738  * Copyright (c) 2003 Poul-Henning Kamp
1739  * All rights reserved.
1740  *
1741  * Redistribution and use in source and binary forms, with or without
1742  * modification, are permitted provided that the following conditions
1743  * are met:
1744  * 1. Redistributions of source code must retain the above copyright
1745  * notice, this list of conditions and the following disclaimer.
1746  * 2. Redistributions in binary form must reproduce the above copyright
1747  * notice, this list of conditions and the following disclaimer in the
1748  * documentation and/or other materials provided with the distribution.
1749  *
1750  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1751  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1752  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1753  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1754  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1755  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
1756  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
1757  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
1758  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
1759  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
1760  * SUCH DAMAGE.
1761  */
1762 
1763 #define FF(a, b, c, d) \
1764  (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
1765 #define FS(a, b, c, d) \
1766  (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
1767 #define FM(a, b, c, d) \
1768  (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
1769 
1770 static void
1771 shownode(NODE *n, enum mtreeKeys_e keys, const char *path)
1772  /*@globals fileSystem @*/
1773  /*@modifies fileSystem @*/
1774 {
1775  printf("%s%s %s", path, n->name, ftype((unsigned)n->type));
1776  if (KF_ISSET(keys, CKSUM))
1777  printf(" cksum=%lu", (unsigned long) n->cksum);
1778  if (KF_ISSET(keys, GID))
1779  printf(" gid=%lu", (unsigned long) n->sb.st_gid);
1780  if (KF_ISSET(keys, GNAME)) {
1781  const char * gname = gidToGname(n->sb.st_gid);
1782  if (gname != NULL)
1783  printf(" gname=%s", gname);
1784  else
1785  printf(" gid=%lu", (unsigned long) n->sb.st_gid);
1786  }
1787  if (KF_ISSET(keys, MODE))
1788  printf(" mode=%o", (unsigned) n->sb.st_mode);
1789  if (KF_ISSET(keys, NLINK))
1790  printf(" nlink=%lu", (unsigned long) n->sb.st_nlink);
1791 /*@-duplicatequals@*/
1792  if (KF_ISSET(keys, SIZE))
1793  printf(" size=%llu", (unsigned long long)n->sb.st_size);
1794 /*@=duplicatequals@*/
1795  if (KF_ISSET(keys, UID))
1796  printf(" uid=%lu", (unsigned long) n->sb.st_uid);
1797  if (KF_ISSET(keys, UNAME)) {
1798  const char * uname = uidToUname(n->sb.st_uid);
1799  if (uname != NULL)
1800  printf(" uname=%s", uname);
1801  else
1802  printf(" uid=%lu", (unsigned long) n->sb.st_uid);
1803  }
1804 
1805  /* Output all the digests. */
1806  if (KF_ISSET(keys, DIGEST)) {
1807  int i;
1808 
1809  if (n->algos != NULL)
1810  for (i = 0; i < (int) n->algos->nvals; i++) {
1811  uint32_t algo = n->algos->vals[i];
1812  const char * tagname = algo2tagname(algo);
1813  if (tagname != NULL)
1814  printf(" %s=%s", tagname, n->digests[i]);
1815  }
1816  }
1817 
1818 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
1819  if (KF_ISSET(keys, FLAGS)) {
1820  /* XXX coverity #978437 */
1821  const char * fflags = flags_to_string(n->sb.st_flags);
1822  printf(" flags=%s", fflags);
1823  fflags = _free(fflags);
1824  }
1825 #endif
1826  printf("\n");
1827 }
1828 
1829 static int
1830 mismatch(NODE *n1, NODE *n2, enum mtreeKeys_e differ, const char *path)
1831  /*@globals fileSystem @*/
1832  /*@modifies fileSystem @*/
1833 {
1834  enum mtreeKeys_e keys = _rpmfts->keys;
1835 
1836  if (n2 == NULL) {
1837  shownode(n1, differ, path);
1838  return 1;
1839  }
1840  if (n1 == NULL) {
1841  printf("\t");
1842  shownode(n2, differ, path);
1843  return 1;
1844  }
1845  if (!(differ & keys))
1846  return 0;
1847  printf("\t\t");
1848  shownode(n1, differ, path);
1849  printf("\t\t");
1850  shownode(n2, differ, path);
1851  return 1;
1852 }
1853 
1854 static int
1855 compare_nodes(NODE *n1, NODE *n2, const char *path)
1856  /*@globals fileSystem @*/
1857  /*@modifies n1, n2, fileSystem @*/
1858 {
1859  enum mtreeKeys_e differs = MTREE_KEYS_NONE;
1860  int xx;
1861 
1862  if (n1 != NULL && n1->type == F_LINK)
1863  n1->flags &= ~MTREE_KEYS_MODE;
1864  if (n2 != NULL && n2->type == F_LINK)
1865  n2->flags &= ~MTREE_KEYS_MODE;
1866  if (n1 == NULL && n2 != NULL) {
1867  differs = n2->flags;
1868  xx = mismatch(n1, n2, differs, path);
1869  return 1;
1870  }
1871  if (n1 != NULL && n2 == NULL) {
1872  differs = n1->flags;
1873  xx = mismatch(n1, n2, differs, path);
1874  return 1;
1875  }
1876  if (n1->type != n2->type) {
1877  differs = MTREE_KEYS_NONE; /* XXX unneeded */
1878  xx = mismatch(n1, n2, differs, path);
1879  return 1;
1880  }
1881  if (FF(n1, n2, MTREE_KEYS_CKSUM, cksum))
1882  differs |= MTREE_KEYS_CKSUM;
1883  if (FF(n1, n2, MTREE_KEYS_GID, sb.st_gid))
1884  differs |= MTREE_KEYS_GID;
1885  if (FF(n1, n2, MTREE_KEYS_GNAME, sb.st_gid))
1886  differs |= MTREE_KEYS_GNAME;
1887  if (FF(n1, n2, MTREE_KEYS_MODE, sb.st_mode))
1888  differs |= MTREE_KEYS_MODE;
1889  if (FF(n1, n2, MTREE_KEYS_NLINK, sb.st_nlink))
1890  differs |= MTREE_KEYS_NLINK;
1891  if (FF(n1, n2, MTREE_KEYS_SIZE, sb.st_size))
1892  differs |= MTREE_KEYS_SIZE;
1893 
1894  if (FS(n1, n2, MTREE_KEYS_SLINK, slink))
1895  differs |= MTREE_KEYS_SLINK;
1896 
1897 /*@-type@*/
1898  if (FM(n1, n2, MTREE_KEYS_TIME, sb.st_mtimespec))
1899  differs |= MTREE_KEYS_TIME;
1900 /*@=type@*/
1901  if (FF(n1, n2, MTREE_KEYS_UID, sb.st_uid))
1902  differs |= MTREE_KEYS_UID;
1903  if (FF(n1, n2, MTREE_KEYS_UNAME, sb.st_uid))
1904  differs |= MTREE_KEYS_UNAME;
1905 
1906  /* Compare all the digests. */
1907  if (KF_ISSET(n1->flags, DIGEST) || KF_ISSET(n2->flags, DIGEST)) {
1908  if ((KF_ISSET(n1->flags, DIGEST) != KF_ISSET(n2->flags, DIGEST))
1909  || (n1->algos == NULL || n2->algos == NULL)
1910  || (n1->algos->nvals != n2->algos->nvals))
1911  differs |= MTREE_KEYS_DIGEST;
1912  else {
1913  int i;
1914 
1915  for (i = 0; i < (int) n1->algos->nvals; i++) {
1916  if ((n1->algos->vals[i] == n2->algos->vals[i])
1917  && !strcmp(n1->digests[i], n2->digests[i]))
1918  continue;
1919  differs |= MTREE_KEYS_DIGEST;
1920  break;
1921  }
1922  }
1923  }
1924 
1925 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
1926  if (FF(n1, n2, MTREE_KEYS_FLAGS, sb.st_flags))
1927  differs |= MTREE_KEYS_FLAGS;
1928 #endif
1929 
1930  if (differs) {
1931  xx = mismatch(n1, n2, differs, path);
1932  return 1;
1933  }
1934  return 0;
1935 }
1936 
1937 static int
1938 mtreeSWalk(NODE *t1, NODE *t2, const char *path)
1939  /*@globals fileSystem @*/
1940  /*@modifies t1, t2, fileSystem @*/
1941 {
1942  NODE *c1 = (t1 != NULL ? t1->child : NULL);
1943  NODE *c2 = (t2 != NULL ? t2->child : NULL);
1944  int r = 0;
1945 
1946  while (c1 != NULL || c2 != NULL) {
1947  NODE *n1, *n2;
1948  char *np;
1949  int i;
1950 
1951  n1 = (c1 != NULL ? c1->next : NULL);
1952  n2 = (c2 != NULL ? c2->next : NULL);
1953  if (c1 != NULL && c2 != NULL) {
1954  if (c1->type != F_DIR && c2->type == F_DIR) {
1955  n2 = c2;
1956  c2 = NULL;
1957  } else if (c1->type == F_DIR && c2->type != F_DIR) {
1958  n1 = c1;
1959  c1 = NULL;
1960  } else {
1961  i = strcmp(c1->name, c2->name);
1962  if (i > 0) {
1963  n1 = c1;
1964  c1 = NULL;
1965  } else if (i < 0) {
1966  n2 = c2;
1967  c2 = NULL;
1968  }
1969  }
1970  }
1971 /*@-noeffectuncon -unrecog@*/
1972  if (c1 == NULL && c2->type == F_DIR) {
1973  if (asprintf(&np, "%s%s/", path, c2->name)) {
1974  perror("asprintf");
1975  }
1976  i = mtreeSWalk(c1, c2, np);
1977  free(np);
1978  i += compare_nodes(c1, c2, path);
1979  } else if (c2 == NULL && c1->type == F_DIR) {
1980  if (asprintf(&np, "%s%s/", path, c1->name)) {
1981  perror("asprintf");
1982  }
1983  i = mtreeSWalk(c1, c2, np);
1984  free(np);
1985  i += compare_nodes(c1, c2, path);
1986  } else if (c1 == NULL || c2 == NULL) {
1987  i = compare_nodes(c1, c2, path);
1988  } else if (c1->type == F_DIR && c2->type == F_DIR) {
1989  if (asprintf(&np, "%s%s/", path, c1->name)) {
1990  perror("asprintf");
1991  }
1992  i = mtreeSWalk(c1, c2, np);
1993  free(np);
1994  i += compare_nodes(c1, c2, path);
1995  } else {
1996  i = compare_nodes(c1, c2, path);
1997  }
1998 /*@=noeffectuncon =unrecog@*/
1999  r += i;
2000  c1 = n1;
2001  c2 = n2;
2002  }
2003  return r;
2004 }
2005 
2006 int
2007 mtreeVSpec(rpmfts fts)
2008 {
2009  NODE * root1 = mtreeSpec(fts, fts->spec1);
2010  NODE * root2 = mtreeSpec(fts, fts->spec2);
2011  int rval = 0;
2012 
2013  rval = mtreeSWalk(root1, root2, "");
2014  rval += compare_nodes(root1, root2, "");
2015  return (rval > 0 ? MISMATCHEXIT : 0);
2016 }
2017 
2018 /*==============================================================*/
2019 
2020 /*@observer@*/
2021 static const char *
2022 rlink(const char * name)
2023  /*@globals h_errno, fileSystem, internalState @*/
2024  /*@modifies fileSystem, internalState @*/
2025 
2026 {
2027  static char lbuf[MAXPATHLEN];
2028  int len;
2029 
2030  if ((len = Readlink(name, lbuf, sizeof(lbuf)-1)) == -1)
2031  mtree_error("%s: %s", name, strerror(errno));
2032  lbuf[len] = '\0';
2033  return lbuf;
2034 }
2035 
2036 #define SKIPDOTSLASH(_f) ((_f)[0] == '.' && (_f)[1] == '/' ? (_f) + 2 : (_f))
2037 
2038 #define COMPAREINDENTNAMELEN 8
2039 #define LABEL \
2040  if (!label++) { \
2041  (void) printf(_("%s changed\n"), SKIPDOTSLASH(p->fts_path)); \
2042  tab = "\t"; \
2043  }
2044 
2045 /*@observer@*/
2046 static const char * algo2name(uint32_t algo)
2047  /*@*/
2048 {
2049  switch (algo) {
2050  case PGPHASHALGO_MD5: return "MD5";
2051  case PGPHASHALGO_SHA1: return "SHA1";
2052  case PGPHASHALGO_RIPEMD160: return "RIPEMD160";
2053  case PGPHASHALGO_MD2: return "MD2";
2054  case PGPHASHALGO_TIGER192: return "TIGER192";
2055  case PGPHASHALGO_HAVAL_5_160: return "HAVAL-5-160";
2056  case PGPHASHALGO_SHA256: return "SHA256";
2057  case PGPHASHALGO_SHA384: return "SHA384";
2058  case PGPHASHALGO_SHA512: return "SHA512";
2059 
2060  case PGPHASHALGO_MD4: return "MD4";
2061  case PGPHASHALGO_RIPEMD128: return "RIPEMD128";
2062  case PGPHASHALGO_CRC32: return "CRC32";
2063  case PGPHASHALGO_ADLER32: return "ADLER32";
2064  case PGPHASHALGO_CRC64: return "CRC64";
2065  case PGPHASHALGO_JLU32: return "JLU32";
2066  case PGPHASHALGO_SHA224: return "SHA224";
2067  case PGPHASHALGO_RIPEMD256: return "RIPEMD256";
2068  case PGPHASHALGO_RIPEMD320: return "RIPEMD320";
2069  case PGPHASHALGO_SALSA10: return "SALSA10";
2070  case PGPHASHALGO_SALSA20: return "SALSA20";
2071 
2072  default: return "Unknown";
2073  }
2074  /*@notreached@*/
2075 }
2076 
2077 static int
2078 compare(rpmfts fts, NODE *const s)
2079  /*@globals errno, h_errno, fileSystem, internalState @*/
2080  /*@modifies errno, fileSystem, internalState @*/
2081 
2082 {
2083  const char * name = s->name;
2084  FTSENT *const p = fts->p;
2085  const char * fts_accpath = p->fts_accpath;
2086  struct stat *const st = p->fts_statp;
2087  enum mtreeKeys_e keys = s->flags;
2088  int label = 0;
2089  const char *cp;
2090  const char *tab = "";
2091 
2092  switch(s->type) {
2093  case F_BLOCK:
2094  if (!S_ISBLK(st->st_mode))
2095  goto typeerr;
2096  break;
2097  case F_CHAR:
2098  if (!S_ISCHR(st->st_mode))
2099  goto typeerr;
2100  break;
2101  case F_DIR:
2102  if (!S_ISDIR(st->st_mode))
2103  goto typeerr;
2104  break;
2105  case F_FIFO:
2106  if (!S_ISFIFO(st->st_mode))
2107  goto typeerr;
2108  break;
2109  case F_FILE:
2110  if (!S_ISREG(st->st_mode))
2111  goto typeerr;
2112  break;
2113  case F_LINK:
2114  if (!S_ISLNK(st->st_mode))
2115  goto typeerr;
2116  break;
2117  case F_SOCK:
2118 /*@-unrecog@*/
2119  if (!S_ISSOCK(st->st_mode)) {
2120 typeerr: LABEL;
2121  (void) printf(_("\ttype expected %s found %s)\n"),
2122  ftype((unsigned)s->type), inotype(st->st_mode));
2123  }
2124 /*@=unrecog@*/
2125  break;
2126  }
2127 
2128  /* Set the uid/gid first, then set the mode. */
2129  if ((KF_ISSET(keys, UID) || KF_ISSET(keys, UNAME)) && s->sb.st_uid != st->st_uid) {
2130  LABEL;
2131  (void) printf(_("%s%s expected %lu found %lu"), tab, "user",
2132  (unsigned long)s->sb.st_uid, (unsigned long)st->st_uid);
2133  if (MF_ISSET(UPDATE)) {
2134  if (Chown(fts_accpath, s->sb.st_uid, -1))
2135  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2136  else
2137  (void) printf(_(" modified)\n"));
2138  } else
2139  (void) printf("\n");
2140  tab = "\t";
2141  }
2142  if ((KF_ISSET(keys, GID) || KF_ISSET(keys, GNAME)) && s->sb.st_gid != st->st_gid) {
2143  LABEL;
2144  (void) printf(_("%s%s expected %lu found %lu"), tab, "gid",
2145  (unsigned long)s->sb.st_gid, (unsigned long)st->st_gid);
2146  if (MF_ISSET(UPDATE)) {
2147  if (Chown(fts_accpath, -1, s->sb.st_gid))
2148  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2149  else
2150  (void) printf(_(" modified)\n"));
2151  } else
2152  (void) printf("\n");
2153  tab = "\t";
2154  }
2155  if (KF_ISSET(keys, MODE) && s->sb.st_mode != (st->st_mode & MBITS)) {
2156  if (MF_ISSET(LOOSE)) {
2157  mode_t tmode = s->sb.st_mode;
2158  mode_t mode = (st->st_mode & MBITS);
2159 
2160  /*
2161  * if none of the suid/sgid/etc bits are set,
2162  * then if the mode is a subset of the target,
2163  * skip.
2164  */
2165  if (!((tmode & ~(S_IRWXU|S_IRWXG|S_IRWXO))
2166  || (mode & ~(S_IRWXU|S_IRWXG|S_IRWXO))))
2167  if ((mode | tmode) == tmode)
2168  goto skip;
2169  }
2170  LABEL;
2171  (void) printf(_("%s%s expected %#o found %#o"), tab, "permissions",
2172  (unsigned)s->sb.st_mode, (unsigned)(st->st_mode & MBITS));
2173  if (MF_ISSET(UPDATE)) {
2174  if (Chmod(fts_accpath, s->sb.st_mode))
2175  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2176  else
2177  (void) printf(_(" modified)\n"));
2178  } else
2179  (void) printf("\n");
2180  tab = "\t";
2181  skip:
2182  ;
2183  }
2184  if (KF_ISSET(keys, NLINK) && s->type != F_DIR &&
2185  s->sb.st_nlink != st->st_nlink)
2186  {
2187  LABEL;
2188  (void) printf(_("%s%s expected %lu found %lu)\n"), tab, "link_count",
2189  (unsigned long)s->sb.st_nlink, (unsigned long)st->st_nlink);
2190  tab = "\t";
2191  }
2192  if (KF_ISSET(keys, SIZE) && s->sb.st_size != st->st_size) {
2193  LABEL;
2194 /*@-duplicatequals@*/
2195  (void) printf(_("%s%s expected %llu found %llu\n"), tab, "size",
2196  (unsigned long long)s->sb.st_size,
2197  (unsigned long long)st->st_size);
2198 /*@=duplicatequals@*/
2199  tab = "\t";
2200  }
2201  /*
2202  * XXX
2203  * Since utimes(2) only takes a timeval, there's no point in
2204  * comparing the low bits of the timespec nanosecond field. This
2205  * will only result in mismatches that we can never fix.
2206  *
2207  * Doesn't display microsecond differences.
2208  */
2209  if (KF_ISSET(keys, TIME)) {
2210  struct timeval tv[2];
2211 
2212 /*@-noeffectuncon -unrecog @*/
2213 #if defined(TIMESPEC_TO_TIMEVAL)
2214  TIMESPEC_TO_TIMEVAL(&tv[0], &s->sb.st_mtimespec);
2215  TIMESPEC_TO_TIMEVAL(&tv[1], &st->st_mtimespec);
2216 #else
2217  tv[0].tv_sec = (long)s->sb.st_mtime;
2218  tv[0].tv_usec = 0L;
2219  tv[1].tv_sec = (long)st->st_mtime;
2220  tv[1].tv_usec = 0L;
2221 #endif
2222 /*@=noeffectuncon =unrecog @*/
2223  if (tv[0].tv_sec != tv[1].tv_sec
2224 #ifdef NOTYET /* XXX avoid timespec jitter issues for now. */
2225  || tv[0].tv_usec != tv[1].tv_usec
2226 #endif
2227  ) {
2228  time_t t1 = (time_t)tv[0].tv_sec;
2229  time_t t2 = (time_t)tv[1].tv_sec;
2230  LABEL;
2231  (void) printf(_("%s%s expected %.24s "), tab, "modification time", ctime(&t1));
2232  (void) printf(_("found %.24s"), ctime(&t2));
2233  if (MF_ISSET(TOUCH)) {
2234  tv[1] = tv[0];
2235  if (Utimes(fts_accpath, tv))
2236  (void) printf(_(" not modified: %s)\n"), strerror(errno));
2237  else
2238  (void) printf(_(" modified\n"));
2239  } else
2240  (void) printf("\n");
2241  tab = "\t";
2242  }
2243  }
2244 
2245  /* Any digests to calculate? */
2246  if (KF_ISSET(keys, CKSUM) || s->algos != NULL) {
2247  FD_t fd = Fopen(fts_accpath, "r.ufdio");
2248  uint32_t vlen, val;
2249  int i;
2250 
2251  if (fd == NULL || Ferror(fd)) {
2252  LABEL;
2253  (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
2254  goto cleanup;
2255  }
2256 
2257  /* Setup all digest calculations. Reversed order is effete ... */
2258  if (s->algos != NULL)
2259  for (i = s->algos->nvals; i-- > 0;)
2260  fdInitDigest(fd, s->algos->vals[i], 0);
2261 
2262  /* Compute the cksum and digests. */
2263  if (KF_ISSET(keys, CKSUM))
2264  i = crc(fd, &val, &vlen);
2265  else {
2266  char buffer[16 * 1024];
2267  while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
2268  {};
2269  i = (Ferror(fd) ? 1 : 0);
2270  }
2271  if (i) {
2272  LABEL;
2273  (void) printf("%scksum: %s: %s\n", tab, fts_accpath, Fstrerror(fd));
2274  goto cleanup;
2275  }
2276 
2277  /* Verify cksum. */
2278  if (KF_ISSET(keys, CKSUM)) {
2279  if (s->cksum != val) {
2280  LABEL;
2281  (void) printf(_("%s%s expected %lu found %lu\n"), tab, "cksum",
2282  (unsigned long) s->cksum, (unsigned long) val);
2283  tab = "\t";
2284  }
2285  }
2286 
2287  /* Verify all the digests. */
2288  if (s->algos != NULL)
2289  for (i = 0; i < (int) s->algos->nvals; i++) {
2290  static int asAscii = 1;
2291  uint32_t algo = s->algos->vals[i];
2292  const char * digest = NULL;
2293  size_t digestlen = 0;
2294 
2295  fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
2296 assert(digest != NULL);
2297  if (strcmp(digest, s->digests[i])) {
2298  LABEL;
2299  printf(_("%s%s expected %s found %s\n"), tab, algo2name(algo),
2300  s->digests[i], digest);
2301  tab = "\t";
2302  }
2303  digest = _free(digest);
2304  digestlen = 0;
2305  }
2306 
2307  /* Accumulate statistics and clean up. */
2308 cleanup:
2309  if (fd != NULL) {
2310  (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
2311  (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
2312  (void) Fclose(fd);
2313  fd = NULL;
2314  }
2315  }
2316 
2317  if (KF_ISSET(keys, SLINK) && strcmp(cp = rlink(name), s->slink)) {
2318  LABEL;
2319  (void) printf(_("%s%s expected %s found %s\n"), tab, "link_ref",
2320  cp, s->slink);
2321  }
2322 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2323  if (KF_ISSET(keys, FLAGS) && s->sb.st_flags != st->st_flags) {
2324  char *fflags;
2325 
2326  LABEL;
2327  fflags = fflagstostr(s->sb.st_flags);
2328  (void) printf(_("%s%s expected \"%s\""), tab, "flags", fflags);
2329  fflags = _free(fflags);
2330 
2331  fflags = fflagstostr(st->st_flags);
2332  (void) printf(_(" found \"%s\""), fflags);
2333  fflags = _free(fflags);
2334 
2335  if (MF_ISSET(UPDATE)) {
2336  if (chflags(fts_accpath, s->sb.st_flags))
2337  (void) printf(" not modified: %s)\n", strerror(errno));
2338  else
2339  (void) printf(" modified)\n");
2340  }
2341  } else {
2342  (void) printf("\n");
2343  tab = "\t";
2344  }
2345 #endif
2346  return label;
2347 }
2348 
2349 /*==============================================================*/
2350 
2351 #define _FTSCALLOC(_p, _n) \
2352  if ((_n) > 0) { \
2353  (_p) = _free(_p); (_p) = xcalloc((_n), sizeof(*(_p))); \
2354  }
2355 
2356 static int
2357 mtreeVisitD(rpmfts fts)
2358  /*@globals fileSystem, internalState @*/
2359  /*@modifies fts, fileSystem, internalState @*/
2360 {
2361  enum mtreeKeys_e keys = fts->keys;
2362  const FTSENT *const parent = fts->p;
2363  const FTSENT * p;
2364  struct stat sb;
2365  gid_t maxgid = 0;
2366  uid_t maxuid = 0;
2367  mode_t maxmode = 0;
2368 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2369  unsigned long maxflags = 0;
2370 #endif
2371 
2372  /* Retrieve all directory members. */
2373  if ((p = Fts_children(fts->t, 0)) == NULL) {
2374  if (errno)
2375  mtree_error("%s: %s", SKIPDOTSLASH(parent->fts_path),
2376  strerror(errno));
2377  return 1;
2378  }
2379 
2380  sb = fts->sb; /* structure assignment */
2381  _FTSCALLOC(fts->g, fts->maxg);
2382  _FTSCALLOC(fts->m, fts->maxm);
2383  _FTSCALLOC(fts->u, fts->maxu);
2384 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2385  _FTSCALLOC(fts->f, fts->maxf);
2386 #endif
2387 
2388  /* Find the most common stat(2) settings for the next directory. */
2389  for (; p != NULL; p = p->fts_link) {
2390  struct stat *const st = p->fts_statp;
2391 
2392  if (MF_ISSET(DIRSONLY) || !S_ISDIR(st->st_mode))
2393  continue;
2394 
2395  if (fts->m != NULL)
2396  { mode_t st_mode = st->st_mode & MBITS;
2397  if (st_mode < fts->maxm && ++fts->m[st_mode] > maxmode) {
2398  sb.st_mode = st_mode;
2399  maxmode = fts->m[st_mode];
2400  }
2401  }
2402  if (fts->g != NULL)
2403  if (st->st_gid < fts->maxg && ++fts->g[st->st_gid] > maxgid) {
2404  sb.st_gid = st->st_gid;
2405  maxgid = fts->g[st->st_gid];
2406  }
2407  if (fts->u != NULL)
2408  if (st->st_uid < fts->maxu && ++fts->u[st->st_uid] > maxuid) {
2409  sb.st_uid = st->st_uid;
2410  maxuid = fts->u[st->st_uid];
2411  }
2412 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2413  /*
2414  * XXX
2415  * note that the below will break when file flags
2416  * are extended beyond the first 4 bytes of each
2417  * half word of the flags
2418  */
2419 #define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
2420  if (fts->f != NULL)
2421  { unsigned long st_flags = FLAGS2IDX(st->st_flags);
2422  if (st_flags < fts->maxf && ++fts->f[st_flags] > maxflags) {
2423  /* XXX note st->st_flags saved, not FLAGS2IDX(st->st_flags) */
2424  sb.st_flags = st->st_flags;
2425  maxflags = fts->f[st_flags];
2426  }
2427  }
2428 #endif
2429  }
2430 
2431  /*
2432  * If the /set record is the same as the last one we do not need to output
2433  * a new one. So first we check to see if anything changed. Note that we
2434  * always output a /set record for the first directory.
2435  */
2436  if (((KF_ISSET(keys, UNAME) || KF_ISSET(keys, UID)) && (fts->sb.st_uid != sb.st_uid))
2437  || ((KF_ISSET(keys, GNAME) || KF_ISSET(keys, GID)) && (fts->sb.st_gid != sb.st_gid))
2438  || (KF_ISSET(keys, MODE) && (fts->sb.st_mode != sb.st_mode))
2439 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2440  || (KF_ISSET(keys, FLAGS) && (fts->sb.st_flags != sb.st_flags))
2441 #endif
2442  || fts->sb_is_valid == 0)
2443  {
2444  fts->sb_is_valid = 1;
2445  if (MF_ISSET(DIRSONLY))
2446  (void) printf("/set type=dir");
2447  else
2448  (void) printf("/set type=file");
2449  if (KF_ISSET(keys, UNAME)) {
2450  const char * uname = uidToUname(sb.st_uid);
2451  if (uname != NULL)
2452  (void) printf(" uname=%s", uname);
2453  else if (MF_ISSET(WARN))
2454  fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
2455  __progname, (unsigned long) sb.st_uid);
2456  else
2457  mtree_error("could not get uname for uid=%lu",
2458  (unsigned long)sb.st_uid);
2459  }
2460  if (KF_ISSET(keys, UID))
2461  (void) printf(" uid=%lu", (unsigned long)sb.st_uid);
2462  if (KF_ISSET(keys, GNAME)) {
2463  const char * gname = gidToGname(sb.st_gid);
2464  if (gname != NULL)
2465  (void) printf(" gname=%s", gname);
2466  else if (MF_ISSET(WARN))
2467  fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
2468  __progname, (unsigned long) sb.st_gid);
2469  else
2470  mtree_error("could not get gname for gid=%lu",
2471  (unsigned long) sb.st_gid);
2472  }
2473  if (KF_ISSET(keys, GID))
2474  (void) printf(" gid=%lu", (unsigned long)sb.st_gid);
2475  if (KF_ISSET(keys, MODE))
2476  (void) printf(" mode=%#o", (unsigned)sb.st_mode);
2477  if (KF_ISSET(keys, NLINK))
2478  (void) printf(" nlink=1");
2479 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2480  if (KF_ISSET(keys, FLAGS)) {
2481  const char * fflags = flags_to_string(sb.st_flags);
2482  (void) printf(" flags=%s", fflags);
2483  fflags = _free(fflags);
2484  }
2485 #endif
2486  (void) printf("\n");
2487  fts->sb = sb; /* structure assignment */
2488  }
2489  return (0);
2490 }
2491 
2492 #define CWALKINDENTNAMELEN 15
2493 #define MAXLINELEN 80
2494 
2495 
2496 static void
2497 output(int indent, int * offset, const char * fmt, ...)
2498  /*@globals fileSystem @*/
2499  /*@modifies *offset, fileSystem @*/
2500 {
2501  char buf[1024];
2502  va_list ap;
2503 
2504  va_start(ap, fmt);
2505  (void) vsnprintf(buf, sizeof(buf), fmt, ap);
2506  va_end(ap);
2507 
2508  if (*offset + strlen(buf) > MAXLINELEN - 3) {
2509  (void)printf(" \\\n%*s", CWALKINDENTNAMELEN + indent, "");
2510  *offset = CWALKINDENTNAMELEN + indent;
2511  }
2512  *offset += printf(" %s", buf) + 1;
2513 }
2514 
2515 static void
2516 mtreeVisitF(rpmfts fts)
2517  /*@globals errno, h_errno, fileSystem, internalState @*/
2518  /*@modifies errno, fileSystem, internalState @*/
2519 {
2520  enum mtreeKeys_e keys = fts->keys;
2521  const char * fts_accpath = fts->p->fts_accpath;
2522  unsigned short fts_info = fts->p->fts_info;
2523  struct stat *const st = fts->p->fts_statp;
2524  int indent = (MF_ISSET(INDENT) ? fts->p->fts_level * 4 : 0);
2525  int offset;
2526 
2527  { const char * fts_name = fts->p->fts_name;
2528  size_t fts_namelen = fts->p->fts_namelen;
2529  char * escname;
2530 
2531  /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
2532  if (fts->p->fts_level == 0 && fts_namelen == 0) {
2533  fts_name = ".";
2534  fts_namelen = sizeof(".") - 1;
2535  }
2536 
2537  escname = xmalloc(fts_namelen * 4 + 1);
2538  /* XXX TODO: Mac OS X uses VIS_GLOB as well */
2539  (void) strvis(escname, fts_name, VIS_WHITE | VIS_OCTAL);
2540 
2541  if (MF_ISSET(INDENT) || S_ISDIR(st->st_mode))
2542  offset = printf("%*s%s", indent, "", escname);
2543  else
2544  offset = printf("%*s %s", indent, "", escname);
2545  escname = _free(escname);
2546  }
2547 
2548  if (offset > (CWALKINDENTNAMELEN + indent))
2549  offset = MAXLINELEN;
2550  else
2551  offset += printf("%*s", (CWALKINDENTNAMELEN + indent) - offset, "");
2552 
2553  if (!S_ISREG(st->st_mode) && !MF_ISSET(DIRSONLY))
2554  output(indent, &offset, "type=%s", inotype(st->st_mode));
2555  if (st->st_uid != fts->sb.st_uid) {
2556  if (KF_ISSET(keys, UNAME)) {
2557  const char * uname = uidToUname(st->st_uid);
2558  if (uname != NULL)
2559  output(indent, &offset, "uname=%s", uname);
2560  else if (MF_ISSET(WARN))
2561  fprintf(stderr, _("%s: Could not get uname for uid=%lu\n"),
2562  __progname, (unsigned long) st->st_uid);
2563  else
2564  mtree_error("could not get uname for uid=%lu",
2565  (unsigned long)st->st_uid);
2566  }
2567  if (KF_ISSET(keys, UID))
2568  output(indent, &offset, "uid=%u", st->st_uid);
2569  }
2570  if (st->st_gid != fts->sb.st_gid) {
2571  if (KF_ISSET(keys, GNAME)) {
2572  const char * gname = gidToGname(st->st_gid);
2573  if (gname != NULL)
2574  output(indent, &offset, "gname=%s", gname);
2575  else if (MF_ISSET(WARN))
2576  fprintf(stderr, _("%s: Could not get gname for gid=%lu\n"),
2577  __progname, (unsigned long) st->st_gid);
2578  else
2579  mtree_error("Could not get gname for gid=%lu",
2580  (unsigned long) st->st_gid);
2581  }
2582  if (KF_ISSET(keys, GID))
2583  output(indent, &offset, "gid=%lu", (unsigned long)st->st_gid);
2584  }
2585  if (KF_ISSET(keys, MODE) && (st->st_mode & MBITS) != fts->sb.st_mode)
2586  output(indent, &offset, "mode=%#o", (st->st_mode & MBITS));
2587  if (KF_ISSET(keys, NLINK) && st->st_nlink != 1)
2588  output(indent, &offset, "nlink=%lu", (unsigned long)st->st_nlink);
2589  if (KF_ISSET(keys, SIZE) && S_ISREG(st->st_mode))
2590  output(indent, &offset, "size=%llu", (unsigned long long)st->st_size);
2591  if (KF_ISSET(keys, TIME)) {
2592  struct timeval tv;
2593 #if defined(TIMESPEC_TO_TIMEVAL)
2594  TIMESPEC_TO_TIMEVAL(&tv, &st->st_mtimespec);
2595 #else
2596  tv.tv_sec = (long)st->st_mtime;
2597  tv.tv_usec = 0L;
2598 #endif
2599  output(indent, &offset, "time=%lu.%lu",
2600  (unsigned long) tv.tv_sec,
2601  (unsigned long) tv.tv_usec);
2602  }
2603 
2604  /* Only files can have digests. */
2605  if (S_ISREG(st->st_mode)) {
2606 
2607  /* Any digests to calculate? */
2608  if (KF_ISSET(keys, CKSUM) || fts->algos != NULL) {
2609  FD_t fd = Fopen(fts_accpath, "r.ufdio");
2610  uint32_t len, val;
2611  int i;
2612 
2613  if (fd == NULL || Ferror(fd)) {
2614 #ifdef NOTYET /* XXX can't exit in a library API. */
2615  (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
2616  __progname, fts_accpath, Fstrerror(fd));
2617  goto cleanup;
2618 #else
2619  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2620  /*@notreached@*/
2621 #endif
2622  }
2623 
2624  /* Setup all digest calculations. Reversed order is effete ... */
2625  if (fts->algos != NULL)
2626  for (i = fts->algos->nvals; i-- > 0;)
2627  fdInitDigest(fd, fts->algos->vals[i], 0);
2628 
2629  /* Compute the cksum and digests. */
2630  if (KF_ISSET(keys, CKSUM))
2631  i = crc(fd, &val, &len);
2632  else {
2633  char buffer[16 * 1024];
2634  while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
2635  {};
2636  i = (Ferror(fd) ? 1 : 0);
2637  }
2638  if (i) {
2639 #ifdef NOTYET /* XXX can't exit in a library API. */
2640  (void) fprintf(stderr, _("%s: %s: cksum: %s\n"),
2641  __progname, fts_accpath, Fstrerror(fd));
2642 #else
2643  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2644  /*@notreached@*/
2645 #endif
2646  goto cleanup;
2647  }
2648 
2649  /* Output cksum. */
2650  if (KF_ISSET(keys, CKSUM)) {
2651  output(indent, &offset, "cksum=%lu", (unsigned long)val);
2652  }
2653 
2654  /* Output all the digests. */
2655  if (fts->algos != NULL)
2656  for (i = 0; i < (int) fts->algos->nvals; i++) {
2657  static int asAscii = 1;
2658  const char * digest = NULL;
2659  size_t digestlen = 0;
2660  uint32_t algo;
2661 
2662  algo = fts->algos->vals[i];
2663  fdFiniDigest(fd, algo, &digest, &digestlen, asAscii);
2664 #ifdef NOTYET /* XXX can't exit in a library API. */
2665 assert(digest != NULL);
2666 #else
2667  if (digest == NULL)
2668  mtree_error("%s: %s", fts_accpath, Fstrerror(fd));
2669 #endif
2670  { const char * tagname = algo2tagname(algo);
2671  if (tagname != NULL)
2672  output(indent, &offset, "%s=%s", tagname, digest);
2673  }
2674  digest = _free(digest);
2675  digestlen = 0;
2676  }
2677 
2678 cleanup: /* Accumulate statistics and clean up. */
2679  if (fd != NULL) {
2680  (void) rpmswAdd(&dc_readops, fdstat_op(fd, FDSTAT_READ));
2681  (void) rpmswAdd(&dc_digestops, fdstat_op(fd, FDSTAT_DIGEST));
2682  (void) Fclose(fd);
2683  fd = NULL;
2684  }
2685  }
2686  }
2687 
2688  if (KF_ISSET(keys, SLINK) && (fts_info == FTS_SL || fts_info == FTS_SLNONE))
2689  {
2690  const char * name = rlink(fts_accpath);
2691  char * escname = xmalloc(strlen(name) * 4 + 1);
2692  (void) strvis(escname, name, VIS_WHITE | VIS_OCTAL);
2693  output(indent, &offset, "link=%s", escname);
2694  escname = _free(escname);
2695  }
2696 
2697  if (KF_ISSET(keys, FLAGS) && !S_ISLNK(st->st_mode)) {
2698 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
2699  char * fflags = fflagstostr(st->st_flags);
2700 
2701  if (fflags != NULL && fflags[0] != '\0')
2702  output(indent, &offset, "flags=%s", fflags);
2703  else
2704  output(indent, &offset, "flags=none");
2705  free(fflags);
2706 #else
2707  output(indent, &offset, "flags=none");
2708 #endif
2709  }
2710  (void) putchar('\n');
2711 }
2712 
2713 /*==============================================================*/
2714 
2715 /*
2716  * Copyright 2000 Massachusetts Institute of Technology
2717  *
2718  * Permission to use, copy, modify, and distribute this software and
2719  * its documentation for any purpose and without fee is hereby
2720  * granted, provided that both the above copyright notice and this
2721  * permission notice appear in all copies, that both the above
2722  * copyright notice and this permission notice appear in all
2723  * supporting documentation, and that the name of M.I.T. not be used
2724  * in advertising or publicity pertaining to distribution of the
2725  * software without specific, written prior permission. M.I.T. makes
2726  * no representations about the suitability of this software for any
2727  * purpose. It is provided "as is" without express or implied
2728  * warranty.
2729  *
2730  * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
2731  * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
2732  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
2733  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
2734  * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2735  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2736  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
2737  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
2738  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2739  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2740  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2741  * SUCH DAMAGE.
2742  */
2743 
2744 /*
2745  * We're assuming that there won't be a whole lot of excludes,
2746  * so it's OK to use a stupid algorithm.
2747  */
2748 
2749 /*@mayexit@*/
2750 static void
2751 mtreeReadExcludes(const char * fn)
2752  /*@globals excludes, h_errno, fileSystem, internalState @*/
2753  /*@modifies excludes, fileSystem, internalState @*/
2754 {
2755  FD_t fd = Fopen(fn, "r.fpio");
2756  FILE *fp;
2757  char buffer[16 * 1024];
2758 
2759  if (fd == NULL || Ferror(fd) || (fp = fdGetFILE(fd)) == NULL) {
2760  fprintf(stderr, _("%s: open of %s failed: %s\n"), __progname,
2761  fn, Fstrerror(fd));
2762  if (fd != NULL) (void) Fclose(fd);
2763  exit(EXIT_FAILURE);
2764  }
2765 
2766  while (fgets(buffer, (int)sizeof(buffer), fp) != NULL) {
2767  struct exclude *e;
2768  char * line;
2769  size_t len;
2770 
2771  buffer[sizeof(buffer)-1] = '\0';
2772  for (line = buffer; *line != '\0'; line++)
2773  if (strchr(" \t\n\r", line[1]) == NULL) /*@innerbreak@*/ break;
2774  if (*line == '\0' || *line == '#')
2775  continue;
2776  for (len = strlen(line); len > 0; len--)
2777  if (strchr(" \t\n\r", line[len-1]) == NULL) /*@innerbreak@*/ break;
2778  if (len == 0)
2779  continue;
2780 
2781  e = xmalloc(sizeof(*e));
2782  e->glob = xstrdup(line);
2783  e->pathname = (strchr(line, '/') != NULL ? 1 : 0);
2784 /*@-immediatetrans@*/
2785  RPM_LIST_INSERT_HEAD(&excludes, e, link);
2786 /*@=immediatetrans@*/
2787  }
2788  if (fd != NULL)
2789  (void) Fclose(fd);
2790 /*@-compmempass -nullstate @*/
2791  return;
2792 /*@=compmempass =nullstate @*/
2793 }
2794 
2795 static int
2796 mtreeCheckExcludes(const char *fname, const char *path)
2797  /*@*/
2798 {
2799  struct exclude *e;
2800 
2801  /* fnmatch(3) has a funny return value convention... */
2802 #define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
2803 
2804 /*@-predboolptr@*/
2805  RPM_LIST_FOREACH(e, &excludes, link) {
2806  if ((e->pathname && MATCH(e->glob, path)) || MATCH(e->glob, fname))
2807  return 1;
2808  }
2809 /*@=predboolptr@*/
2810  return 0;
2811 }
2812 
2813 /*==============================================================*/
2814 
2815 static int
2816 dsort(const FTSENT ** a, const FTSENT ** b)
2817  /*@*/
2818 {
2819  if (S_ISDIR((*a)->fts_statp->st_mode)) {
2820  if (!S_ISDIR((*b)->fts_statp->st_mode))
2821  return 1;
2822  } else if (S_ISDIR((*b)->fts_statp->st_mode))
2823  return -1;
2824  return strcmp((*a)->fts_name, (*b)->fts_name);
2825 }
2826 
2827 #if defined(_RPMFI_INTERNAL)
2828 
2834 static int chkSuffix(const char * fn, const char * suffix)
2835  /*@*/
2836 {
2837  size_t flen = strlen(fn);
2838  size_t slen = strlen(suffix);
2839  return (flen > slen && !strcmp(fn + flen - slen, suffix));
2840 }
2841 
2842 static int _rpmfiStat(const char * path, struct stat * st)
2843  /*@globals fileSystem @*/
2844  /*@modifies fileSystem @*/
2845 {
2846  rpmfts fts = _rpmfts;
2847  rpmfi fi = _rpmfts->fi;
2848  size_t len = strlen(fts->paths[0]);
2849  int rc;
2850 
2851  rc = rpmfiStat(fi, path+len, st);
2852 
2853 if (_fts_debug)
2854 fprintf(stderr, "*** _rpmfiStat(%s, %p) fi %p rc %d\n", path+len, st, fi, rc);
2855 
2856  return rc;
2857 }
2858 
2859 static int _rpmfiClosedir(/*@only@*/ DIR * dir)
2860  /*@globals fileSystem @*/
2861  /*@modifies dir, fileSystem @*/
2862 {
2863  rpmfi fi = _rpmfts->fi;
2864 
2865 if (_fts_debug)
2866 fprintf(stderr, "*** _rpmfiClosedir(%p) fi %p\n", dir, fi);
2867 
2868  return avClosedir(dir);
2869 }
2870 
2871 static /*@null@*/ struct dirent * _rpmfiReaddir(DIR * dir)
2872  /*@globals fileSystem @*/
2873  /*@modifies fileSystem @*/
2874 {
2875  rpmfi fi = _rpmfts->fi;
2876  struct dirent * dp = (struct dirent *) avReaddir(dir);
2877 
2878 if (_fts_debug)
2879 fprintf(stderr, "*** _rpmfiReaddir(%p) fi %p %p \"%s\"\n", dir, fi, dp, (dp != NULL ? dp->d_name : ""));
2880 
2881 /*@-dependenttrans@*/
2882  return dp;
2883 /*@=dependenttrans@*/
2884 }
2885 
2886 static /*@null@*/
2887 uint8_t * rpmfiParentDirNotWithin(rpmfi fi)
2888  /*@*/
2889 {
2890  size_t * dnlens = xmalloc(fi->dc * sizeof(*dnlens));
2891  uint8_t * noparent = memset(xmalloc(fi->dc), 1, fi->dc);
2892  int i, j;
2893 
2894  for (i = 0; i < (int)fi->dc; i++)
2895  dnlens[i] = strlen(fi->dnl[i]);
2896 
2897  /* Mark parent directories that are not contained within same package. */
2898  for (i = 0; i < (int)fi->fc; i++) {
2899  size_t dnlen, bnlen;
2900 
2901  if (!S_ISDIR(fi->fmodes[i]))
2902  continue;
2903 
2904  dnlen = dnlens[fi->dil[i]];
2905  bnlen = strlen(fi->bnl[i]);
2906 
2907  for (j = 0; j < (int)fi->dc; j++) {
2908 
2909  if (!noparent[j] || j == (int)fi->dil[i])
2910  /*@innercontinue@*/ continue;
2911  if (dnlens[j] != (dnlen+bnlen+1))
2912  /*@innercontinue@*/ continue;
2913  if (strncmp(fi->dnl[j], fi->dnl[fi->dil[i]], dnlen))
2914  /*@innercontinue@*/ continue;
2915  if (strncmp(fi->dnl[j]+dnlen, fi->bnl[i], bnlen))
2916  /*@innercontinue@*/ continue;
2917  if (fi->dnl[j][dnlen+bnlen] != '/' || fi->dnl[j][dnlen+bnlen+1] != '\0')
2918  /*@innercontinue@*/ continue;
2919 
2920  /* This parent directory is contained within the package. */
2921  noparent[j] = (uint8_t)0;
2922  /*@innerbreak@*/ break;
2923  }
2924  }
2925  dnlens = _free(dnlens);
2926  return noparent;
2927 }
2928 
2929 static Header rpmftsReadHeader(rpmfts fts, const char * path)
2930  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
2931  /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
2932 {
2933  FD_t fd = Fopen(path, "r.ufdio");
2934  Header h = NULL;
2935 
2936  if (fd != NULL) {
2937  /* XXX what if path needs expansion? */
2938  rpmRC rpmrc = rpmReadPackageFile(fts->ts, fd, path, &h);
2939 
2940  (void) Fclose(fd);
2941 
2942  switch (rpmrc) {
2943  case RPMRC_NOTFOUND:
2944  /* XXX Read a package manifest. Restart ftswalk on success. */
2945  case RPMRC_FAIL:
2946  default:
2947  (void)headerFree(h);
2948  h = NULL;
2949  break;
2950  case RPMRC_NOTTRUSTED:
2951  case RPMRC_NOKEY:
2952  case RPMRC_OK:
2953  break;
2954  }
2955  }
2956  return h;
2957 }
2958 
2959 static /*@null@*/ rpmfi rpmftsLoadFileInfo(rpmfts fts, const char * path)
2960  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
2961  /*@modifies fts, rpmGlobalMacroContext, fileSystem, internalState @*/
2962 {
2963  char * fn = xstrdup(path);
2964  size_t nb = strlen(fn);
2965  Header h = NULL;
2966 
2967  fn[nb-1] = '\0'; /* XXX trim pesky trailing '/' */
2968  h = rpmftsReadHeader(fts, fn);
2969  fn = _free(fn);
2970 
2971  if (h != NULL) {
2972  fts->fi = rpmfiNew(fts->ts, h, RPMTAG_BASENAMES, 0);
2973  (void)headerFree(h);
2974  h = NULL;
2975  }
2976  return fts->fi;
2977 }
2978 
2979 static /*@null@*/ DIR * _rpmfiOpendir(const char * path)
2980  /*@globals _rpmfts, rpmGlobalMacroContext, h_errno,
2981  fileSystem, internalState @*/
2982  /*@modifies _rpmfts, rpmGlobalMacroContext,
2983  fileSystem, internalState @*/
2984 {
2985  rpmfts fts = _rpmfts;
2986  DIR * dir = NULL;
2987 
2988  if (fts->ts == NULL)
2989  fts->ts = rpmtsCreate();
2990 
2991  if (fts->fi == NULL) {
2992  rpmfi fi = rpmftsLoadFileInfo(fts, path);
2993  uint8_t * noparent = rpmfiParentDirNotWithin(fi);
2994  uint16_t * fmodes = xcalloc(rpmfiFC(fi)+1, sizeof(*fmodes));
2995  const char ** fnames = NULL;
2996  int ac = 0;
2997  int i;
2998 
2999  /* Collect top level files/dirs from the package. */
3000  fi = rpmfiInit(fi, 0);
3001  while ((i = rpmfiNext(fi)) >= 0) {
3002  int xx;
3003  if (!S_ISDIR(fi->fmodes[i]) && !noparent[fi->dil[i]])
3004  continue;
3005  xx = argvAdd(&fnames, rpmfiFN(fi));
3006  fmodes[ac++] = fi->fmodes[i];
3007  }
3008 
3009  dir = (DIR *) avOpendir(path, fnames, fmodes);
3010 
3011  fnames = argvFree(fnames);
3012  fmodes = _free(fmodes);
3013  noparent = _free(noparent);
3014 
3015  } else {
3016  const char * dn = path + strlen(fts->paths[0]);
3017 
3018  dir = rpmfiOpendir(fts->fi, dn);
3019  }
3020 
3021 if (_fts_debug)
3022 fprintf(stderr, "*** _rpmfiOpendir(%s) dir %p\n", path, dir);
3023 
3024  return dir;
3025 }
3026 
3027 #define ALIGNBYTES (__alignof__ (long double) - 1)
3028 #define ALIGN(p) (((unsigned long int) (p) + ALIGNBYTES) & ~ALIGNBYTES)
3029 
3030 static FTSENT *
3031 fts_alloc(FTS * sp, const char * name, int namelen)
3032  /*@*/
3033 {
3034  register FTSENT *p;
3035  size_t len;
3036 
3037  /*
3038  * The file name is a variable length array and no stat structure is
3039  * necessary if the user has set the nostat bit. Allocate the FTSENT
3040  * structure, the file name and the stat structure in one chunk, but
3041  * be careful that the stat structure is reasonably aligned. Since the
3042  * fts_name field is declared to be of size 1, the fts_name pointer is
3043  * namelen + 2 before the first possible address of the stat structure.
3044  */
3045  len = sizeof(*p) + namelen;
3046 /*@-sizeoftype@*/
3047  if (!(sp->fts_options & FTS_NOSTAT))
3048  len += sizeof(*p->fts_statp) + ALIGNBYTES;
3049 /*@=sizeoftype@*/
3050  p = xmalloc(len);
3051 
3052  /* Copy the name and guarantee NUL termination. */
3053  memmove(p->fts_name, name, namelen);
3054  p->fts_name[namelen] = '\0';
3055 
3056 /*@-sizeoftype@*/
3057  if (!(sp->fts_options & FTS_NOSTAT))
3058  p->fts_statp = (struct stat *)ALIGN(p->fts_name + namelen + 2);
3059 /*@=sizeoftype@*/
3060  p->fts_namelen = namelen;
3061  p->fts_path = sp->fts_path;
3062  p->fts_errno = 0;
3063  p->fts_flags = 0;
3064  p->fts_instr = FTS_NOINSTR;
3065  p->fts_number = 0;
3066  p->fts_pointer = NULL;
3067  return (p);
3068 }
3069 
3070 static void _rpmfiSetFts(rpmfts fts)
3071  /*@globals h_errno, fileSystem, internalState @*/
3072  /*@modifies fts, fileSystem, internalState @*/
3073 {
3074  char *const * argv = (char *const *) fts->paths;
3075  FTS * sp = fts->t;
3076 #ifdef NOTYET
3077  int (*compar) (const FTSENT **, const FTSENT **) = sp->compar;
3078 #endif
3079  register FTSENT *p, *root;
3080  register int nitems;
3081  FTSENT *parent = NULL;
3082  FTSENT *tmp = NULL;
3083  size_t len;
3084 
3085 if (_fts_debug)
3086 fprintf(stderr, "*** _rpmfiSetFts(%p)\n", fts);
3087 
3088 /*@-type@*/
3089  sp->fts_opendir = _rpmfiOpendir;
3090  sp->fts_readdir = _rpmfiReaddir;
3091  sp->fts_closedir = _rpmfiClosedir;
3092  sp->fts_stat = _rpmfiStat;
3093  sp->fts_lstat = _rpmfiStat;
3094 /*@=type@*/
3095 
3096  /* Allocate/initialize root's parent. */
3097  if (*argv != NULL) {
3098  parent = fts_alloc(sp, "", 0);
3099  parent->fts_level = FTS_ROOTPARENTLEVEL;
3100  }
3101 
3102  /* Allocate/initialize root(s). */
3103  for (root = NULL, nitems = 0; *argv != NULL; ++argv, ++nitems) {
3104  len = strlen(*argv);
3105 
3106  p = fts_alloc(sp, *argv, (int)len);
3107  p->fts_level = FTS_ROOTLEVEL;
3108  p->fts_parent = parent;
3109  p->fts_accpath = p->fts_name;
3110 #ifdef NOTYET
3111  p->fts_info = fts_stat(sp, p, ISSET(FTS_COMFOLLOW));
3112 
3113  /* Command-line "." and ".." are real directories. */
3114  if (p->fts_info == FTS_DOT)
3115  p->fts_info = FTS_D;
3116 
3117 #else
3118  p->fts_name[len-1] = '\0';
3119  { struct stat * st = p->fts_statp;
3120  int xx = Stat(p->fts_accpath, st);
3121  xx = xx;
3122  st->st_mode &= ~S_IFMT;
3123  st->st_mode |= S_IFDIR;
3124  p->fts_dev = st->st_dev;
3125  p->fts_ino = st->st_ino;
3126  p->fts_nlink = st->st_nlink;
3127  }
3128  p->fts_name[len-1] = '/';
3129  p->fts_info = FTS_D;
3130 #endif
3131 
3132 #ifdef NOTYET
3133  /*
3134  * If comparison routine supplied, traverse in sorted
3135  * order; otherwise traverse in the order specified.
3136  */
3137  if (compar) {
3138  p->fts_link = root;
3139  root = p;
3140  } else
3141 #endif
3142  {
3143  p->fts_link = NULL;
3144  if (root == NULL)
3145  tmp = root = p;
3146  else {
3147  if (tmp != NULL) /* XXX can't happen */
3148  tmp->fts_link = p;
3149  tmp = p;
3150  }
3151  }
3152  }
3153 #ifdef NOTYET
3154  if (compar && nitems > 1)
3155  root = fts_sort(sp, root, nitems);
3156 #endif
3157 
3158  /*
3159  * Allocate a dummy pointer and make fts_read think that we've just
3160  * finished the node before the root(s); set p->fts_info to FTS_INIT
3161  * so that everything about the "current" node is ignored.
3162  */
3163  sp->fts_cur = _free(sp->fts_cur);
3164  sp->fts_cur = fts_alloc(sp, "", 0);
3165  sp->fts_cur->fts_link = root;
3166  sp->fts_cur->fts_info = FTS_INIT;
3167 
3168  return;
3169 }
3170 #endif
3171 
3172 int
3173 mtreeCWalk(rpmfts fts)
3174 {
3175 #if defined(_RPMFI_INTERNAL)
3176  int isrpm = chkSuffix(fts->paths[0], ".rpm/");
3177 #else
3178  int isrpm = 0;
3179 #endif
3180  const char * empty = NULL;
3181  char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
3182  int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
3183  int rval = 0;
3184 
3185  fts->t = Fts_open(paths, ftsoptions, dsort);
3186  if (fts->t == NULL)
3187  mtree_error("Fts_open: %s", strerror(errno));
3188 
3189 #if defined(_RPMFI_INTERNAL)
3190  if (isrpm) {
3191  fts->keys &= ~MTREE_KEYS_SLINK; /* XXX no rpmfiReadlink yet. */
3192  _rpmfiSetFts(fts);
3193  }
3194 #endif
3195 
3196  while ((fts->p = Fts_read(fts->t)) != NULL) {
3197  int indent = 0;
3198  if (MF_ISSET(INDENT))
3199  indent = fts->p->fts_level * 4;
3200  if (mtreeCheckExcludes(fts->p->fts_name, fts->p->fts_path)) {
3201  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3202  continue;
3203  }
3204  switch(fts->p->fts_info) {
3205  case FTS_D:
3206  if (!MF_ISSET(DIRSONLY))
3207  (void) printf("\n");
3208  if (!MF_ISSET(NOCOMMENT))
3209  (void) printf("# %s\n", fts->p->fts_path);
3210  (void) mtreeVisitD(fts);
3211  mtreeVisitF(fts);
3212  /*@switchbreak@*/ break;
3213  case FTS_DP:
3214  if (!MF_ISSET(NOCOMMENT) && (fts->p->fts_level > 0))
3215  (void) printf("%*s# %s\n", indent, "", fts->p->fts_path);
3216  (void) printf("%*s..\n", indent, "");
3217  if (!MF_ISSET(DIRSONLY))
3218  (void) printf("\n");
3219  /*@switchbreak@*/ break;
3220  case FTS_DNR:
3221  case FTS_ERR:
3222  case FTS_NS:
3223  (void) fprintf(stderr, "%s: %s: %s\n", __progname,
3224  fts->p->fts_path, strerror(fts->p->fts_errno));
3225  /*@switchbreak@*/ break;
3226  default:
3227  if (!MF_ISSET(DIRSONLY))
3228  mtreeVisitF(fts);
3229  /*@switchbreak@*/ break;
3230  }
3231  }
3232  (void) Fts_close(fts->t);
3233  fts->p = NULL;
3234  fts->t = NULL;
3235  return rval;
3236 }
3237 
3238 /*==============================================================*/
3239 
3240 void
3241 mtreeMiss(rpmfts fts, /*@null@*/ NODE * p, char * tail)
3242 {
3243  int create;
3244  char *tp;
3245  const char *type;
3246 
3247  for (; p != NULL; p = p->next) {
3248  /* XXX Mac OS X doesn't do this. */
3249  if (KF_ISSET(p->flags, OPT) && !KF_ISSET(p->flags, VISIT))
3250  continue;
3251  if (p->type != F_DIR && (MF_ISSET(DIRSONLY) || KF_ISSET(p->flags, VISIT)))
3252  continue;
3253  (void) strcpy(tail, p->name);
3254  if (!KF_ISSET(p->flags, VISIT)) {
3255  /* Don't print missing message if file exists as a
3256  symbolic link and the -q flag is set. */
3257  struct stat sb;
3258 
3259  if (MF_ISSET(QUIET) && Stat(fts->path, &sb) == 0)
3260  p->flags |= MTREE_KEYS_VISIT;
3261  else
3262  (void) printf(_("missing: %s"), fts->path);
3263  }
3264  if (p->type != F_DIR && p->type != F_LINK) {
3265  (void) putchar('\n');
3266  continue;
3267  }
3268 
3269  create = 0;
3270  type = (p->type == F_LINK ? "symlink" : "directory");
3271  if (!KF_ISSET(p->flags, VISIT) && MF_ISSET(UPDATE)) {
3272  if (!(KF_ISSET(p->flags, UID) && KF_ISSET(p->flags, UNAME)))
3273  (void) printf(_(" (%s not created: user not specified)"), type);
3274  else if (!(KF_ISSET(p->flags, GID) || KF_ISSET(p->flags, GNAME)))
3275  (void) printf(_(" (%s not created: group not specified))"), type);
3276  else if (p->type == F_LINK) {
3277  if (Symlink(p->slink, fts->path))
3278  (void) printf(_(" (%s not created: %s)\n"), type,
3279  strerror(errno));
3280  else
3281  (void) printf(_(" (%s created)\n"), type);
3282  if (lchown(fts->path, p->sb.st_uid, p->sb.st_gid) == -1) {
3283  const char * what;
3284  int serr = errno;
3285 
3286  if (p->sb.st_uid == (uid_t)-1)
3287  what = "group";
3288  else if (lchown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
3289  what = "user & group";
3290  else {
3291  what = "user";
3292  errno = serr;
3293  }
3294  (void) printf(_("%s: %s not modified: %s\n"),
3295  fts->path, what, strerror(errno));
3296  }
3297  continue;
3298  } else if (!KF_ISSET(p->flags, MODE))
3299  (void) printf(_(" (%s not created: mode not specified)"), type);
3300  else if (Mkdir(fts->path, S_IRWXU))
3301  (void) printf(_(" (%s not created: %s)"),type, strerror(errno));
3302  else {
3303  create = 1;
3304  (void) printf(_(" (%s created)"), type);
3305  }
3306  }
3307 
3308  if (!KF_ISSET(p->flags, VISIT))
3309  (void) putchar('\n');
3310 
3311  for (tp = tail; *tp != '\0'; ++tp);
3312  *tp = '/';
3313  mtreeMiss(fts, p->child, tp + 1);
3314  *tp = '\0';
3315 
3316  if (!create)
3317  continue;
3318  if (Chown(fts->path, p->sb.st_uid, p->sb.st_gid)) {
3319  const char * what;
3320  int serr = errno;
3321 
3322  if (p->sb.st_uid == (uid_t)-1)
3323  what = "group";
3324  else if (Chown(fts->path, (uid_t)-1, p->sb.st_gid) == -1)
3325  what = "user & group";
3326  else {
3327  what = "user";
3328  errno = serr;
3329  }
3330  (void) printf(_("%s: %s not modified: %s\n"),
3331  fts->path, what, strerror(errno));
3332  continue;
3333  }
3334  if (Chmod(fts->path, p->sb.st_mode))
3335  (void) printf(_("%s: permissions not set: %s\n"),
3336  fts->path, strerror(errno));
3337 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
3338  if (KF_ISSET(p->flags, FLAGS) && p->sb.st_flags != 0 &&
3339  chflags(fts->path, p->sb.st_flags))
3340  (void) printf(_("%s: file flags not set: %s\n"),
3341  fts->path, strerror(errno));
3342 #endif
3343  }
3344 }
3345 
3346 int
3347 mtreeVWalk(rpmfts fts)
3348 {
3349 #if defined(_RPMFI_INTERNAL)
3350  int isrpm = chkSuffix(fts->paths[0], ".rpm/");
3351 #else
3352  int isrpm = 0;
3353 #endif
3354  const char * empty = NULL;
3355  char *const * paths = (char *const *) (isrpm ? &empty : fts->paths);
3356  int ftsoptions = fts->ftsoptions | (isrpm ? (FTS_NOCHDIR|FTS_COMFOLLOW) : 0);
3357  NODE * level = NULL;
3358  NODE * root = NULL;
3359  int specdepth = 0;
3360  int rval = 0;
3361 
3362  fts->t = Fts_open((char *const *)paths, ftsoptions, NULL);
3363  if (fts->t == NULL)
3364  mtree_error("Fts_open: %s", strerror(errno));
3365 
3366 #if defined(_RPMFI_INTERNAL)
3367  if (isrpm) {
3368  fts->keys &= ~MTREE_KEYS_SLINK; /* XXX no rpmfiReadlink yet. */
3369  _rpmfiSetFts(fts);
3370  }
3371 #endif
3372 
3373  while ((fts->p = Fts_read(fts->t)) != NULL) {
3374  const char * fts_name = fts->p->fts_name;
3375  size_t fts_namelen = fts->p->fts_namelen;
3376 
3377 #if 0
3378 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);
3379 #endif
3380  /* XXX fts(3) (and Fts(3)) have fts_name = "" with pesky trailing '/' */
3381  if (fts->p->fts_level == 0 && fts_namelen == 0) {
3382  fts_name = ".";
3383  fts_namelen = sizeof(".") - 1;
3384  }
3385 
3386  if (mtreeCheckExcludes(fts_name, fts->p->fts_path)) {
3387  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3388  continue;
3389  }
3390  switch(fts->p->fts_info) {
3391  case FTS_D:
3392  case FTS_SL:
3393  if (fts->p->fts_level == 0) {
3394 assert(specdepth == 0);
3395  if (root == NULL)
3396  level = root = fts->root;
3397  else if (root->next != fts->root)
3398  level = root = root->next;
3399 assert(level == level->parent);
3400  }
3401  /*@switchbreak@*/ break;
3402  case FTS_DP:
3403  if (specdepth > fts->p->fts_level) {
3404  for (level = level->parent; level->prev != NULL; level = level->prev);
3405  --specdepth;
3406  }
3407  continue;
3408  case FTS_DNR:
3409  case FTS_ERR:
3410  case FTS_NS:
3411  (void) fprintf(stderr, "%s: %s: %s\n", __progname,
3412  SKIPDOTSLASH(fts->p->fts_path),
3413  strerror(fts->p->fts_errno));
3414  continue;
3415  default:
3416  if (MF_ISSET(DIRSONLY))
3417  continue;
3418  }
3419 
3420  if (specdepth == fts->p->fts_level) {
3421  NODE *ep;
3422  for (ep = level; ep != NULL; ep = ep->next)
3423  if ((KF_ISSET(ep->flags, MAGIC) &&
3424 /*@-moduncon@*/
3425  !fnmatch(ep->name, fts_name, FNM_PATHNAME)) ||
3426 /*@=moduncon@*/
3427  !strcmp(ep->name, fts_name))
3428  {
3429  ep->flags |= MTREE_KEYS_VISIT;
3430  if (!KF_ISSET(ep->flags, NOCHANGE) &&
3431  compare(fts, ep))
3432  rval = MISMATCHEXIT;
3433  if (KF_ISSET(ep->flags, IGN))
3434  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3435  else
3436  if (ep->child && ep->type == F_DIR && fts->p->fts_info == FTS_D)
3437  {
3438  level = ep->child;
3439  ++specdepth;
3440  }
3441  /*@innerbreak@*/ break;
3442  }
3443  if (ep != NULL)
3444  continue;
3445  }
3446 
3447  if (!MF_ISSET(IGNORE)) {
3448  (void) printf("%s extra", SKIPDOTSLASH(fts->p->fts_path));
3449  if (MF_ISSET(REMOVE)) {
3450  if ((S_ISDIR(fts->p->fts_statp->st_mode)
3451  ? Rmdir : Unlink)(fts->p->fts_accpath)) {
3452  (void) printf(_(", not removed: %s"), strerror(errno));
3453  } else
3454  (void) printf(_(", removed"));
3455  }
3456  (void) putchar('\n');
3457  }
3458  (void) Fts_set(fts->t, fts->p, FTS_SKIP);
3459  }
3460  (void) Fts_close(fts->t);
3461  fts->p = NULL;
3462  fts->t = NULL;
3463  return rval;
3464 }
3465 
3466 /*==============================================================*/
3467 
3470 static void mtreeArgCallback(poptContext con,
3471  /*@unused@*/ enum poptCallbackReason reason,
3472  const struct poptOption * opt, const char * arg,
3473  /*@unused@*/ void * data)
3474  /*@globals _rpmfts, rpmioFtsOpts, h_errno, fileSystem, internalState @*/
3475  /*@modifies _rpmfts, rpmioFtsOpts, fileSystem, internalState @*/
3476 {
3477  char * p;
3478 
3479  /* XXX avoid accidental collisions with POPT_BIT_SET for flags */
3480  if (opt->arg == NULL)
3481  switch (opt->val) {
3482 
3483  case 'f':
3484  if (_rpmfts->spec1 == NULL) {
3485  if ((_rpmfts->spec1 = fopen(arg, "r")) != NULL)
3486  mtree_error("%s: %s", arg, strerror(errno));
3487  } else if (_rpmfts->spec2 == NULL) {
3488  if ((_rpmfts->spec2 = fopen(arg, "r")) != NULL)
3489  mtree_error("%s: %s", arg, strerror(errno));
3490  } else {
3491  /* XXX error message, too many -f options. */
3492  poptPrintUsage(con, stderr, 0);
3493  exit(EXIT_FAILURE);
3494  /*@notreached@*/
3495  }
3496  break;
3497  case 'k':
3498  /* XXX fts->keys = KEYDEFAULT in main(), clear default now. */
3499  _rpmfts->keys = MTREE_KEYS_TYPE;
3500  /*@fallthrough@*/
3501  case 'K':
3502 /*@-unrecog@*/
3503  while ((p = strsep((char **)&arg, " \t,")) != NULL) {
3504  uint32_t needvalue;
3505  enum mtreeKeys_e type = parsekey(p, &needvalue);
3506  if (type == 0) {
3507  /* XXX unknown key error. */
3508  continue;
3509  }
3510  _rpmfts->keys |= type;
3511  /* XXX dupes can occur */
3512  if (KF_ISSET(_rpmfts->keys, DIGEST) && needvalue)
3513  (void) argiAdd(&_rpmfts->algos, -1, (int)needvalue);
3514  }
3515 /*@=unrecog@*/
3516  break;
3517 
3518  /* XXX redundant with --logical. */
3519  case 'L':
3522  break;
3523  /* XXX redundant with --physical. */
3524  case 'P':
3527  break;
3528  case 'X':
3529  mtreeReadExcludes(arg);
3530  break;
3531 
3532  case '?':
3533  default:
3534  fprintf(stderr, _("%s: Unknown option -%c\n"), __progname, opt->val);
3535  poptPrintUsage(con, stderr, 0);
3536  exit(EXIT_FAILURE);
3537  /*@notreached@*/ break;
3538  }
3539 }
3540 
3541 /*@unchecked@*/ /*@observer@*/
3542 static struct poptOption optionsTable[] = {
3543 /*@-type@*/ /* FIX: cast? */
3544  { NULL, '\0', POPT_ARG_CALLBACK | POPT_CBFLAG_INC_DATA | POPT_CBFLAG_CONTINUE,
3545  mtreeArgCallback, 0, NULL, NULL },
3546 /*@=type@*/
3547 
3548  /* XXX redundant with --logical. */
3549  { NULL,'L', POPT_ARG_NONE, NULL, (int)'L',
3550  N_("Follow symlinks"), NULL },
3551  /* XXX redundant with --physical. */
3552  { NULL,'P', POPT_ARG_NONE, NULL, (int)'P',
3553  N_("Don't follow symlinks"), NULL },
3554  { NULL,'X', POPT_ARG_NONE, NULL, (int)'X',
3555  N_("Read fnmatch(3) exclude patterns from <file>"), N_("<file>") },
3556 
3557  { "create",'c', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_CREATE,
3558  N_("Print file tree specification to stdout"), NULL },
3559  { "dirs",'d', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_DIRSONLY,
3560  N_("Directories only"), NULL },
3561  { "ignore",'e', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_IGNORE,
3562  N_("Don't complain about files not in the specification"), NULL },
3563  { "file",'f', POPT_ARG_STRING, NULL, (int)'f',
3564  N_("Read file tree <spec>"), N_("<spec>") },
3565  { "indent",'i', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_INDENT,
3566  N_("Indent sub-directories"), NULL },
3567  { "add",'K', POPT_ARG_STRING, NULL, (int)'K',
3568  N_("Add <key> to specification"), N_("<key>") },
3569  { "key",'k', POPT_ARG_STRING, NULL, (int)'k',
3570  N_("Use \"type\" keywords instead"), N_("<key>") },
3571  { "loose",'l', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_LOOSE,
3572  N_("Loose permissions check"), NULL },
3573  { "nocomment",'n', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_NOCOMMENT,
3574  N_("Don't include sub-directory comments"), NULL },
3575  { "path",'p', POPT_ARG_ARGV, &__rpmfts.paths, 0,
3576  N_("Use <path> rather than current directory"), N_("<path>") },
3577  /* XXX --quiet collides w poptIO */
3578  { "quiet",'q', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_QUIET,
3579  N_("Quiet mode"), NULL },
3580  { "remove",'r', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_REMOVE,
3581  N_("Remove files not in specification"), NULL },
3582  { "seed",'s', POPT_ARG_INT, &__rpmfts.crc_total, 0,
3583  N_("Display crc for file(s) with <seed>"), N_("<seed>") },
3584  { "touch",'t', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_TOUCH,
3585  N_("Touch files iff timestamp differs"), NULL },
3586  { "update",'u', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_UPDATE,
3587  N_("Update owner/group/permissions to match specification"), NULL },
3588  { "mismatch",'U', POPT_BIT_SET, &mtreeFlags, (MTREE_FLAGS_UPDATE|MTREE_FLAGS_MISMATCHOK),
3589  N_("Same as -u, but ignore match status on exit"), NULL },
3590  { "warn",'w', POPT_BIT_SET, &mtreeFlags, MTREE_FLAGS_WARN,
3591  N_("Treat missing uid/gid as warning"), NULL },
3592  /* XXX duplicated with --xdev. */
3593  { "xdev",'x', POPT_BIT_SET, &rpmioFtsOpts, FTS_XDEV,
3594  N_("Don't cross mount points"), NULL },
3595 
3596  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
3597  N_("Fts(3) traversal options:"), NULL },
3598 
3599  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioDigestPoptTable, 0,
3600  N_("Available digests:"), NULL },
3601 
3602  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioAllPoptTable, 0,
3603  N_("Common options for all rpmio executables:"),
3604  NULL },
3605 
3606  POPT_AUTOALIAS
3607  POPT_AUTOHELP
3608 
3609  { NULL, (char)-1, POPT_ARG_INCLUDE_TABLE, NULL, 0,
3610  "\
3611 Usage: mtree [-cdeilnqrtUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n\
3612 ", NULL },
3613 
3614  POPT_TABLEEND
3615 };
3616 
3617 #if defined(__linux__)
3618 /*@null@*/ /*@observer@*/
3619 static const char *my_getlogin(void)
3620  /*@globals fileSystem @*/
3621  /*@modifies fileSystem @*/
3622 {
3623  const char *s = getlogin();
3624 
3625  if (s && *s) {
3626  return (char *) s;
3627  } else {
3628  struct passwd *pw = getpwuid(geteuid());
3629  char *ss = NULL;
3630 /*@-unrecog@*/
3631  if (pw != NULL && pw->pw_name != NULL && pw->pw_name[0] != '\0') {
3632  if (asprintf(&ss, _("(no controlling terminal) %s"), pw->pw_name) < 0) {
3633  perror("asprintf");
3634  return NULL;
3635  }
3636  } else {
3637  if (asprintf(&ss, _("(no controlling terminal) #%d"), geteuid()) < 0) {
3638  perror("asprintf");
3639  return NULL;
3640  }
3641  }
3642 /*@=unrecog@*/
3643  return ss;
3644  }
3645 }
3646 #define __getlogin my_getlogin
3647 #else
3648 #define __getlogin getlogin
3649 #endif
3650 
3651 int
3652 main(int argc, char *argv[])
3653  /*@globals _rpmfts, mtreeFlags, excludes, __assert_program_name,
3654  rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
3655  /*@modifies _rpmfts, mtreeFlags, excludes, __assert_program_name,
3656  rpmGlobalMacroContext, fileSystem, internalState @*/
3657 {
3658  rpmfts fts = _rpmfts;
3660  int rc = 1; /* assume failure */
3661  int i;
3662 
3663  __progname = "mtree";
3664 
3665  RPM_LIST_INIT(&excludes);
3667  fts->maxg = 5000;
3668  fts->maxu = 5000;
3669  fts->maxm = (MBITS + 1);
3670 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
3671  fts->maxf = 256;
3672  fts->sb.st_flags = 0xffffffff;
3673 #endif
3674 
3675  /* Process options. */
3676  optCon = rpmioInit(argc, argv, optionsTable);
3677 
3678  /* XXX ./rpmmtree w no args waits for stdin. poptPrintUsage more better. */
3679  argv = (char **) poptGetArgs(optCon);
3680  if (!(argv == NULL || argv[0] == NULL)) {
3681  poptPrintUsage(optCon, stderr, 0);
3682  goto exit;
3683  }
3684 
3685  if (MF_ISSET(LOOSE) && MF_ISSET(UPDATE))
3686  mtree_error("-l and -u flags are mutually exclusive");
3687 
3688  /*
3689  * Either FTS_PHYSICAL or FTS_LOGICAL must be set. Don't follow symlinks
3690  * unless explicitly overridden with FTS_LOGICAL.
3691  */
3692  fts->ftsoptions = rpmioFtsOpts;
3693  switch (fts->ftsoptions & (FTS_LOGICAL|FTS_PHYSICAL)) {
3694  case (FTS_LOGICAL|FTS_PHYSICAL):
3695  mtree_error("-L and -P flags are mutually exclusive");
3696  /*@notreached@*/ break;
3697  case 0:
3698  fts->ftsoptions |= FTS_PHYSICAL;
3699  break;
3700  }
3701 
3702  if (fts->paths == NULL || fts->paths[0] == NULL) {
3703  fts->paths = xcalloc(2, sizeof(fts->paths[0]));
3704  fts->paths[0] = xstrdup(".");
3705  }
3706 
3707  /* Use absolute paths since Chdir(2) is problematic with remote URI's */
3708  for (i = 0; fts->paths[i] != NULL; i++) {
3709  char fullpath[MAXPATHLEN];
3710  struct stat sb;
3711  const char * rpath;
3712  const char * lpath = NULL;
3713  int ut = urlPath(fts->paths[i], &lpath);
3714  size_t nb = (size_t)(lpath - fts->paths[i]);
3715  int isdir = (lpath[strlen(lpath)-1] == '/');
3716 
3717  /* Convert to absolute/clean/malloc'd path. */
3718  if (lpath[0] != '/') {
3719  /* XXX GLIBC: realpath(path, NULL) return malloc'd */
3720  rpath = Realpath(lpath, NULL);
3721  if (rpath == NULL)
3722  rpath = Realpath(lpath, fullpath);
3723  if (rpath == NULL)
3724  mtree_error("Realpath(%s): %s", lpath, strerror(errno));
3725  lpath = rpmGetPath(rpath, NULL);
3726  if (rpath != fullpath) /* XXX GLIBC extension malloc's */
3727  rpath = _free(rpath);
3728  } else
3729  lpath = rpmGetPath(lpath, NULL);
3730 
3731  /* Reattach the URI to the absolute/clean path. */
3732  /* XXX todo: rpmGenPath was confused by file:///path/file URI's. */
3733  switch (ut) {
3734  case URL_IS_DASH:
3735  case URL_IS_UNKNOWN:
3736  rpath = lpath;
3737  lpath = NULL;
3738  /*@switchbreak@*/ break;
3739  default:
3740  strncpy(fullpath, fts->paths[i], nb);
3741  fullpath[nb] = '\0';
3742  rpath = rpmGenPath(fullpath, lpath, NULL);
3743  lpath = _free(lpath);
3744  /*@switchbreak@*/ break;
3745  }
3746 
3747  /* Add a trailing '/' on directories. */
3748  lpath = (isdir || (!Stat(rpath, &sb) && S_ISDIR(sb.st_mode))
3749  ? "/" : NULL);
3750  fts->paths[i] = _free(fts->paths[i]);
3751  fts->paths[i] = rpmExpand(rpath, lpath, NULL);
3752  fts->fullpath = xstrdup(fts->paths[i]);
3753  rpath = _free(rpath);
3754  }
3755 
3756  /* XXX prohibits -s 0 invocation */
3757  if (fts->crc_total)
3758  mtreeFlags |= MTREE_FLAGS_SEEDED;
3759  fts->crc_total ^= 0xffffffff;
3760 
3761  (void) rpmswEnter(&dc_totalops, -1);
3762 
3763  if (MF_ISSET(CREATE)) {
3764  if (!MF_ISSET(NOCOMMENT)) {
3765  time_t clock;
3766  char host[MAXHOSTNAMELEN];
3767 
3768  (void) time(&clock);
3769  (void) gethostname(host, sizeof(host));
3770  (void) printf("#\t user: %s\n", __getlogin());
3771  (void) printf("#\tmachine: %s\n", host);
3772  for (i = 0; fts->paths[i] != NULL; i++)
3773  (void) printf("#\t tree: %s\n", fts->paths[i]);
3774  (void) printf("#\t date: %s", ctime(&clock));
3775  }
3776  rc = mtreeCWalk(fts);
3777  if (MF_ISSET(SEEDED) && KF_ISSET(fts->keys, CKSUM))
3778  (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
3779  fts->fullpath, (unsigned)fts->crc_total);
3780 
3781  } else {
3782  if (fts->spec2 != NULL) {
3783  rc = mtreeVSpec(fts);
3784  } else {
3785 /*@-evalorder@*/
3786  fts->root = mtreeSpec(fts, fts->spec1);
3787 /*@=evalorder@*/
3788  fts->path = xmalloc(MAXPATHLEN);
3789  rc = mtreeVWalk(fts);
3790  mtreeMiss(fts, fts->root, fts->path);
3791  fts->path = _free(fts->path);
3792  if (MF_ISSET(SEEDED))
3793  (void) fprintf(stderr, _("%s: %s checksum: %u\n"), __progname,
3794  fts->fullpath, (unsigned) fts->crc_total);
3795  }
3796  }
3797  if (MF_ISSET(MISMATCHOK) && (rc == MISMATCHEXIT))
3798  rc = 0;
3799 
3800  (void) rpmswExit(&dc_totalops, 0);
3802  rpmswPrint(" total:", &dc_totalops, NULL);
3803  rpmswPrint(" read:", &dc_readops, NULL);
3804  rpmswPrint("digest:", &dc_digestops, NULL);
3805  }
3806 
3807 exit:
3808 #if defined(_RPMFI_INTERNAL)
3809  (void)rpmtsFree(fts->ts);
3810  fts->ts = NULL;
3811  fts->fi = rpmfiFree(fts->fi);
3812  tagClean(NULL); /* Free header tag indices. */
3813 #endif
3814  if (fts->spec1 != NULL && fileno(fts->spec1) > 2) {
3815  (void) fclose(fts->spec1);
3816  fts->spec1 = NULL;
3817  }
3818  if (fts->spec2 != NULL && fileno(fts->spec2) > 2) {
3819  (void) fclose(fts->spec2);
3820  fts->spec2 = NULL;
3821  }
3822  fts->paths = argvFree(fts->paths);
3823 #if defined(HAVE_STRUCT_STAT_ST_FLAGS)
3824  fts->f = _free(fts->f);
3825 #endif
3826  fts->g = _free(fts->g);
3827  fts->m = _free(fts->m);
3828  fts->u = _free(fts->u);
3829  fts->fullpath = _free(fts->fullpath);
3830  /* XXX TODO: clean excludes */
3831 
3832  optCon = rpmioFini(optCon);
3833 
3834  return rc;
3835 }
const char * name
Definition: rpmmtree.c:328
static void fdInitDigest(FD_t fd, pgpHashAlgo hashalgo, int _flags)
Attach digest to fd.
static const char * suffix[]
Definition: rpmgrep.c:188
#define FTS_SLNONE
Definition: fts.h:141
#define ISSET(opt)
Definition: fts.c:196
int xx
Definition: spec.c:744
#define FTS_XDEV
Definition: fts.h:93
iter fi
Definition: fsm.c:170
poptContext rpmioInit(int argc, char *const argv[], struct poptOption *optionsTable)
Definition: poptIO.c:752
#define S_OCTAL2
Definition: rpmmtree.c:792
fts maxm
Definition: rpmmtree.c:3669
format
Definition: hdrfmt.c:5560
#define SKIPDOTSLASH(_f)
#define CWALKINDENTNAMELEN
static FTSENT * fts_sort(FTS *sp, FTSENT *head, int nitems)
Definition: fts.c:1149
struct poptOption rpmioDigestPoptTable[]
Digest options using popt.
Definition: poptIO.c:151
FTS * Fts_open(char *const *argv, int options, int(*compar)(const FTSENT **, const FTSENT **))
Create a handle for file hierarchy traversal.
Definition: fts.c:207
#define headerFree(_h)
Definition: rpmtag.h:870
#define FNM_PATHNAME
Definition: fnmatch.h:41
static KEY keylist[]
Definition: rpmmtree.c:336
void * fts_pointer
Definition: fts.h:111
#define __getlogin
Definition: rpmmtree.c:3648
ARGint_t vals
Definition: argv.h:18
rpmtime_t rpmswAdd(rpmop to, rpmop from)
Sum statistic counters.
Definition: rpmsw.c:280
int pathname
Definition: rpmmtree.c:282
static PyObject *int type
Definition: rpmmi-py.c:151
return tagname
Definition: rpmrepo.c:1053
static struct rpmfts_s __rpmfts
Definition: rpmmtree.c:271
#define KF_ISSET(_keys, _KEY)
#define EXIT_FAILURE
unsigned nvals
Definition: argv.h:17
int Symlink(const char *oldpath, const char *newpath)
symlink(3) clone.
Definition: rpmrpc.c:2120
char * xstrdup(const char *str)
Definition: rpmmalloc.c:321
const char * name
Definition: hdrfmt.c:4425
const char * string
Definition: fnmatch.c:281
FTSENT * Fts_children(FTS *sp, int instr)
Return list of children of the current node.
Definition: fts.c:705
FD_t Fopen(const char *path, const char *_fmode)
fopen(3) clone.
Definition: rpmio.c:2831
Definition: db3.c:181
u_short fts_instr
Definition: fts.h:153
#define RPM_LIST_INIT(head)
Definition: rpmmtree.c:76
char * rpmGetPath(const char *path,...)
Return (malloc&#39;ed) expanded, canonicalized, file path.
Definition: macro.c:3371
#define __progname
Definition: system.h:363
ARGI_t algos
Definition: rpmmtree.c:150
int Utimes(const char *path, const struct timeval *times)
utimes(2) clone.
Structure(s) used for file info tag sets.
static char *size_t nb
fgets(3) analogue that reads \ continuations.
Definition: macro.c:409
#define FTS_ROOTLEVEL
Definition: fts.h:126
struct _node NODE
#define VIS_OCTAL
Definition: rpmmtree.c:557
struct rpmts_s * rpmts
The RPM Transaction Set.
Definition: rpmtypes.h:14
int rc
Definition: poptALL.c:670
#define S_META1
Definition: rpmmtree.c:790
static struct poptOption optionsTable[]
Definition: rpmqv.c:148
static const char copyright[]
Definition: rpmmtree.c:35
rpmts rpmtsFree(rpmts ts)
Destroy transaction set, closing the database as well.
The Header data structure.
int main(int argc, const char **argv, char **envp)
Definition: rpmqv.c:433
char * gidToGname(gid_t gid)
Definition: ugid.c:171
int(* fts_stat)(const char *path, struct stat *st)
Definition: fts.h:80
#define UNVIS_VALID
Definition: rpmmtree.c:578
enum mtreeKeys_e keys
Definition: rpmmtree.c:180
struct poptOption rpmioFtsPoptTable[]
Definition: poptIO.c:529
#define F_LINK
Definition: rpmmtree.c:163
gid_t * g
Definition: rpmmtree.c:202
static void mtreeMiss(rpmfts fts, NODE *p, char *tail)
DIR *(* fts_opendir)(const char *path)
Definition: fts.h:71
size_t maxg
Definition: rpmmtree.c:200
#define FTS_SL
Definition: fts.h:140
#define MAXPATHLEN
int Stat(const char *path, struct stat *st)
stat(2) clone.
Definition: rpmrpc.c:1361
static const uint32_t crctab[]
Definition: rpmmtree.c:453
struct _node * prev
Definition: rpmmtree.c:147
static FTSENT * fts_alloc(FTS *sp, const char *name, int namelen)
Definition: fts.c:1185
ARGV_t paths
Definition: rpmmtree.c:179
fts m
Definition: rpmmtree.c:3827
#define S_ISLNK(mode)
Definition: system.h:651
u_short fts_flags
Definition: fts.h:147
NODE * root
Definition: rpmmtree.c:177
int errno
#define KEYDEFAULT
Definition: rpmmtree.c:262
u_short fts_namelen
Definition: fts.h:119
static struct rpmop_s dc_readops
Definition: rpmmtree.c:292
#define FTS_NS
Definition: fts.h:138
long int value
Definition: rpmds.c:2712
#define MAGIC
rpmfi rpmfiFree(rpmfi fi)
Destroy a file info set.
short fts_level
Definition: fts.h:127
s fts
Definition: rpmfts-py.c:99
const char * rpmfiFN(rpmfi fi)
Return current file name from file info set.
Definition: rpmfi.c:163
fts paths
Definition: rpmmtree.c:3822
else
Definition: signature.c:602
#define S_CTRL
Definition: rpmmtree.c:791
rpmfi rpmfiNew(const void *_ts, Header h, rpmTag tagN, int flags)
Create and load a file info set.
Definition: rpmfi.c:1403
int(* fts_closedir)(DIR *dir)
Definition: fts.h:77
int ac
Definition: rpmgrep.c:1431
#define S_ISSOCK(mode)
Definition: system.h:655
Header h
Definition: spec.c:739
static u_short fts_stat(FTS *sp, FTSENT *p, int follow)
Definition: fts.c:1069
unsigned val
Definition: rpmmtree.c:329
#define S_START
Definition: rpmmtree.c:788
s compare
Definition: rpmfts-py.c:96
argv
Definition: rpmmtree.c:3679
struct rpmfts_s * rpmfts
Definition: rpmmtree.c:115
int rpmfiFC(rpmfi fi)
Return file count from file info set.
Definition: rpmfi.c:87
static PyObject *char * mode
Definition: rpmfd-py.c:115
static struct rpmop_s dc_digestops
Definition: rpmmtree.c:295
char name[1]
Definition: rpmmtree.c:166
int Chown(const char *path, uid_t owner, gid_t group)
chown(2) clone.
Definition: rpmrpc.c:1674
enum rpmRC_e rpmRC
RPM return codes.
Definition: signature.c:616
#define F_FILE
Definition: rpmmtree.c:162
goto exit
Definition: db3.c:1903
static int mtreeCWalk(rpmfts fts)
memset(_r, 0, sizeof(*_r))
u_short fts_info
Definition: fts.h:143
#define FTS_D
Definition: fts.h:129
size_t clen
Definition: rpmds.c:2711
#define fdGetFILE(_fd)
Definition: rpmio.c:157
rpmts rpmtsCreate(void)
Create an empty transaction set.
Definition: rpmts.c:1468
#define _KFB(n)
Definition: rpmmtree.c:89
#define FTS_NOINSTR
Definition: fts.h:151
const char * Fstrerror(FD_t fd)
strerror(3) clone.
Definition: rpmio.c:2399
void * xcalloc(size_t nmemb, size_t size)
Definition: rpmmalloc.c:300
assert(key->size==sizeof(hdrNum))
* needvaluep
Definition: rpmmtree.c:390
#define F_DIR
Definition: rpmmtree.c:160
char * p
Definition: macro.c:413
int Rmdir(const char *path)
rmdir(2) clone.
Definition: rpmrpc.c:141
size_t nr
Definition: rpmmtree.c:526
#define UNVIS_SYNBAD
Definition: rpmmtree.c:581
size_t maxm
Definition: rpmmtree.c:203
struct _node * next
Definition: rpmmtree.c:147
#define F_SOCK
Definition: rpmmtree.c:164
void mtree_error(const char *fmt,...)
Definition: rpmmtree.c:309
#define ALIGNBYTES
Definition: fts.c:157
fprintf(stderr,"--> %s(%p,%p,%p) sig %p sigp %p\n", __FUNCTION__, dig, t, rsactx, sig, sigp)
#define N_(Text)
Definition: system.h:531
void rpmswPrint(const char *name, rpmop op, FILE *fp)
Print operation statistics.
Definition: rpmsw.c:304
int gnameToGid(const char *thisGname, gid_t *gid)
Definition: ugid.c:71
Definition: rpmmtree.c:326
#define S_OCTAL3
Definition: rpmmtree.c:793
static void
Definition: rpmmtree.c:306
static int strvis(char *dst, const char *src, int flag)
Definition: rpmmtree.c:715
char * fts_path
Definition: fts.h:63
struct stat sb
Definition: rpmmtree.c:148
int Mkdir(const char *path, mode_t mode)
mkdir(2) clone.
Definition: rpmrpc.c:73
#define FTS_COMFOLLOW
Definition: fts.h:87
uint32_t flags
Definition: rpmmtree.c:331
#define S_GROUND
Definition: rpmmtree.c:787
#define FF(a, b, c, d)
char * tbuf
Definition: macro.c:668
static rpmfts _rpmfts
Definition: rpmmtree.c:273
ARGV_t argvFree(ARGV_t argv)
Destroy an argv array.
Definition: argv.c:44
static NODE * mtreeSpec(rpmfts fts, FILE *fp)
#define F_BLOCK
Definition: rpmmtree.c:158
uint8_t type
Definition: rpmmtree.c:157
void rpmswExit(op, 0)
rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char *fn, Header *hdrp)
Return package header from file handle, verifying digests/signatures.
Definition: package.c:83
const char * glob
Definition: rpmmtree.c:281
#define MATCH(g, n)
#define COMPUTE(var, ch)
Definition: rpmmtree.c:522
#define FTS_DOT
Definition: fts.h:133
FTS * t
Definition: rpmmtree.c:170
static struct @9 excludes
static int mtreeVWalk(rpmfts fts)
char * fts_path
Definition: fts.h:115
The FD_t File Handle data structure.
const char * rpmGenPath(const char *urlroot, const char *urlmdir, const char *urlfile)
Merge 3 args into path, any or all of which may be a url.
Definition: macro.c:3417
return k val
Definition: rpmmtree.c:401
int sb_is_valid
Definition: rpmmtree.c:173
int argvAdd(ARGV_t *argvp, ARGstr_t val)
Add a string to an argv array.
Definition: argv.c:199
int fts_options
Definition: fts.h:99
ARGI_t algos
Definition: rpmmtree.c:182
Definition: fts.h:54
static int unvis(char *cp, char c, int *astate, int flag)
Definition: rpmmtree.c:803
size_t maxu
Definition: rpmmtree.c:206
#define NEEDVALUE
Definition: rpmmtree.c:330
int rpmioFtsOpts
Definition: poptIO.c:526
FTSENT * Fts_read(FTS *sp)
Return next node in the file hierarchy traversal.
Definition: fts.c:467
char * rpmExpand(const char *arg,...)
Return (malloc&#39;ed) concatenated macro expansion(s).
Definition: macro.c:3178
size_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd)
fread(3) clone.
Definition: rpmio.c:2410
int _fts_debug
Definition: fts.c:165
#define lchown
Definition: system.h:682
static unsigned
Definition: rpmmtree.c:386
#define dirent
Definition: system.h:245
rpmRC rpmrc
Definition: rpmts-py.c:924
* op
Definition: rpmps-py.c:266
static int strunvis(char *dst, const char *src)
Definition: rpmmtree.c:956
#define RPM_LIST_INSERT_HEAD(head, elm, field)
Definition: rpmmtree.c:78
uint32_t crc_total
Definition: rpmmtree.c:174
node fd
Definition: rpmfd-py.c:124
#define MISMATCHEXIT
Definition: rpmmtree.c:266
#define MAXLINELEN
int Fclose(FD_t fd)
fclose(3) clone.
Definition: rpmio.c:2532
spectags st
Definition: spec.c:741
#define TIMESPEC_TO_TIMEVAL(tv, ts)
Definition: system.h:120
#define RPM_LIST_ENTRY(type)
Definition: rpmmtree.c:68
int j
Definition: spec.c:743
char * n
Definition: macro.c:744
Cumulative statistics for an operation.
Definition: rpmsw.h:39
static struct rpmop_s dc_totalops
Definition: rpmmtree.c:289
#define FTS_DNR
Definition: fts.h:132
char * fts_accpath
Definition: fts.h:113
int rpmfiNext(rpmfi fi)
Return next file iterator index.
Definition: rpmfi.c:526
#define isvisible(c)
Definition: rpmmtree.c:605
fts keys
Definition: rpmmtree.c:3666
int Fts_set(FTS *sp, FTSENT *p, int instr)
Modify the traversal for a file set member.
Definition: fts.c:688
#define S_META
Definition: rpmmtree.c:789
optCon
Definition: poptALL.c:604
int fnmatch(char *__pattern, char *__name, int __flags)
char fts_name[1]
Definition: fts.h:157
#define S_IFSOCK
Definition: system.h:647
static const char *char c
Return text between pl and matching pr characters.
Definition: macro.c:470
#define UNVIS_VALIDPUSH
Definition: rpmmtree.c:579
int Readlink(const char *path, char *buf, size_t bufsiz)
readlink(2) clone.
Definition: rpmrpc.c:2154
#define FTS_INIT
Definition: fts.h:137
const char * fullpath
Definition: rpmmtree.c:190
void * rpmfiOpendir(rpmfi fi, const char *name)
Return directory stream onto file info set.
Definition: rpmfi.c:1834
#define VIS_WHITE
Definition: rpmmtree.c:567
int Ferror(FD_t fd)
ferror(3) clone.
Definition: rpmio.c:2942
#define L(CS)
Definition: fnmatch.c:161
rpmfi rpmfiInit(rpmfi fi, int fx)
Initialize file iterator index.
Definition: rpmfi.c:548
return strcmp(ame->name, bme->name)
const char * s
Definition: poptALL.c:734
struct _node * child
Definition: rpmmtree.c:146
char * t
Definition: rpmds.c:2716
urltype urlPath(const char *url, const char **pathp)
Return path component of URL.
Definition: url.c:430
#define UNVIS_NOCHAR
Definition: rpmmtree.c:580
struct key_s KEY
#define FTS_NOCHDIR
Definition: fts.h:89
#define VIS_NOSLASH
Definition: rpmmtree.c:573
Definition: fts.h:102
static int vsnprintf(char *buf, int nb, const char *fmt, va_list ap)
Definition: rpmps.c:212
#define LABEL
FTSENT * p
Definition: rpmmtree.c:171
char * path
Definition: poptALL.c:744
uid_t * u
Definition: rpmmtree.c:208
struct stat * fts_statp
Definition: fts.h:156
static enum mtreeFlags_e mtreeFlags
Definition: rpmmtree.c:276
#define FM(a, b, c, d)
if(__progname==NULL)
Definition: poptALL.c:683
int fts_errno
Definition: fts.h:116
#define UNVIS_END
Definition: rpmmtree.c:587
int(* fts_lstat)(const char *path, struct stat *st)
Definition: fts.h:83
#define FS(a, b, c, d)
char * uidToUname(uid_t uid)
Definition: ugid.c:135
Structures and prototypes used for an &quot;rpmts&quot; transaction set.
struct stat sb
Definition: rpmmtree.c:172
#define ALIGN(p)
Definition: fts.c:161
char * slink
Definition: rpmmtree.c:149
#define RPM_LIST_FOREACH(var, head, field)
Definition: rpmmtree.c:83
static int chkSuffix(const char *fn, const char *suffix)
Check string for a suffix.
Definition: depends.c:313
FILE * spec2
Definition: rpmmtree.c:187
struct dirent *(* fts_readdir)(DIR *dir)
Definition: fts.h:74
#define MBITS
Definition: rpmmtree.c:268
int argiAdd(ARGI_t *argip, int ix, int val)
Add an int to an argi array.
Definition: argv.c:178
ARGV_t digests
Definition: rpmmtree.c:151
#define F_FIFO
Definition: rpmmtree.c:161
static char * vis(char *dst, int c, int flag, int nextc)
Definition: rpmmtree.c:618
mode_t * m
Definition: rpmmtree.c:205
#define FTS_PHYSICAL
Definition: fts.h:91
unsigned lineno
Definition: rpmmtree.c:175
#define FTS_LOGICAL
Definition: fts.h:88
FILE * f
Definition: macro.c:411
#define F_CHAR
Definition: rpmmtree.c:159
#define _FTSCALLOC(_p, _n)
int Fts_close(FTS *sp)
Destroy a file hierarchy traversal handle.
Definition: fts.c:380
return NULL
Definition: poptALL.c:613
#define RPM_LIST_HEAD(name, type)
Definition: rpmmtree.c:66
int _rpmsw_stats
Definition: rpmsw.c:20
static int mtreeVSpec(rpmfts fts)
static int indent
Definition: rpmgi.c:47
int Chmod(const char *path, mode_t mode)
chmod(2) clone.
Definition: rpmrpc.c:1765
k
Definition: rpmmtree.c:394
static void fdFiniDigest(FD_t fd, pgpHashAlgo hashalgo, void *datap, size_t *lenp, int asAscii)
static const char * name
char * buf
Parse (and execute) macro undefinition.
Definition: macro.c:703
#define _(Text)
Definition: system.h:29
mtreeKeys_e
Bit field enum for mtree keys.
Definition: rpmmtree.c:121
char * b
Definition: macro.c:746
static rpmop fdstat_op(FD_t fd, fdOpX opx)
int
Save source and expand field into target.
Definition: rpmds.c:2709
struct _ftsent * fts_cur
Definition: fts.h:56
struct rpmfi_s * rpmfi
File info tag sets from a header, so that a header can be discarded early.
Definition: rpmfi.h:83
#define xmalloc
Definition: system.h:32
#define FTS_DP
Definition: fts.h:134
tagClean(NULL)
ARGstr_t * ARGV_t
Definition: argv.h:12
mtreeFlags_e
Bit field enum for mtree CLI options.
Definition: rpmmtree.c:95
struct poptOption rpmioAllPoptTable[]
Definition: poptIO.c:551
int rpmfiStat(rpmfi fi, const char *path, struct stat *st)
Return lstat(2) data of path from file info set.
Definition: rpmfi.c:1803
void rpmswEnter(op, 0)
int unameToUid(const char *thisUname, uid_t *uid)
Definition: ugid.c:16
poptContext rpmioFini(poptContext optCon)
Definition: poptIO.c:719
poptContext
Definition: poptALL.c:525
struct exclude::@10 link
offset
Definition: rpmdb-py.c:185
* cval
Definition: rpmmtree.c:547
static void * _free(const void *p)
Wrapper to free(3), hides const compilation noise, permit NULL, return NULL.
Definition: rpmiotypes.h:647
Definition: argv.h:16
#define _MFB(n)
Definition: rpmmtree.c:90
FILE * spec1
Definition: rpmmtree.c:185
int i
Definition: spec.c:743
#define MF_ISSET(_FLAG)
Definition: rpmmtree.c:260
strncpy(sbuf, f, flen)
#define FTS_NOSTAT
Definition: fts.h:90
In Memoriam: Steve Taylor staylor@redhat.com was here, now he&#39;s not.
long fts_number
Definition: fts.h:109
char * Realpath(const char *path, char *resolved_path)
realpath(3) clone.
Definition: rpmrpc.c:2330
struct _node * parent
Definition: rpmmtree.c:146
char * path
Definition: rpmmtree.c:192
#define FTS_ERR
Definition: fts.h:135
struct _ftsent * fts_link
Definition: fts.h:108
#define FTS_ROOTPARENTLEVEL
Definition: fts.h:125
uint32_t cksum
Definition: rpmmtree.c:153
size_t fn
Definition: macro.c:1698
#define isoctal(c)
Definition: rpmmtree.c:604
static int nitems
Definition: rpmcache.c:81
#define VIS_CSTYLE
Definition: rpmmtree.c:558
int len
Definition: rpmdb-py.c:119
fts fullpath
Definition: rpmmtree.c:3829
enum mtreeKeys_e flags
Definition: rpmmtree.c:155
const unsigned char * digest
Definition: rpmfi-py.c:247
int ftsoptions
Definition: rpmmtree.c:193
int Unlink(const char *path)
unlink(2) clone.
Definition: rpmrpc.c:397
#define FTS_SKIP
Definition: fts.h:152