rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

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